
OS가 새로운 프로세서를 만들고 메모리에 로드하여 실행하는 과정을 의미한다. 부모 프로세서가 새로운 자식 프로세서를 생성하며 이는 운영체제의 멀티테스킹과 프로세스 관리의 핵심 메커니즘이다.
Parent and children execut concurrently(동시 실행)
-> 부모와 자식 프로세스가 병렬(concurrently) 로 실행된다.
-> example) 웹 서버가 여러 클라이언트 요청을 처리하기 위해 자식 프로세스를 생성하는 경우.
Parent waits until children terminate
-> 부모 프로세스는 자식이 종료될 때까지 대기(Block) 한다.
-> wait() 시스템 호출을 하는 경우.
현재 실행중인 프로세스(부모) 를 완전히 복사해 자식 프로세스를 생성한다.
부모와 자식은 동일한 코드, 메모리, 파일 디스크립터, PC 를 공유한다.
Copy-on-Write(COW) : 호출이 되면 복사가 아니라 실제 쓰기 작업이 들어가면 복사함.
fork()의 반환 값
현재 프로세스의 메모리 공간을 새로운 프로그램 으로 교체한다.
fork()로 생성된 자식 프로세스가 새로운 프로그램을 실행 할때 사용된다.
원본 프로세스의 PID가 유지되지만, 코드와 데이터는 완전히 새로 로드된다.
성공 시 리턴하지 않음, 실패 시 -1리턴
execlp(), execvp(), execle(), execv(), execve() 등이 존재
:모두 동일하지만 $PATH 사용 여부, 인자 전달 방식, 환경 변수 변경에서 차이가 있음.
fork()로 자식 프로세스 생성
-> 부모와 자식이 동일한 코드를 실행.
자식 프로세스에서 exec() 호출
-> 자식만 새로운 프로그램으로 덮어쓰고, 부모는 원래 작업을 계속.
부모는 wait()으로 자식 종료 대기
-> 자식이 종료되면 부모가 다음 코드를 실행.
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main() {
pid_t pid = fork(); // 1. 프로세스 복제
if (pid < 0) { // fork 실패
fprintf(stderr, "Fork Failed");
return 1;
}
else if (pid == 0) { // 2. 자식 프로세스
execlp("/bin/ls", "ls", NULL); // 3. "ls"로 교체
}
else { // 4. 부모 프로세스
wait(NULL); // 5. 자식 종료 대기
printf("Child Complete");
}
return 0;
}
부모 프로세스에 wait()이 없으면 부모와 자식간에 실행 순서는 확정적이지 않음. -> 스케쥴러가 결정하며 Race Condition이 발생할 수 있음.
Race Condition : 두 개 이상의 프로세스(또는 스레드)가 공유 자원에 동시에 접근할 때, 실행 순서에 따라 결과가 달라지는 상황.
자식이 종료되었지만, 부모가 wait()으로 회수하지 않으면 "좀비 프로세스"가 된다. (시스템 리소스를 잠식하지 않지만, 프로세스 테이블을 잠식함)
exec()를 하면 기존 데이터가 지워지기 때문에 이전으로 돌아갈 수 없음.
블로킹(Blocking) 방식으로 아무 자식 프로세스가 하나가 종료될 때까지
대기합니다.
자식 프로세스의 종료 상태(exit status)를 status 포인터로 반환합니다. (status : 종료 상태를 저장할 변수 )
자식 프로세스의 PCB(Process Control Block)를 정리합니다 (좀비 프로세스 방지).
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// 자식 프로세스
printf("Child PID: %d\n", getpid());
sleep(2);
return 42; // 종료 코드 42
} else {
// 부모 프로세스
int status;
pid_t child_pid = waitpid(pid, &status, 0); // 특정 자식 대기
if (WIFEXITED(status)) {
printf("Child %d exited with code %d\n", child_pid, WEXITSTATUS(status));
}
}
return 0;
}
pid_t waitpid(pid_t pid, int *status, int options);
-> 특정 자식 프로세스(pid) 가 종료될 때까지 대기합니다.
-> options : 0이면 블로킹, WNOHANG 이면 논 블로킹
WIFEXITED(status) : status분석 -> true(자식이 정상 종료), false(비정상 종료)
좀비 프로세스 (Zombie):
자식이 종료되었지만, 부모가 wait()으로 상태를 읽지 않아 프로세스 테이블에 잔류.
해결: 반드시 wait() 호출 또는 SIGCHLD 시그널 처리.
고아 프로세스 (Orphan):
부모가 먼저 종료되어 고아처럼 남겨진 프로세스.
부모가 먼저 종료되면, 자식은 init 프로세스(PID=1)에 의해 자동으로 수거.
프로세스가 마지막 명령어를 실행한 후 운영체제에 종료를 요청(exit)하는 경우.
#include <stdlib.h>
int main() {
exit(0); // 정상 종료 (종료 코드 0)
}
부모 프로세스가 자식 프로세스를 강제로 종료시키는 경우.
Child has exceeded allocated resources -> 자식 프로세스가 할당된 자원을 초과한다.
Task assigned to child is no longer required -> 자식 프로세스에 할당된 작업이 더이상 필요하지 않는다.
부모의 종료: 일부 운영체제는 부모 종료 시 모든 자식을 함께 종료시킨다. (Cascading Termination(계단식 종료))
프로세스 간 데이터를 주고받거나 동작을 조율하는 통신 매커니즘이다.

/ Unix/Linux 공유 메모리 예제
int shm_id = shmget(key, size, IPC_CREAT | 0666);
char *data = (char*)shmat(shm_id, NULL, 0);
sprintf(data, "Hello from PID %d", getpid()); // 데이터 쓰기| Message Passing | Shared Memory | |
|---|---|---|
| Implementation | Easier | Difficult |
| Speed | Slower | Faster |
| Kernal intervention(개입) | A lot | 초기 메모리 할당만 관여, 이후에는 프로세스가 직접 접근 |
| Data size | small amount - kb | large amount - GB |

