프로세스

실행 중인 프로그램(Program in Execution)

— 현대 컴퓨터 과학 커리큘럼의 알파이자 오메가

분명 브라우저 탭 하나만 켰는데 왜 작업 관리자에는 프로세스가 30개씩 돌고 있는가? 그리고 그중 절반은 은행 보안 솔루션과 그리드 딜리버리 프로그램이다.

1. 개요

프로세스는 운영체제로부터 독립된 메모리 공간(코드, 데이터, 힙, 스택)을 할당받아 실행 중인 프로그램을 의미한다. 디스크에 처박혀 숨만 쉬던 정적인 프로그램이 기지개를 켜고 메모리 위로 올라와 CPU의 간택을 받는 순간 비로소 생명력을 얻어 프로세스로 탈바꿈한다. 현대 멀티태스킹 환경에서는 수많은 프로세스가 CPU 코어를 차지하기 위해 기를 쓰며 아주 빠른 속도로 교대 근무(컨텍스트 스위칭)를 서고 있지만, 그만큼 덩치가 무겁고 둔해 실무에서는 늘 스레드와의 성능 저울질 속에서 골머리를 썩힌다.(...)

2. 메모리 구조의 4대 천왕: Code, Data, Heap, Stack

프로세스는 자신만의 철저히 격리된 독립 영토(Virtual Address Space)를 갖는다. 코드 영역에는 컴파일된 기계어가 정갈하게 들어가고, 데이터 영역에는 전역 변수와 정적 변수가 둥지를 틀며, 힙 영역에는 개발자가 런타임에 동적으로 할당한 자바스크립트 객체 따위가 거주한다. 마지막으로 스택 영역에는 지역 변수와 함수 호출 정보가 임시로 쌓인다. 이 중 힙과 스택은 서로를 향해 마주 보며 자라나는 기묘한 구조를 취하고 있어, 영역 침범이 일어나는 순간 힙 오버플로우나 스택 오버플로우1가 터지며 프로세스가 폭사한다. 독립된 공간 덕분에 프로세스 하나가 뻗어도 다른 놈들은 끄떡없다는 극강의 안정성을 자랑하지만, 역설적으로 프로세스끼리 서로 데이터를 주고받으려면 IPC(Inter-Process Communication)라는 극도의 복잡하고 번거로운 짓거리(...)를 행해야만 한다.

3. 생성과 소멸: 부모와 자식의 잔혹사

현대 OS에서 새로운 프로세스는 보통 무에서 태어나지 않고 기존 프로세스의 유전자를 복제하여 태어난다. 유닉스 계열 OS의 fork()2 시스템 콜이 대표적인데, 부모를 쏙 빼닮은 자식 프로세스가 복제되어 구동을 시작한다. 그러나 이 아름다운 대물림 뒤에는 잔혹한 비극이 도사린다. 자식 프로세스가 종료되었음에도 부모가 그 시체를 거두어(exit status 수거) 주지 않으면 자식은 좀비 프로세스(Zombie Process)가 되어 시스템 테이블을 좀먹고, 반대로 부모가 자식을 버려두고 먼저 요단강을 건너버리면 자식은 졸지에 고아 프로세스(Orphan Process)가 되어 init 프로세스에게 강제 입양되는 막장 드라마가 시스템 내부에서 나노초 단위로 펼쳐진다.(...)

4. 관련 밈 및 드립

4.1. 크롬의 메모리 폭식(Chrome Memory Eater)

웹 브라우저인 구글 크롬이 탭 하나하나를 독립된 프로세스로 분리해 실행하는 멀티 프로세스 아키텍처를 적극 차용하면서 파생된 유구한 밈. 탭을 대여섯 개만 띄워도 내 소중한 RAM이 순식간에 증발하는 경이로운 광경을 지켜보며 개발자들은 '크롬은 램 귀신'이라 울부짖는다. 웹 서핑의 안락함과 크래시 방지를 대가로 사용자의 지갑(RAM 추가 장착 비용)을 제물로 바치는 셈이다.(...)

4.2. 포크 폭탄(Fork Bomb)

:(){ :|:& };:이라는 극도로 함축적인 셸 스크립트를 터미널에 때려 박아 실행하는 무한 자식 복제 테러 기법. 실행되는 순간 부모가 기하급수적으로 자식 프로세스를 복제해 시스템 자원을 통째로 질식시켜 OS를 완전히 다운시킨다. OS에 미숙한 학부생들에게 '컴퓨터가 빨라지는 마법의 명령어'라며 장난으로 권했다가, 서버가 뻗어 담당자가 피눈물을 흘리며 서버실로 뛰어가는 참사가 가끔 벌어진다. 실행하는 순간 리부트 외엔 답이 없다.

5. 여담

  • 자원 격리의 환상: 프로세스는 가상 메모리 덕분에 자신이 물리 메모리를 독차지하여 0x00000000번지부터 온전히 쓰고 있다는 해피한 착각 속에서 살아간다. 실제로는 커널이 뒤에서 뺑이를 치며 페이징 단위로 조각내어 물리 RAM에 쑤셔 넣는 중이다.
  • Task와의 차이: 정작 리눅스 커널 소스코드를 까보면 프로세스와 스레드를 칼같이 나누지 않고, 둘 다 task_struct라는 하나의 구조체로 관리한다. 단지 자원을 공유하는 범위가 다를 뿐이라 내부적으로는 그냥 다 '태스크(Task)'로 퉁쳐 부른다.
  • PID 1번의 지배자: 시스템이 부팅된 후 커널이 가장 먼저 올리는 최초의 프로세스는 고유 번호(PID)로 항상 1을 부여받는다. 리눅스 계열의 init이나 systemd가 이에 해당하며, 시스템 안의 모든 프로세스는 이 1번 프로세스의 방계 후손들이다.

6. 관련 문서

각주

  1. 우리가 하루에 스무 번도 넘게 에러 검색하러 들어가는 그 전 세계 개발자 구원 투수 커뮤니티의 이름이 바로 여기서 유래했다.

  2. 복제하는 과정에서 메모리 전체를 곧이곧대로 복사하면 심각하게 느려지기 때문에, 실제로 자식이 데이터를 수정(Write)하기 전까지는 부모의 메모리를 숟가락만 얹어 같이 쓰는 Copy-On-Write(COW)라는 극강의 꼼수를 부린다.