Linux内核分析之进程管理-00

韩乔落

Linux 6.12 进程管理概览

基于 Linux 6.12.38 源码,涵盖进程与线程管理的核心机制


1. 概述

1.1 进程与线程的概念

进程 (Process):

  • 程序执行的实例
  • 拥有独立的地址空间
  • 资源分配的基本单位

线程 (Thread):

  • 进程内的执行单元
  • 共享进程地址空间
  • CPU 调度的基本单位

Linux 中的实现:

1
2
3
// Linux 使用统一的 task_struct 表示进程和线程
// 通过 clone() 标志控制共享程度
// 轻量级进程 (LWP) 实现

1.2 进程管理子系统架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌─────────────────────────────────────────────────────────────────┐
│ 进程管理子系统 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ 进程描述符 │ │ 进程调度器 │ │ 进程创建/退出 │ │
│ │ task_struct │ │ Scheduler │ │ fork/exec/exit │ │
│ └──────────────┘ └──────────────┘ └─────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ PID 管理 │ │ 等待队列 │ │ 信号处理 │ │
│ │ PID Namespace│ │ wait_queue │ │ signal_frame │ │
│ └──────────────┘ └──────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

1.3 关键源码位置

功能 目录 主要文件
进程核心 kernel/ fork.c, exit.c, exec.c
调度器 kernel/sched/ core.c, fair.c, rt.c, deadline.c
PID 管理 kernel/ pid.c, pid_namespace.c
信号处理 kernel/ signal.c
内核线程 kernel/ kthread.c
进程统计 kernel/ acct.c, delayacct.c
头文件 include/linux/ sched.h, sched/*.h
系统调用 kernel/ sys.c, sys_ni.c
体系相关 arch/*/kernel/ process.c, signal.c, entry*.S

1.4 相关数据结构概览

1
2
3
4
5
6
7
8
9
10
11
struct task_struct;      // 进程描述符
struct pid; // PID 编号管理
struct pid_namespace; // PID 命名空间
struct signal_struct; // 信号共享信息
struct sighand_struct; // 信号处理函数
struct rq; // 运行队列
struct sched_entity; // 调度实体 (CFS)
struct sched_rt_entity; // 调度实体 (RT)
struct sched_dl_entity; // 调度实体 (Deadline)
struct wait_queue_head; // 等待队列头
struct task_group; // 任务分组 (cgroup)

2. 进程描述符 task_struct

2.1 task_struct 概览

位置: include/linux/sched.h

大小: 约 8KB (64位系统),包含内核栈

分配方式: 通过 slab 分配器 (task_struct_cachep)

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
struct task_struct {
// 状态信息
unsigned int __state; // 进程状态
int exit_state; // 退出状态
unsigned int flags; // 进程标志

// 栈信息
void *stack; // 内核栈指针
unsigned long stack_canary; // 栈保护

// 调度相关
struct sched_entity se; // CFS 调度实体
struct sched_rt_entity rt; // 实时调度实体
struct sched_dl_entity dl; // Deadline 调度实体
int prio; // 静态优先级
int static_prio; // 基本优先级
int normal_prio; // 标准优先级
unsigned int rt_priority; // 实时优先级

// 进程标识
pid_t pid; // 进程 ID
pid_t tgid; // 线程组 ID
struct pid *thread_pid; // 线程 PID
struct task_struct *group_leader; // 线程组首领

// 进程关系
struct task_struct __rcu *real_parent; // 真实父进程
struct task_struct __rcu *parent; // 当前父进程
struct list_head children; // 子进程链表
struct list_head sibling; // 兄弟进程链表

// 内存管理
struct mm_struct *mm; // 进程地址空间
struct mm_struct *active_mm; // 活跃地址空间 (内核线程)

// 文件系统
struct fs_struct *fs; // 文件系统信息
struct files_struct *files; // 打开的文件表
struct nsproxy *nsproxy; // 命名空间代理

// 信号处理
struct signal_struct *signal; // 信号信息
struct sighand_struct *sighand; // 信号处理函数
sigset_t blocked; // 阻塞信号集
sigset_t real_blocked; // 实际阻塞信号集

// 证书与安全
const struct cred __rcu *real_cred; // 真实证书
const struct cred __rcu *cred; // 有效证书
char comm[TASK_COMM_LEN]; // 命令名

// 时间与资源
u64 utime; // 用户态时间
u64 stime; // 内核态时间
u64 gtime; // 虚拟机时间 (guest)
unsigned long min_flt; // 次要缺页
unsigned long maj_flt; // 主要缺页

// 更多字段...
};

2.2 进程状态

状态定义: include/linux/sched.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 可运行状态
#define TASK_RUNNING 0x00000000
#define TASK_INTERRUPTIBLE 0x00000001
#define TASK_UNINTERRUPTIBLE 0x00000002
#define __TASK_STOPPED 0x00000004
#define __TASK_TRACED 0x00000008

// 退出状态
#define EXIT_DEAD 0x00000010
#define EXIT_ZOMBIE 0x00000020

// 特殊状态
#define TASK_PARKED 0x00000040
#define TASK_DEAD 0x00000080
#define TASK_WAKEKILL 0x00000100
#define TASK_WAKING 0x00000200
#define TASK_NOLOAD 0x00000400
#define TASK_NEW 0x00000800
#define TASK_RTLOCK_WAIT 0x00001000
#define TASK_FREEZABLE 0x00002000
#define TASK_FROZEN 0x00008000

状态转换图:

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
                ┌─────────────┐
│ TASK_NEW │
└──────┬──────┘

fork() │

┌─────────────────────────────────────────┐
│ TASK_RUNNING │
│ (就绪态或运行态 - 可被调度) │
└───────┬───────────────────────┬─────────┘
│ │
调度器选择 等待事件
│ │
↓ ↓
┌──────────────┐ ┌────────────────────┐
│ 正在运行 │ │ TASK_INTERRUPTIBLE │
│ │ │ TASK_UNINTERRUPTIBLE│
└──────┬───────┘ └───────────┬────────┘
│ │
│ │ 事件到达
│ │ 或信号
│ ↓
│ ┌──────────────┐
│ │ TASK_RUNNING │
│ └──────────────┘

time slice / 主动放弃


┌──────────────┐
│ TASK_RUNNING │
│ (重新排队) │
└──────────────┘

特殊状态转换:
┌──────────────┐
│ __TASK_STOPPED│ ←─ SIGSTOP
└──────────────┘
┌──────────────┐
│ __TASK_TRACED │ ←─ ptrace
└──────────────┘

退出状态:
┌──────────────┐ do_exit() ┌─────────────┐
│ TASK_DEAD │ ───────────────→ │ EXIT_ZOMBIE │
└──────────────┘ └──────┬──────┘

wait()


┌─────────────┐
│ EXIT_DEAD │
│ (被回收) │
└─────────────┘

