無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

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つです。
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(&current->signal->live);
               atomic_inc(&current->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を更新していると言う事なのでしょうか・・・・? 


最終更新 2015/06/12 19:03:59 - north
(2015/06/12 19:03:05 作成)