# Spawn threads using `clone()` ###### tags: `linux2021` 測試環境: `5.4.0-80-generic` 若按照 *linux kernel scheduler internals* 所說明的 flags 去使用 `clone` 函式的話,會發現其所創建的新的 tasks 並沒有和 parent 有相同的 `pid`。 測試程式碼 (檔名為 `test.c`): ```c=1 #define _GNU_SOURCE #include <pthread.h> #include <sched.h> #include <semaphore.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #define NUM_THREAD 10 sem_t mutex; int do_someting(void *nothing) { /* from man page: gettid() is Linux-specific and should not be used in * programs that are intended to be portable. */ pid_t pid = getpid(); #ifdef __linux__ pid_t tid = gettid(); #else pid_t tid = getpid(); #endif sem_wait(&mutex); printf("pid = %d, tid = %d\n", pid, tid); fflush(stdout); sem_post(&mutex); return 0; } int main(void) { sem_init(&mutex, 0, 1); // stack size for cloned child const int STACK_SIZE = 65535; for (int i = 0; i < NUM_THREAD; i++) { // start and end of stack buffer area void *stack, *stackTop; stack = malloc(STACK_SIZE); stackTop = stack + STACK_SIZE; // spawns a new thread clone(&do_someting, stackTop, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0); } sleep(1); return 0; } ``` :::info 第 46 行的 `sleep(1)` ,是為了要等待所有 child 執行結束,parent 才能繼續執行並回傳,因為在使用 `CLONE_THREAD` 建立的 threads,在 child 結束時並不會傳送 `SIGCHLD` signal。不確定是否因為利用此 flag 建立的 thread 是 detached 的狀態,因此在書中才沒有加上此 flag。 from `man 2 clone`: A new thread created with `CLONE_THREAD` has the same parent process as the process that made the clone call (i.e., like `CLONE_PARENT`), so that calls to getppid(2) return the same value for all of the threads in a thread group. When a `CLONE_THREAD` thread terminates, the thread that created it is not sent a `SIGCHLD` (or other termination) signal; nor can the status of such a thread be obtained using wait(2). (The thread is said to be **detached**.) ::: 對應的編譯命令為: ``` gcc test.c -lpthread -o test ``` 在第 44 行中,按照原本 *linux kernel scheduler internals* 的 flags 來呼叫 `clone()`,會有以下輸出: ``` $ ./test pid = 151648, tid = 151648 pid = 151649, tid = 151649 pid = 151650, tid = 151650 pid = 151651, tid = 151651 pid = 151656, tid = 151656 pid = 151657, tid = 151657 pid = 151652, tid = 151652 pid = 151653, tid = 151653 pid = 151654, tid = 151654 pid = 151655, tid = 151655 ``` 可以發現每個利用 `clone` 生出來的 task,都有自己不同的 pid,且與 tid 相同,代表每個人都是 thread group leader,雖然因為 `clone()` 的 flags 設定,在許多資源上是共用的,但若是從書中提到的 pid 和 tid 的關係來看,會認為這些是獨立的 processes 而非 threads。 若將第 44 行加上 `CLONE_THREAD`,則會有以下輸出: ``` $ ./test pid = 153531, tid = 153532 pid = 153531, tid = 153539 pid = 153531, tid = 153534 pid = 153531, tid = 153535 pid = 153531, tid = 153540 pid = 153531, tid = 153541 pid = 153531, tid = 153536 pid = 153531, tid = 153537 pid = 153531, tid = 153538 pid = 153531, tid = 153533 ``` 可以發現大家的 pid 都相同了,只有 tid 不同,這樣的行為較符合書中所描述的 pid 和 tid 的關係。