2.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
// 进程标志定义
#define PF_EXITING 0x00000004 // 正在退出
#define PF_EXITPIDONE 0x00000008 // PID 已释放
#define PF_VCPU 0x00000010 // 作为 vCPU 运行
#define PF_WQ_WORKER 0x00000020 // workqueue worker
#define PF_FORKNOEXEC 0x00000040 // fork 但未 exec
#define PF_MCE_PROCESS 0x00000080 // MCE 正在处理
#define PF_SUPERPRIV 0x00000100 // 需要 superuser 权限
#define PF_DUMPCORE 0x00000200 // 正在 dump core
#define PF_SIGNALED 0x00000400 // 被信号杀死
#define PF_MEMALLOC 0x00000800 // 内存分配上下文
#define PF_NPROC_EXCEEDED 0x00001000 // 超过进程数限制
#define PF_USED_MATH 0x00002000 // 使用 FPU
#define PF_USED_ASYNC 0x00004000 // 使用异步
#define PF_NOFREEZE 0x00008000 // 不被冻结
#define PF_FROZEN 0x00010000 // 已被冻结
#define PF_KSWAPD 0x00020000 // kswapd 进程
#define PF_MEMALLOC_NOIO 0x00080000 // 内存分配时不做 IO
#define PF_MEMALLOC_NOFS 0x00100000 // 内存分配时不做 FS 操作
#define PF_MEMALLOC_PIN 0x00200000 // pinned memory allocation
#define PF_MEMALLOC_PIN 0x00200000 // pinned memory allocation
#define PF_KTHREAD 0x00200000 // 内核线程
#define PF_RANDOMIZE 0x00400000 // 地址随机化
#define PF_SWAPWRITE 0x00800000 // 正在写入 swap
#define PF_UMH 0x02000000 // user mode helper
#define PF_SETDPERPID 0x04000000 // 每进程设置

2.4 内核栈与 thread_info

内核栈布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
┌────────────────────────────────────────────┐
│ struct thread_info │
│ (ARM64/x86_64 合并到 task_struct) │
├────────────────────────────────────────────┤
│ │
│ 内核栈空间 │
│ (THREAD_SIZE - 16KB/8KB) │
│ │
│ │
│ ↑ │
│ 栈增长方向 │
└────────────────────────────────────────────┘
struct task_struct *task

THREAD_SIZE:

  • x86_64: 16KB (4页) 或 8KB (2页)
  • ARM64: 16KB (4页)

2.5 current 宏

获取当前进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// x86_64
#define current get_current()

static __always_inline struct task_struct *get_current(void)
{
return this_cpu_read_stable(current_task);
}

// ARM64
static __always_inline struct task_struct *get_current(void)
{
return (struct task_struct *)
(read_sysreg(sp_el0) & ~THREAD_MASK);
}

3. 进程标识符与命名空间

3.1 进程 ID 类型

定义: include/linux/pid.h

1
2
3
4
5
6
7
enum pid_type {
PIDTYPE_PID, // 进程 ID
PIDTYPE_TGID, // 线程组 ID
PIDTYPE_PGID, // 进程组 ID
PIDTYPE_SID, // 会话 ID
PIDTYPE_MAX,
};

3.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
29
30
31
32
33
34
// PID 编号结构 (全局或命名空间局部)
struct pid {
atomic_t count;
unsigned int level; // 命名空间层级
struct hlist_head tasks[PIDTYPE_MAX]; // 每类任务哈希表

// 每个 PID 命名空间的编号
struct {
int nr; // 该命名空间内的编号
struct hlist_node pid_chain; // 全局 PID 哈希
} numbers[1];
};

// PID 命名空间
struct pid_namespace {
struct kref kref;
struct user_namespace *user_ns;
struct pid_namespace *parent; // 父命名空间

// 可用的 PID 范围
unsigned int level; // 层级
unsigned int nr_hashed;
struct pid *nr_hashed[PID_MAX_HASHED];

// PID 分配器
struct idr idr;

// 最后分配的 PID
int last_pid;
unsigned int nr_allocated;
struct kmem_cache *pid_cachep;

// 更多字段...
};

3.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
┌────────────────────────────────────────────────────────────────┐
│ PID Namespace 层级 │
├────────────────────────────────────────────────────────────────┤
│ │
│ Level 0: 初始命名空间 (init_ns) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ PID 1: systemd/init │ │
│ │ ├─ 子容器 PID 1 │ │
│ │ └─ 子容器 PID 2 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Level 1: 容器 A 命名空间 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ PID 1: containerA_init │ │
│ │ ├─ 子容器 PID 1 │ │
│ │ └─ 子进程 PID 2 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Level 2: 嵌套容器 A-1 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ PID 1: containerA1_init │ │
│ │ └─ 子进程 PID 2 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘

视图映射:
┌─────────────┬───────────────┬───────────────┬─────────────┐
│ 全局视角 │ Level 0 视角 │ Level 1 视角 │ Level 2 视角│
├─────────────┼───────────────┼───────────────┼─────────────┤
│ PID 1 │ PID 1 │ - │ - │
│ PID 100 │ PID 100 │ PID 1 │ - │
│ PID 200 │ PID 200 │ PID 2 │ PID 1 │
└─────────────┴───────────────┴───────────────┴─────────────┘

3.4 PID 分配 API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 分配新 PID
struct pid *alloc_pid(struct pid_namespace *ns);

// 查找 PID
struct pid *find_vpid(int nr); // 当前命名空间
struct pid *find_pid_ns(int nr, struct pid_namespace *ns);

// 获取 PID 编号
pid_t pid_nr(struct pid *pid); // 当前命名空间
pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns);
pid_t pid_vnr(struct pid *pid); // 虚拟编号

// PID 引用计数
void get_pid(struct pid *pid);
void put_pid(struct pid *pid);

3.5 进程组与会话

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
// 进程组
struct task_struct {
pid_t __pgrp; // 进程组 ID (废弃)
struct pid *thread_pid; // 现代 PID 结构
// ...
};

// 会话
struct task_struct {
struct pid *session; // 会话 ID
// ...
};

// 会话与进程组的关系:
/*
init (PID 1, SID 1, PGID 1)
├─ systemd (PID 1000, SID 1, PGID 1000)
│ ├─ service1 (PID 1001, SID 1, PGID 1000)
│ └─ service2 (PID 1002, SID 1, PGID 1000)
├─ login (PID 2000, SID 2000, PGID 2000)
│ └─ bash (PID 2001, SID 2000, PGID 2000)
│ └─ command (PID 2002, SID 2000, PGID 2002)
└─ container (PID 3000, SID 3000, PGID 3000)
└─ container_init (PID 3001, SID 3000, PGID 3000)
*/

4. 进程创建

