从源码角度看Linux进程组和线程组

1.进程ID

线程组ID:

  • 设置了CLONE_THREAD flag创建的进程(线程)同属于同一个线程组,拥有同一个线程组ID(TGID)。
  • pthread_create创建线程的时候,底层通过clone函数实现就指定了CLONE_THREAD参数,即一个进程中的各个线程属于同一个线程组。
  • 如果一个进程没有使用线程,则其PID=TGID。
  • 线程组中的主进程叫组长(group leader)。Linux中task_struct结构体中的group_leader指向线程组组长。

进程组ID:

  • 独立进程可以通过setpgrp合并成进程组。
  • 进程组组长的PID即进程组ID。
  • 管道连接的进程包含在同一个进程组中。
  • 父进程和其子进程属于同一个进程组。
  • 进程组组长进程退出,其他进程并不会因此退出。比如父进程退出,子进程不是必须退出。

2. 图解PID PPID PGID关系

getpid getpgid返回值的值:

父进程中调用:

getpid = 69485           getpgid = 69485

子进程中调用:

getpid = 69485           getpgid = 69485.

3.源码看进程的各种ID

分析进程相关的ID直接的过程是分析进程/线程创建的过程

fork.c::copy_process

    p->pid = pid_nr(pid);

    //指定CLONE_THREAD标志,新建进程的group_leaer就是当前进程的group_leader,tgid是当前进程的tgid
    //典型场景:pthread_create创建线程
    if (clone_flags & CLONE_THREAD) {
        p->exit_signal = -1;
        p->group_leader = current->group_leader;
        p->tgid = current->tgid;
    } else {
        //没有指定CLONE_THREAD,新建进程的group_leader指向新建进程的task_struct,tgid指向新建进程的pid.
        if (clone_flags & CLONE_PARENT)
            p->exit_signal = current->group_leader->exit_signal;
        else
            p->exit_signal = (clone_flags & CSIGNAL);
        p->group_leader = p;
        p->tgid = p->pid;
    }

    ...

    if (likely(p->pid)) {
        ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);

        //设置新建进程task_struct成员pids[PIDTYPE_PID]指向pid
        init_task_pid(p, PIDTYPE_PID, pid);
        if (thread_group_leader(p)) {
            //如果是线程组组长,设置新建进程task_struct成员pids[PIDTYPE_PGID]指向当前进程的pid.
            init_task_pid(p, PIDTYPE_PGID, task_pgrp(current));
            init_task_pid(p, PIDTYPE_SID, task_session(current));

            if (is_child_reaper(pid)) {
                ns_of_pid(pid)->child_reaper = p;
                p->signal->flags |= SIGNAL_UNKILLABLE;
            }

            p->signal->leader_pid = pid;
            p->signal->tty = tty_kref_get(current->signal->tty);
            list_add_tail(&p->sibling, &p->real_parent->children);
            list_add_tail_rcu(&p->tasks, &init_task.tasks);
            attach_pid(p, PIDTYPE_PGID);
            attach_pid(p, PIDTYPE_SID);
            __this_cpu_inc(process_counts);
        } else {
            current->signal->nr_threads++;
            atomic_inc(&current->signal->live);
            atomic_inc(&current->signal->sigcnt);
            list_add_tail_rcu(&p->thread_group,
                      &p->group_leader->thread_group);
            list_add_tail_rcu(&p->thread_node,
                      &p->signal->thread_head);
        }
        attach_pid(p, PIDTYPE_PID);
        nr_threads++;
    }

内核ID相关函数:

//用户态gettid系统调用即该函数
static inline struct pid *task_pid(struct task_struct *task)
{
    return task->pids[PIDTYPE_PID].pid;
}

//用户态getpid系统调用即该函数
static inline struct pid *task_tgid(struct task_struct *task)
{
    return task->group_leader->pids[PIDTYPE_PID].pid;
}

/*
 * Without tasklist or rcu lock it is not safe to dereference
 * the result of task_pgrp/task_session even if task == current,
 * we can race with another thread doing sys_setsid/sys_setpgid.
 */

//用户态getpgid函数对应该函数
static inline struct pid *task_pgrp(struct task_struct *task)
{
    return task->group_leader->pids[PIDTYPE_PGID].pid;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值