프로세스의 개념

  • 프로그램이 실행되고 있는 상태(CPU)
  • 2가지 key abstractions
    • Logical control flow : 혼자 cpu를 독자적으로 쓰는 것과 같은 느낌을 줌 -> 그러나 동시에 여러가지 프로세스가 실행중임
      1. 빠르게 스위칭을 하여 multitasking -> 여러 프로세스 실행
    • private viretual addess space : 시스템에 있는 메인 메모리를 프로그램 혼자 전부 사용하는 것과 같은 효과
      1. CPU마다 virtual memory라는 가상메모리를 제공
      2. 프로세스가 스위칭 될 때마다 예전의 프로세스의 내용을 디스크에 저장하고 새로운 프로세스를 읽어옴

Context Switching

  • 수십, 수백개의 프로세스들이 각자 CPU하나를 독자적으로 사용하고 있다고 느끼게 함

  1. CPU의 코어가 1개라고 가정
  2. process는 A와 B 두 개
  3. processA가 10ms가 진행된 후 process B로 switching시켜줌 -> kernel의 역할 : A의 레지스터값과 메모리의 상태들을 저장하고 B가 수행되기 위해 필요한 값들을 복원
  4. processB 실행
  5. 10ms후에 A로 스위칭 -> 레지스터값과 메모리 상태를 저장 후 A로 옮기고 A의 정보를 불러옴
  6. 반복 -> 짧은 시간동안 많은 switching이 일어나기 때문에 1개의 cpu를 사용하고 있는 것 같은 착각을 하게됨

Process States

  1. Idle : 처음에 생성되어 cpu에 할당, 준비 안된상태
  2. Runnable : cpu에서 돌아가지는 않지만 cpu에서 돌아갈 준비가 다 되었다는 상태
  3. Running : cpu를 할당받아 cpu상에서 돌아가는 상태
  4. Sleeping : 특정한 이벤트가 일어날때 까지 기다리는 상태 -> ex)디스크에서 값을 읽어 오는 경우 불러오는 시간동안 sleeping -> 디스크에서 값을 읽어오면 Runnable상태로 / 타이머의 경우도 비슷함
  5. 돌아가는 상태의 프로세스에서 중단 Signal을 받게되면 프로세스를 중단시킬 수 있음 ->  다시 실행 Signal을 받으면 실행됨 - ex) ctrl + c
  6. 수행이 모두 끝나고 나면 Exit로 종료 -> 끝나기 전에 Zombified라는 상태가 될 수 있음 - 부모프로세스가 종료된 프로세스의 뒷처리를 해야함 이것이 완벽하게 안끝난 상태

Process의 구성

  • 크게 5개의 부분으로 나뉨
  • a code area : 프로그램이 메모리에 로드되어 실행되는 것 -> 프로그램의 코드가 필요, 이것이 메모리에 저장된 것
  • a data area : 변수들이나 melloc으로 할당된 메모리들
  • a stack area : 함수를 호출할 때, stack에 데이터들이 쌓임
  • a user area : 해당 프로세스에대한 정보들을 가지고 있음
  • page tables : 사용하고 있는 메모리에대한 정보를 가지고 있음
  • 예제 : x86에서 돌아가는 리눅스의 메모리 맵 - 프로세스

  • 0번부터 시작하여 총 4GB : 1GB는 kernel에서 맵핑되어 사용되고 3GB는 사용자 프로세스에 의해 사용됨
  • 위 그림은 하나의 프로세스에 대한 메모리 맵
  • stack, code, data(heap - malloc) 등 저장되어있음
  • 4GB가 1개의 프로세스
  • 만양 100개의 프로세스라면 400GB의 메모리가 필요한 셈 -> 가상메모리를 사용
  • 가상메모리 : 스위칭 되는 시점에서 사용됨

Process Table

  • 커널은 어떤 프로세스가 시스템에서 돌아가고 있는지에 대한 총괄적인 정보 저장
  • 각각의 프로세스마다 테이블의 엔트리 차지
    • 프로세스의 부모프로세스 ID
    • 프로세스를 소유한 user나 group
    • 프로세스의 현재 상태(6가지)
    • code나 data, stack, user area들이 어디에 저장되어 있는지
    • pending된 signal들

  1. 각각의 프로세스에 대한 entry
  2. PID(프로세스 ID), PPID(부모 process ID), Stat 등에 대한 정보들 저장
  3. process들을 스위칭 시키며 스케줄링 -> 순서를 정함