4.1 fork/vfork/clone 系统调用

系统调用入口: kernel/fork.c

1
2
3
4
5
6
7
8
9
// 系统调用定义
SYSCALL_DEFINE0(fork);
SYSCALL_DEFINE0(vfork);
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
int __user *, parent_tidptr, unsigned long, tls,
int __user *, child_tidptr);

// 新的 clone3 系统调用 (Linux 5.3+)
SYSCALL_DEFINE2(clone3, struct clone_args __user *, uargs, size_t, size);

4.2 clone_flags 标志

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
// clone 标志定义
#define CSIGNAL 0x000000ff // 信号掩码
#define CLONE_VM 0x00000100 // 共享地址空间
#define CLONE_FS 0x00000200 // 共享文件系统信息
#define CLONE_FILES 0x00000400 // 共享文件描述符表
#define CLONE_SIGHAND 0x00000800 // 共享信号处理函数
#define CLONE_PIDFD 0x00001000 // 返回 pidfd
#define CLONE_PTRACE 0x00002000 // 如果父进程被 traced,子进程也被 traced
#define CLONE_VFORK 0x00004000 // vfork - 父进程阻塞直到子进程 exec 或 exit
#define CLONE_PARENT 0x00008000 // 使用父进程的父进程作为新进程的父进程
#define CLONE_THREAD 0x00010000 // 加入到父进程的线程组
#define CLONE_NEWNS 0x00020000 // 新的 mount 命名空间
#define CLONE_SYSVSEM 0x00040000 // 共享 System V SEM_UNDO 语义
#define CLONE_SETTLS 0x00080000 // 创建新的 TLS
#define CLONE_PARENT_SETTID 0x00100000 // 在父进程中设置子进程 TID
#define CLONE_CHILD_CLEARTID 0x00200000 // 在子进程中清除 TID
#define CLONE_DETACHED 0x00400000 // (废弃)
#define CLONE_UNTRACED 0x00800000 // 即使父进程被 traced,子进程也不被 traced
#define CLONE_CHILD_SETTID 0x01000000 // 在子进程中设置 TID
#define CLONE_NEWCGROUP 0x02000000 // 新的 cgroup 命名空间
#define CLONE_NEWUTS 0x04000000 // 新的 UTS 命名空间
#define CLONE_NEWIPC 0x08000000 // 新的 IPC 命名空间
#define CLONE_NEWUSER 0x10000000 // 新的 user 命名空间
#define CLONE_NEWPID 0x20000000 // 新的 PID 命名空间
#define CLONE_NEWNET 0x40000000 // 新的 network 命名空间
#define CLONE_IO 0x80000000 // 克隆 I/O 上下文

4.3 fork() 实现流程

核心函数: kernel/fork.c:do_fork()

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
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
struct task_struct *p;
int trace = 0;
long nr;

// ... 追踪相关处理

// 复制进程
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, parent_tidptr, trace);

if (!IS_ERR(p)) {
struct completion vfork;
struct pid *pid;

trace_sched_process_fork(current, p);

// 获取 PID
pid = get_task_pid(p, PIDTYPE_PID);
nr = pid_vnr(pid);

if (clone_flags & CLONE_PARENT_SETTID)
put_user(nr, parent_tidptr);

// vfork 特殊处理
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
get_task_struct(p);
}

// 唤醒新进程
wake_up_new_task(p);

// vfork 等待子进程 exec 或 exit
if (clone_flags & CLONE_VFORK) {
if (!wait_for_vfork_done(p, &vfork))
ptrace_event(PTRACE_EVENT_VFORK_DONE, pid_vnr(pid));
}

put_pid(pid);
} else {
nr = PTR_ERR(p);
}

return nr;
}

4.4 copy_process() 流程

位置: kernel/fork.c

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
static struct task_struct *copy_process(
unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *child_tidptr,
int __user *parent_tidptr,
unsigned long trace)
{
int retval;
struct task_struct *p;

// 1. 检查标志的有效性
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);

// ... 更多标志检查

// 2. 检查进程数限制
retval = -EAGAIN;
if (atomic_read(&p->user->processes) >=
task_rlimit(p, RLIMIT_NPROC)) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE))
goto bad_fork_free;
}

// 3. 分配 task_struct
p = dup_task_struct(current);
if (!p)
goto bad_fork_free;

// 4. 复制各种属性
retval = copy_creds(p, clone_flags);
// ...

// 5. 复制语义 (共享 vs 复制)
shm_init_task(p);

retval = copy_thread(clone_flags, stack_start, stack_size, p);
if (retval)
goto bad_fork_cleanup_io;

if (clone_flags & CLONE_THREAD)
p->exit_signal = -1;
else if (clone_flags & CLONE_PARENT_SETTID)
p->exit_signal = SIGCHLD;
else
p->exit_signal = fork_exit_signal;

// 6. 复制进程资源
retval = copy_semundo(clone_flags, p);
if (retval)
goto bad_fork_cleanup_signal;

retval = copy_files(clone_flags, p);
if (retval)
goto bad_fork_cleanup_semundo;

retval = copy_fs(clone_flags, p);
if (retval)
goto bad_fork_cleanup_files;

retval = copy_sighand(clone_flags, p);
if (retval)
goto bad_fork_cleanup_fs;

retval = copy_signal(clone_flags, p);
if (retval)
goto bad_fork_cleanup_sighand;

retval = copy_mm(clone_flags, p);
if (retval)
goto bad_fork_cleanup_signal;

retval = copy_namespaces(clone_flags, p);
if (retval)
goto bad_fork_cleanup_mm;

retval = copy_io(clone_flags, p);
if (retval)
goto bad_fork_cleanup_namespaces;

retval = copy_thread(clone_flags, stack_start, stack_size, p);
if (retval)
goto bad_fork_cleanup_io;

// 7. 初始化调度相关
retval = sched_fork(clone_flags, p);
if (retval)
goto bad_fork_cleanup_cleanup;

// 8. 初始化进程树关系
p->pid = pid_nr(pid);
if (clone_flags & CLONE_THREAD) {
p->exit_signal = -1;
p->group_leader = current->group_leader;
} else {
p->group_leader = p;
}

// 9. 初始化 PID
retval = alloc_pid(p->nsproxy->pid_ns_for_children, &pid);
if (retval)
goto bad_fork_cleanup_thread;

// 10. 设置进程名
p->pid = pid_nr(pid);
// ...

return p;

// 错误处理路径
// ...
}

4.5 写时复制 (Copy-on-Write)

实现原理:

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
fork() 之前:
┌────────────────────────────────────────────────────────────┐
│ 父进程地址空间 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 代码段 (只读) │ │
│ │ 数据段 (可写) ───→ 物理页 A │ │
│ │ 堆段 (可写) ───→ 物理页 B │ │
│ │ 栈段 (可写) ───→ 物理页 C │ │
│ └──────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘

