부모 프로세스와 자식 프로세스
모든 프로세스는 그 프로세스를 생성한 다른 프로세스가 있다. 프로세스를 생성한 프로세스를 부모 프로세스(parent process), 부모 프로세스가 생성한 프로세스를 자식 프로세스(child process)라고 한다. 물론, 자식 프로세스도 프로세스를 만들면 새로 생성한 프로세스의 부모 프로세스가 된다.
init 프로세스
모든 프로세스에 부모 프로세스가 있다면 프로세스의 조상으로 거슬러 올라갔을 때 그 끝에는 무엇이 있을까요? 리눅스에는 모든 프로세스의 시조인 init 프로세스가 존재한다. init 프로세스는 리눅스 커널이 부팅하면서 만들어내는 최초 프로세스이다. 그래서 init 프로스는 부모 프로세스가 존재하지 않는다. init 프로세스의 부모는 리눅스 커널이라고 볼 수 있다. 하지만 리눅스 커널은 프로세스가 아니므로 init 프로세스의 부모 프로세스라고 할 수는 없다.
init 프로세스는 전통적으로 PID 1을 부여한다. 그리고 init 프로세스의 PPID(부모 프로세스의 PID)는 0으로 설정한다. PPID가 0인 프로세스는 커널이 직접 생성한 프로세스를 의미한다.
프로세스 종료
프로세스가 종료될 때 제대로 종료되려면 종료되는 프로세스와 종료되는 프로세스의 부모 프로세스 간 상호작용이 필요하다. 프로세스는 종료될 때 종료 상태를 남긴다. 프로세스가 남기는 종료 상태는 해당 프로세스의 부모 프로세스가 받아야 한다. 부모 프로세스는 자식 프로세스의 종료 상태를 받아 종료 상태에 따른 처리를 할 수 있다. 부모 프로세스가 자식 프로세의 종료 처리를 하면 자식 프로세스와 관련한 모든 정보가 삭제되며 완전히 종료된다. 이처럼 부모 프로세스는 자식 프로세의 종료에 중요한 역할을 한다.
부모 프로세스가 자식 프로세스의 종료 처리를 하려면 자식 프로세스의 종료 시점을 알아야 한다. 그런데 종료 시점을 정확하게 예측할 수는 없다. 다만, 자식 프로세스의 종료를 알아챌 수 있는 방법이 몇 가지 있다.
- 자식 프로세스가 종료될 때까지 무한히 대기하는 방법
가장 단순하고 확실하지만 부모 프로세스가 다른 작업을 할 수 없는 단점이 있다. - 부모 프로세스가 다른 작업을 하다가 주기적으로 자식 프로세스가 종료됐는지 확인하는 방법
그다지 좋은 방법은 아니다. 경우에 따라 자식 프로세스가 종료된 지 한참 후에 알게 될 수도 있다. - SIGCHLD 시그널을 수신했을 때 자식 프로세스를 종료 처리하는 방법
SIGCHLD는 자식 프로세스의 종료를 의미하는 시그널이다. 자식 프로세스가 종료되거나 중단되면 리눅스 커널이 부모 프로세스에 SIGCHLD 프로세스를 보낸다. 부모 프로세스는 다른 작업을 하다가 해당 시그널을 받으면 종료 처리를 한다. 부모 프로세스가 자식 프로세스를 위해 불필요한 동작을 하지 않아서 효율적이다.
프로세스 목록 확인하기
ps(process status) 명령어는 현재 실행 중인 프로세스에 대한 정보를 보여준다. 시스템에서 실행 중인 프로세스의 목록과 함께 프로세스 ID, 실행 시간, 메모리 사용량, 사용자 이름 등 다양한 정보를 제공한다. 이 정보들을 활용해 시스템의 현재 상태를 모니터링하고, 문제 있는 프로세스를 식별하거나 시스템 자원 현황을 확인할 수 있다.
ps 명령어의 형식은 다음과 같다.
$ ps [option]
ps 명령어의 주요 옵션
- -e : 시스템의 모든 프로세스를 표시한다.
- -f : 상세한 정보까지 보기 좋은 형태로 제공한다.
- -u 사용자 : 지정된 사용자의 프로세스를 보여준다.
- -p PID: 특정 PID를 가진 프로세스를 보여준다.
- -o 필드_코드 : 결과를 출력할 때 원하는 필드만 출력한다. 출력할 필드 코드가 여럿일 때는 쉼표로 구분한다.
- --sort 필드_코드 : 주어진 필드를 기준으로 프로세스를 정렬한다.
- --forest : 프로세스 목록을 트리 구조로 보여준다.
ps 주요 필드
- PID(Process ID) : 각 프로세스를 구별하는 고유 번호
- PPID(Parent Process ID) : 해당 프로세스를 생성한 부모 프로세스의 ID
- C(CPU Usage) : 프로세스의 CPU 사용량
- STIME(Start Time) : 프로세스가 시작된 시각
- TTY(Terminal Type) : 프로세스가 실행되는 터미널의 종류나 번호
- TIME(CPU Time) : 프로세스가 실행되면서 소비한 총 CPU 시간
- CMD(Command) : 프로세스를 시작한 명령 또는 실행 파일 이름
- STAT(Status) : 프로세스의 현재 상태로, R은 실행 상태, S는 대기 상태, Z는 좀비 상태를 의미함
- %MEM(Memory Usage) : 전체 시스템 메모리 대비 프로세스가 사용하는 메모리의 비율
- VSZ(Vitual Memory Size) : 프로세스에 할당된 전체 가상 메모리의 양
- RSS(Resident Set Size) : 프로세스가 실제로 사용하는 메모리의 양
프로세스 생성과 종료하기
셸에서 어떤 프로그램을 실행하면 프로세스가 생성된다. 프로그램이 모든 작업을 마치면 프로세스가 종료되면서 프로그램 실행도 종료된다. 프로세스가 종료되기 전에 강제로 종료하는 방법을 알아보자.
프로세스를 강제로 종료시키는 데는 명령어 kill을 사용한다. 정확히 말하면, kill 명령어는 대상 프로세스에 시그널을 전송한다. 전송하는 시그널에 따라 대상 프로세스가 종료될 수도 있고, 그렇지 않을 수도 있다. 수신하는 시그널에 따라 지정된 기능(예: 설정 파일 다시 읽기)을 실행하는 프로그램도 있다.
$ kill [option] PID
PID 부분에는 종료 명령을 보낼 프로세스의 PID를 입력한다. 옵션은 어떤 시그널을 보낼 것인지 결정한다. 옵션 부분에 시그널 이름이나 시그널 번호를 입력해 전송할 시그널의 종류를 설정할 수 있다. kill 명령어로 전송할 수 있는 시그널의 종류는 -l 옵션으로 확인할 수 있다.
$ killall [option] process_name
killall 명령어는 뒤에 넣은 프로세스 이름을 기준으로 하나 이상의 프로세스를 종료한다. kill 명령어는 한 프로세스에 시그널을 전송하지만, killall 명령어는 프로세스 이름과 일치하는 모든 프로세스에 시그널을 전송한다.
$ sleep [second]
sleep 명령어는 입력받은 시간 동안 잠자는 상태를 유지하다가 정해진 시간이 지나면 종료된다. 숫자만 입력하면 기본 단위인 초로 설정된다. sleep 명령어를 통해서 원하는 만큼 실행되는 프로세스를 간단하게 생성할 수 있다.
sleep 300을 실행한 터미널에서는 "Terminated"라는 메시지가 나오며 프로세스 실행이 종료되었다. 이번에는 시그널을 입력해서 프로세스를 종료시켜 보자. SIGKILL은 강제 종료를 의미하는, 가장 강력한 종료 시그널이다. SIGKILL 시그널은 옵션에서 -KILL로 표시한다.
$ kill -KILL [pid]
이번에는 "Terminated" 메시지가 아니라 "Killed" 메시지가 출력되면서 프로세스가 종료된 것을 확인할 수 있다. 이는 단순히 kill 명령을 내렸을 때와 달리 강제 종료됐음을 의미한다.
killall 명령어로도 프로세스를 종료해 보자. 이전과 같이 sleep 300 명령을 실행하고 다른 터미널에서 killall -TERM sleep이라고 입력하자. PID 대신 프로세스 이름인 sleep을 입력하는 부분에 유의하자. -TERM은 SIGTERM 시그널을 나타내는 옵션이다.
$ apt-get install psmisc
killall 명령어를 사용하기 위해서는 psmisc 패키지를 설치해야 한다. 위에 명령어를 사용해서 설치해 주자.
$ killall -TERM [process_name]
killall 명령어를 통해서 sleep이라는 이름을 가진 모든 프로세스를 종료했다. 여러 터미널을 띄워서 sleep을 실행한 뒤에 killall 명령어를 실행해 보면 모든 sleep 프로세스에 대해서 종료된 것을 확인할 수 있다.
함께 읽으면 좋은 글
'프로그래밍' 카테고리의 다른 글
127.0.0.1 vs 0.0.0.0 vs localhost 차이점 정리해보자 (0) | 2025.01.31 |
---|---|
SSH 키 생성을 위한 두 가지 방법 PuTTYgen vs OpenSSH (0) | 2025.01.17 |
더 나쁜 것이 더 좋습니다? Worse is better (0) | 2025.01.13 |
C# Web API 구조 - 비동기, 멀티스레드, 쓰레드 풀 (1) | 2025.01.03 |
도메인 주도 개발(Domain-Driven Development) 모델과 모델링 (1) | 2024.11.20 |
댓글