Information Security Study

240117 프로세스 잡 관리(bg, 잡/프로세스 종료, kill, killall), 운영체제, 셸스크립트(개요, 셔뱅, source) 본문

네트워크 캠퍼스/Linux

240117 프로세스 잡 관리(bg, 잡/프로세스 종료, kill, killall), 운영체제, 셸스크립트(개요, 셔뱅, source)

gayeon_ 2024. 1. 17. 15:53

bg명령어를 활용해 잡을 백그라운드로 전환하기

ctrl + z로 잡을 중지시키면 해당 잡이 동작을 멈추기 때문에

아무리 기다려도 자동으로 종료되지 않는다.

 

이런 경우는 잡을 종료하지는 않은 채 셸로 돌리는게 낫다.

 

포그라운드가 나의 제어권에 잡을 두는것이라면

백그라운드는 내가 조작할 수는 없지만 돌아가게 한다.

 

$ bg %번호

를 이용해 전환시킬 수 있다.

 

먼저 sleep 5(5초간 동작 중지) 와 hello world를 콘솔에 찍는 echo를 파이프로 연결하고

즉시 종료시키면 아래와 같이 잡이 멈춰지게 된다.

 

포그라운드로 실행하면 sleep이 끝날 때까지(명령이 끝날 때까지) 아무 입력도 못하다가 

모든 작업이 종료되어야 제어권이 돌아온다.

 

 

 

bg 입력 후 60초 기다리지 않고도 바로 명령어 입력 가능

반면 bg를 이용해 백그라운드로 살려내는 경우는 sleep 60이 끝나지 않고도

제어권을 되찾아오고, 잡은 백그라운드에서 알아서 60초가 될때까지 돌아가는

병렬적인 작업을 수행하는 것도 가능하다.

 

 

jobs를 입력하면 bg로 입력한 작업이 수행 중인지 완료됐는지 확인할 수 있다.

 

 

잡과 프로세스의 종료

윈도우 pc나 맥에 프로그램 강제 종료가 있듯(정확히는 프로세스 강제 종료)

리눅스에서도 잡과 프로세스를 강제로 종료할 수 있는 명령어가 있다.

 

 

잡의 종료

포그라운드에서 실행중인 잡은 ctrl + c로 종료할 수 있고

 

백그라운드에서 실행중인 잡은

$ kill %잡번호

로 종료할 수 있다.

 

 

 

프로세스의 종료

프로세스의 경우는 ps를 조회해 나온 pid를 이용해 종료한다.

 

$ kill pid번호

 

잡 종료 명령어와 같지만 잡을 대상으로 할 때는 % 가 추가로 붙고

프로세스 대상인 경우는 번호만 입력하면 된다.

 

단 프로세스는 기본적으로 현재 로그인중인 유저가 실행한 프로세스만 삭제할 수 있고

예외적으로 root계정만 다른 사용자의 프로세스를 강제로 종료할 수 있다.

 

sleep 60 명령 실행 후 중지한 다음 ps를 확인하면 sleep이 올라온 걸 볼 수 있다.

PID 확인 후 bg로 실행한 뒤 

 

kill pid를 입력하면 프로세스가 종료되었다.

 

 

리눅스에서 사용가능한 신호들과 kill의 실제적 동작 원리

kill은 사실 죽이는 명령어라기 보다는 프로세스에 특정한 신호를 보내는 명령어다.

예) 설정파일 다시 읽기, 프로세스 중지, 재시작, 종료 등

 

아래와 같이 신호에는 고유 이름과 번호가 있다.

SIGHUP, 1

: 터미널 제어시 또는 제어 프로세스의 종료시 감지되는 행업 신호

 

SIGUNT, 2

: 키보드에서 보내는 인터럽트 신호

 

SIGQUIT, 3 

: 키보드에서 보낸 종료 신호

 

SIGABRT, 6 

: abort(3)로 받은 신호 무시

 

SIGKILL, 9 

: kill 신호

 

SIGTERM, 15

: 중단 신호

 

SIGCONT, 19, 18, 25

: 중지된 프로세스 재개

 