fork() 之后 (exec 之前):
┌───────────────────────────┐ ┌───────────────────────────┐
│ 父进程 │ │ 子进程 │
│ ┌───────────────────────┐ │ │ ┌───────────────────────┐ │
│ │ 代码段 (只读, 共享) │◄┼───┼──►│ 代码段 (只读, 共享) │ │
│ │ 数据段 ──────→ A │ │ │ │ 数据段 ──────→ A │ │
│ │ 堆段 ──────→ B │ │ │ │ 堆段 ──────→ B │ │
│ │ 栈段 ──────→ C │ │ │ │ 栈段 ──────→ C │ │
│ │ (页表: 只读) │ │ │ │ (页表: 只读) │ │
│ └───────────────────────┘ │ │ └───────────────────────┘ │
└───────────────────────────┘ └───────────────────────────┘

子进程写操作触发缺页:
┌───────────────────┐ ┌───────────────────┐
│ 父进程 │ │ 子进程 │
│ 数据段 ──→ A │ │ 数据段 ──→ A' │
│ 堆段 ──→ B │ │ 堆段 ──→ B' │
└───────────────────┘ └───────────────────┘

相关代码: mm/memory.c:copy_page_range(), do_wp_page()

4.6 vfork() vs fork() vs clone()

特性 fork() vfork() clone()
地址空间 复制 (COW) 共享 可选
页表 复制 不复制 可选
父进程阻塞 是 (子进程 exec/exit) 可选 (CLONE_VFORK)
用途 创建进程 创建后立即 exec 灵活创建进程/线程
性能 较低

4.7 execve() 系统调用

位置: fs/exec.c

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
static int do_execveat_common(int fd, struct filename *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp,
int flags)
{
struct linux_binprm *bprm;
int retval;

// 1. 分配 linux_binprm
bprm = alloc_binprm();

// 2. 打开可执行文件
retval = bprm_execve(bprm, fd, filename, flags);

// 3. 复制参数和环境变量
retval = copy_string_kernel(bprm->filename, &bprm->exec);
// ...

// 4. 释放旧的内存描述符
retval = exec_binprm(bprm);

// 5. 释放 bprm
free_bprm(bprm);

return retval;
}

5. 进程调度

5.1 调度器架构

调度器类层次:

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
┌────────────────────────────────────────────────────────────────┐
│ 调度器架构 │
├────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Core Scheduler (sched/core.c) │ │
│ │ - schedule() 主调度函数 │ │
│ │ - pick_next_task() 选择下一个任务 │ │
│ │ - context_switch() 上下文切换 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────┼──────────────────┐ │
│ │ │ │ │
│ ↓ ↓ ↓ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ stop_sched_class │ │ dl_sched_class │ │ rt_sched_class │ │
│ │ (空闲任务) │ │ (Deadline) │ │ (实时) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────┐ │
│ │ fair_sched_class │ │
│ │ (CFS) │ │
│ └─────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────┐ │
│ │ idle_sched_class │ │
│ │ (idle) │ │
│ └─────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘

调度类优先级:

  1. stop_sched_class - 停机任务 (最高优先级)
  2. dl_sched_class - Deadline 调度
  3. rt_sched_class - 实时调度 (SCHED_FIFO/SCHED_RR)
  4. fair_sched_class - 完全公平调度 (SCHED_NORMAL/SCHED_BATCH)
  5. idle_sched_class - 空闲任务 (最低优先级)

5.2 调度实体

调度实体结构:

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
// CFS 调度实体
struct sched_entity {
// 权重与负载
struct load_weight load;
unsigned long runnable_weight;

// 调度信息
u64 exec_start;
u64 sum_exec_runtime;
u64 vruntime; // 虚拟运行时间
u64 vruntime_copy;

// 红-黑树节点
struct rb_node run_node;

// 队列信息
struct list_head group_node;
unsigned int on_rq;

// 调度组支持
struct cfs_rq *cfs_rq;
struct cfs_rq *my_q;
};

// 实时调度实体
struct sched_rt_entity {
struct list_head run_list;
unsigned long timeout;
unsigned int time_slice;

struct sched_rt_entity *back;
struct sched_rt_entity *parent;
struct rt_rq *rt_rq;
struct rt_rq *my_q;
};

// Deadline 调度实体
struct sched_dl_entity {
struct rb_node rb_node;

// Deadline 参数
u64 dl_runtime; // 运行时间预算
u64 dl_deadline; // 绝对截止时间
u64 dl_period; // 周期
u64 dl_bw; // 带宽

// 状态
unsigned int dl_throttled:1;
unsigned int dl_boosted:1;
unsigned int dl_yielded:1;
unsigned int dl_non_contending:1;

// 队列
struct dl_rq *dl_rq;
struct dl_rq *my_q;
};

5.3 运行队列 (Run Queue)

位置: kernel/sched/sched.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
30
31
32
33
34
35
struct rq {
// 运行队列自旋锁
raw_spinlock_t lock;

// 调度统计
unsigned long nr_running;
unsigned long nr_numa_running;
unsigned long nr_preferred_running;
unsigned long nr_uninterruptible;

// CPU 负载
u64 nr_switches;
u64 nr_load_updates;

// 调度类队列
struct cfs_rq cfs;
struct rt_rq rt;
struct dl_rq dl;

// 空闲调度
struct task_struct *idle;
struct task_struct *stop;

// 时钟
u64 clock;
u64 clock_task;

// 上下文切换统计
unsigned int nr_context_switches;

// CPU 热迁移
struct cpu_stop_work stop_work;

// 更多字段...
};

5.4 完全公平调度器 (CFS)

位置: kernel/sched/fair.c

核心思想: 基于 vruntime (虚拟运行时间) 的红-黑树调度

vruntime 计算:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// vruntime += delta_exec * (NICE_0_LOAD / weight)
static void update_curr(struct cfs_rq *cfs_rq)
{
struct sched_entity *curr = cfs_rq->curr;
u64 now = rq_clock_task(rq_of(cfs_rq));
u64 delta_exec;

delta_exec = now - curr->exec_start;
if (unlikely((s64)delta_exec <= 0))
return;

curr->exec_start = now;

// 更新 vruntime
curr->vruntime += calc_delta_fair(delta_exec, curr);

update_min_vruntime(cfs_rq);
}

红-黑树结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌───────────────────────────────┐
│ CFS 运行队列 │
│ (rb_root_cached) │
└───────────────────────────────┘


┌────────────────┐
│ 左子树 │ vruntime 小
│ (更早需要CPU) │
└────────────────┘

┌────────────────┐
│ rb_leftmost │ ← 下一个被调度任务
│ (最小vruntime) │
└────────────────┘

┌────────────────┐
│ 右子树 │ vruntime 大
│ (较晚需要CPU) │
└────────────────┘

