5장 프로세스API
Unix 시스템은 프로세스를 생성하기 위하여 fork()와 exec() 시스템 콜을 사용한다.
wait()는 프로세스가, 자신이 생성한 프로세스가 종료되기를 기다리기 원할 때 사용된다
Unix 시스템의 프로세스 생성
프로세스를 생성하고 제어하려면 운영체제가 어떤 인터페이스를 제공해야 하는가?
유용하고 편하게 사용하기 위해서 이 인터페이스는 어떻게 설계되어야 하는가?
1. fork() 시스템 콜
프로세스 생성에 fork() 시스템 콜이 사용된다

실행이 시작될 때 프로세스는 “hello world ...” 메시지를 출력한다.
이 메시지에는 PID로도 알려진 프로세스 식별자(process identiier)가 포함된다.
부모: 6... rc== 29147
자식: 7.....rc==0
Unix 시스템에서 PID는 프로세스의 실행이나 중단과 같이
특정 프로세스를 대상으로 작업을 해야 할 경우 프로세스를 지칭하기 위해 사용된다.
프로세스는 fork() 시스템 콜을 호출한다.
운영체제는 프로세스 생성을 위해 이 시스템 콜을 제공한다.
이상한 부분이 있다: 생성된 프로세스가 호출한 프로세스의 복사본이라는 것이다.
프로그램 p1이, 2개가 존재한다.
두 프로세스가 모두 fork() 에서 리턴하기 직전이다.
자식 프로세스: 새로 생성된 프로세스
부모 프로세스: 생성한 프로세스
main()함수 첫 부분부터 시작하지 않았다는 것을 알 수 있다 (“hello, world” 메시지가 한 번만 출력되었다는 것에 유의하기 바란다).
자식 프로세스는 fork()를 호출하면서부터 시작되었다.
자식 프로세스는 부모 프로세스와 완전히 동일하지는 않다.
자식 프로세스는 자신의 주소 공간, 자신의 레지스터, 자신의 PC 값을 갖는다.
fork() 시스템 콜의 반환 값이 서로 다르다.
부모: 6... rc== 29147
자식: 7.....rc==0
부모 프로세스는 생성된 자식 프로세스의 PID를 반환받고,
자식 프로세스는 0을 반환받는다.
CPU 스케줄러(scheduler)
실행할 프로세스를 선택한다.
스케줄러의 동작은 일반적으로 상당히 복잡하고 상황에 따라 다른 선택이 이루어지기 때문에,
어느 프로세스가 먼저 실행된다라고 단정하는 것은 매우 어렵다. == 비결정성(nondeterminism)
멀티 쓰레드 프로그램 실행 시 다양한 문제가 발생한다.
2. wait() 시스템 콜
부모 프로세스가 자식 프로세스의 종료를 대기해야 하는 경우도 발생할 수 있다.
wait(), waitpid()

부모 프로세스는 wait() 시스템 콜을 호출하여
자식 프로세스 종료 시점까지 자신의 실행을 잠시 중지시킨다.
자식 프로세스가 종료되면 wait()는 리턴한다.

부모: 6.... rc==29267
자식: 7..... rc==0
자식 프로세스 먼저 실행되는 경우:
- 부모 프로세스보다 자식 프로세스가 먼저 실행되면, 자식 프로세스가 먼저 출력된다.
부모 프로세스가 먼저 실행되는 경우:
- 부모가 곧바로 wait()를 호출한다.
- 자식 프로세스가 종료될 때까지 리턴하지 않는다.
- 부모가 먼저 실행되더라도 자식 종료 후 wait()가 리턴한다.
- 부모 프로세스가 출력한다
3. exec() 시스템 콜
자기 자신이 아닌
다른 프로그램으로 실행을 바꿔치기 한다.
fork() 시스템 콜은 자신의 복사본을 생성하여 실행한다.


부모 프로세스: 3............rc==29384...............wc==4
자식 프로세스: 4............rc==0
줄 수 (lines) 단어 수 (words) 문자 수 (bytes) 파일명




