在 Linux 6.1(含 RT 分支)中,除早期启动阶段的静态 swapper/0(PID 0)外,系统里所有后续可调度任务——无论用户进程还是内核线程、无论称为“进程”还是“线程”(本质皆为 task_struct,仅以 clone 共享标志区分资源)——其创建路径最终都会汇入 copy_process()。

用户态通过 fork/vfork/clone/clone3 → kernel_clone() → copy_process() → wake_up_new_task()。

内核线程通过 kthread_create*() → kernel_clone() → copy_process()。

PID 1 的诞生亦是 rest_init() 先以 kernel_thread()(现为 kernel_clone())创建内核态 kernel_init 后再 execve 切换为 /sbin/init/systemd——需要强调,execve 仅替换当前映像并不新建任务,因而不经过 copy_process();在 PID 命名空间等场景中,新进程同样经由 clone/clone3 → kernel_clone → copy_process。综上,从 init 之后(严格说,从 PID 0 之后的“可调度任务”起),新建任务的唯一入口就是 copy_process();而诸如 execve、set*id、prctl 等操作并不创建新任务,自然不会触达该路径。


1、PID命名空间

1.1、Linux内核提供的宏

思路:遍历所有存在的进程或者线程 然后使用task_active_pid_ns接口获取​pid​命名空间

之前已经提过了,不管是用户进程还是用户线程以及内核线程其实都是用task_struct来进行维护的,也就是说在内核视角里面看线程和进程都是一个个task_struct对象。那么就很好理解了,如果遍历所有已经创建的task_struct对象不就相当于遍历了所有的线程吗,Linux内核已经提供的遍历所有线程的宏:

#define do_each_thread(g, t) \
    for (g = t = &init_task ; (g = t = next_task(g)) != &init_task ; ) do

#define while_each_thread(g, t) \
    while ((t = next_thread(t)) != g)

#define next_task(p) \
    list_entry_rcu((p)->tasks.next, struct task_struct, tasks)
    
static inline struct task_struct *next_thread(const struct task_struct *p)
{
    return list_entry_rcu(p->thread_group.next,
                  struct task_struct, thread_group);
}

展开看就很清楚:

for (g = t = &init_task ; (g = t = next_task(g)) != &init_task ; ) 
    do​ {
        
    }while​ ((t = next_thread(t)) != g);

1.2、从copy_process源码分析遍历依据

必须强调,所有的非init进程都是通过copy_process进行创建

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

        init_task_pid(p, PIDTYPE_PID, pid);
        if (thread_group_leader(p)) { //判断是否是线程组领导 
            ...
            list_add_tail_rcu(&p->tasks, &init_task.tasks);
            ...
        } else {
            ...
            list_add_tail_rcu(&p->thread_group,
                      &p->group_leader->thread_group);
            list_add_tail_rcu(&p->thread_node,
                      &p->signal->thread_head);
            ...
        }
 }

先给出上下文:p 是新建出来的 task_struct;pid 已经分配成功(likely(p->pid));随后:

  • ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace); 初始化跟踪(ptrace)相关状态,决定新任务是否处于被跟踪态及继承哪些 ptrace 标志。
  • init_task_pid(p, PIDTYPE_PID, pid); 把 ​线程级 PID​(PIDTYPE_PID)与 p 绑定:在所在的 PID 命名空间里把 pid → p 建立反向索引,同时填好 p->thread_pid 等。

接下来进入两个分支,核心差别是:​这是一个新的“进程(线程组)”的组长,还是现有进程里的一个新线程​。


​分支一:​​**thread_group_leader(p)​​ 为真 —— 新“进程”的组长**

当未设置 CLONE_THREAD(或等价条件成立)时,p 就是​线程组长​:tgid == pid。此时需要把“这个进程”纳入全局的进程​​链表​,以便系统级遍历能看到它:

list_add_tail_rcu(&p->tasks, &init_task.tasks);

