第3章 进程标识符与命名空间
基于 Linux 6.12.38 源码分析
3.1 进程 ID 类型 3.1.1 PID 类型定义 位置: include/linux/pid.h
1 2 3 4 5 6 7 enum pid_type { PIDTYPE_PID, PIDTYPE_TGID, PIDTYPE_PGID, PIDTYPE_SID, PIDTYPE_MAX, };
3.1.2 各类型用途 PIDTYPE_PID:
标识单个线程(轻量级进程)
每个线程都有唯一的 PID
通过 getpid() 系统调用获取
PIDTYPE_TGID:
线程组 ID,即传统意义的进程 ID
同一进程内所有线程共享相同的 TGID
主线程的 PID = TGID
PIDTYPE_PGID:
进程组 ID
用于作业控制
通过 getpgid() 系统调用获取
PIDTYPE_SID:
会话 ID
进程登录会话标识
通过 getsid() 系统调用获取
3.2 PID 管理数据结构 3.2.1 struct pid 位置: include/linux/pid.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 struct upid { int nr; struct pid_namespace *ns ; }; struct pid { refcount_t count; unsigned int level; spinlock_t lock; struct dentry *stashed ; u64 ino; struct hlist_head tasks [PIDTYPE_MAX ]; struct hlist_head inodes ; wait_queue_head_t wait_pidfd; struct rcu_head rcu ; struct upid numbers []; };
3.2.2 struct pid_namespace 位置: include/linux/pid_namespace.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct pid_namespace { struct idr idr ; struct rcu_head rcu ; unsigned int pid_allocated; struct task_struct *child_reaper ; struct kmem_cache *pid_cachep ; unsigned int level; struct pid_namespace *parent ; #ifdef CONFIG_BSD_PROCESS_ACCT struct fs_pin *bacct ; #endif struct user_namespace *user_ns ; struct ucounts *ucounts ; int reboot; struct ns_common ns ; #if defined(CONFIG_SYSCTL) && defined(CONFIG_MEMFD_CREATE) int memfd_noexec_scope; #endif } __randomize_layout; extern struct pid_namespace init_pid_ns ;
3.2.3 PID 命名空间层级 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ┌────────────────────────────────────────────────────────────────┐ │ PID Namespace 层级 │ ├────────────────────────────────────────────────────────────────┤ │ │ │ Level 0: 初始命名空间 (init_pid_ns) │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ level = 0 │ │ │ │ child_reaper = init (PID 1) │ │ │ │ │ │ │ │ PID 1: systemd/init │ │ │ │ PID 2: kthreadd │ │ │ │ PID 100: container-A │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ │ │ clone(CLONE_NEWPID) │ │ ↓ │ │ Level 1: 容器 A 命名空间 │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ level = 1 │ │ │ │ parent = init_pid_ns │ │ │ │ child_reaper = container-A-init │ │ │ │ │ │ │ │ PID 1: container-A-init (外部: PID 100) │ │ │ │ PID 2: app-process (外部: PID 101) │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ │ │ clone(CLONE_NEWPID) │ │ ↓ │ │ Level 2: 嵌套容器命名空间 │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ level = 2 │ │ │ │ parent = container-A-pid-ns │ │ │ │ │ │ │ │ PID 1: nested-init (外部: PID 101, 容器A: PID 2) │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────────┘
3.2.4 视图映射 不同命名空间视角:
进程
全局视角
Level 0 视角
Level 1 视角
Level 2 视角
init
PID 1
PID 1
-
-
container-A-init
PID 100
PID 100
PID 1
-
app-process
PID 101
PID 101
PID 2
PID 1
3.3 PID 分配与释放 3.3.1 PID 分配 位置: kernel/pid.c:alloc_pid()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 struct pid *alloc_pid (struct pid_namespace *ns, pid_t *set_tid, size_t set_tid_size) { struct pid *pid ; enum pid_type type ; int i, nr; struct pid_namespace *tmp ; struct upid *upid ; pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL); if (!pid) return ERR_PTR(-ENOMEM); tmp = ns; pid->level = ns->level; for (i = ns->level; i >= 0 ; i--) { int tid = 0 ; if (set_tid && set_tid_size) { tid = set_tid[ns->level - i]; retval = -EINVAL; if (tid < 1 || tid >= pid_max) goto out_free; nr = idr_alloc(&tmp->idr, NULL , tid, tid + 1 , GFP_ATOMIC); } else { int pid_min = 1 ; if (idr_get_cursor(&tmp->idr) > RESERVED_PIDS) pid_min = RESERVED_PIDS; nr = idr_alloc_cyclic(&tmp->idr, NULL , pid_min, pid_max, GFP_ATOMIC); } if (nr < 0 ) { retval = (nr == -ENOSPC) ? -EAGAIN : nr; goto out_free; } pid->numbers[i].nr = nr; pid->numbers[i].ns = tmp; tmp = tmp->parent; } get_pid_ns(ns); refcount_set(&pid->count, 1 ); spin_lock_init(&pid->lock); for (type = 0 ; type < PIDTYPE_MAX; ++type) INIT_HLIST_HEAD(&pid->tasks[type]); upid = pid->numbers + ns->level; spin_lock_irq(&pidmap_lock); for (; upid >= pid->numbers; --upid) { idr_replace(&upid->ns->idr, pid, upid->nr); upid->ns->pid_allocated++; } spin_unlock_irq(&pidmap_lock); return pid; }
3.3.2 PID 释放 位置: kernel/pid.c:free_pid()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 void free_pid (struct pid *pid) { int i; unsigned long flags; spin_lock_irqsave(&pidmap_lock, flags); for (i = 0 ; i <= pid->level; i++) { struct upid *upid = pid->numbers + i; struct pid_namespace *ns = upid->ns; switch (--ns->pid_allocated) { case 2 : case 1 : wake_up_process(ns->child_reaper); break ; case 0 : break ; } idr_remove(&ns->idr, upid->nr); } spin_unlock_irqrestore(&pidmap_lock, flags); call_rcu(&pid->rcu, delayed_put_pid); }
3.4 PID 查找与转换 3.4.1 查找 PID 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 struct pid *find_vpid (int nr) { return find_pid_ns(nr, task_active_pid_ns(current)); } struct pid *find_pid_ns (int nr, struct pid_namespace *ns) { return idr_find(&ns->idr, nr); } struct task_struct *find_task_by_vpid (pid_t vnr) { return find_task_by_pid_ns(vnr, task_active_pid_ns(current)); } struct task_struct *find_task_by_pid_ns (pid_t nr, struct pid_namespace *ns) { struct pid *pid ; struct task_struct *result ; rcu_read_lock(); pid = find_pid_ns(nr, ns); if (pid) { result = pid_task(pid, PIDTYPE_PID); } else { result = NULL ; } rcu_read_unlock(); return result; }
3.4.2 PID 编号转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 static inline pid_t pid_nr (struct pid *pid) { pid_t nr = 0 ; if (pid) nr = pid->numbers[task_active_pid_ns(current)->level].nr; return nr; } 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; } static inline pid_t pid_vnr (struct pid *pid) { return pid_nr_ns(pid, task_active_pid_ns(current)); }
3.5 进程组与会话 3.5.1 进程组 定义: 进程组是一个或多个进程的集合,用于作业控制。
1 2 3 4 5 6 struct task_struct { struct pid *pgrp ; struct pid *session ; };
进程组操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 SYSCALL_DEFINE2(setpgid, pid_t , pid, pid_t , pgid) { struct task_struct *p ; struct task_struct *group_leader ; struct pid_namespace *ns ; int err; p = find_vpid(pid); if (!p) return -ESRCH; ns = task_active_pid_ns(p); if (!check_pergroup_permission(p, ns, pid, pgid)) return -EPERM; err = __setpgid(p, pgid); return err; }
3.5.2 会话 定义: 会话是进程组的集合,通常与登录会话关联。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 SYSCALL_DEFINE0(setsid) { struct task_struct *p = current; if (p->signal->leader) return -EPERM; if (!process_session(p)) return -ENOMEM; set_signal_session(p->signal, process_session(p)); p->signal->leader = 1 ; return process_session(p); }
3.5.3 进程树示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 init (PID 1, SID 1, PGID 1) │ ├─ systemd (PID 1000, SID 1, PGID 1000) │ │ session: 1 │ │ pgrp: 1000 │ │ │ ├─ NetworkManager (PID 1001, SID 1, PGID 1000) │ ├─ sshd (PID 1002, SID 1, PGID 1000) │ │ │ └─ cron (PID 1003, SID 1, PGID 1000) │ ├─ login (PID 2000, SID 2000, PGID 2000) │ │ session: 2000 (新会话) │ │ pgrp: 2000 │ │ │ └─ bash (PID 2001, SID 2000, PGID 2000) │ │ session: 2000 │ │ pgrp: 2000 │ │ │ └─ command (PID 2002, SID 2000, PGID 2002) │ │ session: 2000 │ │ pgrp: 2002 (新进程组) │ └─ containerd (PID 3000, SID 3000, PGID 3000) │ session: 3000 (新会话) │ pgrp: 3000 │ └─ dockerd (PID 3001, SID 3000, PGID 3000)
3.6 PID Namespace 操作 3.6.1 创建新 PID 命名空间 1 2 3 4 5 pid = clone(CLONE_NEWPID | SIGCHLD, NULL ); unshare(CLONE_NEWPID);
3.6.2 进入已有 PID 命名空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 SYSCALL_DEFINE2(setns, int , fd, int , nstype) { struct file *file ; struct ns_common *ns ; int err; file = fget(fd); if (!file) return -EBADF; ns = get_ns_from_fd(fd); if (IS_ERR(ns)) { err = PTR_ERR(ns); goto out; } err = switch_task_namespaces(current, ns); out: fput(file); return err; }
3.7 /proc 文件系统中的 PID 信息 3.7.1 /proc/[pid]/status 1 2 3 4 5 6 7 8 9 $ cat /proc/1234/status Name: systemd State: S (sleeping) Tgid: 1234 Pid: 1234 PPid: 1 TracerPid: 0 Uid: 0 0 0 0 Gid: 0 0 0 0
3.7.2 进程树查看 1 2 3 4 systemd(1)─┬─NetworkManager(1000) ├─sshd(1002)───bash(2000)───vim(2001) └─cron(1003)
3.7.3 命名空间查看 1 2 3 4 5 ls -la /proc/1234/ns/pidls /proc/[pid]/ns/pid | xargs -I{} readlink {}
3.8 本章小结 本章介绍了 Linux 进程标识符与命名空间的管理:
PID 类型 :PID、TGID、PGID、SID 四种类型,各有不同用途
数据结构 :struct pid 管理编号,struct pid_namespace 管理命名空间
命名空间层级 :支持嵌套的 PID 命名空间,实现容器隔离
PID 分配 :通过 IDR 机制分配和释放 PID
PID 查找 :支持跨命名空间的 PID 查找和转换
进程组与会话 :支持作业控制和登录会话管理
容器支持 :PID 命名空间是实现容器隔离的关键技术
下一章将介绍进程的创建机制。