[OS] 프로세스의 작업과 관련 시스템콜

RepDay1·2023년 2월 27일
0

운영체제

목록 보기
3/8

이화여대 반효경 교수님의 강의, 유튜브 주니온님의 운영체제 강의 내용들을 정리한것

프로세스의 작업

  • os는 프로세스 생성, 종료를 위한 메커니즘을 제공

  • 프로세스는 여러 개의 새로운 프로세스를 생성할 수 있음

    • 생성된 프로세스 : 상위 프로세스
    • 새로 생성된 프로세스 : 상위 프로세스가 fork()를 통해 생성한 자식 프로세스
  • 프로세스 트리
    image

  • 두 가지의 실행 방식

    • 상위 프로세스와 하위 프로세스가 concurrently(병행)으로 동시에 실행
    • 상위 프로세스는 자식 프로세스의 일부 또는 모든 자식 프로세스가 terminated될 때까지, wait상태로 유지
  • 주소 공간의 두 가지 방식

    • 하위 프로세스의 context가 같으면, 상위 프로세스의 주소 공간을 복제
    • 새로운 프로그램을 로드하는 경우,
int main()
{
    pid_t pid; //프로세스 id
    pid = fork(); //fork child 프로세스
    if(pid < 0){// error 발생
        fprintf(stderr, "Fork Failed");
        return 1;
    }
    else if(pid==0){ //child 프로세스
        execlp("/bin/ls","ls",NULL);
    }
    else { //parent process
        wait(NULL);
        printf("Child Complete(Terminated)")
    }
    return 0;
}

image

  • 프로세스의 종료

    • 프로세스의 마지막 instruction(return 0;) 실행이 끝나면 종료,
    • exit() 시스템 콜 : os한테 delete를 요청
    • OS는 프로세스에 관련된 모든 리소스(할당 메모리, 파일,I/O버퍼 등)을 해제
  • zombi, orphan 프로세스

    • zombi process : 자식 프로세스가 이미 종료했는데, 부모 프로세스가 wait()를 호출하지 않아서 프로세스 정보만 남아 있는 상태
    • orphan process : 자신보다 상위 프로세스가 먼저 종료된 상태, 부모가 먼저 종료돼서 자식 프로세스만 남은 경우
    • 부모 프로세스가 자식 프로세스보다 먼저 종료되면 init 프로세스가 자식 프로세스의 새로운 부모 프로세스가 됨
    • 종료되는 프로세스가 발생할 때 커널은 이 프로세스가 누구의 부모인지 확인 후 커널이 자식의 부모 프로세스 ID를 init으로 바꿈
orphan process.c
int main() {
     
    pid_t childPid;
    int i;
     
    childPid = fork();
     
    if(childPid > 0) {  // 부모 프로세스
        printf("부모 PID : %ld, pid : %d\n",(long)getpid(), childPid);
        sleep(2);
        printf("부모 종료\n");
        exit(0);
    }
    else if(childPid == 0){  // 자식 코드
        printf("자식 시작\n");
         
        for(i=0;i<10;i++) {
            printf("자식 PID : %ld 부모 PID : %ld\n",(long)getpid(), (long)getppid());
            sleep(1);
        }
         
        printf("자식 종료\n");
        exit(0);
    }
    else {  // fork 실패
        perror("fork Fail! \n");
        return -1;
    }
     
    return 0;
}

실행 결과

image

고아 프로세스가 작업을 종료하면 init 프로세스가 wait함수를 호출하여 고아 프로세스의 종료 상태를 회수함으로써 좀비 프로세스가 되는것을 방지함


fork()

  • 새로운 프로세스는 fork()라는 시스템콜로 생성됨
  • 유닉스 계열에서의 자식 프로세스는 부모 프로세스의 주소 공간 복사본으로 구성되어있음
  • 두 프로세스는 fork() 이후의 명령어들을 계속 실행함
  • fork()의 반환값이 0이면 자식 프로세스, os커널이 부여한 id(양수)면 부모 프로세스, -1이면 fork실패
fork().c

int main(){
    pid_t pid;
    pid = fork();
    if( pid > 0){
        printf("부모 프로세스 실행 중 pid = %d\n", pid);
    }
    else if(pid == 0){
        printf("자식 프로세스 실행 중 pid = %d\n", pid);
    }
    
    return 0;
}