현재 실행 중인 프로세스의 코드 세그멘트(코드도 메모리에 띄운다)와 정적 데이터 부분(static)을 덮어 쓴다.
힙 세그먼트, 스택 세그먼트, 프로그램 다른 주소 공간들로 새로운 프로그램의 실행을 위해 다시 초기화된다.
그런 다음 운영체제는 프로세스의 argv와 같은 인자를 전달하여 프로그램을 실행시킨다.
새로운 프로세스를 생성하지는 않는다.
현재 실행 중인 프로그램을 (p3) 다른 실행 중인 프로그램으로 (wc) 대체하는 것이다.
자식 프로세스가 exec()을 호출한 후에는 p3.c는 전혀 실행되지 않은 것처럼 보인다.
---> 21 printf(“this shouldn't print out ”); 실행안됨(바꿔치기 해서)
exec() 시스템 콜이 성공하게 되면 p3.c는 절대로 리턴하지 않는다.
쉘(Shell)
단순한 사용자 프로그램
tcsh, bash, zsh
쉘은 프롬프트를 표시하고 사용자가 무언가 입력하기를 기다린다.
명령어를 입력한다 (예, 실행 프로그램의 이름과 필요한 인자).
쉘은 fork()와 exec()을 분리함으로써 쉘은 많은 유용한 일을 조금 쉽게 할 수 있다.
목표: 파일 시스템에서 1)실행 파일의 위치를 찾고 2)명령어를 실행하기
- fork()를 호출하여 새로운 자식 프로세스를 만든다.
- exec()의 변형 중 하나를 호출하여 프로그램을 실행시킨 후
- wait()를 호출하여 명령어가 끝나기를 쉘은 기다린다.
- 자식 프로세스가 종료되면 쉘은 wait()로부터 리턴
- 쉘은 다시 프롬프트를 출력한다
- 쉘은 다음 명령어를 기다린다.

wc p3.c > newfile.txt
- wc 프로그램의 출력은 newfile.txt라는 출력 파일로 저장
- 자식이 생성되고
- exec()이 호출되기 전에 표준 출력(standard output) 파일을 닫고
- newfile.txt 파일을 연다.
- 실행될 프로그램인 wc의 출력은 모두 화면이 아니라 파일로 보내진다


Unix 시스템은 미사용 중인 파일 디스크립터를 0번부터 찾아 나간다.
STDOUT_FILENO가 첫 번째 사용 가능 파일 디스크립터로 탐색되어
open()이 호출될 때 할당된다.
표준 출력 파일을 닫았기 때문이다.이후 자식 프로세스가 표준 출력 파일 디스크립터를 대상으로 하는 모든 쓰기,
예를 들어 printf()에 의한 쓰기는 화면이 아니라 새로 열린 파일로 향하게 된다.
프로그램 p4 는 fork()를 호출하여 새로운 자식 프로세스를 생성하고
execvp()를 호출하여 wc 프로그램을 실행시킨다.
출력이 p4.output 파일로 재지정 때문에 화면에는 아무 것도 출력되지 않는다.
출력 파일을 cat해 보면 wc를 실행시켰을 때 얻을 수 있는 모든 출력이 파일에 저장되어 있는 것을 발견하게 될 것이다.
"fork()의 복잡함에 대하여 동료에게 물어보면 ‘RTFM’이라는 아주 간단한 대답이 돌아올 것이다."
📌 fork()는?
- 유닉스/Linux 시스템에서 **프로세스를 복제(fork)**할 때 사용하는 시스템 호출이에요.
- 멀티프로세스 프로그래밍의 핵심인데, 처음 보면 꽤 복잡하고 헷갈립니다.
📌 RTFM은 뭘까?
RTFM = Read The F*ing Manual**
- 직역: “그 X 설명서 좀 읽어봐”
- 의미: "질문하지 말고 공식 문서를 먼저 읽어봐!"
(조금 거칠지만, 개발자들 사이에 종종 쓰이는 표현이에요)
😅 그래서 이 문장의 진짜 의미는?
“fork()가 왜 이렇게 복잡한지 동료에게 물어보면, 그는 친절하게 설명해주기보다는 ‘RTFM’이라고 말할지도 몰라. 즉, 네가 직접 매뉴얼을 읽고 이해해야 할 만큼 어렵다는 뜻이야.”
- fork()는 진입장벽이 높은 함수고
- 다른 개발자들도 "나도 설명하기 힘들어, 문서 읽어봐"라고 할 만큼 복잡하고
- 결국 스스로 리눅스 매뉴얼이나 공식 문서를 찾아봐야 제대로 이해할 수 있다는 유머 섞인 표현이에요.
'학습 기록 (Learning Logs) > Today I Learned' 카테고리의 다른 글
✨ Operating Systems: Three Easy Pieces ✨ - 개요 (0) | 2025.03.23 |
---|---|
머신러닝 (0) | 2025.03.21 |
✨ Operating Systems: Three Easy Pieces ✨ - 가상화 (0) | 2025.03.19 |
빅데이터 (0) | 2025.03.18 |
Spring Boot + STOMP로 카카오톡 스타일 채팅방 만들기 (0) | 2025.03.10 |