任务选择:

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
static struct sched_entity *
pick_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{
// 获取最左节点 (最小 vruntime)
struct sched_entity *left = __pick_first_entity(cfs_rq);
struct sched_entity *se;

if (!left || (curr && entity_before(curr, left)))
se = curr;
else
se = left;

// 跳过正在运行但需要被抢占的任务
if (cfs_rq->skip == se) {
struct sched_entity *second;
if (se == curr)
second = __pick_first_entity(cfs_rq);
else
second = __pick_next_entity(se);
if (second && wakeup_preempt_entity(second, left) < 1)
se = second;
}

return se;
}

5.5 实时调度器

位置: kernel/sched/rt.c

策略类型:

  • SCHED_FIFO - 先进先出,运行直到主动让出
  • SCHED_RR - 轮转调度,时间片用完重新排队

实时优先级: 0-99 (数值越大优先级越高)

调度逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq,
struct rt_rq *rt_rq)
{
struct rt_prio_array *array = &rt_rq->active;
struct sched_rt_entity *next = NULL;

// 查找最高优先级非空队列
struct list_head *queue = __sched_pos_from_prio(array, idx);

// 获取队列第一个任务
next = list_entry(queue->next, struct sched_rt_entity, run_list);

return next;
}

5.6 Deadline 调度器

位置: kernel/sched/deadline.c

调度算法: EDF (Earliest Deadline First)

任务参数:

  • runtime - 执行时间预算
  • deadline - 绝对截止时间
  • period - 任务周期

带宽管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 总带宽不能超过: (runtime / period) * 100% <= available
// 默认: 95% CPU 时间可用于 deadline 任务

static int dl_check_constraints(struct sched_dl_entity *dl_se)
{
u64 period = dl_se->dl_period;
u64 runtime = dl_se->dl_runtime;
u64 new_bw = to_ratio(period, runtime);

// 检查带宽
if (new_bw > to_ratio(global_rt_period(), global_rt_runtime()))
return -EBUSY;

return 0;
}

5.7 上下文切换

位置: kernel/sched/core.c:context_switch()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static __always_inline struct rq *
context_switch(struct rq *rq, struct task_struct *prev,
struct task_struct *next, struct rq_flags *rf)
{
struct mm_struct *mm, *oldmm;

prepare_task_switch(rq, prev, next);

mm = next->mm;
oldmm = prev->active_mm;

// 切换地址空间
switch_mm_irqs_off(oldmm, mm, next);

// 切换寄存器状态
switch_to(oldmm, mm, prev, next);

finish_task_switch(rq, prev);

return rq;
}

switch_to 宏 (体系相关):

1
2
3
4
5
6
7
8
9
10
11
12
/* x86_64 */
#define switch_to(prev, next, last) \
asm volatile("pushq %%rbp\n\t" \
"movq %%rsp, %P[thread_rsp](%[prev])\n\t" \
"movq %P[thread_rsp](%[next]), %%rsp\n\t" \
"call __switch_to\n\t" \
"popq %%rbp\n\t" \
: "=a" (last) \
: [prev] "D" (prev), \
[next] "S" (next) \
: "memory", "rcx", "rbx", "r8", "r9", \
"r10", "r11", "r12", "r13", "r14", "r15");

5.8 负载均衡

位置: kernel/sched/topology.c

负载均衡时机:

  1. 周期性负载均衡 (scheduler_tick)
  2. CPU 空闲时 (idle_balance)
  3. 创建新进程时 (select_task_rq_fair)
  4. CPU 热迁移时

负载计算:

1
2
3
4
5
6
7
8
9
10
11
12
13
// PELT (Per-Entity Load Tracking)
static __always_inline u64
___update_load_sum(u64 now, u64 last_update, u64 period)
{
long delta = now - last_update;

if (delta > period) {
// 负载衰减
delta = decay_load(last_update, now);
}

return delta;
}

6. 进程状态与转换

6.1 进程状态定义

状态位: include/linux/sched.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 基本状态
#define TASK_RUNNING 0x00000000
#define TASK_INTERRUPTIBLE 0x00000001
#define TASK_UNINTERRUPTIBLE 0x00000002
#define __TASK_STOPPED 0x00000004
#define __TASK_TRACED 0x00000008

// 退出状态
#define EXIT_DEAD 0x00000010
#define EXIT_ZOMBIE 0x00000020

// 特殊状态
#define TASK_PARKED 0x00000040
#define TASK_DEAD 0x00000080
#define TASK_WAKEKILL 0x00000100
#define TASK_WAKING 0x00000200
#define TASK_NOLOAD 0x00000400
#define TASK_NEW 0x00000800
#define TASK_RTLOCK_WAIT 0x00001000
#define TASK_FREEZABLE 0x00002000
#define TASK_FROZEN 0x00008000

6.2 状态转换图

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
                创建进程


┌─────────────────────────────────────────────────────┐
│ TASK_NEW │
│ (新创建,尚未运行) │
└──────────────────────┬──────────────────────────────┘
│ wake_up_new_task()

┌─────────────────────────────────────────────────────┐
│ TASK_RUNNING │
│ (就绪队列中,可被调度) │
└───────────────┬─────────────────────────┬───────────┘
│ │
schedule() 选中 等待资源/事件
│ │
↓ ↓
┌──────────────────────┐ ┌──────────────────────────┐
│ 运行中 │ │ TASK_INTERRUPTIBLE │
│ (current = task) │ │ TASK_UNINTERRUPTIBLE │
└─────────┬────────────┘ │ (可中断/不可中断睡眠) │
│ └────────────┬─────────────┘
│ │
│ 时间片用完/主动yield │ 事件到达
│ │
↓ ↓
┌──────────────────────┐ ┌──────────────────────────┐
│ TASK_RUNNING │ │ TASK_RUNNING │
│ (重新排队) │ └──────────────────────────┘
└──────────────────────┘

特殊状态:
┌──────────────────┐ SIGSTOP ┌────────────────────┐
│ __TASK_STOPPED │ ←────────────── │ 暂停调试 │
│ (进程被暂停) │ │ │
└─────────┬────────┘ └────────────────────┘

│ SIGCONT

TASK_RUNNING

┌──────────────────┐ SIGKILL ┌────────────────────┐
│ __TASK_TRACED │ ←────────────── │ ptrace 调试 │
│ (被跟踪调试) │ │ │
└──────────────────┘ └────────────────────┘

退出路径:
┌──────────────────┐ do_exit() ┌────────────────────┐
│ TASK_DEAD │ ←────────────── │ EXIT_ZOMBIE │
│ (正在退出) │ │ (僵尸进程) │
└──────────────────┘ └─────────┬──────────┘

wait()/waitpid()


┌────────────────────┐
│ EXIT_DEAD │
│ (进程描述符回收) │
└────────────────────┘

