
저자: Michael Kerrisk
이 책의 소스 코드
-- 프로세스 위주로
[Vol.1]
1~5장까지는 가볍게 읽기
6장 프로세스
24장 프로세스 생성
25장 프로세스 종료
26장 자식 프로세스 감지
27장 프로그램 실행
28~31장 읽기
-- 파이프란?
[Vol.2]
7장 파이프와 FIFO
안에 무엇을 담고 있나
binary format identification: 실행 파일의 포맷에 대한 정보
file [실행파일명]
기계어 명령
프로그램의 진입점 주소(entry point)
데이터: 변수의 초기값과 문자 상수
symbol table, relocation table
공유 라이브러리와 동적 링크 정보
기타
sysctl kernel.pid_max
[출력⬇️]
kernel.pid_max = 4194304
segment: 프로세스의 메모리 공간을 용도별로 나누어 관리하는 논리적 영역
각 프로세스에 독립적인 주소 공간 부여
1) 프로세스 격리 & 보호
2) 단순하고 일관된 주소 공간 제공
3) 효율적인 메모리 관리 및 자원 활용
4) 공유 라이브러리, 코드 재사용
5) 안정성 & 에러 탐지
환경 변수(environment variable): 프로세스 동작 방식에 영향을 주는 문자열 기반의 키-값 쌍의 집합
1) 프로세스마다 별도의 환경 변수 집합을 가짐
2) 프로그램의 동작, 실행 환경에 영향
3) 환경 변수 목록은 순서대로 접근하기보다 각 환경변수에 개별적으로 접근한다
1) 메인 함수의 세 번째 인자로
int main(int argc, char *argv[], char *envp[])
2) 전역 변수로 접근
extern char **environ;
3) 환경 변수 조회 함수
char *getenv(const char *name);
/proc = procfs: 리눅스의 가상 파일 시스템
시스템과 프로세스의 상태를 파일/디렉토리처럼 "실시간으로" 제공
PID = 1234인 프로세스를 예시로 하자면
| 파일명 | 내용 | 예시 (PID=1234) |
|---|---|---|
cmdline | 실행 명령줄(인자 포함) | cat /proc/1234/cmdline |
environ | 환경변수 목록 (null 문자로 구분) | cat /proc/1234/environ |
cwd | 현재 작업 디렉토리(심볼릭 링크) | ls -l /proc/1234/cwd |
exe | 실행파일 경로(심볼릭 링크) | ls -l /proc/1234/exe |
fd | 열린 파일 디스크립터 목록(디렉토리) | ls -l /proc/1234/fd/ |
status | 프로세스 상태(읽기 쉬운 텍스트) | cat /proc/1234/status |
stat | 다양한 프로세스 통계(공백 구분) | cat /proc/1234/stat |
maps | 메모리 매핑 상태 | cat /proc/1234/maps |
smaps | 상세 메모리 매핑/사용량 | cat /proc/1234/smaps |
mem | 실제 프로세스 메모리(읽기 어려움) | |
task | 스레드 정보 디렉토리 | ls /proc/1234/task |
cat /proc/1234/environ | tr '\0' '\n'
1) fork()
2) exit(status)
3) wait(status)
4) execve(pathname, argv, envp)
int execve(const char *pathname, char *const argv[], char *const envp[]);
1) copy-on-wite: fork() 직후에 부모/자식이 물리 메모리 공유. 둘 중 하나가 메모리 내용을 바꿀 떄만 실제로 복사
2) 좀비 프로세스: 자식이 종료됐는데, 부모가 wait/watipid로 회수하지 않으면 남음
3) 고아 프로세스: 부모가 먼저 종료되면 자식은 init이 부모가 된다.
- 뒤에 구체적으로 더 나올 예정
-- 6.3 1차 정리(요약 위주로.. 추후에 추가 정리할 수 있으면 그 때 마무리)
왜 자식 프로세스 감시가 중요할까?
- 프로세스는 독립적이지만 부모-자식 관계로 연결되어 있다.
- 부모는 자식의 종료, 비정상 종료, 신호, 일시 정지 등 상태 변화를 알아야 리소스를 회수하고(잘못하면 좀비..!) 필요한 대응을 할 수 있다.
#include <sys/wait.h>
#include <time.h>
#include "curr_time.h" /* 선언문 currTime() */
#include "tlpi_hdr.h"
int main(int argc, char *argv[])
{
int numDead; /* 기다리고 있는 자식의 수 */
pid_t childPid; /* 기다리는 자식의 프로세스 ID */
int j;
if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s sleep-time...\n", argv[0]);
setbuf(stdout, NULL); /* stdout이 버퍼링되지 않게 함 */
for (j = 1; j < argc; j++) { /* 인수의 수만큼 자식 프로세스 생성 */
switch (fork()) {
case -1:
errExit("fork");
case 0: /* 자식 프로세스는 잠시 잠들었따가 종료 */
printf("[%s] child %d started with PID %ld, sleeping %s "
"seconds\n", currTime("%T"), j, (long) getpid(),
argv[j]);
sleep(getInt(argv[j], GN_NONNEG, "sleep-time"));
_exit(EXIT_SUCCESS);
default: /* 부모 프로세스 계속 진행 */
break;
}
}
numDead = 0;
for (;;) { /* 부모는 자식 프로세스들이 종료되기를 기다린다 */
childPid = wait(NULL);
if (childPid == -1) {
if (errno == ECHILD) {
printf("No more children - bye!\n");
exit(EXIT_SUCCESS);
} else { /* 예기치 못한 에러 발생 */
errExit("wait");
}
}
numDead++;
printf("[%s] wait() returned child PID %ld (numDead=%d)\n",
currTime("%T"), (long) childPid, numDead);
}
}

pid_t waitpid(pid_t pid, int wstatus, int options);
자식의 프로세스 ID 또는 0 반환. 에러가 발생하면 -1 리턴
1) wait(), waitpid() 함수
2) Non-blocking(비동기) 감시
WNOHANG : 플래그. id에서 명시한 조건에 맞는 어떤 자식 프로세스도 리턴할 정보가 없다면 바로 리턴한다.(0)3) 여러 자식 관리
부모가 wait/waitpid로 종료된 자식의 상태를 반드시 "회수"해야 좀비 프로세스가 쌓이지 않는다
int execve(const char *pathname, char *const argv[], char *const envp[]);