지난 포스트에 이어서..!
1. 프로세스의 생성과 복사
1. 프로세스 생성
▶ 프로세스는 언제 생성되는가?
1) 시스템 부팅과정에서 필요한 프로세스 생성
2) 사용자의 로그인 후 사용자의 대화를 위한 프로세스 생성
3) 새로운 프로세스를 생성하도록 하는 사용자의 명령
4) 배치 작업 실행 시
5) 사용자의 응용 프로그램이 시스템 호출로 새 프로세스 생성
하지만, 메모리에 올라갔다고 단순히 프로세스가 아니다!
→ 그냥 메모리에만 올라와 있는 것이 아닌 PCB가 존재하여 OS가 제어 가능한 형태가 되어야 한다.
2. 프로세스 생성 과정
1) 생성하려는 실행 파일의 경로를 OS에 전달
2) OS는 메모리에 프로그램을 적재
→ Code 영역에 프로그램의 코드를 적재시키고, Data 영역에 전역/정적 변수들을 할당
→ 스택과 힙은 아직 아무것도 없으므로 초기화만 시킴
3) PCB 공간을 할당 받고 필요한 정보를 채움
→ 프로세스 식별자 결정 - 새로운 PID 번호 할당
→ 프로세스 정보 기록
→ 프로세스 테이블에서 새 항목 할당
→ 새로 할당된 프로세스 테이블에 PCB 연결
4) PCB에 프로세스 상태를 ready 상태로 표시하고, 준비 큐에 장착
3. 프로세스 복사 : fork() 시스템 콜
프로세스 복사
앞서 프로세스 과정에 대해 설명했는데, 수많은 프로세스를 저 과정대로 하면 굉장히 복잡하다. 그래서 복사에 의해서 생성하는 방법이 있다! 프로세스 복사는 기존에 있는 프로세스가 다른 프로세스를 생성한다. 프로세스를 복사하는 시스템 콜을 통해서 프로세스를 생성한다.
프로세스를 복사하여 생성하는 것에 장점은 자주 사용되는 프로세스에 대해 매번 저 복잡하고 할 일 많은 과정을 반복할 필요가 없다는 것이다. 또한, 관리도 편할 뿐더러 프로세스 간 통신도 용이하다.
fork() 시스템 콜
fork() 함수는 실행 중인 프로세스로부터 새로운 프로세스르 복사하는 함수이다. 말 그대로 실행 중인 프로세스와 똑같은 프로세스가 하나 더 만들어진다. 여기서 fork()를 호출한 함수는 부모 프로세스, 호출 당하여 fork된 프로세스를 자식 프로세스로 한다. 이렇게 fork된 자식 프로세스는 부모의 모든 환경, 메모리, PCB 등을 복사하고, 독립된 주소 공간에 위치한다. 하지만 PCB에서 아래 내용은 달라진다.
- PID는 다르다. (새로운 프로세스이기에)
- PPID : fork된 프로세스는 자식 프로세스라고 했다. PPID는 fork를 호출한 부모 프로세스의 PID이다.
- CPID : 자식이 없으면 -1 (운영체제마다 다르다.)
- 메모리 관련 정보가 다르다. (독립된 주소 공간을 가지기에)
여기 실행 예제가 있다.
#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(); // fork! -> create a child process
if(pid > 0) { // Run by the parent
printf("Parent: fork()'s return == Child pid = %d\n", pid);
printf("Parent: pid = %d\n", getpid());
wait(NULL); // Wait for the child
printf("Parent has been finished\n");
return 0;
}
else if(pid == 0) { // Run by the child
printf("\t-Child: fork()'s return pid = %d\n", pid);
printf("\t-Child: pid = %d, parent's pid = %d\n", getpid(), getppid());
for (i=1; i<=100; i++) sum += i;
printf("\t-Child: sum = %d\n", sum);
return 0;
}
else { // Error
printf("fork error");
return 0;
}
}
내가 가지고 있는 맥북 터미널에서 다음을 돌렸더니 다음과 같은 실행 결과가 나왔다.
부모가 fork한 자식 프로세스의 PID는 93234이다.
부모 프로세스는 PID는 93233이다. 또한 자식 프로세스가 만들어지고 for문을 실행했더니 잘 돌아갔다.
이렇게 프로세스 복사의 예제가 있다.
그렇다면 fork()의 장단점은?
먼저 fork()의 장점은 프로세스의 생성 속도가 빠르다. 또한, 추가 자원 없이 자원을 상속할 수 있다.
하지만, 매번 복사본을 만드는 것은 매우 비효율적이다. 또한, 가장 중요한 단점은
맨 처음 만드는 프로그램 프로세스 이외에는 다른 프로그램을 동작할 수 없다.
→ 그래서 exec()를 호출한다!
4. 프로세스 대기 : wait() 시스템 콜
wait() 시스템 콜은 자식 프로세스가 끝나기를 기다렸다가, 자식 프로세스가 종료되면 이어서 실행을 계속하는 시스템 콜이다.
5. 프로세스 종료 : exit() 시스템 콜
exit() 시스템 콜은 종료를 명시적으로 알림으로서 부모는 자식이 사용하던 자원을 빨리 회수한다.
이 exit() 시스템 콜을 부모 프로세스가 확인해야 최종적으로 자식 프로세스가 종료된다. (만약 안 그런다면?)
exit() 시스템 콜을 통한 프로세스의 종료 과정은 다음과 같다.
1) 프로세스의 모든 자원 반환
2) PCB에 프로세스 상태를 Terminated로 변경, PCB에 종료 코드 저장
3) 자식 프로세스들을 init 프로세스에게 입양
4) 부모 프로세스에게 SIGCHLD 신호 전송 → 종료 신호
→ 아니 그래서 안 그러면? 좀비가 된다!
2. 프로세스 계층 구조
1. init 프로세스
UNIX 계열에서 모든 프로세스는 init 프로세스의 자식이 되어 트리 구조를 이룬다.
2. 계층 구조 (부모 - 자식 관계의 실행)
1) 프로세스는 일반적으로 부모-자식 관계이다.
2) 모든 프로세스는 부모에 의해 생성된다.
3) PID 0,1,2 등의 몇몇 조상 프로세스는 OS 차원에서 수작업으로 생성됨
▶ 왜 계층 'fork-exec' 구조인가?
이 구조의 장점이 있다. 첫 번째는 여러 작업을 처리하기에 용이하고, 두 번째로는 프로세스 재사용에 용이하다. 또한, 관리도 용이하다.
▶ 좀비 프로세스의 제거
1) 제거해야 하는 이유 : PCB의 낭비 발생, 커널이 유지할 수 있는 PCB 테이블 크기에 제한이 있기에 성능에 영향을 줄 수 있다.
2) 제거 방법 : 간략하게 말하자면, 부모 프로세스를 강제로 종료하여 좀비 프로세스를 고아화 시킨다! -> init 프로세스의 자식이 되고 init이 wait()를 호출하여 제거한다.
3. 셸 작업 / 세션 관리
1. 셸이란?, 작업이란?, 세션이란?
셸이란 사용자와 운영체제 사이의 인터페이스 역할을 하는 프로그램 또는 환경이다.
작업이란 셸이 백그라운드로 프로세서를 제어하는 동작 구조이다.
세션이란 사용자가 터미널을 사용해 시스템에 로그인 했을 때 활동하는 상태 등을 관리하는 단위이다.
2. 긴 작업을 돌려야 하는 경우
1) ssh : timeout을 증가하거나, heartbeat를 주기적으로 보내도록 한다.
2) nohup : SIGHUP을 무시하도록 설정하고 프로세스 기동
길고 긴 프로세스 끝!
'CS 전공 > OS' 카테고리의 다른 글
[운영체제] 6. 스케줄링 (2) | 2024.04.21 |
---|---|
[운영체제] 5. 쓰레드 (1) | 2024.04.20 |
[운영체제] 3. 프로세스 (1) (0) | 2024.04.18 |
[운영체제] 2. 컴퓨터 아키텍쳐와 운영체제 (0) | 2024.04.16 |
[운영체제] 1. 운영체제 개요 (0) | 2024.04.15 |