6.3 睡眠与唤醒

睡眠函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 可中断睡眠
static inline int signal_pending_state(long state, struct task_struct *p)
{
if (!(state & TASK_INTERRUPTIBLE))
return 0;
return signal_pending(p);
}

// 等待事件
wait_event_interruptible(wq, condition);
wait_event_uninterruptible(wq, condition);
wait_event_killable(wq, condition);
wait_event_timeout(wq, condition, timeout);

唤醒函数:

1
2
3
4
5
// 唤醒等待队列
void wake_up(struct wait_queue_head *wq_head);
void wake_up_all(struct wait_queue_head *wq_head);
void wake_up_interruptible(struct wait_queue_head *wq_head);
void wake_up_process(struct task_struct *tsk);

6.4 等待队列

位置: include/linux/wait.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
// 等待队列头
struct wait_queue_head {
spinlock_t lock;
struct list_head head;
};

// 等待队列项
struct wait_queue_entry {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head entry;
};

// 初始化
DECLARE_WAIT_QUEUE_HEAD(name);
init_waitqueue_head(wq_head);

// 添加等待项
void add_wait_queue(struct wait_queue_head *wq_head,
struct wait_queue_entry *wq_entry);

// 移除等待项
void remove_wait_queue(struct wait_queue_head *wq_head,
struct wait_queue_entry *wq_entry);

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 等待队列定义
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int condition = 0;

// 等待进程
static int waiting_thread(void *data)
{
wait_event_interruptible(wq, condition != 0);
return 0;
}

// 唤醒进程
static void wake_condition(void)
{
condition = 1;
wake_up(&wq);
}

7. 进程退出与等待

7.1 进程退出

位置: kernel/exit.c:do_exit()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void __noreturn do_exit(long code)
{
struct task_struct *tsk = current;

// 1. 设置退出状态
tsk->exit_code = code;

// 2. 释放各种资源
exit_signals(tsk); // 信号处理
exit_notify(tsk); // 通知父进程
exit_mm(tsk); // 释放地址空间
exit_sem(tsk); // 释放信号量
__exit_fs(tsk); // 释放文件系统
exit_files(tsk); // 释放文件描述符
exit_thread(tsk); // 线程相关
exit_creds(tsk); // 释放证书

// 3. 更新状态为僵尸
tsk->state = TASK_DEAD;
tsk->flags |= PF_EXITPIDONE;

// 4. 调度出去,永不返回
__schedule(SM_DEAD);
}

7.2 僵尸进程与孤儿进程

僵尸进程 (Zombie):

  • 进程已退出,但父进程尚未 wait()
  • 保留进程描述符中的部分信息 (退出码等)
  • 进程状态为 EXIT_ZOMBIE

孤儿进程 (Orphan):

  • 父进程先于子进程退出
  • 被 init 进程 (PID 1) 收养
  • init 会定期调用 wait() 回收僵尸

7.3 wait 系列系统调用

系统调用:

1
2
3
4
5
6
7
8
9
SYSCALL_DEFINE5(waitid, int, which, pid_t, upid,
struct siginfo __user *, infop,
int, options, struct rusage __user *, ru);

SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
int, options, struct rusage __user *, ru);

SYSCALL_DEFINE3(waitpid, pid_t, pid, int __user *, stat_addr,
int, options);

实现位置: kernel/exit.c:do_wait()

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
static long do_wait(struct wait_opts *wo)
{
struct task_struct *tsk;
int retval;

// 查找符合条件的子进程
tsk = current;
do {
struct task_struct *p;

list_for_each_entry(p, &tsk->children, sibling) {
int ret = wait_consider_task(wo, p);
if (ret)
return ret;
}

if (wo->flags & WNOTHREAD)
break;

tsk = next_thread(tsk);
} while (tsk != current);

// 等待子进程状态变化
retval = wait_task_inactive(wo, tsk);

return retval;
}

7.4 退出通知

位置: kernel/exit.c:exit_notify()

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 void exit_notify(struct task_struct *tsk)
{
// 1. 向父进程发送 SIGCHLD
do_notify_parent(tsk, tsk->exit_signal);

// 2. 如果父进程忽略 SIGCHLD,直接回收
if (tsk->exit_signal == SIGCHLD &&
!do_notify_parent(tsk, tsk->exit_signal)) {
tsk->exit_state = EXIT_DEAD;
release_task(tsk);
return;
}

// 3. 设置僵尸状态
tsk->exit_state = EXIT_ZOMBIE;

// 4. 如果成为孤儿,被 init 收养
if (tsk->parent == tsk->real_parent) {
struct task_struct *reaper;

reaper = find_child_reaper(tsk);
if (reaper != tsk->parent) {
for_each_thread(tsk, p)
p->real_parent = reaper;
tsk->parent = reaper;
}
}
}

8. 内核线程

8.1 内核线程特点

  • 运行在内核空间,没有用户地址空间
  • mm 为 NULL,active_mm 指向前一个进程的 mm (借用)
  • 只能调用内核函数,不能访问用户空间
  • 直接访问所有内核数据和函数

8.2 kthread_create API

位置: kernel/kthread.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建内核线程
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[], ...);

// 创建并唤醒内核线程
struct task_struct *kthread_run(int (*threadfn)(void *data),
void *data,
const char namefmt[], ...);

// 停止内核线程
int kthread_stop(struct task_struct *k);

// 检查是否应该停止
bool kthread_should_stop(void);

8.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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
static int kthread(void *_create)
{
struct kthread_create_info *create = _create;
int (*threadfn)(void *data) = create->threadfn;
void *data = create->data;

// 设置完成信号
complete(&create->started);

// 执行线程函数
if (!threadfn) {
/* kthread 守护进程,永不返回 */
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
}

return threadfn(data);
}

struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
void *data, int node,
const char namefmt[],
...)
{
struct kthread_create_info create;

create.threadfn = threadfn;
create.data = data;
init_completion(&create.started);
init_completion(&create.done);

// 实际创建进程
pid = kernel_thread(kthread, &create, CLONE_FS | CLONE_FILES |
SIGCHLD);

// 等待创建完成
wait_for_completion(&create.started);

return task;
}

8.4 常见内核线程

线程名 功能 位置
ksoftirqd/n 软中断处理 kernel/softirqd.c
kworker/n:n 工作队列执行 kernel/workqueue.c
kswapd/n 页面回收 mm/vmscan.c
kthrotld/n 磁盘 I/O 限流 block/blk-throttle.c
migration/n 负载均衡 kernel/sched/topology.c
rcuop/n RCU 回调 kernel/rcu/tree.c
rcuog/n RCU 强制宽限 kernel/rcu/tree.c
rcu_preempt RCU 抢占 kernel/rcu/tree.c
kcompactd/n 内存压缩 mm/compaction.c
ksmd KSM 合并 mm/ksm.c

