getpid/gettid/getsid/getpgid
プロセスを管理するID情報は、プロセスID/スレッドID/セッションID/プロセスグループIDのgetpid/gettid/getsid/getpgidシステムコールを実装しています。
ただしstruct task_structのプロセスとして管理しているIDは、pids[]のPIDTYPE_PID/PIDTYPE_PGID/PIDTYPE_SIDの3つです。
スレッド(CLONE_FLAG)作成は、task->group_leader=親プロセス、そうでない場合task->group_leader=taskとなり、プロセスの場合、自身のpidが、スレッドの場合、親のpidがプロセスIDとなります。
p->group_leader = pとし、CLONE_THREAD時はp->group_leader = current->group_leaderで、getpid/gettid/getsid/getpgidはp->group_leaderから取得しますので、親のグループリーダを継承する事になります。
p->group_leader = pの時も、attach_pid()でpに親プロセスのPIDTYPE_PGID/PIDTYPE_SIDのIDをp->pids[PIDTYPE_PGID/PIDTYPE_SID]に設定しており、親のグループ/セッションIDを継承します。
最後に、CLONE_THREADに関係なくattach_pid(p, PIDTYPE_PID, pid)でalloc_pid()で新規取得した独自pidをp->pids[PIDTYPE_PID]に設定します。
struct task_struct のp->pid/p->tgidは、ルートネームスペース(新規ネームスペースを作成しなければはカレントネームスペース)のPIDTYPE_PIDが設定されます。カーネル2.xはp->pid/p->tgidからid等を取得していました。ネームスペースを実装していないカーネル下での残存ではと思います。
プロセス/スレッドに関係なく、プロセスグループID/セッションIDは継承されますが、シェルでコマンド起動する毎にプロセスグループID/セッションIDは、独自のIDが割り当てられてます。シェルがコマンド起動後にプロセスグループID/セッションIDを更新してるのではと思います・・・。
ただしstruct task_structのプロセスとして管理しているIDは、pids[]のPIDTYPE_PID/PIDTYPE_PGID/PIDTYPE_SIDの3つです。
enum pid_type { PIDTYPE_PID, PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_MAX };getpid()はカレントプロセスのtask->group_leader->pids[PIDTYPE_PID].pid配下のネームスペースのpidとなります。
スレッド(CLONE_FLAG)作成は、task->group_leader=親プロセス、そうでない場合task->group_leader=taskとなり、プロセスの場合、自身のpidが、スレッドの場合、親のpidがプロセスIDとなります。
SYSCALL_DEFINE0(getpid) { return task_tgid_vnr(current); } static inline pid_t task_tgid_vnr(struct task_struct *tsk) { return pid_vnr(task_tgid(tsk)); } static inline struct pid *task_tgid(struct task_struct *task) { return task->group_leader->pids[PIDTYPE_PID].pid; } pid_t pid_vnr(struct pid *pid) { return pid_nr_ns(pid, current->nsproxy->pid_ns); }gettidはカレントプロセス自身のpidのIDを取得します。プロセス/スレッドに関わらず、task->pids[PIDTYPE_PID].pidには、自身のIDが設定されます。プロセスではtask->group_leader=task故、gettid()とgetpid()は同じ値となり、スレッドでは違う値です。
SYSCALL_DEFINE0(gettid) { return task_pid_vnr(current); } static inline pid_t task_pid_vnr(struct task_struct *tsk) { return __task_pid_nr_ns(tsk, PIDTYPE_PID, NULL); } pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type, struct pid_namespace *ns) { pid_t nr = 0; rcu_read_lock(); if (!ns) ns = current->nsproxy->pid_ns; if (likely(pid_alive(task))) { if (type != PIDTYPE_PID) task = task->group_leader; nr = pid_nr_ns(task->pids[type].pid, ns); } rcu_read_unlock(); return nr; }pid_nr_ns()がpidの指定するネームスペースのIDを返します。pidはルートネームスペースから順に配列0からpid->numbers[]にstruct upidが設定されており、ns->levelがネームスペースの階層数となり、従ってpid->numbers[ns->level]はカレントプロセスのネームスペースのpidとなります。
pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns) { struct upid *upid; pid_t nr = 0; if (pid && ns->level <= pid->level) { upid = &pid->numbers[ns->level]; if (upid->ns == ns) nr = upid->nr; } return nr; }getsidは引数pidが0なら、カレントプロセスgroup_leaderのPIDTYPE_SIDを取得します。group_leaderはプロセス/スレッドなら自身/親で、スレッドは親のセッションIDを継承します。プロセスの自身のpidからの取得では、copy_process()でプロセス作成時、親のPIDTYPE_SIDが設定され、従ってプロセスも親を継承します。すなわち、プロセス/スレッド作成では、必ず親のセッションIDを継承するということです。
SYSCALL_DEFINE1(getsid, pid_t, pid) { struct task_struct *p; struct pid *sid; int retval; rcu_read_lock(); if (!pid) sid = task_session(current); else { retval = -ESRCH; p = find_task_by_vpid(pid); if (!p) goto out; sid = task_session(p); if (!sid) goto out; retval = security_task_getsid(p); if (retval) goto out; } retval = pid_vnr(sid); out: rcu_read_unlock(); return retval; } static inline struct pid *task_session(struct task_struct *task) { return task->group_leader->pids[PIDTYPE_SID].pid; }getpgid()はグループIDを取得します。PIDTYPE_SIDの代わりにPIDTYPE_PGIDからIDを取得するだけで、getsid()と実装は同じです。親のグループIDを継承するということです。
SYSCALL_DEFINE1(getpgid, pid_t, pid) { struct task_struct *p; struct pid *grp; int retval; rcu_read_lock(); if (!pid) grp = task_pgrp(current); else { retval = -ESRCH; p = find_task_by_vpid(pid); if (!p) goto out; grp = task_pgrp(p); if (!grp) goto out; retval = security_task_getpgid(p); if (retval) goto out; } retval = pid_vnr(grp); out: rcu_read_unlock(); return retval; } static inline struct pid *task_pgrp(struct task_struct *task) { return task->group_leader->pids[PIDTYPE_PGID].pid; }CLONE_THREAD時p->exit_signal=-1で、thread_group_leader(p)は真となります。
p->group_leader = pとし、CLONE_THREAD時はp->group_leader = current->group_leaderで、getpid/gettid/getsid/getpgidはp->group_leaderから取得しますので、親のグループリーダを継承する事になります。
p->group_leader = pの時も、attach_pid()でpに親プロセスのPIDTYPE_PGID/PIDTYPE_SIDのIDをp->pids[PIDTYPE_PGID/PIDTYPE_SID]に設定しており、親のグループ/セッションIDを継承します。
最後に、CLONE_THREADに関係なくattach_pid(p, PIDTYPE_PID, pid)でalloc_pid()で新規取得した独自pidをp->pids[PIDTYPE_PID]に設定します。
struct task_struct のp->pid/p->tgidは、ルートネームスペース(新規ネームスペースを作成しなければはカレントネームスペース)のPIDTYPE_PIDが設定されます。カーネル2.xはp->pid/p->tgidからid等を取得していました。ネームスペースを実装していないカーネル下での残存ではと思います。
static struct task_struct *copy_process(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *child_tidptr, struct pid *pid, int trace) { int retval; struct task_struct *p; int cgroup_callbacks_done = 0; : retval = -ENOMEM; p = dup_task_struct(current); if (!p) goto fork_out; : if (pid != &init_struct_pid) { retval = -ENOMEM; pid = alloc_pid(p->nsproxy->pid_ns); if (!pid) goto bad_fork_cleanup_io; } p->pid = pid_nr(pid); p->tgid = p->pid; if (clone_flags & CLONE_THREAD) p->tgid = current->tgid; : p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL); : p->group_leader = p; 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); } if (likely(p->pid)) { ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace); if (thread_group_leader(p)) { if (is_child_reaper(pid)) p->nsproxy->pid_ns->child_reaper = p; p->signal->leader_pid = pid; p->signal->tty = tty_kref_get(current->signal->tty); attach_pid(p, PIDTYPE_PGID, task_pgrp(current)); attach_pid(p, PIDTYPE_SID, task_session(current)); list_add_tail(&p->sibling, &p->real_parent->children); list_add_tail_rcu(&p->tasks, &init_task.tasks); __this_cpu_inc(process_counts); } attach_pid(p, PIDTYPE_PID, pid); nr_threads++; } : return p; } static inline bool thread_group_leader(struct task_struct *p) { return p->exit_signal >= 0; } void attach_pid(struct task_struct *task, enum pid_type type, struct pid *pid) { struct pid_link *link; link = &task->pids[type]; link->pid = pid; hlist_add_head_rcu(&link->node, &pid->tasks[type]); } static inline struct pid *task_pgrp(struct task_struct *task) { return task->group_leader->pids[PIDTYPE_PGID].pid; } static inline struct pid *task_session(struct task_struct *task) { return task->group_leader->pids[PIDTYPE_SID].pid; }プロセスグループ/セッションの名称としての意味合いなく、プロセスグループ/セッションと言うカテゴリとして意味合いとなります。
プロセス/スレッドに関係なく、プロセスグループID/セッションIDは継承されますが、シェルでコマンド起動する毎にプロセスグループID/セッションIDは、独自のIDが割り当てられてます。シェルがコマンド起動後にプロセスグループID/セッションIDを更新してるのではと思います・・・。
検証サンプル
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <sys/syscall.h> #include <sys/types.h> void get_pid(char* p); void* thread(void* pParam); int main(int argc, char *argv[]){ pthread_t tid; get_pid("parent"); if (fork()) { pthread_create(&tid, NULL, thread, NULL); pthread_join(tid,NULL); } else { get_pid("child"); } return 0; } void* thread(void* pParam) { get_pid("parent in thread"); if (!fork()) { get_pid("child in thread"); } } 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)); } [root@localhost test]# ./a.out parent : pid=4588, tid=4588, sid=4258 gid=4588 child : pid=4589, tid=4589, sid=4258 gid=4588 parent in thread : pid=4588, tid=4590, sid=4258 gid=4588 child in thread : pid=4591, tid=4591, sid=4258 gid=4588シェルからコマンド起動する毎に、そのプロセスはグループリーダとなり、親のgidを継承せず、自身のpidをgidとしています。上記認識では、gidは必ず親のgidを継承します。で、これはシェルが改めて更新してるものとの理解ですが、検証サンプルではプロセス自身でgidを表示させていますので、この設定はカーネルマターと言うことです。copy_process()の各呼び出し側で、gidを更新していると言う事なのでしょうか・・・・?