CLONE_THREAD
cloneシステムコールをCLONE_THREADフラグによるpid/tid/sid/gidの検証サンプルです。
pidとgidが同じプロセスはグループリーダとなります。シェルがコマンドを起動する時、fork/cloneでプロセスを作成し、シェルがそのプロセスのgidをpidで更新して後、execで差し替えていると思います。(execが差し替えてるのかもしれません・・・)シェルで起動したコマンドプロセス毎に独自のpidをgidに設定することで、、以降の子プロセスはこのgidを継承することで、シェルコマンド下単位での管理が可能となります。
CLONE_THREADフラグは、 CLONE_SIGHANDが設定されてなければなりません。そしてCLONE_SIGHANDフラグは、CLONE_VMが設定されなければなりません。CLONE_THREADはCLONE_SIGHAND/CLONE_VMが設定されなければ、cloneはエラーとなります。
CLONE_SIGHANDはシグナル・ハン ドラを共有しますが、親プロセスのcurrent->sighand->countインクリメントするだけで、CLONE_VMはp->mm = current->mm/p->active_mm = current->mmとし、親プロセスのメモリ空間を共有します。故にCLONE_SIGHANDはcurrent->sighand->countインクリメントするだけでシグナルハンドラの共有が可能となり、CLONE_SIGHANDフラグは、CLONE_VMが設定されなければなりません。
#define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/syscall.h> #define STACK_SIZE (1024 * 1024) void get_pid(char *); static int childFunc(void *arg) { get_pid("clone"); return 0; } int main(int argc, char *argv[]) { char *stack; char *stackTop; pid_t pid; stack = malloc(STACK_SIZE); stackTop = stack + STACK_SIZE; if(argc == 2) { get_pid("thread parent"); pid = clone(childFunc, stackTop, CLONE_THREAD | CLONE_SIGHAND | CLONE_VM, NULL); } else { get_pid("parent"); pid = clone(childFunc, stackTop, 0, NULL); } sleep(1); waitpid(pid, NULL, 0); exit(EXIT_SUCCESS); } void get_pid(char* p) { printf("%-20s: pid=%d, tid=%d, sid=%d gid=%d\n", p, syscall(SYS_getpid), syscall(SYS_gettid), syscall(SYS_getsid, 0), syscall(SYS_getpgid, 0)); }SYS_getpid/SYS_gettid/SYS_getsid/SYS_getpgidシステムコールは以下の通りです。
pid=task->group_leader->pids[PIDTYPE_PID].pid tid=task->pids[PIDTYPE_PID].pid sid=task->group_leader->pids[PIDTYPE_SID].pid; gid=task->group_leader->pids[PIDTYPE_PGID].pid;
結果
[root@localhost test]# ./a.out parent : pid=3415, tid=3415, sid=3222 gid=3415 clone : pid=3416, tid=3416, sid=3222 gid=3415CLONE_THREADでない時、クーロン作成時、 p->group_leader = pで、p->pids[PIDTYPE_PID]にプロセス自身のIDが、p->pids[PIDTYPE_PGID]にcurrent->pids[PIDTYPE_PGID]が、p->pids[PIDTYPE_SID]にcurrent->pids[PIDTYPE_SID]が設定されます。pidとtidはプロセス自身のI同で、sid/gidは親プロセスを継承します。CLONE_THREADならpidとtidは同値になります。
[root@localhost test]# ./a.out THREAD thread parent : pid=3425, tid=3425, sid=3222 gid=3425 clone : pid=3425, tid=3426, sid=3222 gid=3425CLONE_THREADの時、p->group_leader = current->group_leaderで、p->pids[PIDTYPE_PID]のみプロセス自身のIDが設定されます。従ってpid/tid/sid/gidは親のそれを継承します。pidとtidが異なるプロセスはCLONE_THREADである。と言う事です。pidが同じで、そのpidとtidプロセスが違うプロセス等は、pidのプロセスからCLONE_THREADで作成されたプロセスと言う事です。
補足
検証結果のparent/hread parentのgidは、pidと同じです。forkからcloneをコールすると、必ずgidは親のgidを継承しますが、シェルコマンドでのgidはカレントのpidと同じで、親(シェル)のgidを継承していません。pidとgidが同じプロセスはグループリーダとなります。シェルがコマンドを起動する時、fork/cloneでプロセスを作成し、シェルがそのプロセスのgidをpidで更新して後、execで差し替えていると思います。(execが差し替えてるのかもしれません・・・)シェルで起動したコマンドプロセス毎に独自のpidをgidに設定することで、、以降の子プロセスはこのgidを継承することで、シェルコマンド下単位での管理が可能となります。
CLONE_THREADフラグは、 CLONE_SIGHANDが設定されてなければなりません。そしてCLONE_SIGHANDフラグは、CLONE_VMが設定されなければなりません。CLONE_THREADはCLONE_SIGHAND/CLONE_VMが設定されなければ、cloneはエラーとなります。
CLONE_SIGHANDはシグナル・ハン ドラを共有しますが、親プロセスのcurrent->sighand->countインクリメントするだけで、CLONE_VMはp->mm = current->mm/p->active_mm = current->mmとし、親プロセスのメモリ空間を共有します。故にCLONE_SIGHANDはcurrent->sighand->countインクリメントするだけでシグナルハンドラの共有が可能となり、CLONE_SIGHANDフラグは、CLONE_VMが設定されなければなりません。