반응형

유닉스(리눅스)시스템에서 모든 프로세스는 PID가 1인 init 프로세스의 자손이다. init 부트 과정의 최종단계에서 커널이 실행하는 프로세스다

 

시스템의 모든 프로세스는 정확히 하나의 부모 프로세스를 가지며 모든 프로세스는 하나이상의 자식 프로세스를 가질수 있다. 같은 부모 프로세스를 가지는 자식 프로세스를 형제(sibling)이라고 부른다.

 

task_struct 구조체에는 부모의 task_struct를 가리키는 parent라는 포인터와 task_struct 리스트를 가리키는 children 포인트가 있으며 결과적으로 주어진 현재 프로세스에서 부모 프로세스의 task_struct를 얻을 수 있다.

 

<부모 프로세스 찾기>

struct task_struct *my_parent = current->parent;

 

<자식 프로세스 찾기>

struct task_struct *task;

struct list_head *list;

 

list_for_each(list, current->children)

{

task = list_entry(list, struct task_struct, sibling)

// 이제 task는 현재 프로세스의 자식 프로세스 중 하나를 가르킨다

}

 

 

프로세스 생성

 

대부분의 운영체제는 스폰(Spawn)방식을 사용하여 새로운 주소공간에 새 프로세스를 만들고, 실행 파일을 읽은 다음 그 코드를 실행한다. 유닉스&리눅스는 이 과정을 fork(), exec()라는 두 함수로 나누어 실행한다

 

먼저 fork()는 현재 태스크를 복제해 자식 프로세를 만든다. 이렇게 만들어진 프로세스는 PID(프로세스의 고유값), PPID(부모 프로세스의 PID), 상속되지 않은 지연된 시그널과 같은 일부자원, 통계수치를 제외하고는 부모와 동일하다. 다음으로 exec()는 새로운 실행파일을 주소공간에 불러오고 실행하게 된다.

 

Copy-on-Write (기록사항 발생시 복사, COW)

 

리눅스에서는 copy on write 페이지를 이용해 fork() 함수를 구현했다. COW기능은 데이터 복사를 지연 또는 방지하는 기능이다. 프로세스 주소공간을 복사하는 대신 부모와 자식 프로세스가 같은 공간을 공유한다.

 

그러나 기록 사항이 발생해 데이터 변경이 필요하면 그 순간 사본이 만들어 지고 각 프로세스가 별도의 내용을 가지게 된다. 따라서 리소스 복사는 해당 리소스에 대한 기록이 발생하는 경우에만 발생한다.

 

이 방법은 주소공간에 실제 기록작업이 일어 날때가지 각 페이지의 복사 작업을 지연시키며 프로세스가 절대 기록을 하지 않는 경우 복사가 필요 없어 진다.

 

리눅스의 스레드

 

스레드를 이용해 메모리 주소공간을 공유하는 같은 프로그램 여러개를 동시에 실행 할 수 있다. 스레드를 통해 동시 프로그래밍(Concurrent Programming)이 가능하고, 다중 프로세서 시스템에서는 진정한 병렬처리를 구현 할 수 있다.

 

리눅스는 기본적인 프로세스로 모든 스레드를 구현한다.  별도의 자료구조나 스케줄링 기법없이 특정 자원을 다른 프로세스와 공유하는 특별한 프로세스로 다루게 된다. 각 스레드는 task_struct 구조체를 가지고 있으며 커널 입장에서는 주소공간과 같은 자원을  다른 프로세스와 공유하고 있는 프로세스이다.

 

스레드의 생성

 

정상적인 프로세스(태스크)와 같은 방식으로 만들어지지만 clone() 시스템에서 특정 자원을 공유할 수 있도록 Flag를 지정해 준다.

 

커널 스레드

 

커널의 일부 동작을 백 그라운드에서 실행하는 것이 좋을 때가 있다 . 커널 공간에서만 존재하는 표준 프로세스인 커널 스레드를 이용해 백그라운드 작업을 수행 할 수 있다.

 

정상 프로세스와의 차이점은 커널 스레드에는 주소공간이 없다는 점이다. (mm포인터 값이 NULL) 커널 스레드는 커널 공간에서만 동작하며 사용자 공간에서만 동작하며 사용자 공간으로 컨텍스트 전환이 일어나지 않는다. 하지만 커널 스레도도 스케줄링이 되며 선점이 가능하다.

 

대표적인 예로는 flush 및 ksoftirq 작업을 예로 들 수 있다. 리눅스는 kthreadd 커널 프로세스가 모든 커널 스레드를 만드는 방식으로 커널 스레드를 관리한다.

 

struct task_struct *kthread_create(int (*threadfn)(void *data),
                                             void *data,
                                                 const char namefmt[], ...);

 

kthread_create()는 date 매개변수를 통해 전달된 threadfn 함수를 실행하며 namefmt문자열에 해당하는 이름을 갖게 된다.

 

 

처음 프로세스는 실행할 수 없는 상태로 만들어지기 때문에 wake_up_process()함수를 통해 명시적으로 깨워주지 않으면 실행되지 않는다.

 

커널 스레드는 한번 시작되면 스스로 do_exit() 함수를 호출하거나, 커널의 다른 부분에서 kthread_create()가 반환한 task_struct 구조체의 주소와 함께 kthread_stop() 함수를 호출할 때 까지 계속 실행된다
 

 

 

반응형

+ Recent posts