ps, systemd, top은 커널 프로그램일까?
아니다. 그냥 이름 탓이다. 다만 헷갈릴 여지는 충분하다고 생각한다. 그래서 이번 글에서는 유저 프로그램과 커널 프로그램의 차이, 그리고 유저 프로세스와 커널 스레드의 차이를 정리해보려고 한다.
/proc: 실체 없는 파일 시스템
리눅스 커널은 /proc이라는 가상 파일 시스템을 제공한다. ‘가상’인 이유는, 디스크에 실제로 저장된 파일이 아니라 커널이 자기 내부 자료구조를 파일처럼 보이게 만드는 시스템이기 때문이다.
/proc은 익히 알듯 디렉터리 형태다. 하지만 이 하위에는 파일 데이터가 저장되어 있는 게 아니라, 그 내용을 만들어내는 커널 함수가 연결되어 있다. 그래서 /proc/[pid]/stat 같은 파일을 열어서 읽으면, 그 순간 대응하는 커널 함수가 호출되어 현재 상태를 즉석에서 만들어 응답한다.
ps와 top은 그냥 이 /proc 아래 파일들을 읽고 파싱해서 사람이 보기 좋은 형태로 출력해주는 평범한 유저스페이스 프로그램이다.
systemd, sshd, cron이 헷갈리는 진짜 이유: 데몬
그렇다면 systemd, sshd, cron 등은 어떨까? 이것들 역시 유저스페이스 프로그램이다. 다만 이름 말고도 헷갈릴 이유가 하나 더 있는데, 바로 **데몬(daemon)**이라는 점이다.
ps나 top은 한 번 실행하면 결과를 출력하고 끝난다. 반면 이런 데몬들은 계속 떠 있다. 그래서 “어? 이건 자동으로 도는 커널 프로세스인가?”라는 착각을 하기 쉽다.
가장 헷갈리기 쉬운 건 systemd다. PID가 항상 1번이라 뭔가 특별해 보이고, 모든 유저 프로세스의 조상이기도 하다. 하지만 이건 “커널 프로세스 = 시스템과 관련된 프로세스”라는 착각에서 오는 오해일 뿐, systemd도 결국 유저스페이스 프로그램이다.
그렇다면 ‘커널 프로세스’란 무엇인가
용어부터 짚어보자. 리눅스에서 프로세스란 독립적인 메모리 공간(주소 공간)을 가진 실행 단위를 뜻한다. 그런데 커널이 독립적인 메모리 공간을 따로 가질까? 아니다. 커널은 하나의 주소 공간을 공유한다. 그래서 실무에서는 “커널 프로세스”라는 표현 대신 커널 스레드라는 용어를 쓴다. 여러 실행 흐름이 하나의 주소 공간을 공유한다는 점에서, 한 프로세스 안의 여러 스레드가 주소 공간을 공유하는 것과 성격이 같기 때문이다.
그럼 “커널 프로그램”이라는 표현은 어떨까? 이것도 마땅치 않다. 보통 프로그램은 실행될 때 메모리에 적재되어 프로세스로 뜨지만, 커널은 이미 부팅 시점부터 메모리에 올라가 있다. (시스템의 뼈대가 되는 부분이라 스왑 대상이 될 수도 없다.) 즉 커널은 ‘실행되는 프로그램’이라기보다 커널 스레드들이 돌아가는 이미 상주한 환경에 가깝다.
정리하면, 커널 안에서 실행 흐름의 단위로 존재하는 것은 프로세스가 아니라 커널 스레드다.
커널 스레드와 유저 프로세스, 어떻게 구분할까
가장 쉬운 방법은 실제로 매핑된 메모리 공간이 있는지 보는 것이다. 커널 스레드는 위에서 설명한 이유로 독립된 유저 주소 공간이 없다.
또는 프로세스의 조상을 따라가 보는 방법도 있다.
커널 (부팅 시 이미 메모리에 상주, 프로세스 아님)
├─ kthreadd (PID 2) ← 모든 커널 스레드가 여기서 파생
│ ├─ ksoftirqd/0
│ ├─ kworker/0:1
│ └─ ...
│
└─ systemd (PID 1) ← 모든 유저 프로세스가 여기서 파생
├─ sshd
├─ bash
│ └─ ps (지금 이 명령어)
├─ NetworkManager
└─ ...
엄밀히 말하면 위 트리의 진짜 루트는 PID 0(swapper/idle)이고, kthreadd와 systemd는 둘 다 그 자식이다. 다만 PID 0은
ps/pstree출력에 보통 나타나지 않아서, 위 다이어그램에서는 편의상 “커널”로 뭉뚱그려 생략했다.
마치며
유저 프로세스와 커널 프로세스는 전공책에서 자주 비교되는 주제지만, 막상 실제 환경(ps, top, systemd 같은 이름들)에서는 헷갈리기 쉬워서 한번 정리해봤다. 도움이 되었으면 좋겠다.