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を更新していると言う事なのでしょうか・・・・? 




