fork()
- 리눅스의 fork()는 clone() 시스템콜을 이용하여 구현된다. 이 함수는 또한 어떤 자원을 부모와 자식 프로세스가 공휴할 것인가를 지정하는 여러 플래그들을 사용한다
- 라이브러리함수인 fork(), vfork(), __clone()은 적절한 플래그를 사용해서, clone()를 호출하고, clone() 시스템콜은 다시 do_fork()함수를 호출한다.
- 프로세스 생성의 대부분의 작업은 do_fork()에서 처리되는데, 이것은 kernel/fork.c에 정의되어 있다. 이 함수는 copy_process()를 호출한다음, 프로세스를 시작시킨다.
- 여기서 copy_process() 함수의 동작이 중요한데, 그것은 다음과 같다.
*dup_task_struct()를 호출하여 새 커널 스택과 thread_info 구조체, 그리고 새 프로세스를 위한, 현재 태스크와 동일한 값들을 갖는 task-struct 구조체를 생성한다. 이 시점에서 자식과 부모의 프로세서 서술자는 완전히 동일하다.
* 다음으로 새 자식 프로세스의 생성이 현재 유저 프로세스 개수 제한을 위반하지 않는가를 체크한다.
* 이제 자식과 프로세와 부모프로세스를 구별할 필요가 있다. 프로세스 서술자의 많은 멤버들을 초기값으로 설정한다. 주로 프로세스 서술자의 통계관련 멤버들이 초기화된다. 프로세스 서술자의 데이터 대부분은 공유된다.
*다음, 자식의 상태를 TASK_UNINTERRUPTIBLE로 설정하여 아직 실행되지 않도록한다.
*이제, copy_flags()를 호출하여 task_struct 구조체의 flags멤버를 갱신한다. 이 때 태스크가 수퍼 유저 권한으로 실행되는가를 나타내는 PF_SUPERRIV 플래그가 초기화되며, 아직, exec()를 호출하지 않은 프로세스를 나타내는 PF_FORKNOEXEC플래그가 설정된다.
*다음으로 get_pid()를 호출하여 새 태스크에 가용한 PID를 부여한다.
*clone() 함수에 넘겨진 플래그에 따라 열린 파일, 파일 시스템 정보, 시그널 핸들러, 프로세스 주소영역, 네임스페이스 등을 복제하거나 고융한다. 이러한 자원들은 일반적으로 동일 프로세스의 스레드들 사이에 공유된다. 그렇지 않을 경우 개별적으로 이용되므로 이 단계에서 복제한다.
*마지막으로 새 자식 프로세스의 포인터를 반환한다.
do_fork() 함수로 돌아가자. 만약 copy_process()가 성공적으로 반환되면, 새 자식 프로세스가 깨어나서 실행된다. 커널은 의도적으로 자식 프로세스를 먼저 실행시킨다.
- 리눅스의 fork()는 clone() 시스템콜을 이용하여 구현된다. 이 함수는 또한 어떤 자원을 부모와 자식 프로세스가 공휴할 것인가를 지정하는 여러 플래그들을 사용한다
- 라이브러리함수인 fork(), vfork(), __clone()은 적절한 플래그를 사용해서, clone()를 호출하고, clone() 시스템콜은 다시 do_fork()함수를 호출한다.
- 프로세스 생성의 대부분의 작업은 do_fork()에서 처리되는데, 이것은 kernel/fork.c에 정의되어 있다. 이 함수는 copy_process()를 호출한다음, 프로세스를 시작시킨다.
- 여기서 copy_process() 함수의 동작이 중요한데, 그것은 다음과 같다.
*dup_task_struct()를 호출하여 새 커널 스택과 thread_info 구조체, 그리고 새 프로세스를 위한, 현재 태스크와 동일한 값들을 갖는 task-struct 구조체를 생성한다. 이 시점에서 자식과 부모의 프로세서 서술자는 완전히 동일하다.
* 다음으로 새 자식 프로세스의 생성이 현재 유저 프로세스 개수 제한을 위반하지 않는가를 체크한다.
* 이제 자식과 프로세와 부모프로세스를 구별할 필요가 있다. 프로세스 서술자의 많은 멤버들을 초기값으로 설정한다. 주로 프로세스 서술자의 통계관련 멤버들이 초기화된다. 프로세스 서술자의 데이터 대부분은 공유된다.
*다음, 자식의 상태를 TASK_UNINTERRUPTIBLE로 설정하여 아직 실행되지 않도록한다.
*이제, copy_flags()를 호출하여 task_struct 구조체의 flags멤버를 갱신한다. 이 때 태스크가 수퍼 유저 권한으로 실행되는가를 나타내는 PF_SUPERRIV 플래그가 초기화되며, 아직, exec()를 호출하지 않은 프로세스를 나타내는 PF_FORKNOEXEC플래그가 설정된다.
*다음으로 get_pid()를 호출하여 새 태스크에 가용한 PID를 부여한다.
*clone() 함수에 넘겨진 플래그에 따라 열린 파일, 파일 시스템 정보, 시그널 핸들러, 프로세스 주소영역, 네임스페이스 등을 복제하거나 고융한다. 이러한 자원들은 일반적으로 동일 프로세스의 스레드들 사이에 공유된다. 그렇지 않을 경우 개별적으로 이용되므로 이 단계에서 복제한다.
*마지막으로 새 자식 프로세스의 포인터를 반환한다.
do_fork() 함수로 돌아가자. 만약 copy_process()가 성공적으로 반환되면, 새 자식 프로세스가 깨어나서 실행된다. 커널은 의도적으로 자식 프로세스를 먼저 실행시킨다.
'OS이야기' 카테고리의 다른 글
커널 스레드 (0) | 2011.10.11 |
---|---|
리눅스의 스레드 구현 (0) | 2011.10.11 |
container_of 매크로 (1) | 2011.10.11 |
커널 (0) | 2011.10.11 |
삽입정렬 (0) | 2011.10.11 |