# 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 的關係。