Unix/Linux의 process관리

  • 처음에 부팅시 1개의 process만 존재 - Init(sbin/init) -> PID는 1
  • 프로세스를 만듦 - 기존에 있는 프로세스를 복제하는 방식 -> init(parent process)에서 복제(child process -> init에서 만들었으므로 PPID = 1)
  • 그러므로 init은 모든 프로세스들의 조상프로세스
  • 프로세스들은 fork()라는 시스템콜로 복제 -> parent process와 child process는 완전히 동일(PIDs, PPIDs만 다름) - ex) code, data, stack을 그대로 카피
  • 예를들어 init이 fork로 init을 만들었다면? -> 이 자식프로세스가 프로그램 파일을 가지고 어떤 작업을 수행하길 원함
  • code를 원하는 실행파일의 코드로 바꿔줌 -> execve() 시스템 콜 이용 -> 원하는 프로세스가 만들어짐

  • fork()를 통해 여러 process를 만들어 놓음 -> 그 후 exec(getty) (= getty라는 프로세스 실행)와 같이 쓰면 프로세스의 code가 바뀜
  • getty : login을 handling -> 로그인 메시지를 처리
  • login후 fork(bash) -> bash를 실행하는 자식프로세스로 변경됨
  • shell prompt에서 명령어를 입력하여 프로세스를 실행시키는 과정
    1. 콘솔이 깜빡임 : shell process가 수행이 되고 있는 상태
    2. 실행파일의 이름을 입력하고 엔터
    3. shell process자체를 복제시킴 - fork이용
    4. 원하는 명령어를 수행하기위해 코드부분을 우리가 지정한 실행파일의 코드로 대치 -> exec()
    5. 실행파일의 코드를 수행하는 자식 프로세스가 생성됨
    6. shell process는 자식프로세스가 명령어를 수행하고 끝나기를 기다림 -> wait()
    7. 자식 프로세스가 종료되면 부모 프로세스가 깨어나서 뒷처리 후 다음 shell prompt를 화면에 표시

 

  1. A라는 shell process(parent)가 있다고 가정
  2. 실행파일 입력 후 엔터
  3. fork()실행
  4. process 복제(child)
  5. execv라는 시스템 콜을 실행
  6. 코드영역이 B로 바뀌면서 다른 process가 됨
  7. 수행 후 종료 -> exit()
  8. parent process는 자식프로세스가 진행되는 동안에도 같이 실행될 수 있음 -> wait()로 기다림
  9. child process가 끝나면 대기하고 있던 parent process가 수행을 재기 -> prompt 깜빡거림

