printf
도 시스템 콜인가요? 그건 표준 C 라이브러리. 라이브러리가 write
시스템 콜을 호출함.pid_t fork(void)
:
fork
)을 사용해야 한다.fork()
를 하면 동일한 프로세스가 만들어짐.pid_t getpid(void)
: 자신의 (진짜) PID.pid_t getppid(void)
: 부모의 PID.fork
직후의 인스트럭션을 실행한다.fork
하는 시점의 메모리 섹션을 그대로 복제하므로, 변수도 fork
시점의 값을 갖는다.exec
family:
exec("vi")
하면 vi를 실행하는 프로세스가 됨.fork
를 날리죠. 그러면 탐색기 프로세스가 하나 더 만들어집니다. 근데 파워포인트를 실행해야하니 exec
로 스스로를 교체.void exit(int status)
:
pid_t wait(int *wstatus)
: 부모 프로세스는 wait
시스템 콜을 통해 자식의 반환 값을 기다림.pid_t waitpid(pid_t pid, int* status, int options)
: 특정 자식을 기다림.void abort(void)
: 특정 프로세스와 그 부모까지 삭제.wait
를 하지 않는다면?init
프로세스의 자식으로 만든다. init
은 주기적으로 wait
을 호출해 자식을 정리.task_struct
로 구현된다.shm_open
send
, receive
를 이용해서 프로세스간 메시지 교환을 할 수 있음.sigaction
으로 어떤 시그널을 받았을 때 어떤 행동을 하라고 정의할 수 있음.fork
어떤 시그널은 복사가 되지만, 어떤 시그널은 안 됨.pipe[1]
에 쓴 값을 pipe[0]
에서 읽을 수 있음. 프로세스 하나에서만 쓰면 그냥 버퍼같겠지.pipe[1]
에 값을 쓰면 부모가 pipe[0]
에서 읽을 수 있다. 물론 그 반대도 가능.fork
해서 두 개의 스레드로 분리, 각 스레드의 결과는 join
으로 병합.#pragma omp parallel
같은 디렉티브를 만나면 코어 개수만큼 스레드를 만들고 알아서 병렬 처리.fork
와 exec
는 싱글코어를 상정한 것.fork
하면 어떻게 되나? 스레드도 다 복사되나?
A call to forkall() replicates in the child process all of the threads in the parent process.
A call to fork1() replicates only the calling thread in the child process. (…) In Solaris 10, a call to fork() is identical to a call to fork1(); only the calling thread is replicated in the child process. This is the POSIX-specified behavior for fork().
forkall
: 부모의 모든 스레드를 자식으로 복사한다. (강의에서는 fork
로 소개함.)fork
, fork1
: 호출한 스레드만 복사한다.exec
는 멀티스레딩이어도 상관없죠. 어떤 스레드 하나가 exec
하면 그냥 프로세스의 모든 스레드가 죽음.for (i = 0; i < 10; i++) {
- LOCK();
- sum_global = sum_global + i
- UNLOCK();
sum_tls = sum_tls + i;
}
+ LOCK();
+ sum_global = sum_tls;
+ UNLOCK();
errno
값을 다룰 때도 유용하다:
errno
를 -1
로 설정, 이어서 들어온 다른 스레드가 errno
를 0
으로 덮는 문제.errno
를 TLS로 만들면 문제를 해결할 수 있음.task_struct
로 구현됨.
task_struct
.clone
:
clone() creates a new process, in a manner similar to fork(2). (…) The main use of clone() is to implement threads: multiple threads of control in a program that run concurrently in a shared memory space.
task_struct
를 만든다.task_struct
를 만들 수 있다. 즉, 스레드를 만드는 것.bool ready = false;
void wait(void) {
while (!ready) {
do_nothing();
}
}
void signal(void) {
ready = true;
}
ready
변수를 레지스터에서 읽도록 최적화하면 문제가 생김.volatile
키워드를 사용하면 매번 메모리에서 변수를 읽도록 개발자가 강제할 수 있다.in == out
일 때 empty, (in + 1) % SIZE == out
일 때 full.in
과 out
을 비롯한 공유 자원에 접근하는 임계 영역에 락을 걸어주면 해결.00004AFE
가 들어오면, 00004
는 VPN, AFE
는 오프셋.00004
를 참조한다. 여기에 대응되는 PFN이 6
이라고 한다면…46AFE
. 이때 46
은 PFN, AFE
는 오프셋.fork
가 매우 쉬워짐:
fork
를 하면 부모의 페이지를 복사하는 대신, 자식이 부모와 같은 페이지 프레임을 바라봄.malloc
도 CoW로 동작한다:
malloc
을 하는 시점에는 아무 일이 일어나지 않음.jmp 0xc0000100
처럼 커널 주소공간으로 점프하면 커널의 시스템 콜을 실행.!R && !M
: 최근에 참조도 안 됐고, 수정도 안 됐으므로 바로 교체 가능.!R && M
: 참조는 안됐는데 교체 전에 파일에 쓰기가 되어야 함.R && !M
: 수정은 안 됐는데 조만간 다시 참조될 가능성이 높음.R && M
: 최근에 참조됐고, 수정도 일어났음.malloc
으로 공간 할당받고 GPU 작업을 수행하는 상황을 생각해보자./proc
이나 /tmp
디렉토리..exe
, .jpg
처럼 이름에 표시.#!/bin/sh
하는 것.rm
이 없고 unlink
가 있음.