[OS] - 핵심 개념 정리(1) - Process
1. Process의 정의
Process = 실행중인 프로그램
- 커널에 등록되고 커널의 관리하에 있는 작업
- 각종 자원들을 요청하고 할당 받을 수 있는 개체
- 프로세스 관리 블록(PCB)을 할당 받은 개체
- 능독적인 개체(active entity)
 - 실행 중에 각종 자원을 요구, 할당, 반납하며 진행
여기서 Job은 실행 할(아직 실행하지 않은) 프로그램이다 !
2. Resource의 개념
커널의 관리 하에 프로세스에게 할당/반납 되는 수동적 개체(passive entity)
| 분류 | H/W resources | S/W resources | 
|---|---|---|
| 종류 | Processor Memory disk monitor keyboard Etc. | Message signal files installed SWs Etc. | 
3. PCB(Process Control Block)
각 프로세스들에 대한 상태정보 저장
PCB가 관리하는 정보
| 정보 종류 | 의미 | 
|---|---|
| PID | 프로세스 고유 식별 번호 | 
| 스케줄링 정보 | 프로세스 우선순위 등과 같은 스케줄링 관련 정보들 | 
| 프로세스 상태 | 자원 할당, 요청 정보 등 | 
| 메모리 관리 정보 | Page table, segment table 등 | 
| 입출력 상태 정보 | 할당 받은 입출력 장치, 파일 등에 대한 정보 등 | 
| 문맥 저장 영역 | 프로세스의 레지스터 상태를 저장하는 공간 등 | 
| 계정 정보 | 자원 사용 시간 등을 관리 | 
PCB 참조 및 갱신 속도는 OS의 성능을 결정 짓는 중요한 요소 중 하나! 아주 중요!!
4. Process States
| 상태 | 자원 할당 상태 | ||
|---|---|---|---|
| Active (swapped-in) | Running Ready Blocked, aleep | 프로세서 O 프로세서 X, 기타 자원 O 프로세서 X, 기타 자원 X | 메모리 O | 
| Suspend (swapped-in) | Suspended ready Suspended block | 프로세서 X 프로세서 X, 기타 자원 X | 메모리 X | 

| State | 의미 | 이동 가능 | 
|---|---|---|
| Create State | 작업(Job)을 커널에 등록 PCB 할당 및 프로세스 생성 | Ready or Suspended ready | 
| Ready State | Processor 외에 다른 모든 자원을 할당 받은 상태 = 즉시 실행 가능 상태 | Dispatch (or Schedule) Ready state -> running state | 
| Running State | Processor와 필요한 자원을 모두 할당 받은 상태 | Preemption Running -> Ready States (Processor scheduling ex) time-out, priority changes) Block/sleep Running state -> asleep state (I/O등 자원 할당 요청) | 
| Blockd/Asleep State | Processor 외에 다른 자원을 기다리는 상태 | Wake-up Asleep state -> ready state | 
| Suspended State | 메모리를 할당 받지 못한(빼앗긴) 상태 | Swap-out(suspended) ready / asleep-> suspended ready / blocked Swap-in(resume) suspended ready / blocked-> ready / asleep | 
| Terminated / Zombie State | 프로세스 수행이 끝난 상태 | 
5. Process 관련 system call
1. fork()
- fork(): 자식 프로세스를 생성 및 생성된 프로세스의 id(PID)를 반환한다.
#include <unistd.h>
pid_t fork(void);
#include <unistd.h>
pid_t fork(void);| Argument | Description | 
|---|---|
| void | – | 
| Return (PID) | Description | 
|---|---|
| 0 | Child process인 경우 | 
| 0보다 큰 경우 | Parent process인 경우 | 
| -1 | 비정상적 종료가 발생한 경우 | 
#include <stdio.h>
#include <unistd.h>
int main() { 
    fork();
    printf("Hello\n");
    return 0;
}위 코드를 수행하게 되면 둘 다 동일하게 printf("Hello\n");를 호출하게 되는데 이는 race condition 때문에 누가 먼저 호출하는지 알 수 없다.
여기서
Race condition이란 두 개 이상의 프로세스가 공통 자원을 병행적으로(concurrently) 읽거나 쓰는 동작을 할 때, 공용 데이터에 대한 접근이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 같지 않고 달라지는 상황을 말한다. Race의 뜻 그대로, 간단히 말하면 경쟁하는 상태, 즉 두 개의 스레드가 하나의 자원을 놓고 서로 사용하려고 경쟁하는 상황을 말한다.
2. Process 종료
생성된 process는 exit 함수 library를 호출해 작업을 종료하게 되며, 만약 비정상 종료를 할 경우 signal을 호출하게 된다.
정상적인 종료를 할 경우에는
- Open한 file descriptor close
- Memory de-allocation
- exit함수는 standard I/O 정리 roution을 수행 후- _exit을 호출
- C compiler는 main함수에서 return하는 경우 자동으로exit함수 호출되도록 코드를 생성
Example (zombie process 생성)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
    pid_t pid;
    pid = fork();
    if(pid == 0) {
        for (int i=0; i<3; i++) {
            printf("\t\t\tchild\n");
            sleep(1);
        }   
        exit(0);
    }   
    while(1) {
        printf("parent\n");
        sleep(1);
    }   
    return 0;
}$ ./a.out 
 parent
             child
 parent
             child
 parent
             child
 parent
 parent위 코드를 수행시키면 위와 같이 3초가 지나면 child process는 exit(0) 호출 뒤에 더 이상 "child"라는 단어를 출력하지 않게 된다. 이 때 child process는 죽은 것이 아니라 TASK_RUNNING state에서 EXIT_ZOMBIE state로 변하게 된다. Parent가 child의 종료 값을 wait(0)로 가져가야 몸체를 파괴 할 수 있다. 위 코드는 wait(0)를 사용하지 않고 exit(0)만을 사용했기 때문에 child process는 죽지 않는 상태가 된다.