SIGSTOP, 17, 19, 23 

: 프로세스 중지

 

pid가 10023인 프로세스를 중지시킬 경우

$ kill 10023

$ kill -15 10023

$ kill -SIGKILL 10023

와 같이 사용할 수 있다.

 

또한 1833번 pid의 설정파일을 다시 읽게 하고 싶은 경우는

# kill -1 1833

# systemctl reload httpd

위와 같이 1번 신호를 준 다음 명령어를 쓰면 된다.

 

 

killall을 이용해 프로세스명으로 종료시키기

만약 특정 pid가 아닌 프로세스의 이름만으로 종료를 시키고싶다면 9번 명령을 내리면 된다.

 

$ killall -9 프로세스명

 

위와 같이 사용하면 특정 pid가 아닌 같은 이름을 가진 모든 프로세스를 동시에 종료한다.

 


1장

시스템 구성 정보 확인하기

리눅스 명령어만으로도 하드웨어의 상태나 시스템 구성 정보를 확인할 수 있다.

 

다음은 명령어와 역할을 정리한 표이다.

명령어 역할 책에서 수행한 것
uname 커널 버전 관련 정보 조회 커널 버전 및 운영체제 등 확인
dmesg(grafana 로그 체킹에 사용한 사례가 있음, 백로그 부분 메세지가 뜨는듯한데 아무튼 체크 가능) 디버깅 메세지 체크 부팅시 사용한 파라미터, 메모리 인식 등 확인
dmidecode bios, system, processor 서버 제조사, 장비 모델명, 인식 메모리 등등
dmidecode(누마 아키텍처의 등장으로 건너뛰어도 무방?) memory 메모리 키워드 영역 조회
df(파티션 나눌때 씀) 디스크 파티션 타입 정보 조회 디스크에서 인식중인 것들, 디스크 위치 등
smartctl 디스크의 물리적 정보 조회 물리적 디스크 정보 확인
lspci 네트워크 정보 조회 네트워크 지원 속도, 현재 연결 속도, 정상 연결 여부 등
ethtool 네트워크 카드의 세팅 정보 조회 Ring Buffer, 네트워크 카드 최적화 옵션 등

 

 

2장

top명령어를 통해 프로세스 정보 조회하기

  • top 명령어를 통해 시스템 정보를 확인할 수 있다.
  • 확인 가능한 정보는 구동된 시간, 로그인된 사용자 숫자, 실행중인 프로세스의 개수, cpu, mem, swap 메모리의 사용량 등
  • 우선순위, 조정 우선순위, 가상배정메모리, 실제배정메모리, 공통사용영역 등

VIRT, RES, SHR이란?

  • VIRT, RES, SHR은 현재 프로세스가 사용중인 메모리와 관련된 값이다.

 

VIRT는 위의 VIRT + RES + SHR를 모두 포함한 영역으로 각 Task에 할당된 메모리 전체이다.

RES는 할당된 메모리 중 실제로 사용되고있는 물리 메모리의 양이다.

SHR은 다른 프로세스와 공유중인 메모리의 양이다.

 

 

가상 메모리 배정과 memory commit

메모리는 동적으로 배정하게 되는데 물리적으로 아예 처음부터 할당을 해버리면 비효율적이기 때문에

프로세스의 요청에 따라 가상으로 최대 얼마까지 배분하겠다는 상한선만 정해놓고

실제로 메모리에 쓰기 작업이 들어가야 진짜 할당이 되기 시작한다.

 

이렇게 메모리에 쓰기 작업이 들어가야 진짜 할당이 되도록 하는 것을 memory commit이라고 한다.

 

그럼 이론적으로 VIRT와 같이 개념적으로만 할당하는 메모리 공간은 무한대로 할당할 수 있는가?

-> 이는 그렇게 할 수도, 막을 수도 있다고 할 수 있다.

 

 

가상 메모리 배정의 이유

fork() 프로세스 콜을 사용하면 커널은 현재 실행중인 프로세스와 똑같은 프로세스를 하나 더 할당한다.

 

이 과정에서 만약 memory commit이 아닌 형태로 프로세스를 분리하게 된다면