Process 관리 시스템 콜

  • getpid : 프로세스 ID를 가져오기 위함
  • getppid : 부모 프로세스의 ID를 가져옴
  • fork : 자신을 복제하여 자식 프로세스를 만들기 위함
  • exec : 코드부분을 바꿔서 프로그램을 실행
  • exit : 프로세스를 종료하면서 상태변수 전달
  • wait : 자식프로세스가 끝날때까지 기다리는 역할
  • getpid(2), getppid(2)

 

    • getpid : process ID를 받아오는 함수 : 양의 정수형태, 아무도 사용하지 않는 번호가 있으면 사용
    • getpptid : parent process의 process ID를 받아오는 함수
    • init process : process ID가 항상 1
  • fork(2)

 

  • 자기자신을 복제하여 새로운 프로세스를 만듦
  • 두 프로세스는 완전히 똑같은 프로세스 -> PID와 PPID만 다름
  • pid_t 타입을 return - return을 두 번하는 시스템 콜
  • parent process는 child process의 PID를 child process는 0이라는 값을 return값으로 받음
  • 예제

 

  1. fork()라는 시스템 콜을 불렀을 경우 완전히 똑같은 프로세스를 복사
  2. parent process는 child process의 PID가 리턴, child process는 0이 리턴
  3. fork된 시점부터 process는 두 개
  4. parent process는 retrun값이 0이 아니므로 else문으로
  5. child process는 return값이 0이므로 if문으로
  6. 마지막 printf 수행
  • 예제2

  1. 어떤 함수를 수행하는 프로세스가 있음
  2. L0 print
  3. fork하면 동일한 process가 실행됨
  4. L1 print 두 번 -> 부모, 자식 프로세스가 각각 실행
  5. fork 하면 부모 프로세스와 자식 프로세스가 각각 동일한 process를 만듦
  6. L2를 네 번 출력
  7. fork -> 8개의 프로세스
  8. Bye를 8번 출력
  9. 스케줄러가 프로세스들의 순서를 정하기 때문에 우선순위를 주지 않는 이상은 순서를 예측할 수 없음
  • exec(3) family

 

  • execve를 이용하여 만든 라이브러리들이 모여있음
  • fork를 통해서 생성된 프로세스가 코드부분을 새로운 실행파일의 코드로 바꿈
  • parent process와 다른 코드를 실행하게 됨
  • execve(실행시키고 싶은 경로이름, 벡터형태로 인자 전달, 환경변수들을 벡터형태로 전달)
  • v가 붙어있는 exec함수들 : 벡터로써 argument들을 전달
  • l이 붙어있는 exec함수들 :  argument들을 순서대로 전달 - 쓰고 싶은 만큼 씀
  • e가 붙어있는 exec함수들 : 환경변수를 넘겨줄 수 있음
  • p가 붙어있는 exec함수들 : file name을 넘겨주고 경로를 지정된 PATH라는 절대경로에서 찾음
  • 예제

 

  1. fork값이 0이 나온다면(자식 프로세스)
  2. execve(프로그램, argv, 환경변수) 넘겨줌
  3. 리턴값이 0보다 작으면 에러메시지를 띄우고 아니라면 프로그램을 실행
  • exit(3), atexit(3)

  • 정상적으로 종료시킬때 사용하는 시스템 콜
  • 정상적이지 않은 경우는? -> 외부에서 signal을 받아 강제종료하는경우
  • child process가 parent process에게 SIGCHLD라는 signal을 보내면 wait하고 있다가 프로세스의 status를 받아서 처리
  • function포인터 (함수에 대한 포인터) : 함수를 등록해놓으면 exit하기 전에 함수를 호출해줌
  • 예제

  1. cleanup이라는 함수 등록
  2. fork후 exit하기 전 cleanup을 부름
  3. 부모 자식 둘 다 cleanup
  • wait(2), waitpid(2)

  • 자식 프로세스가 종료되기를 기다림
  • 종료된 프로세스가 보내준 status를 가져와서 진행
  • return값은 종료된 프로세스에 대한 ID
  • status value

 

  1. 오른쪽 8bit가 0이면 process가 정상 종료되었다는 의미
  2. status code는 왼쪽의 8bit에 저장
  3. 외부 signal에 의해 종료된 경우는 그 signal의 정보가 오른쪽에 저장
  4. 테스트 매크로를 사용하여 status값을 조작 가능
  • 특정 프로세스를 종료하기까지 기다리는 경우 waitpid(pid, status값, option-종료될 때까지 기다리는 것이 아니라 상태변화 했을 경우 등 여러 옵션들로 지정할 수 있음 -> 정교한 조작 가능)
  • 예제

  1. N번 반복하여 자식 프로세스 생성
  2. 0이 리턴되면 -> child 종료 - exit(100 + i)
  3. for문을 이용하여 parent process들이 wait
  4. 종료된 child process들의 status가 변경되어 전달
  5. WIFEXITED : 정상종료되었는지 정보를 return -> 정상종료시 1 아니면 0
  6. WEXITSTATUS : status코드 중에서 status부분만 추출(앞 8bit)
  7. 종료될 때 까지 대기하고 정상종료 되면 printf 메시지 출력

Orphan process

  • 자식 프로세스가 실행되는 중 부모 프로세스가 종료?
  • parent process가 없는 child process가 됨 -> 이것이 orphan process
  • PPID = 1로 세팅하여 init프로세스가 입양한 것 처럼 됨
  • exit(staus)할 경우 init프로세스로 전달됨

Zombie process

  • child process가 exit된 경우 완전히 종료 된 것이 아님 -> parent process가 wait하여 child process의 status를 가져가야 완전히 종료 된 것
  • parent process가 wait를 안한 경우 : 시스템 리소스들은 모두 삭제되지만 완전히 종료는 안됨
  • 이러한 경우를 Zombie process라고 함

+ Recent posts