要点说明:

  • &init_task.tasks 是全局进程​双向链表的锚点(以 init_task 为表头)。 把 p->tasks 挂到它的尾部后,诸如 for_each_process() 这样的遍历就能枚举到这个新进程。
  • 之所以用 list_add_tail_rcu(): 读侧(比如 for_each_process)大多在 rcu_read_lock() 下无锁遍历,全局链表的写侧采用 RCU 插入,缩小同步开销。
  • 线程组长此刻尚未把“线程成员”挂到 thread_group 链里——组长本身就是“组”的代表,后续如果再创建线程,线程会挂到组长的 thread_group 链上(见下一分支)。

直观效果(全局维度):

init_task.tasks  <->  ...  <->  p(tasks)  <->  ...

这意味着从“系统里有哪些进程”的视角,现在多了一个新的顶层进程实体。


分支二:否则 —— 在现有“进程”里新增一个线程

当设置了 CLONE_THREAD 等条件时,新任务不是组长,而是某个组长 ​​**p->group_leader​​​ 下面的一个线程​。这时需要做两件事:挂到线程组链与​共享信号体链**​。

list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);
list_add_tail_rcu(&p->thread_node,   &p->signal->thread_head);

要点说明:

  • thread_group: 这是同一线程组内的线程​​链表​(以组长的 thread_group 作为表头/锚点)。把 p 插进去,表示“这个线程属于该进程”。 传统约定:​组长自身不在这个链里​,链里挂的是“其他线程”。
  • signal->thread_head: struct signal_struct 是​线程组共享的信号体​(组长与各线程共用)。 把 p->thread_node 挂到 signal->thread_head,便于组级信号/作业控制/退出收敛等逻辑统一遍历整个线程组。
  • 为什么没有把 p 加到 init_task.tasks? 全局进程链表通常只挂​线程组长​。因此系统级的“进程枚举”(ps 的进程视图)不会因为增加线程就多出一个“进程”;但在按线程视图展示(如 ps -T、/proc/<tgid>/task/)时能看到它。

直观效果(组内维度):

(group_leader).thread_group  <->  ...  <->  p(thread_group)  <->  ...
(signal).thread_head         <->  ...  <->  p(thread_node)   <->  ...

这意味着从“该进程内部的线程成员”的视角,现在多了一个可被信号、退出收敛、调度统计等逻辑统一管理的线程节点。


1.3、测试模块源码

/* scan_active_pidns.c */
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pid_namespace.h>
#include <linux/hashtable.h>
#include <linux/slab.h>

#define NS_HASH_BITS 6​  ​// 最多 2^6 = 64 个命名空间(通常足够)
DEFINE_HASHTABLE(ns_hash, NS_HASH_BITS);

struct ns_entry {
    struct pid_namespace *ns;
    struct hlist_node hash_node;
};

static void free_ns_hash(void)
{
    struct ns_entry *entry;
    struct hlist_node *tmp;
    int bkt;

    hash_for_each_safe(ns_hash, bkt, tmp, entry, hash_node) {
        put_pid_ns(entry->ns);​  ​// 释放引用
        kfree(entry);
    }
}

static int add_ns_to_hash(struct pid_namespace *ns)
{
    struct ns_entry *entry;
    int found = 0;

    // 检查是否已存在
    hash_for_each_possible(ns_hash, entry, hash_node, (unsigned long)ns) {
        if (entry->ns == ns) {
            found = 1;
            break;
        }
    }

    if (!found) {
        entry = kmalloc(sizeof(*entry), GFP_KERNEL);
        if (!entry)
            return -ENOMEM;

        get_pid_ns(ns);​  ​// 增加引用,防止在遍历时被释放
        entry->ns = ns;
        hash_add(ns_hash, &entry->hash_node, (unsigned long)ns);
        return 1;​ ​// 新增
    }
    return 0;​ ​// 已存在
}

