使用代码对PID相关源码进行验证
在 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(¤t->signal->live);
refcount_inc(¤t->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++;
更多推荐

所有评论(0)