execのpid/tid/sid/pgid
execでコマンドを実行すると、実行元タスクがスレッドなら、プロセスID等は実行元タスクのグループリーダ(通常は親プロセス)のプロセスID等に更新されます。
子プロセスのスレッド有無のチェックは、thread_group_empty(tsk)で行います。tskがスレッドでもthread_group_empty(tsk)はFALSEとなります。従ってgoto no_thread_groupはスレッドを有しないプロセスのみです。
存在する子スレッドを削除し、if (!thread_group_leader(tsk))で、tskがスレッドの場合の処理となります。
スレッドの場合、leader = tsk->group_leaderは親プロセスです。tsk->group_leader = tskでexecはスレッドグループリーダとなり、親プロセスIDをexecのtskのPIDTYPE_PIDとし、親プロセスのpid/tidと同値となり、transfer_pid()は、スレッドリストのヘッドのleaderをtskに差し替えることで、スレッドのtask_structがスレッドグループリーダとなります。
親だったプロセスは、exeのスレッドと名実ともに入れ替わり、leader->exit_state = EXIT_DEAD/release_task(leader)で親のリソースは解放されます。
検証サンプル
#define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/syscall.h> #include <unistd.h> void get_pid(char *); static int childFunc(void *arg) { get_pid("clone"); execl("./show_pid", "./exe_pid", NULL); eturn 0; } #define STACK_SIZE (1024 * 1024) 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)); }show_pid.c
#include <stdio.h> #include <sys/syscall.h> void main() { printf("%-20s: pid=%d, tid=%d, sid=%d gid=%d\n", "exe cmd", syscall(SYS_getpid), syscall(SYS_gettid), syscall(SYS_getsid, 0), syscall(SYS_getpgid, 0)); }
結果
スレッドグループリーダのプロセス(pid=tid)なら、cloneプロセスのidとなり、cloneタスクと差し替えます。[root@localhost test]# ./a.out parent : pid=12069, tid=12069, sid=11772 gid=12069 clone : pid=12070, tid=12070, sid=11772 gid=12069 exe cmd : pid=12070, tid=12070, sid=11772 gid=12069スレッド(pid != tid)なら、スレッドの親プロセスのidとなり、thread parentと差し替えます。(呼び出し元スレッドのcloneはkillされます。)
[root@localhost test]# ./a.out thread thread parent : pid=12072, tid=12072, sid=11772 gid=12072 clone : pid=12072, tid=12073, sid=11772 gid=12072 exe cmd : pid=12072, tid=12072, sid=11772 gid=12072execlは、struct linux_binfmtの.load_shlibコールバックからflush_old_exec()がコールされ、該当するタスクと差し替えます。その時、de_thread()で差替えられるプロセスの処理と、差替えるプロセスのIDが設定されます。
static struct linux_binfmt elf_format = { .module = THIS_MODULE, .load_binary = load_elf_binary, .load_shlib = load_elf_library, .core_dump = elf_core_dump, .min_coredump = ELF_EXEC_PAGESIZE, };tskは差替えられるタスクです。thread_group_empty(tsk)は子スレッドを有しているかチェックします。子スレッド有しているなら、そのスレッド削除します。子のプロセスは親とリソースは独立しているため問題ありません。
子プロセスのスレッド有無のチェックは、thread_group_empty(tsk)で行います。tskがスレッドでもthread_group_empty(tsk)はFALSEとなります。従ってgoto no_thread_groupはスレッドを有しないプロセスのみです。
存在する子スレッドを削除し、if (!thread_group_leader(tsk))で、tskがスレッドの場合の処理となります。
スレッドの場合、leader = tsk->group_leaderは親プロセスです。tsk->group_leader = tskでexecはスレッドグループリーダとなり、親プロセスIDをexecのtskのPIDTYPE_PIDとし、親プロセスのpid/tidと同値となり、transfer_pid()は、スレッドリストのヘッドのleaderをtskに差し替えることで、スレッドのtask_structがスレッドグループリーダとなります。
親だったプロセスは、exeのスレッドと名実ともに入れ替わり、leader->exit_state = EXIT_DEAD/release_task(leader)で親のリソースは解放されます。
static int de_thread(struct task_struct *tsk) { struct signal_struct *sig = tsk->signal; struct sighand_struct *oldsighand = tsk->sighand; spinlock_t *lock = &oldsighand->siglock; if (thread_group_empty(tsk)) goto no_thread_group; spin_lock_irq(lock); if (signal_group_exit(sig)) { spin_unlock_irq(lock); return -EAGAIN; } sig->group_exit_task = tsk; sig->notify_count = zap_other_threads(tsk); if (!thread_group_leader(tsk)) sig->notify_count--; while (sig->notify_count) { __set_current_state(TASK_UNINTERRUPTIBLE); spin_unlock_irq(lock); schedule(); spin_lock_irq(lock); } spin_unlock_irq(lock); if (!thread_group_leader(tsk)) { struct task_struct *leader = tsk->group_leader; sig->notify_count = -1; /* for exit_notify() */ for (;;) { write_lock_irq(&tasklist_lock); if (likely(leader->exit_state)) break; __set_current_state(TASK_UNINTERRUPTIBLE); write_unlock_irq(&tasklist_lock); schedule(); } tsk->start_time = leader->start_time; BUG_ON(!same_thread_group(leader, tsk)); BUG_ON(has_group_leader_pid(tsk)); detach_pid(tsk, PIDTYPE_PID); tsk->pid = leader->pid; attach_pid(tsk, PIDTYPE_PID, task_pid(leader)); transfer_pid(leader, tsk, PIDTYPE_PGID); transfer_pid(leader, tsk, PIDTYPE_SID); list_replace_rcu(&leader->tasks, &tsk->tasks); list_replace_init(&leader->sibling, &tsk->sibling); tsk->group_leader = tsk; leader->group_leader = tsk; tsk->exit_signal = SIGCHLD; leader->exit_signal = -1; BUG_ON(leader->exit_state != EXIT_ZOMBIE); leader->exit_state = EXIT_DEAD; if (unlikely(leader->ptrace)) __wake_up_parent(leader, leader->parent); write_unlock_irq(&tasklist_lock); release_task(leader); } sig->group_exit_task = NULL; sig->notify_count = 0; no_thread_group: tsk->exit_signal = SIGCHLD; if (current->mm) setmax_mm_hiwater_rss(&sig->maxrss, current->mm); exit_itimers(sig); flush_itimer_signals(); if (atomic_read(&oldsighand->count) != 1) { struct sighand_struct *newsighand; newsighand = kmem_cache_alloc(sighand_cachep, GFP_KERNEL); if (!newsighand) return -ENOMEM; atomic_set(&newsighand->count, 1); memcpy(newsighand->action, oldsighand->action, sizeof(newsighand->action)); write_lock_irq(&tasklist_lock); spin_lock(&oldsighand->siglock); rcu_assign_pointer(tsk->sighand, newsighand); spin_unlock(&oldsighand->siglock); write_unlock_irq(&tasklist_lock); __cleanup_sighand(oldsighand); } BUG_ON(!thread_group_leader(tsk)); return 0; }copy_process()で、スレッドはグループリーダの親のthread_groupをヘッダーとするリストに必ず登録され、初期値であるlist->next = listは、スレッドならlist->next != listです。従ってthread_group_empty()はFALSEとなります。
copy_process() { : INIT_LIST_HEAD(&p->thread_group); : if (clone_flags & CLONE_THREAD) { current->signal->nr_threads++; atomic_inc(¤t->signal->live); atomic_inc(¤t->signal->sigcnt); p->group_leader = current->group_leader; list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group); } : } static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } static inline int thread_group_empty(struct task_struct *p) { return list_empty(&p->thread_group); } static inline int list_empty(const struct list_head *head) { return head->next == head; }