이미 할당된 영역 대다수가 쓸모 없는 영역이 될 것이다.

 

따라서 이를 방지하기 위해 실질적으로 사용하는 영역만 배정한다.

복사된 메모리 영역에 실제 쓰기 작업이 발생한 후에야 실질적으로 메모리 할당을 시작하는데

이를 COW(Copy On Write)라고 한다.

 

 

프로세스의 상태 변화

SHR앞의 S항목으로 프로세스의 상태를 확인할 수 있다.

 

D : 디스크 혹은 네트워크 I/O를 위해 대기하는 프로세스. 대기시 Wait Queue에 있음. uninterruotible sleep

요청이 오기전까지는 아무것도 할 수 없는 상태

 

R : 실행중인 프로세스. 실제로 자원을 소모하고 있는 중임. run

 

S : 요청한 리소스를 즉시 사용할 수 있는 상태의 프로세스. sleeping

 

T : strace등으로 프로세스의 시스템 콜을 추적하고 있는 상태이다. traced or stopped상태라고 부른다.

 

Z : 부모 프로세스가 죽은 자식 프로세스로 Zombie 프로세스라고 한다.

 

프로세스가 종료되었을때 종료되었음을 알려줄 수 있는 곳이 없는 경우가 바로 좀비 프로세스가 생기는 대표적 예시.

 

 

프로세스의 우선순위

PR과 NI항목을 통해 조절한다.

 

CPU마다 Run Queue라는것이 존재해서 우선순위별로 프로세스를 연결한다.

유휴 프로세스를 실행하거나, 특정 프로세스가 양보해 여유가 생기면 Run Queue에서

다음 프로세스를 꺼내서 디스패처가 실행하게 되는데 이 때 NI(nice value)를 호출하면 값이 낮아진다.

 

이는 순위가 높아짐을(10위 → 1위면 숫자값은 낮아지고, 우선순위는 올라간 상태) 의미한다.

 

NI를 호출해 PR을 올리면 점점 호출 우선순위가 올라가 실행될 확률이 높아진다.

 

 

3장

load average란?

R/D 상태인 프로세스의 1, 5, 15분마다의 평균 개수이다.

  • 얼마나 많은 프로세스가 실행중이거나 혹은 대기 중인지를 나타내는 수치이다.
  • 값이 높을수록 실행/대기중인 프로세스가 많음을, 낮을수록 적음을 의미한다.
  • 하나의 코어에 2개의 프로세스가 실행 중인 경우와 두 개의 코어에 2개의 프로세스가 실행 중인 경우는 load average값은 같으나(R인 요소 2개씩) 가용자원측면에서는 다르게 인식될 수 있다.

 

 

Load Average 계산 과정

uptime 명령어로 계산을 할 수 있다.

 

 

CPU Bound vs I/O Bound

부하를 일으키는 프로세스는 CPU Bound와 I/O Bound로 나뉜다.

 

CPU Bound는 CPU의 자원을 많이 필요로 하는 프로세스이고

I/O Bound는 많은 I/O 자원을 필요로 하는 프로세스이다.

 

그리고 이 부하의 종류에 따라서 처리하는 방법론이 달라지기 때문에 구분하는 것이 중요하다.

 

 

vmstat으로 부하 원인 확인하기

vmstat을 이용해 부하 원인을 확인해보면 r, b값이 중요하다.

 

r은 현재 실행되기를 기다리거나 실행되고 있는 프로세스의 개수이고

b는 I/O를 위해 대기열에 있는 프로세스의 개수이다.

 

r이 높다면 cpu bound일 확률이 높고

b가 높다면 i/o bound일 확률이 높다.

 

 

Load Average가 시스템에 끼치는 영향

Load Average값이 같다고 해도 실제로 시스템에 미치는 영향은 부하의 원인에 따라 달라진다.

 

같은 Load Average라고 해도 cpu자원에 대한 경합이나 I/o자원에 대한 경합 정도에 따라서

딜레이되는 시간이 다를 수 있다.

 

 

OS버전과 Load Average

cent os 6.5버전과 7.2버전에서 동시에 같은 프로세스를 돌렸을 때