static int __init scan_active_pidns_init(void)
{
    struct task_struct *g, *t;
    int count = 0;

    printk(KERN_INFO "Scanning active PID namespaces...\n");

    // 初始化哈希表
    hash_init(ns_hash);

    rcu_read_lock();
    //遍历所有线程
    do_each_thread(g, t) {
        struct pid_namespace *ns = task_active_pid_ns(t);
        if (ns && add_ns_to_hash(ns) > 0) {
            count++;
            printk(KERN_INFO "Found active PID NS: level=%d, child_reaper=%d\n",
                   ns->level,
                   ns->child_reaper ? task_pid_nr(ns->child_reaper) : -1);
        }
    } while_each_thread(g, t);
    rcu_read_unlock();

    printk(KERN_INFO "Total active PID namespaces: %d\n", count);

    // 清理(实际使用中可保留供后续查询)
    free_ns_hash();

    return 0;
}

static void __exit scan_active_pidns_exit(void)
{
    printk(KERN_INFO "Active PID namespace scanner unloaded.\n");
}

module_init(scan_active_pidns_init);
module_exit(scan_active_pidns_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Scan all active PID namespaces by walking task list");

由于用到的函数或者宏 都是内核代码 所以要编写内核模块(可以理解为驱动)进行测试

模版编译的​Makefile

obj-m += scan_active_pidns.o

KDIR := /root/workspace/linux-rt-6.1.96/
PWD := $(shell pwd)

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean

1.4、使用模块进行测试

PID 命名空间有一个特殊限制:必须配合 fork() 或 clone() 使用,因为新命名空间中的第一个进程将成为该命名空间的“init”进程(即 child_reaper)。

unshare 命令本身不会自动 fork,因此需加上 --fork 参数:

# 创建新 PID 命名空间,并在其中启动一个 shell
unshare --pid --fork --mount-proc bash

说明:

  • --pid:创建新的 PID 命名空间;
  • --fork:fork 一个子进程,在子进程中启用新命名空间;
  • --mount-proc:重新挂载 /proc,使其反映新命名空间的进程视图。

进入新 shell 后,可验证:

# 在新命名空间中
ps aux    # 应只看到少量进程(如 bash、ps)
echo $$   # 当前 shell PID 应为 1(在该命名空间中)

此时系统中已存在 至少两个 PID 命名空间:初始命名空间(level=0)和新创建的(level=1)。


在另一个终端(不要在 unshare 的 shell 中操作),加载你的模块:

sudo insmod scan_active_pidns.ko

查看内核日志:

dmesg | tail -10

预期输出类似:

[ 1234.567890] Scanning active PID namespaces...
[ 1234.567891] Found active PID NS: level=0, child_reaper=1
[ 1234.567892] Found active PID NS: level=1, child_reaper=12345
[ 1234.567893] Total active PID namespaces: 2

说明:

  • level=0:初始 PID 命名空间;
  • level=1:你刚用 unshare 创建的命名空间;
  • child_reaper 是该命名空间的“init”进程 PID(在父命名空间中的真实 PID)。

退出 unshare 的 shell 后,该命名空间会自动销毁(当其中所有进程退出时)。再次加载模块,应只看到 level=0。


2、PID对象和task_struct对象的关系

在 Linux 内核开发或调试中,我们经常需要根据用户空间提供的 进程 ID(PID),在内核中找到对应的 task_struct 结构体——这是内核中表示进程/线程的核心数据结构。


2.1、PID 如何关联到 task_struct?

Linux 中的 PID 并非直接指向 task_struct,而是通过 PID 命名空间(pid_namespace) 和 struct pid 间接关联,参考上篇Linux进程-PID:

PID (数值) 
   → struct pid (通过全局 pid_hash) 
   → task_struct (通过 pid->tasks[PIDTYPE_PID].first)

内核提供了标准接口:

  • find_get_pid(pid_t nr):根据 PID 数值查找 struct pid*(增加引用);
  • pid_task(struct pid *pid, enum pid_type type):从 struct pid 获取 task_struct*;
  • 使用完毕后需调用 put_pid() 释放引用。

注意:必须在 RCU​​ 临界区 或持有适当锁的情况下访问 task_struct,否则可能因进程退出导致悬空指针。


2.2、编写模块查找task_struct对象

①、先创建进程和线程
// multi_thread_test.c
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/types.h>

// 获取当前线程的 TID(真实内核线程 ID)
pid_t get_tid(void) {
    return syscall(SYS_gettid);
}

void* worker(void* arg) {
    int id = *(int*)arg;
    printf("Worker %d: TID = %d\n", id, get_tid());
    while(1)
    {
        sleep(60);​ ​// 保持运行 60 秒
    }
    
    return NULL;
}

int main() {
    pthread_t t1, t2;
    int id1 = 1, id2 = 2;

    printf("Main thread: PID = %d, TID = %d\n", getpid(), get_tid());

    // 创建两个线程
    pthread_create(&t1, NULL, worker, &id1);
    pthread_create(&t2, NULL, worker, &id2);

    printf("All threads started. Press Ctrl+C to exit.\n");
    while(1)
    {
        sleep(60);​ ​// 保持运行 60 秒
    }

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    return 0;
}
②、编写查询模块

使用Makefile编译

/* find_task_by_pid.c */
#include <linux/module.h>
#include <linux/sched.h>      // task_struct, find_get_pid, pid_task
#include <linux/pid.h>        // struct pid
#include <linux/rcupdate.h>   // rcu_read_lock/unlock

static int target_pid = -1;
module_param(target_pid, int, 0444);
MODULE_PARM_DESC(target_pid, "PID to look up task_struct");

// 函数用于遍历pid对象 中的tasks成员 得到task_struct对象地址
static void get_pid_tasks(struct pid *pid_struct)
{
    int type = 0;
    struct task_struct *pos;

    for (type = 0; type < PIDTYPE_MAX; type++) {
        struct hlist_head *head = &pid_struct->tasks[type];
        printk(KERN_INFO "Traversing tasks[%s] (type=%d):\n",
            (type == PIDTYPE_PID) ? "PID" :
            (type == PIDTYPE_TGID) ? "TGID" :
            (type == PIDTYPE_PGID) ? "PGID" : "SID",
            type);

        if (hlist_empty(head)) {
            printk(KERN_INFO "  [empty]\n");
            continue;
        }

        hlist_for_each_entry_rcu(pos, head, pid_links[type]) {
            printk(KERN_INFO "  -> task: comm=%s, pid=%d, tgid=%d, addr=0x%px\n",
                pos->comm,
                task_pid_nr(pos),
                task_tgid_nr(pos),
                pos);
        }
    }
}

static void print_process_task_thread_group(struct task_struct *task)
{
    struct pid *pid_struct;
    struct task_struct *thread_pos;
    int count = 0;

    if (task == task->group_leader) {
        printk(KERN_INFO "PID %d: is process", task->pid);
    }

    if (!list_empty(&task->thread_group))
        {
            list_for_each_entry_rcu(thread_pos, &task->group_leader->thread_group, thread_group) {
                count ++;
                printk(KERN_INFO "Thread %d:", count);
                printk(KERN_INFO "  Thread: comm=%s, pid=%d, tgid=%d, state=%d\n",
                    thread_pos->comm,
                    task_pid_nr(thread_pos),
                    task_tgid_nr(thread_pos),
                    thread_pos->__state);

                // 根据 PID 数值获取 struct pid(增加引用)
                pid_struct = find_get_pid(task_pid_nr(thread_pos));
                if (!pid_struct) {
                    printk(KERN_ERR "PID %d not found\n", task_pid_nr(thread_pos));
                    return ;
                }else{
                    printk(KERN_INFO "struct pid real address: 0x%px\n", pid_struct);
                }
                
                // 打印pid对象中的tasks信息
                get_pid_tasks(pid_struct);
        }
    }else{
        printk(KERN_INFO " Current task_struct no thread!\n");
    }
}

static int __init find_task_init(void)
{
    struct pid *pid_struct;
    struct task_struct *task;
    int ret = -ESRCH;

    if (target_pid <= 0) {
        printk(KERN_ERR "Invalid PID: %d\n", target_pid);
        return -EINVAL;
    }

    printk(KERN_INFO "Looking up task for PID %d\n", target_pid);

    // 根据 PID 数值获取 struct pid(增加引用)
    pid_struct = find_get_pid(target_pid);
    if (!pid_struct) {
        printk(KERN_ERR "PID %d not found\n", target_pid);
        return -ESRCH;
    }else{
        printk(KERN_INFO "struct pid real address: 0x%px\n", pid_struct);
    }

    // 遍历一下 pid结构体下面的
    rcu_read_lock();
    // 打印一下进主线程的 pid对象->tasks
    get_pid_tasks(pid_struct);
​    ​/* 遍历 thread_group 链表 */
    task = pid_task(pid_struct, PIDTYPE_PID);
    if (task) {
        // 打印进程信息(仅读取,无需额外锁)
        printk(KERN_INFO "Found task: addr = 0x%px, comm=%s, pid=%d, state=%d, thread_pid=0x%px, group_leader=%px, tgid=%d\n",
            task,
            task->comm,
            task_pid_nr(task),
            task->__state,
            task->thread_pid,
            task->group_leader,
            task->tgid);
        ret = 0;
    } else {
        printk(KERN_ERR "task_struct not found for PID %d\n", target_pid);
    }

    print_process_task_thread_group(task);

    rcu_read_unlock();

    // 释放 struct pid 引用
    put_pid(pid_struct);

    return ret;
}

static void __exit find_task_exit(void)
{
    printk(KERN_INFO "find_task_by_pid module unloaded.\n");
}

module_init(find_task_init);
module_exit(find_task_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Find task_struct by PID number");
MODULE_AUTHOR("Name");
③、最终执行结果

查询进程ID

root@QEMU-Ubuntu2204:/mnt/shced_test/pid# insmod find_task_by_pid.ko target_pid=594
[ 6268.475956] Looking up task for PID 594
[ 6268.476104] struct pid real address: 0xffff010d85144580
[ 6268.476259] Traversing tasks[PID] (type=0):
[ 6268.476346]   -> task: comm=multi_thread_te, pid=594, tgid=594, addr=0xffff010d81cc6e40
[ 6268.476432] Traversing tasks[TGID] (type=1):
[ 6268.476451]   -> task: comm=multi_thread_te, pid=594, tgid=594, addr=0xffff010d81cc6e40
[ 6268.476501] Traversing tasks[PGID] (type=2):
[ 6268.476524]   -> task: comm=multi_thread_te, pid=594, tgid=594, addr=0xffff010d81cc6e40
[ 6268.476544] Traversing tasks[SID] (type=3):
[ 6268.476569]   [empty]
[ 6268.476664] Found task: addr = 0xffff010d81cc6e40, comm=multi_thread_te, pid=594, 
state=8193, thread_pid=0xffff010d85144580, group_leader=ffff010d81cc6e40, tgid=594
[ 6268.476747] PID 594: is process
[ 6268.476818] Thread 1:
[ 6268.476856]   Thread: comm=multi_thread_te, pid=595, tgid=594, state=8193
[ 6268.476908] struct pid real address: 0xffff010d85157a00
[ 6268.476935] Traversing tasks[PID] (type=0):
[ 6268.476951]   -> task: comm=multi_thread_te, pid=595, tgid=594, addr=0xffff010d803c0fc0
[ 6268.476971] Traversing tasks[TGID] (type=1):
[ 6268.476984]   [empty]
[ 6268.476995] Traversing tasks[PGID] (type=2):
[ 6268.477009]   [empty]
[ 6268.477019] Traversing tasks[SID] (type=3):
[ 6268.477049]   [empty]
[ 6268.477090] Thread 2:
[ 6268.477108]   Thread: comm=multi_thread_te, pid=596, tgid=594, state=8193
[ 6268.477130] struct pid real address: 0xffff010d85157e80
[ 6268.477145] Traversing tasks[PID] (type=0):
[ 6268.477159]   -> task: comm=multi_thread_te, pid=596, tgid=594, addr=0xffff010d85118000
[ 6268.477177] Traversing tasks[TGID] (type=1):
[ 6268.477191]   [empty]
[ 6268.477202] Traversing tasks[PGID] (type=2):
[ 6268.477216]   [empty]
[ 6268.477226] Traversing tasks[SID] (type=3):
[ 6268.477240]   [empty]

查询线程ID:

[ 6677.536538] Looking up task for PID 595
[ 6677.536688] struct pid real address: 0xffff010d85157a00
[ 6677.536835] Traversing tasks[PID] (type=0):
[ 6677.536934]   -> task: comm=multi_thread_te, pid=595, tgid=594, addr=0xffff010d803c0fc0
[ 6677.537042] Traversing tasks[TGID] (type=1):
[ 6677.537082]   [empty]
[ 6677.537128] Traversing tasks[PGID] (type=2):
[ 6677.537146]   [empty]
[ 6677.537157] Traversing tasks[SID] (type=3):
[ 6677.537170]   [empty]
[ 6677.537263] Found task: addr = 0xffff010d803c0fc0, comm=multi_thread_te, pid=595, 
state=8193, thread_pid=0xffff010d85157a00, group_leader=ffff010d81cc6e40, tgid=594
[ 6677.537382] Thread 1:
[ 6677.537421]   Thread: comm=multi_thread_te, pid=595, tgid=594, state=8193
[ 6677.537475] struct pid real address: 0xffff010d85157a00
[ 6677.537504] Traversing tasks[PID] (type=0):
[ 6677.537522]   -> task: comm=multi_thread_te, pid=595, tgid=594, addr=0xffff010d803c0fc0
[ 6677.537541] Traversing tasks[TGID] (type=1):
[ 6677.537555]   [empty]
[ 6677.537565] Traversing tasks[PGID] (type=2):
[ 6677.537579]   [empty]
[ 6677.537589] Traversing tasks[SID] (type=3):
[ 6677.537602]   [empty]
[ 6677.537649] Thread 2:
[ 6677.537669]   Thread: comm=multi_thread_te, pid=596, tgid=594, state=8193
[ 6677.537692] struct pid real address: 0xffff010d85157e80
[ 6677.537711] Traversing tasks[PID] (type=0):
[ 6677.537726]   -> task: comm=multi_thread_te, pid=596, tgid=594, addr=0xffff010d85118000
[ 6677.537788] Traversing tasks[TGID] (type=1):
[ 6677.538448]   [empty]
[ 6677.538484] Traversing tasks[PGID] (type=2):
[ 6677.538525]   [empty]
[ 6677.538553] Traversing tasks[SID] (type=3):
[ 6677.538585]   [empty]

2.4、捋清楚三个对象的关系

只关注重点需要关注的对象即可 所有内核源码 参考6.1.96

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在Linux内核中,所有的进程或者线程都使用task_struct来描述:

关注图中的几个成员,首先thread_pid成员就是每个进程或者线程必有得一个属性,这个指针指向了该进程/线程相关的pid对象,log信息如下:

[ 6677.536538] Looking up task for PID 595
[ 6677.536688] struct pid real address: 0xffff010d85157a00

[ 6677.537263] Found task: addr = 0xffff010d803c0fc0, comm=multi_thread_te, pid=595, 
state=8193, thread_pid=0xffff010d85157a00, group_leader=ffff010d81cc6e40, tgid=594

属性group_leader,用来表示该线程的组领导,如果是进程,则指向自己:

[ 6268.476664] Found task: addr = 0xffff010d81cc6e40, comm=multi_thread_te, pid=594, 
state=8193, thread_pid=0xffff010d85144580, group_leader=ffff010d81cc6e40, tgid=594

属性pid_links理解为埋在task_struct中的一个钩子,用于在pid对象中遍历相关的task_struct对象,在创建期间被赋值,需要注意的是线程和进程内容不同,log信息:

pid_links这个用来从pid对象container_of到task_struct对象

////进程id log
[ 6268.476259] Traversing tasks[PID] (type=0):
[ 6268.476346]   -> task: comm=multi_thread_te, pid=594, tgid=594, addr=0xffff010d81cc6e40
[ 6268.476432] Traversing tasks[TGID] (type=1):
[ 6268.476451]   -> task: comm=multi_thread_te, pid=594, tgid=594, addr=0xffff010d81cc6e40
[ 6268.476501] Traversing tasks[PGID] (type=2):
[ 6268.476524]   -> task: comm=multi_thread_te, pid=594, tgid=594, addr=0xffff010d81cc6e40
[ 6268.476544] Traversing tasks[SID] (type=3):
[ 6268.476569]   [empty]
////线程id log
[ 6677.537504] Traversing tasks[PID] (type=0):
[ 6677.537522]   -> task: comm=multi_thread_te, pid=595, tgid=594, addr=0xffff010d803c0fc0
[ 6677.537541] Traversing tasks[TGID] (type=1):
[ 6677.537555]   [empty]
[ 6677.537565] Traversing tasks[PGID] (type=2):
[ 6677.537579]   [empty]
[ 6677.537589] Traversing tasks[SID] (type=3):
[ 6677.537602]   [empty]
[ 6677.537649] Thread 2:
[ 6677.537669]   Thread: comm=multi_thread_te, pid=596, tgid=594, state=8193
[ 6677.537692] struct pid real address: 0xffff010d85157e80
[ 6677.537711] Traversing tasks[PID] (type=0):
[ 6677.537726]   -> task: comm=multi_thread_te, pid=596, tgid=594, addr=0xffff010d85118000
[ 6677.537788] Traversing tasks[TGID] (type=1):
[ 6677.538448]   [empty]
[ 6677.538484] Traversing tasks[PGID] (type=2):
[ 6677.538525]   [empty]
[ 6677.538553] Traversing tasks[SID] (type=3):
[ 6677.538585]   [empty]

线程只有tasks[PID]是自己本身 其余都为NULL

在copy_proces函数源码中 有这么一段,就说明了上述提到的问题,对于进程和线程的配置不同:

//如果是进程
        if (thread_group_leader(p)) {
            init_task_pid(p, PIDTYPE_TGID, 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->shared_pending.signal = delayed.signal;
            p->signal->tty = tty_kref_get(current->signal->tty);
​            ​/*
​             * Inherit has_child_subreaper flag under the same
​             * tasklist_lock with adding child to the process tree
​             * for propagate_has_child_subreaper optimization.
​             */
            p->signal->has_child_subreaper = p->real_parent->signal->has_child_subreaper ||
                             p->real_parent->signal->is_child_subreaper;
            list_add_tail(&p->sibling, &p->real_parent->children);
            list_add_tail_rcu(&p->tasks, &init_task.tasks);
            attach_pid(p, PIDTYPE_TGID);
            attach_pid(p, PIDTYPE_PGID);
            attach_pid(p, PIDTYPE_SID);
            __this_cpu_inc(process_counts);
        } else { //是线程
            current->signal->nr_threads++;
            current->signal->quick_threads++;
            atomic_inc(&current->signal->live);
            refcount_inc(&current->signal->sigcnt);
            task_join_group_stop(p);
            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++;
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