실행 결과
image

  • fork().c 과정 (부모프로세스 P0, 자식 프로세스 P1)
    • P0 메모리에 로드, fork() 인스트럭션 실행
    • P1가 P0의 메모리 정보를 복사하여, 새롭게 메모리에서 로드(Ready상태이므로 Ready큐에서 대기)
    • P0의 다음 인스트럭션 (printf()에 해당하는 부분) 실행,이때 pid는 양수이므로 if문이 실행됨
    • P0 exit() cpu 릴리즈 후 ready 큐에 있는 p1이 cpu를 할당받음,
    • P1 fork() 이후의 인스트럭션 (printf()에 해당하는 부분) 실행, 이때 pid는 0이므로 else if문 실행
    • P1 exit() cpu 릴리즈

wait() - fork() 호출 후

  • fork()호출 후 부모 프로세스는 계속 실행될 수 있음
  • 또는, 자식 프로세스가 계속 실행되는 동안 다른 작업이 없다면 wait() 시스템 콜을 호출 할 수 있음
  • wait()가 호출되면, wait큐로 이동하여, 자식 프로세스가 terminated될때 까지 기다림
wait().c
int main(){
    pid_t pid;
    pid = fork();
    if( pid > 0){ //부모 프로세스
        wait(NULL);
    } 
    printf("pid = %d\n", pid); 
    return 0;
}

실행 결과 image

  • wait().c 과정

    • P0 메모리에 로드, fork()실행
    • P1가 P0의 메모리 정보를 복사하여, 새롭게 메모리에서 로드(Ready상태이므로 Ready큐에서 대기)
    • P0의 다음 인스트럭션 wait() 실행, pid가 양수이므로(부모 프로세스)
    • P0는 wait큐로 이동하고, cpu릴리즈
    • p1이 cpu를 할당받고, printf문 실행하고 난 후에 terminated되고, 인터럽트
    • wait큐에 있는 p0은 다시 cpu를 할당받아 나머지 인스트럭션 실행
  • waitpid()
    자식 프로세스가 종료될 때 까지 차단되는 것을 원하지 않을 경우, 옵션을 사용하여 차단을 방지함 또 기다릴 자식 프로세스를 좀더 상세히 지정 가능

pid_t waitpid(pid_t pid, int *status, int options);

인자로 주어진 pid 번호의 자식 프로세스가 종료되거나, 시그널 함수를 호출하는 신호가 전달될때까지 waitpid 호출한 영역에서 일시 중지
만약 pid로 지정한 자식이 waitpid 호출전에 이미 종료됐으면,함수는 즉시 리턴하고 자식 프로세스는 좀비프로세스로 남게됨
pid 값

  • pid < -1 : 프로세서 그룹 id가 pid의 절대값과 같은 자식 프로세스를 기다림
  • pid == -1 : 임의의 자식프로세스를 기다림 wait(2)와 같음
  • pid == 0 : 프로세스 그룹 ID가 호출 프로세스의 ID와 같은 자식 프로세스를 기다림
  • pid > 0 : 프로세스 ID가 pid의 값과 같은 자식 프로세스를 기다림
  • 상세한 내용 : manpage https://www.joinc.co.kr/w/man/2/waitpid

exec() 시스템 콜

  • exec()를 호출하면, exec()에 의해 호출된 프로세스의 context가 호출한 프로세스의 context에 덮어지게됨
  • exec()는 6가지의 변형이 존재함, execl, execle, execlp, execv, execvp, execve

image

  • EX) execlp()
int main(){
    pid_t pid;
    pid = fork();

    if( pid == 0){
        execlp("/bin/ls","ls",NULL);
        printf("LINE J\n");
    }
    else if(pid>0){
        wait(NULL);
        printf("Child Complete\n");
    }
    return 0;
}

실행 결과 image

자식 프로세스의 context는 execlp로 인해 부모 프로세스의 복사본에서 execlp()에 의해 호출된 프로세스의 context로 바뀜(덮어쓰기)
그래서 execlp() 다음 명령어인 Line J는 출력이 안됨(부모프로세스는 if문에 의해 출력못하고..) 그 대신에 ls명령어가 실행됨(실제 ls명령어 결과하고 같다)

Reference
주니온 교수님 유튜브 채널
반효경 교수님 KOCW 운영체제

0개의 댓글