9. 进程关系与组管理

9.1 进程树结构

1
2
3
4
5
6
7
8
struct task_struct {
// 进程关系
struct task_struct __rcu *real_parent; // 真实父进程
struct task_struct __rcu *parent; // 当前父进程
struct list_head children; // 子进程链表
struct list_head sibling; // 兄弟进程链表
struct task_struct *group_leader; // 线程组首领
};

进程树示例:

1
2
3
4
5
6
7
8
9
10
11
12
init (PID 1)
├─ systemd (PID 1000, PGID 1000)
│ ├─ NetworkManager (PID 1001, PGID 1000)
│ ├─ sshd (PID 1002, PGID 1000)
│ │ └─ sshd (PID 2000, PGID 2000, SID 2000)
│ │ └─ bash (PID 2001, PGID 2000, SID 2000)
│ │ └─ command (PID 2002, PGID 2002, SID 2000)
│ └─ cron (PID 1003, PGID 1000)
└─ containerd (PID 500, PGID 500)
└─ dockerd (PID 501, PGID 500)
└─ container-init (PID 10000, PGID 10000)
└─ app-process (PID 10001, PGID 10000)

9.2 线程组

多线程进程结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
进程 (TGID = 1000):
┌─────────────────────────────────────────────────────┐
│ shared: mm, fs, files, signal, sighand │
└─────────────────────────────────────────────────────┘

┌───────────────┼───────────────┐
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ thread 1 │ │ thread 2 │ │ thread 3 │
│ PID=1000 │ │ PID=1001 │ │ PID=1002 │
│ TGID=1000 │ │ TGID=1000 │ │ TGID=1000 │
└───────────┘ └───────────┘ └───────────┘

task_struct 关系:
thread 1: group_leader = thread1, thread_pid = PID 1000
thread 2: group_leader = thread1, thread_pid = PID 1001
thread 3: group_leader = thread1, thread_pid = PID 1002

9.3 进程组与会话

1
2
3
4
struct task_struct {
struct pid *pgrp; // 进程组 (PGID)
struct pid *session; // 会话 (SID)
};

会话与进程组:

1
2
3
4
5
6
7
8
9
10
11
12
Session 1 (SID 1):
┌──────────────────────────────────────────────────────┐
│ Process Group 1000 (PGID 1000): │
│ ├─ bash (PID 1000, leader) │
│ ├─ command1 (PID 1001) │
│ └─ command2 (PID 1002) │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ Process Group 2000 (PGID 2000): │
│ ├─ background_task1 (PID 2000, leader) │
│ └─ background_task2 (PID 2001) │
└──────────────────────────────────────────────────────┘

9.4 控制终端

1
2
3
struct signal_struct {
struct tty_struct *tty; // 控制终端
};

终端相关信号:

  • SIGINT (Ctrl+C) - 中断
  • SIGQUIT (Ctrl+) - 退出
  • SIGTSTP (Ctrl+Z) - 停止
  • SIGHUP - 挂断 (终端关闭)

10. 实时进程管理

10.1 实时策略

策略类型:

1
2
3
4
5
6
#define SCHED_NORMAL    0   // 普通分时进程 (CFS)
#define SCHED_FIFO 1 // 先进先出实时
#define SCHED_RR 2 // 轮转实时
#define SCHED_BATCH 3 // 批处理进程
#define SCHED_IDLE 5 // 空闲进程
#define SCHED_DEADLINE 6 // Deadline 调度

10.2 实时优先级

1
2
3
4
5
6
#define MAX_RT_PRIO     100     // 实时优先级数量
#define MAX_USER_RT_PRIO 100 // 用户可设置的实时优先级范围
#define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH) // 总优先级级数

// 实时优先级: 0-99
// 普通优先级: 100-139 (nice -20 到 +19)

10.3 实时调度 API

系统调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 设置调度策略和优先级
int sched_setscheduler(pid_t pid, int policy,
const struct sched_param *param);

// 获取调度策略
int sched_getscheduler(pid_t pid);

// 设置优先级
int sched_setparam(pid_t pid, const struct sched_param *param);

// 获取优先级
int sched_getparam(pid_t pid, struct sched_param *param);

// 让出 CPU
int sched_yield(void);

10.4 优先级继承

用途: 解决优先级反转问题

实现: kernel/locking/rtmutex.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 优先级继承链
// 高优先级任务A 等待 被低优先级任务C 持有的互斥锁
// 中等优先级任务B 抢占 C,导致 A 饿死
// 解决: C 继承 A 的优先级,快速执行并释放锁

void rt_mutex_set_prio(struct task_struct *task, int prio)
{
// 提升优先级
task->prio = prio;
task->rt_priority = prio;

// 如果在运行队列中,重新排队
if (task->on_rq)
enqueue_task(rq, task, ENQUEUE_RESTORE);
}

11. 进程资源限制与统计

11.1 资源限制 (rlimit)

位置: include/linux/sched.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct task_struct {
struct rlimit rlim[RLIM_NLIMITS];
};

enum rlimit_resource {
RLIMIT_CPU, // CPU 时间
RLIMIT_FSIZE, // 文件大小
RLIMIT_DATA, // 数据段大小
RLIMIT_STACK, // 栈大小
RLIMIT_CORE, // Core 文件大小
RLIMIT_RSS, // 驻留集大小
RLIMIT_NPROC, // 进程数
RLIMIT_NOFILE, // 打开文件数
RLIMIT_MEMLOCK, // 锁定内存
RLIMIT_AS, // 地址空间大小
RLIMIT_LOCKS, // 文件锁数
RLIMIT_SIGPENDING, // 挂起信号数
RLIMIT_MSGQUEUE, // POSIX 消息队列
RLIMIT_NICE, // Nice 值
RLIMIT_RTPRIO, // 实时优先级
RLIMIT_RTTIME, // 实时 CPU 时间
RLIMIT_NLIMITS,
};

11.2 进程统计

位置: kernel/sched/stats.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// CPU 时间统计
struct task_struct {
u64 utime; // 用户态时间
u64 stime; // 内核态时间
u64 gtime; // Guest 时间
unsigned long nvcsw; // 自愿上下文切换
unsigned long nivcsw; // 非自愿上下文切换
u64 utimescaled; // 缩放的用户时间
u64 stimescaled; // 缩放的内核时间
u64 prev_cputime; // 之前的 CPU 时间
};

// 缺页统计
struct task_struct {
unsigned long min_flt; // 次要缺页
unsigned long maj_flt; // 主要缺页
};

11.3 /proc 文件系统