$ ps -ef | grep a.out
oddong   18961 18373  0 23:37 pts/24   00:00:00 ./a.out
oddong   18962 18961  0 23:37 pts/24   00:00:00 [a.out] <defunct>실제로 ps 명령어를 통해 보면 child process인 18962가 죽지 남아 있는 것을 볼 수 있다.
3. Zombie Process vs. Orphaned Process
Zombie process: child process가 종료했으나 parent process의 “wait” 처리가 끝나지 않은 child process를 말한다. 즉, parent process 입장에서 child process의 종료 상태를 회수하지 못한 경우다. 이 때 Kernel은 process ID, status, CPU 사용 시간 등 정보를 가지고 있다.
Parent process가 zombie process의 종료 상태를 회수하게 되면 (wait system call을 통해) zombie process는 제거된다.
Orphaned process: 고아 프로세스라고 불리며, child process가 종료하기 전에 parent process가 수행을 마친 경우의 child process를 말한다. 이 때 Kernel은 child process를 찾아서 “init” process의 child가 되도록 한다.
Example (no concurrency)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
    pid_t pid;
    pid = fork();
    if(pid == 0) {
        for (int i=0; i<3; i++) {
            printf("\t\t\tchild\n");
            sleep(1);
        }   
        exit(7);
    }   
    wait(0);
    while(1) {
        printf("parent\n");
        sleep(1);
    }   
    return 0;
}$ ./a.out 
             child
             child
             child
 parent
 parent
 parent
 parent 위 코드는 child process가 3초동안 동작한 뒤에 exit(7)을 통해 종료하고 난 뒤에 parent process가 wait(0)에서 깨어나서 동작하는 프로그램이다. 이런 경우 zombie process가 되는 것을 막을 수 있지만 concurrency가 보장되지 못하여 multi process의 의미를 상실하게 된다.
4. Signal
Child process가 종료 될 때 exit 함수를 호출하면서 zombie process가 되는데, 이 때 parent process PID를 통해 signal (SIGCHLD)을 보내게 되는데, default로는 SIGCHLD -> SIG_IGN로 변경하게 되어있다.
SIG_IGN: signal을 무시하도록 하는 신호
따라서 parent process가 asynchronous하게 SIG_IGN 신호 대신 wait를 호출시켜서 zombie process를 수거해주는 코드가 필요하다.
Example (zombie process 제거)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void sig(int sig_no) {
    printf("sig(%d)\n", sig_no);
    printf("PID: %d\n", getpid());
    wait(0);
}
int main() {
    pid_t pid;
    printf("Parent PID: %d\n", getpid());
    signal(SIGCHLD, sig);
    pid = fork();
    if(pid == 0) {
        printf("Child PID: %d\n", getpid());
        for (int i=0; i<3; i++) {
            printf("\t\t\tchild\n");
            sleep(1);
        }   
        exit(7);
    }   
    while(1) {
        printf("parent\n");
        sleep(1);
    }   
    return 0;
} Parent PID: 16384
 Child PID: 16385
             child
             child
             child
 sig(17)
 PID: 16384
 parent
 parent
 parent
 parent
 parent 따라서 기존에 wait()를 그냥 사용하게 되면 parent process가 child process가 죽을 때 까지 기다리는 문제가 있었는데, signal을 통해서 concurrency를 살릴 수 있게 됐다.
Author And Source
이 문제에 관하여([OS] - 핵심 개념 정리(1) - Process), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@odh0112/OS-핵심-개념-정리1-Process저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)