《Linux 内核深度解析:基于 ARM64 架构的 Linux 4.x 内核》 第二章:进程管理(Process Management)
本章深入 Linux 内核的进程管理子系统,涵盖进程(Task)数据结构、创建与销毁、调度机制、上下文切换,以及与进程管理密切相关的信号、倒计时器与 CPU 热插拔等内容。
1. 进程与线程——Task 描述符
1.1 task_struct
在 Linux 内核中,每个执行单元(包括用户态进程与内核线程)都由一个 struct task_struct 描述,位于内核头文件 include/linux/sched.h。其核心字段包括:
-
进程标识
pid:进程号tgid:线程组 ID(针对线程模型)
-
状态与优先级
state:TASK_RUNNING、TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE、TASK_STOPPED、TASK_TRACED 等prio、static_prio、normal_prio、rt_priority:调度优先级
-
上下文保存
thread:架构相关的寄存器集(struct thread_struct),包含通用寄存器、SP_ELx、PSTATE、SVE/FP 上下文等
-
调度与运行队列
run_list:链接同一调度队列中的双向链表节点on_rq:是否在运行队列
-
进程树
parent、children、sibling:管理父子关系
-
文件与内存
files:文件描述符表mm:用户空间地址空间(struct mm_struct)或NULL(内核线程)
1.2 thread_struct(架构相关)
在 arch/arm64/include/asm/thread_info.h 与 arch/arm64/include/asm/thread_struct.h 中,定义了 ARM64 特有的线程上下文:
- 一组通用寄存器
x0–x30 - 程序计数器
pc - 进程状态寄存器
pstate - 堆栈指针
sp对应各特权级别的SP_EL0/SP_EL1 - SVE/FP 扩展的上下文保存指针
在上下文切换时,内核通过 __switch_to() (定义在 arch/arm64/kernel/switch.S)精确地保存与恢复这些寄存器。
2. 进程创建与销毁
2.1 fork() 与 clone()
系统调用接口位于 kernel/fork.c,核心入口为 sys_clone() 和 do_fork()。进程创建流程:
-
参数解析
clone_flags决定是否共享地址空间(CLONE_VM)、文件描述符(CLONE_FILES)、信号处理(CLONE_SIGHAND)等。
-
分配
task_struct- 调用
dup_task_struct(),复制父进程的task_struct,并初始化必要字段,如清空 exit state。
- 调用
-
分配与复制进程上下文
- 若非线程(
CLONE_VM未设置),调用copy_process_mm()复制mm_struct; - 调用
copy_thread()(arch/arm64/kernel/process.c)设置子进程内核栈与寄存器上下文,实现子进程在用户态返回 0。
- 若非线程(
-
文件、信号、cgroup、namespaces
- 根据
clone_flags,共享或复制文件描述符表、信号处理结构、命名空间等。
- 根据
-
进程就绪
- 将子进程放入调度器运行队列(
enqueue_task()),并返回子进程 PID 给父进程。
- 将子进程放入调度器运行队列(
2.2 进程退出:exit()
当用户进程调用 exit() 或者内核线程调用 do_exit(),会走以下路径:
-
释放资源
- 调用各类
release_函数,依次释放文件描述符、内存映射、信号处理、cgroup 关联、PID 等。
- 调用各类
-
通知父进程
- 将子进程状态设置为
TASK_DEAD,并唤醒在wait()上阻塞的父进程。
- 将子进程状态设置为
-
等待回收
- 当父进程调用
waitpid(),内核调用do_wait(),回收task_struct并最终销毁。
- 当父进程调用
3. 调度器:完全公平调度(CFS)
Linux 默认调度器为 CFS(Completely Fair Scheduler),其目标是在可运行进程间分配“公平”的 CPU 时间。
3.1 红黑树与虚拟运行时间
-
红黑树
- CFS 维护一个按
vruntime(虚拟运行时间)排序的红黑树(struct rb_root_cached)。 - 每当进程运行时,其
vruntime按照权重(优先级)增长。
- CFS 维护一个按
-
选取下一个进程
- 调度时,调用
pick_next_task_fair(),选择红黑树中最左侧(min_vruntime)的节点。
- 调度时,调用
-
负载平衡
- 在 SMP 系统中,内核周期性地执行
load_balance(),将任务在各 CPU 之间迁移,以均衡负载。
- 在 SMP 系统中,内核周期性地执行
3.2 时间片与 sched_latency
-
调度周期(
sched_latency_ns)- 所有可运行进程在完整周期内至少能获得一次运行机会。
-
最小/最大时间片
-
每个任务的时间片由
sched_min_granularity_ns与进程权重共同决定:time_slice = weight/task_sum_weight * sched_latency
-
4. 上下文切换(Context Switch)
上下文切换是调度器的核心支撑。在 ARM64 上,通过汇编与 C 混合实现:
-
保存当前上下文
- 在
__switch_to()(arch/arm64/kernel/switch.S)中,先保存通用寄存器到当前进程的thread_struct。
- 在
-
更新
current- 将内核的
current指针指向新进程的task_struct。
- 将内核的
-
恢复新进程上下文
- 从新进程的
thread_struct恢复通用寄存器、SP、PSTATE 等; - 最后跳转到用户态或内核态继续执行。
- 从新进程的
性能优化要点:
- 批量 TLBI:切换页面表时使用
tlbi批指令清理旧映射。 - Lazy FPU/SVE:仅在进程首次使用浮点/SVE 时才保存与恢复 FP/SVE 寄存器,减少上下文切换开销。
5. 信号与定时器
5.1 信号(Signals)
-
信号队列
- 每个
task_struct包含struct sigpending pending与struct k_sigaction sigaction[NSIG]。
- 每个
-
发送与处理
- 内核调用
force_sig()、kill_proc()等发送信号; - 在
do_signal()中,根据进程状态(可中断/不可中断)决定是否立即中断系统调用并执行信号处理函数。
- 内核调用
5.2 进程定时器
进程可创建多种定时器(alarm(timer_t)、setitimer()、timerfd等)。内核通过:
- 系统范围时钟事件(
clockevents):驱动硬件定时器,分发tick_sched_timer()。 - 高精度定时器(hrtimer):基于 HRTIMER API,支持纳秒级精度。
在调度与信号处理过程中,定时器触发会产生软中断(softirq),最终由 do_softirq() 调用相关处理函数,如 hrtimer_run()。
6. CPU 热插拔与负载迁移
在 NUMA 或需热插拔 CPU 的系统中:
-
离线 CPU
- 通过
/sys/devices/system/cpu/cpuX/online控制,调用cpu_disable(),迁移该 CPU 上的所有进程到其它在线 CPU。
- 通过
-
上线 CPU
- 调用
cpu_enable(),重新将 CPU 添加到调度器的 CPU 掩码,并参与后续负载均衡。
- 调用
负载迁移依赖 sched_domains 架构,用于定义不同层级的负载平衡范围(同簇、同节点、跨节点)。
本章小结
本章详细剖析了 Linux 4.x 内核在 ARM64 平台上的进程管理子系统:
task_struct与thread_struct的关键字段及架构相关扩展;- 进程创建(
fork/clone) 与 销毁(exit) 流程; - CFS 调度器 的红黑树设计、时间片计算与负载均衡;
- 上下文切换 在 ARM64 上的实现细节与性能优化;
- 信号处理与定时器 机制;
- CPU 热插拔 与任务迁移策略。
理解这些机制,是后续深入 内存管理、中断与设备驱动 以及 文件系统 等子系统的基础。