6.5버전에서 커널 버그로 인해 LoadAverage가 정상적으로 표시되지 않는 바람에

극적으로 차이가 나는 사례가 있었다.

실제 운영 중에도 커널차이로 인해서(버그든 실제든) 차이가 발생할 수 있다.


셸 스크립트란?

리눅스 사용시 일련의 명령어를 반복적으로 실행하는 경우가 있다.

이 경우 매번 명령어를 전체적으로 다 적는것이 귀찮기 때문에

실행할 명령어를 미리 파일에 작성해놓고

“해당 파일에 작성된 내역을 실행해라” 라는 명령을 내리면

파일명만 입력하고도 사전에 작성된 긴 명령어를 매크로처럼 실행할 수 있다.

 

 

셸 스크립트의 종류

대부분의 명령어는 셸별로 비슷하기 때문에 호환이 대부분 되겠지만

간혹 특정 종류의 셸에서만 실행되는 스크립트가 포함된다면 문제가 될 수 있다.

되도록 범용적으로 작성하는 것이 좋다.

 

sh가 가장 예전부터 쓰이던 셸이니 sh 형식으로 작성해도 좋겠지만

요즘은 bash를 가장 많이 사용하므로 bash기반으로 연습할 것이다.

 

 

기본 작성하기

$ du -h ~ | tail -n 1

: 홈 디렉토리의 파일 사용량을 출력한다.

 

이 명령어는 옵션도 많고 파이프 문자도 사용하기 때문에 직접 타이핑하기 불편하다.

그러므로 셸 스크립트를 작성해서 짧게 실행해 볼 것이다.

 

 

$ vim homesize.sh

셸 스크립트 파일은 .sh 확장자를 가진다.

vim에디터에서 아래와 같은 두 줄을 작성한다.

 

첫 줄은 셔뱅이라고 부르는 행이다.

셔뱅은 #! 뒤에 오는 파일/경로를 실행매개로 간주하고 명령을 내린다는 의미를 갖고 있다.

즉, 어디에서 해당 파일을 실행해도(지금은 ~ 폴더 기준으로 실행중)

/bin/bash (bash의 경로) 를 통해 실행을 한다.

 

그리고 두 번째 줄부터가 명령어이다.

 

위와 같이 작성된 셸 스크립트는 아래와 같이 실행할 수 있다.

$ ./homesize.sh

.은 현재 경로를 나타내므로, 현재 경로 하위에 있는 homesize.sh를 실행하겠다는 의미이다.

./ 를 붙이지 않으면 아래와 같이 command not found가 나오니 주의해야 한다.

 

실행해보니 permission denied가 나와 chmod로 실행 권한을 주고 다시 실행해 볼 것이다.

 

 

파일 권한을 확인하니 ubuntu로는 읽고 쓸 수 밖에 없음을 알 수 있었다.

 

 

chmod로 homesize.sh에 대한 실행 권한을 부여한 뒤 다시 ls -l를 해서

권한이 변경되었는지 확인했다.

 

* r은 4, w는 2, x는 1이다.

 

 

실행 권한 추가 후 셸 스크립트를 실행하니 홈 디렉토리의 파일 사용량이 잘 출력되었다.

 

 

셔뱅 이외의 방법으로 셸스크립트 실행하기

source명령어를 이용해서도 셸스크립트를 실행할 수 있다.

 

$ source ./파일명.sh

: 위와 같은 형식으로 실행하며 셔뱅을 적어넣지 않아도 현재 실행한 셸을 이용해 스크립트를 실행한다.

또한 source를 대체할 수 있는 명령어로는 . 이 있다.

 

homesize2.sh을 만들고 위와 같이 셔뱅 없이 명령어만 작성한다.

 

 

저장 및 종료 후 source로 실행하니 셔뱅이 없어도 셸 스크립트가 실행되었다.

 

 

$ . ./파일명.sh

sh에서는 .으로 실행하는 것이 기본이었기 때문에 .도 여전히 많이 쓰인다.

그렇지만 가독성 문제로 source를 사용하는 경우가 더 많다.