임베디드/[ Linux Kernel ]

[ Linux Kernel ] 06. Child Process 생성하기

kim.svadoz 2020. 8. 15. 14:54
728x90
반응형

8. Child Process 생성하기


컴퓨터를 부팅하면 제일 먼저 커널 프로세스가 로드된다. 그리고 이 커널은 터미널이 켜질때 마다 그에 해당하는 Shell, 즉 Child Process를 만든다. Shell은 사용자의 입력을 기다리고 입력이 들어오면 그에 따른 작업을 수행해주는 프로그램이다. 사용자가 Mail이라고 입력하면 Mail이라는 Child Process가 생성 된다. 이처럼 프로세스들이 진행될 때는 자식 프로세스(Child Process)가 생성되면서 진행된다. 따라서 커널을 공부할 때 Child Process는 반드시 알아야하는 개념이다. 지금부터의 설명은 아래 그림과 함께 살펴보도록 한다.

image-20200806164057432

# Note
잠시 여기서 프로그램과 프로세스의 차이에 대해 알아보자. 프로그램과 프로세스의 차이는 명확하다. 프로그램은 보조 기억 장치에서 실행이 되기만을 기다리는 정적인 데이터의 집합이고, 프로그램이 명령어와 데이터와 함께 메모리에 적재되면 프로세스가 되는 것 이다. 즉, 프로세스란 실행 중인 프로그램을 뜻한다.

1강에서 배웠듯 프로그램에는 User StackKernel Stack이 존재한다. User Stack은 프로그램에서 function을 사용할 때 사용된다. Kernel Stack은 유저 모드에서 시스템 콜을 통해 커널의 function들을 사용할 때 필요한 자료구조로 프로그램 실행에 필요한 Local Variable들을 저장하기 위한 공간이다. 만약 자료구조를 가변적인 Stack구조로 사용하지 않고 늘 공간을 확보해둔다면, 프로그램 크기가 엄청 커지고 운영 비용만 비싸질 것이다.

지금부터는 Child Process를 생성하기 위한 과정들을 살펴볼 것이다. Child Process 생성을 위해서는 먼저 Process의 정보가 들어있는 PCB(Process Control Block)를 만들고 그 PCB에 해당하는 Process를 만들어 줘야한다.

진행 순서는 아래와 같다.

  1. PCB 공간을 만들어 준다. 초기값으로 Parent Process의 PCB를 복사해온다. Parent가 사용하던 Resource(터미널, 키보드, 스크립트)를 자식 프로세스도 사용하게 되는 것이다. Parent Process의 실행 환경이 Child Process의 실행 환경이 된다.
  2. Child Process가 들어갈 수 있는 메모리 공간을 확보하여 초기값을 지정한다. 이를 위해 커널은 Memory의 Data Structure에 가서 빈 메모리 공간을 찾아 공간을 지정해준다. 지정된 공간에 Child Process의 값들을 넣기 전에 먼저 Parent Processimage를 똑같이 복사를 해준다. 이 이유는 후에 등장한다. 프로세스 처리과정을 간편화하기 위해 복사한다고 일단 기억해두자.
  3. 디스크로부터 Child Process새로운 image를 로드한다.
  4. 새로 생긴 Child ProcessPCBCPU의 ready queue에 등록하여 CPU를 사용 할 수 있게끔 준비해준다. (아직까지 CPU는 Parent Process가 사용 중 이기 때문이다.)

이러한 4가지 과정을 시스템 콜의 용어로 정리하면 두가지로 정리할 수 있는데,

  1. 1번과 2번의 과정을 Fork라고 부른다. (Parent와 동일한 것을 만든다.)
  2. 3번과 4번의 과정을 Exec이라고 부른다. (디스크로 부터 새 이미지를 읽어온다.)

8.1 Fork

일단 Fork(포크)에 대해 알아보기 전에 Fork는 한번 호출하면 두번 리턴한다라는 개념으로 기억하자. 지금은 이해가지 않더라도 일단 이 사실을 받아들이고 설명을 읽어보자.

두번의 리턴 중 첫번째 리턴은 Parent Process가 본인이 가지고 있는 Process 상태를 그대로 Child Process에 복사하고 CPU의 ready queueChild Process를 등록 시켜놓고 다시 Parent Process로 리턴하는 과정이다.

# Note
단순히 함수를 호출한 후 리턴해서 그 다음 실행흐름으로 위치했다는 의미다. 프로그램적으로 너무나도 당연한 과정이다.

그 후 ready queue에 등록되어 대기중이었던 Child Process가 CPU를 점유하게 된다. Child Process가 실행되는데, Child Process는 만들어질 당시 Parent Process와 동일한 PCB(Process Control Block) 즉, 같은 State Vector를 가지고 생성되었기 때문에 Fork를 호출하고 난 바로 그 다음 진행 시점에서 실행된다.

Child ProcessParent Process가 가지고 있는 정보들 뿐만 아니라 프로그램 진행 상황까지 완전히 똑같은 상태를 가지게 되고 이런 현상 때문에 Child Process 또한 Fork에서 리턴하게 된다.

그렇기 때문에 한번 Fork를 해서 두번 돌아온다는 표현이 생긴 것 이다. 단, 운영체제가 이런 두 가지의 return으로 일어나는 혼동을 막기위해 리턴하는 값은 다르게 해준다. 지금까지 설명한 과정을 아래 그림과 함께 살펴보자.

image-20200806164202055

Fork가 두번 리턴되는데 한번은 Parent Process로, 한번은 Child Process으로 리턴한다. 그리고 리턴할 때의 값은 pid 값이다. pid는 Process Id라는 의미로 이는 유닉스 시스템에서 각 프로세스에게 할당하는 고유 식별값이다. pid값이 0이라면 가면 Child Process를 의미하고 그게 아니라면 현재 실행 중인 프로세스는 Parent Process다.

# Note
fork()는 두 번 리턴하는데, 각 리턴값은 다음과 같다. Child Process에게는 0값을 리턴하고 Parent Process에게는 Child Process의 pid(process id)를 리턴한다.

아래의 프로그램을 리뷰하면서 내용을 정리해보자.

image-20200806164234130

fork.c라는 이름을 가진 소스파일이고, fork를 호출하는 프로그램이다. 프로그램의 출력 결과를 예상 해보고 확인하면서 지금까지 배운 fork를 리뷰해본다.

fork()를 호출하면 위 그림에 나와 있는 코드가 그대로 복사되어 Child Process에게 할당된다. 하나 더 생성이 되는 것이다. 그럼 Parent ProcessChild Process는 서로 같은 코드와 상태를 가지고 있게 되는 것이다.

fork()호출의 리턴값 pid의 값에 따라 Child Process가 실행되거나 Parent Process가 실행된다. 결국엔 둘 다 실행되겠지만 둘 중 누가먼저 실행되는지 위 코드에서는 정확히 파악하기 어렵다. 보통은 부모 프로세스가 먼저 실행된다.


이번 강의에서는 System Call이 일어나는 절차에 대해 System Call Wrapper Routine, System Call Number 등을 배웠고 커널이 프로세스들과 하드웨어들을 관리하기 위해 정보를 모아둔 Data Structure인 metadata에 대해서 배웠다. 또 마지막으로 Child Process의 생성과정 중 Fork에 대해 간략히 알아보았다. 다음 3강에는 Fork를 좀 더 자세히 살펴보고 Exec에 대해 설명한다.


728x90
반응형