부모 - 자식 프로세스
부모-자식 관계의 프로세스
- #0 프로세스는 시스템 부팅 시 실행되는 최초의 프로세스
- 부모 프로세스는 여러 개의 자식 프로세스를 가질 수 있음
- #0 프로세스를 제외한 나머지 프로세스는 모두 부모 프로세스를 가짐
자식 프로세스 생성
- 부모 프로세스에 의해 생성
- System Call을 통해 생성
> fork(), clone() 함수
리눅스에서의 프로세스
- #0 프로세스: swapper/idle 프로세스 (hand-crafted)
- #1 프로세스: init 프로세스 (hand-crafted)
> 부팅 후 생성되는 모든 사용자 프로세스의 조상
- #2 프로세스: kthreadd 프로세스 (hand-crafted)
> 커널 모드에서 커널 코드로만 실행되는 모든 커널 프로세스의 조상
#0 프로세스
- 가장 윗 어른(조상) 프로세스
Unix #0 프로세스
- swapper라고 불림
- 부팅을 담당하고 #1 프로세스 생성
Linux #0 프로세스
- idle 프로세스, 부팅에 관여하지 않고 루프만 돎
- 우선 순위가 가장 낮은 프로세스
- 실행중인 프로세스가 1개도 없는 상태에 대비하여 만든 프로세스
- Unix 의 관례에 따라 swapper라고도 부름
Windows #0 프로세스
- system idle 프로세스
- 아무일도 하지 않고 루프만 도는 단순 프로세스
- 사용자가 컴퓨터를 사용하지 않는 시간 동안 실행
프로세스 생성
프로세스가 생성되는 5가지 경우
- 시스템 부팅 과정에서 필요한 프로세스 생성
- 사용자가 로그인 한 후 대화를 위한 프로세스 생성 (bash 등의 shell)
- 새로운 프로세스를 생성하는 사용자의 명령
- 배치 작업 실행
- 사용자 프로세스가 System Call을 통해 새로운 프로세스 생성
> 리눅스의 fork(), Windows의 CreateProcess()
프로세스의 생성 과정
- 새로운 PID 번호 할당
- PCB 구조체 생성
- 프로세스 테이블에 새로운 항목 할당
- 새로 할당된 프로세스 테이블 항목에 PCB 구조체 연결
- 프로세스를 위한 메모리 공간 할당
> 코드, 데이터, 스택, 힙 영역
> 메모리 공간에 코드 및 데이터 적재
- PCB에 프로세스 정보 기록
- PCB에 프로세스 상태를 Ready로 표시하고 Ready Queue에 넣어서 스케쥴링 대기함
fork() System Call
- 현재 프로세스를 복사하여 자식 프로세스 생성
- int pid = fork();
> 자식 프로세스 생성
> 부모 프로세스의 모든 환경, 메모리, PCB를 복사
> 부모 프로세스와 생긴건 같으나 독립된 주소 공간을 소유함
- 리턴값
> 부모 프로세스에게는 자식 프로세스의 PID 리턴
> 자식 프로세스에게는 0 리턴
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid;
int i, sum=0;
pid = fork(); // 자식프로세스 생성
if(pid > 0) { // 부모 프로세스에 의해 실행되는 코드
printf("부모프로세스: fork()의 리턴 값 = 자식프로세스 pid = %d\n", pid);
printf("부모프로세스: pid = %d\n", getpid());
wait(NULL); // 자식 프로세스가 종료할 때까지 대기
printf("부모프로세스종료\n");
return 0;
}
else if(pid == 0) { // 자식 프로세스에 의해 실행되는 코드
printf("자식프로세스: fork()의 리턴 값 pid = %d\n", pid);
printf("자식프로세스: pid = %d, 부모프로세스 pid = %d\n", getpid(), getppid());
for (i=1; i<=100; i++)
sum += i;
printf("자식프로세스: sum = %d\n", sum);
return 0;
}
else { // fork() 오류
printf("fork 오류");
return 0;
}
}
exec() System Call
프로세스 오버레이(Process Overlay)
- 현재 실행중인 프로세스의 주소 공간에 새로운 응용 프로그램을 적재하여 실행시키는 기법
- exec()는 프로세스 오버레이를 기반으로 프로세스를 생성함
exec 관련 함수들
- execlp(), execv(), execvp() System Call 함수들
- 실행 파일을 적재하여 현재 프로세스의 메모리 공간에 단순히 덮어씌움
- fork()와 달리 새로운 프로세스의 생성 과정을 거치지 않음
exec 특징
- 프로세스의 PID 변경 없음
- 프로세스의 코드, 데이터, 힙, 스택 영역에 새로운 프로세스가 적재됨
- fork()로 자식 프로세스 생성 후 바로 exec() 실행하는 것이 일반적인 형태
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid;
pid = fork(); // 자식프로세스 생성
if (pid > 0) { // 부모 프로세스 코드
printf("부모프로세스: fork()의 리턴 값 = 자식프로세스 pid = %d\n", pid);
printf("부모프로세스: 프로세스 pid = %d\n", getpid());
wait(NULL); // 자식프로세스가 종료할 때까지 대기
return 0;
}
else if (pid == 0) { // 자식 프로세스 코드
printf("자식프로세스: fork()의 리턴 값 pid = %d\n", pid);
printf("자식프로세스: pid = %d, 부모프로세스 pid = %d\n", getpid(), getppid());
execlp("/bin/ls", "ls", "-l", NULL); // /bin/ls를 현재프로세스에 오버레이하여 실행
}
else { // fork() 오류
printf("fork 오류");
return 0;
}
}
exit() System Call
- C 프로그램의 main()에서 리턴되는 경우
> exit() System Call이 진행됨
exit()를 통한 프로세스 종료 과정
- 프로세스의 모든 자원(코드, 데이터, 스택, 힙) 반납
- 열어 놓은 파일 및 소켓을 닫음
- PCB에 프로세스 상태를 Terminated로 변경, PCB에 종료 코드 저장
- 자식 프로세스가 있다면 init 프로세스에게 입양
- 부모 프로세스에게 SIGCHLD 신호 전송
종료코드
- 부모 프로세스에게 전달하는 값
- main() 함수의 리턴 값, return 종료 코드
- exit(종료코드)
- 프로세스가 종료한 상태 및 이유를 부모에게 전달하기 위한 것
- POSIX 표준에서는 0-255 사이의 1바이트 숫자
> 정상 종료는 0
> 1~255 는 개발자가 종료 이유를 임의로 정해서 사용함
Zombie Process
프로세스가 종료할 때
- PCB에 종료코드(exit status) 저장
- PCB에 프로세스 상태를 Terminated라고 표시
- 프로세스에 할당된 모든 메모리를 반납함
> 아직 프로세스 테이블 항목과 PCB 자체는 제거되지 않음
프로세스 종료 시 부모 프로세스의 역할
- wait() System Call 함수를 통해서 자식 프로세스의 종료 코드를 읽어야 함
- 자식이 종료되면 부모에게 SIGCHLD 신호가 전송됨
- 부모가 해당 신호를 받으면 wait() System Call 함수를 통해 처리
1. 좀비 프로세스(Zombie Process)
- 자식은 종료하였으나 부모가 어떤 이유로 인해 종료코드를 읽지 않았음
- 프로세스 테이블에는 관련 항목이 남아있음
- ps 명령을 실행하면 좀비 프로세스가 목록에 나타남
좀비 프로세스 제거 방법
- 방법1: Shell 에서 부모 프로세스에게 SIGCHLD 신호 보내기
> $kill -SIGCHLD 부모프로세스의 PID
> 부모 프로세스의 SIGCHLD 핸들러가 wait()를 호출하여 좀비 자식을 제거함
> SIGCHLD 핸들러가 wait()를 호출하지 않으면 좀비 자식을 제거할 수 없음
- 방법2: 부모 프로세스의 강제 종료
> $kill -9 부모프로세스의 PID
> 부모가 사라져서 좀비 자식은 init 프로세스의 자식으로 입양됨
> init 프로세스가 wait()를 호출하여 좀비 자식을 제거함
고아 프로세스(Orphan Process)
- 자식이 종료되기 전, 부모가 먼저 종료한 프로세스
부모 프로세스가 종료되면..
- 커널(exit() System Call)은 자식 프로세스가 있는지 확인함
- (고아가 된) 자식 프로세스가 있다면 init 프로세스에게 입양함
- 운영체제에 따라 입양 대신 모든 자식 프로세스를 강제 종료시킬수도 있음
'운영체제' 카테고리의 다른 글
4. 스레드와 멀티스레딩 (2) | 2023.04.10 |
---|---|
3. 프로세스와 프로세스 관리(1) (1) | 2023.04.04 |
2. 컴퓨터 시스템과 운영체제 (0) | 2023.04.01 |
1. 운영체제의 시작과 발전 (2) | 2023.04.01 |