<핵심 특징>
<추가 정보>
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#define BUFF_SIZE 1024
int main() {
// 1. 익명 메모리 매핑 (공유 가능한 메모리 할당)
char *buffer = mmap(NULL, BUFF_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS,
-1, 0);
if (buffer == MAP_FAILED) {
perror("mmap failed");
return 1;
}
// 2. 부모 프로세스가 데이터 작성
strncpy(buffer, "I am a parent", BUFF_SIZE-1);
// 3. 자식 프로세스 생성
pid_t pid = fork();
if (pid == 0) {
// 자식 프로세스: 데이터 수정
strncpy(buffer, "I am a child", BUFF_SIZE-1);
} else {
// 부모 프로세스: 자식 종료 대기 후 출력
wait(NULL);
printf("buffer: %s\n", buffer); // 출력: "I am a child"
// 4. 메모리 매핑 해제
munmap(buffer, BUFF_SIZE);
}
return 0;
}
void *mmap(void *addr, size_t size, int prot,
int flags, int fd, off_t offset);
하나의 시스템에서 데이터를 생성하는 역할 과 소비하는 역할이 동시에 작용하는 모델. 직접 통신하지 않고 버퍼를 통해 주고받는다.

Producer : 데이터를 만드는 역할을 하는 주체, 버퍼에 데이터를 write함.
Consumer : 버퍼에서 데이터를 읽어서 사용하는 주체, 버퍼에서 read함.
Buffer : 생산자와 소비자가 직접 데이터를 주고받지 않고, 임시로 데이터를 저장해 놓는 중간 공간. (일종의 큐 혹은 배열로 구현됨)
생산자와 소비자가 데이터를 주고받기 위해 사용하는 공유된 메모리 공간을 기반으로 한 모델
| 유형 | Unbounded Buffer | Bounded Buffer |
|---|---|---|
| 버퍼 크기 | 무제한 | 고정된 크기 (BUFF_SIZE) |
| Producer | 항상 데이터 생성 가능 | 버퍼가 가득 차면 대기 |
| Consumer | 버퍼가 비어있으면 소비 불가 (out==in 시 중단) | 동일 |
| 실제 사용 | 거의 사용되지 않음 (메모리 고갈) | 대부분 채택(파이프, 메시지 큐) |

<Producer>
int in = 0;
int out = 0;
while(true){
while((in+1) % BUFFER_SIZE == out){,,대기,,}
buffer[in] = get_data();
in = (in+1) % BUFFER_SIZE;
}
<Consumer>
int in = 0;
int out = 0;
while(true){
while(in==out){,,대기,,}
read_data(buffer[out]);
out = (out + 1) % BUFFER_SIZE;
}
| 방식 | 설명 | 예시 |
|---|---|---|
| Direct | 프로세스끼리 직접 이름을 지정해서 메시지를 주고받음 | send(Q, msg) -> Q에게 직접 전송 receive(P, message) -> P에게 전달받기 |
| Indeirect | 메일박스를 통해 메시지를 주고 받음 | send(mailbox1, msg) -> Q는 receive(mainbox) |
Direct
- 링크는 자동적으로 확립(established)된다.
- 하나의 링크는 오직 한 쌍의 프로세스와만 연결된다.
- 프로세스 쌍 사이에는 반드시 하나의 링크만 존재한다.
- 링크는 단방향, 양방향 둘다 가능하다.
User Mode에서 send(B, message) 같은 system call 호출
System Call 인터페이스를 통해 커널 모드로 전환됨
커널은 전달받은 수신자 이름 (B) 를 기반으로
처리 후 다시 User Mode로 복귀
Indirect
- 생산자와 소비자가 서로의 이름을 몰라도, 같은 메일박스를 공유하고 있다면 통신이 가능하다.
- 메일박스는 고유한 ID를 가지고 있다.
- 프로세스들이 공통의 메일박스를 공유할 경우에만 링크가 설정된다.
- 하나의 링크(=메일박스)는 여러 프로세스와 연결될 수 있음
- 프로세스 쌍은 여러 개의 메일박스(링크)를 공유할 수 있음 (로그용, 데이터용 등등)
- 링크는 단방향, 양방향 둘다 가능하다.
<보낼 때>
<받을 때>
| 방식 | 설명 | 예시 |
|---|---|---|
| Synchronus | 보내는 쪽(send)과 받는 쪽(receive)이 동시에 준비되어 있어야 한다. | 실시간 채팅 |
| Asynchronus | 보내는 쪽은 그냥 보내고, 받는 쪽은 언제든 나중에 받을 수 있음 | 이메일 |
Blocking - 동기식
- Blocking send : 메시지가 수신될 때까지 sender를 Block(대기)한다.
- Blocking receive : 수신한 메시지가 도착할 때까지 receiver를 Block한다.
Non-Blocking - 비동기식
- Non-Blocking send : 메시지를 보내고 즉시 다음 작업을 수행함.
- Non-Blocking receive : 메시지가 있으면 즉시 수신, 없으면 null 또는 실패 상태 반환하고 다음 작업 진행.
두 프로세스간의 데이터 통신을 위한 관 역할. 데이터를 한쪽에서는 쓰고, 한쪽에서는 읽는다.

파일 시스템에 이름으로 등록되는 파이프
<Unix에서 Named Pipe>
<window에서 Named Pipie>
동일한 시스템 내에서만 작동하는 소켓 기반 통신 방식, 파일 시스템 내의 파일을 경로로 통신한다.