进程相关文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/proc/[pid]/
├── cmdline # 命令行参数
├── cwd # 当前工作目录链接
├── environ # 环境变量
├── exe # 可执行文件链接
├── fd/ # 文件描述符目录
├── maps # 内存映射
├── mem # 进程内存
├── mounts # 挂载点
├── stat # 进程状态
├── statm # 内存统计
├── status # 详细状态
├── task/ # 线程子目录
└── ...

12. 进程调试与监控

12.1 strace

用途: 追踪系统调用

1
2
3
4
5
6
7
8
9
10
11
# 追踪系统调用
strace ls

# 追踪特定进程
strace -p 1234

# 统计系统调用时间
strace -c ls

# 追踪子进程
strace -f ls

12.2 perf

用途: 性能分析

1
2
3
4
5
6
7
8
9
10
11
# 记录调度事件
perf sched record -- sleep 60

# 分析调度延迟
perf sched latency

# 分析调度映射
perf sched map

# 实时监控
perf top

12.3 ftrace

用途: 函数追踪

1
2
3
4
5
6
7
8
9
10
11
# 可用的追踪器
cat /sys/kernel/debug/tracing/available_tracers

# 启用函数图追踪器
echo function_graph > /sys/kernel/debug/tracing/current_tracer

# 追踪特定函数
echo sched_fork > /sys/kernel/debug/tracing/set_ftrace_filter

# 查看追踪结果
cat /sys/kernel/debug/tracing/trace

12.4 BPF/BCC

用途: 动态追踪

1
2
3
4
5
6
7
8
9
10
11
# 追踪进程创建
bcc execsnoop

# 追踪进程退出
bcc exitsnoop

# 追踪调度延迟
bcc runqlat

# 追踪 CPU 使用
bcc profile

12.5 /proc/sys/kernel 调优参数

1
2
3
4
5
6
7
8
9
# 进程相关参数
/proc/sys/kernel/pid_max # 最大 PID
/proc/sys/kernel/threads-max # 最大线程数
/proc/sys/kernel/randomize_va_space # ASLR 设置
/proc/sys/kernel/sched_rt_period_us # 实时调度周期
/proc/sys/kernel/sched_rt_runtime_us # 实时调度运行时间
/proc/sys/kernel/sched_min_granularity_ns # CFS 最小调度粒度
/proc/sys/kernel/sched_wakeup_granularity_ns # CFS 唤醒粒度
/proc/sys/kernel/sched_latency_ns # CFS 目标延迟

附录 A: 系统调用参考

fork/vfork/clone

系统调用 描述 头文件
fork() 创建子进程 unistd.h
vfork() 创建共享地址空间的子进程 unistd.h
clone() 灵活创建进程/线程 sched.h
clone3() 新版 clone (Linux 5.3+) sched.h

exec 系列函数

函数 描述 头文件
execl() 执行程序 unistd.h
execlp() 执行程序 (PATH 搜索) unistd.h
execle() 执行程序 (指定环境) unistd.h
execv() 执行程序 (参数数组) unistd.h
execvp() 执行程序 (PATH + 参数数组) unistd.h
execve() 系统调用 unistd.h

wait 系列函数

函数 描述 头文件
wait() 等待子进程 sys/wait.h
waitpid() 等待指定子进程 sys/wait.h
waitid() 等待子进程状态变化 sys/wait.h
wait3() 等待并获取资源使用 sys/wait.h
wait4() 等待指定子进程并获取资源 sys/wait.h

调度相关系统调用

系统调用 描述 头文件
sched_setscheduler() 设置调度策略 sched.h
sched_getscheduler() 获取调度策略 sched.h
sched_setparam() 设置优先级 sched.h
sched_getparam() 获取优先级 sched.h
sched_yield() 让出 CPU sched.h
sched_getaffinity() 获取 CPU 亲和性 sched.h
sched_setaffinity() 设置 CPU 亲和性 sched.h
sched_get_priority_max() 获取最大优先级 sched.h
sched_get_priority_min() 获取最小优先级 sched.h
sched_rr_get_interval() 获取 RR 时间片 sched.h

其他相关系统调用

系统调用 描述 头文件
getpid() 获取进程 ID unistd.h
getppid() 获取父进程 ID unistd.h
gettid() 获取线程 ID unistd.h
getpgid() 获取进程组 ID unistd.h
setpgid() 设置进程组 ID unistd.h
getsid() 获取会话 ID unistd.h
setsid() 创建新会话 unistd.h
getpriority() 获取优先级 sys/resource.h
setpriority() 设置优先级 sys/resource.h
getrlimit() 获取资源限制 sys/resource.h
setrlimit() 设置资源限制 sys/resource.h
nice() 调整 nice 值 unistd.h

附录 B: 相关配置选项

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
# 调度器相关
CONFIG_SCHED_CORE # 核心调度支持
CONFIG_SCHED_AUTOGROUP # 自动进程组
CONFIG_FAIR_GROUP_SCHED # CFS 分组调度
CONFIG_RT_GROUP_SCHED # 实时分组调度
CONFIG_CFS_BANDWIDTH # CFS 带宽控制
CONFIG_RT_GROUP_SCHED # 实时分组调度
CONFIG_UCLAMP_TASK # 任务利用率钳制
CONFIG_UCLAMP_BUCKETS_COUNT # 钳制桶数量

# 进程调试
CONFIG_PROC_FS # /proc 文件系统
CONFIG_PROC_CHILDREN # /proc/[pid]/children
CONFIG_PROC_PID_CPUSET # /proc/[pid]/cpuset
CONFIG_STACKTRACE # 栈追踪
CONFIG_DEBUG_STACK_USAGE # 栈使用调试

# 进程资源限制
CONFIG_RCU_BOOST # RCU 优先级提升
CONFIG_RCU_NOCB_CPU # RCU 回调离线 CPU

# 其他
CONFIG_MEMCG # 内存 cgroup
CONFIG_CPUSETS # CPU 集合
CONFIG_CGROUP_SCHED # cgroup 调度

附录 C: 参考文献

  1. Linux 内核源码

    • kernel/fork.c - 进程创建
    • kernel/exit.c - 进程退出
    • kernel/sched/core.c - 调度器核心
    • kernel/sched/fair.c - CFS 调度
    • kernel/sched/rt.c - 实时调度
    • include/linux/sched.h - 进程描述符
  2. Linux 内核文档

    • Documentation/scheduler/ - 调度器文档
    • Documentation/admin-guide/ - 管理指南
  3. 理解 Linux 内核 (ULK3) - Daniel P. Bovet & Marco Cesati

  4. Linux 内核设计与实现 (LKD3) - Robert Love

  5. Linux Kernel Development - Robert Love

  • Title: Linux内核分析之进程管理-00
  • Author: 韩乔落
  • Created at : 2024-01-29 22:11:49
  • Updated at : 2026-01-19 13:40:29
  • Link: https://jelasin.github.io/2024/01/29/Linux内核分析之进程管理-00/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
Linux内核分析之进程管理-00