1. program and process
program은 instruction, data, metadata등으로 구성된 disk에 저장되어 있는 파일이다.
program이 execute되면 memory에 이 코드들이 올라간다. memory에 올라와 있는, 실행 중인 running program을 process라고 한다.
예를들어 pwd라는 명령어를 치면 저장된 pwd program이 실행되고 pwd를 실행하는 process가 생성된다.
linux는 process마다 PID를 부여해서 관리한다.
2. fork()
user-level code에서도 fork() system call을 활용하면 우리도 process를 생성할 수 있다.
fork()를 호출하면 현재 parent process의 state를 그대로 복사해서 child process를 생성한다.
새로운 process 생성이 성공하면 fork()를 호출한 parent process에게는 new pid를 반환하고
새롭게 생성된 child process에게는 0을 반환한다.
parent process의 output은 parent parent and child data = 3 |
child process의 output은 parent and child data = 2 |
fork()가 실패했을 때 parent parent and children can’t fork, error |
3. process descriptor : task_struct
kernel은 이 process들을 task list로 관리한다. task list는 circular doubly linked list로 구현되어있다.
각 task list의 element인 task_struct를 process descriptor라고 하고 그 특정 프로세스의 모든 정보를 담고 있다.
task_struct의 구현은 <linux/sched.h>에서 살펴볼 수 있다.
linux는 각 process와 thread를 task라고 부른다.
초기에 process는 main thread 하나만 가지고 생성(task_struct1)된다.
이 프로세스가 thread2, thread3, thread4를 생성했다고 해보자.
그럼 task_struct2, task_struct3, task_struct4 세개가 추가로 생성되고 task list에서 관리된다.
4. process states
task가 생성되고 나면 ready상태가 된다. 실행 준비가 완료된 상태이다. run-queue에서 대기한다.
task가 cpu에 의해 실행되고 있을때 running 상태라고 한다.
task가 할당된 cpu 시간을 다 써서, 다른 task에게 cpu를 양보해야하면 preempted돼서 ready로 돌아간다.
실행중인 task에 interrupt가 걸리면 sleep시켜놓고 waiting(blocked) 상태가 된다. 인터럽트 처리가 완료됐거나 시그널이 도달하면 다시 ready 상태가 된다.
task가 아예 끝나면 terminated 상태가 된다.
리눅스에서는 아래처럼 process의 state를 manipulate한다.
TASK_RUNNING : running or ready(waiting in run-queue)
TASK_INTERRUPTIBLE : is sleeping, is blocked, when waiting condition becomes true or a signal is received, process's state becomes TASK_RUNNING
TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE와 동일하다. 단 interruption 없이 계속 대기해야 할때의 상태를 나타낸다.
TASK_STOPPED : task 종료
5. Task creation
linux user level에서는 fork(), clone(), vfork()를 통해 task를 생성할 수 있다.
kernel이 직접 task를 생성할 때는 Kerenel_thread()로 task를 생성한다.
fork()는 parent state를 전부 복사해서 child state를 만든다.
clone()은 parent state의 일부를 복사해서 child state를 만든다. 일부 state를 같이 공유할 수 있다.
clone flag에 따라서 share하는 process state subset을 달리할 수 있다.
만약 CLONE_VM이 set되었다면 parent와 child process는 동일한 메모리공간에서 실행된다. 그렇지 않다면 child process는 복사해온 새롭게 분리된 메모리 공간에서 실행된다.
cpu 코어마다 red-black tree로 run-queue가 구현되어있는데 _do_fork가 수행되면 copy state, allocate new PID 이후 run-queue에 막 생성한 new task를 삽입한다.
files_struct_structure는 열려있는 파일의 목록을 관리하는 자료구조이다. process가 복사될때 파일도 복사해오는게 아니라 files_struct_structure를 만들어서 열려있는 파일의 정보를 가져온다.
7. Copy on Write
do_fork()로 child process가 생성될 때, 모든 state에 해당하는 page frame을 복사해오는 일은 상당한 시간이 소요되는 무거운 작업이다.
그래서 write가 수행될 때, copy를 하자는 발상을 하게 되었다.
일단은 child process를 생성할 때 state를 전부 복사해오지 말고, child 혹은 parent가 state를 변경할 때, parent로부터 copy를 해오자는 아이디어이다. 만약 read만 할때는 parent로부터 읽어온다.
따라서 do_fork()가 수행되면 각 task마다 유지되는 page table만 일단 복사해온다.
이후 parent가 100번지에 해당하는 page frame을 변경하려고하면 그때 해당하는 page frame을 child에게 실제로 복사해서 넘긴다.
8. Signal
SIGINT 2: ctrl+c를 누르면 보내는 신호를 말한다. 실행중인 process를 kill한다.
interrupt handler를 우리가 직접 custom해서 singal이 왔을때 처리할 작업을 지정할 수 있다.
'ComputerScience > Linux' 카테고리의 다른 글
Linux 10. Synchronization (1) | 2023.10.22 |
---|---|
Linux 9. Thread (1) | 2023.10.22 |
Linux6. Makefile (0) | 2023.09.28 |
Linux4. Design Principle of Linux Kernel (0) | 2023.09.28 |
Linux3. System Call (1) | 2023.09.28 |