news 2026/10/4 17:54:43

【linux内核专栏 03】进程管理与调度

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【linux内核专栏 03】进程管理与调度

本篇定位:Linux 内核的"宇宙中心"是task_struct——所有子系统都围绕它。对于有过轻量级实时操作系统,如FreeRTOS TCB 到 port 层经验的工程师,本篇把"Linux 进程"对你 FreeRTOS 任务的增量讲清:task_struct 比 TCB 胖十倍(含 mm/fs/files/signals)、调度从"优先级抢占"跳到"CFS 公平 + EEVDF"、上下文切换从"切栈"跳到"切地址空间 + 切栈 + 切 fs/files"。读完能写内核线程、能调调度问题、能讲清 CFS vs FreeRTOS 优先级调度的本质差异。


目录

  • 一、task_struct:宇宙中心的数据结构
    • 1.1 task_struct 有多大
    • 1.2 task_struct 核心字段(分组)
    • 1.3 对照 FreeRTOS TCB
  • 二、进程状态
    • 2.1 Linux 进程状态
    • 2.2 状态机
    • 2.3 关键状态解读
    • 2.4 ps 看状态
    • 2.5 对照 FreeRTOS 状态
  • 三、进程 vs 线程 vs 内核线程
    • 3.1 Linux 不区分进程线程(都是 task_struct)
    • 3.2 fork / clone / vfork
    • 3.3 内核线程(kthread)
    • 3.4 PID vs TGID vs TID
  • 四、上下文切换(对照 RISC-V trap)
    • 4.1 两种"切换"
    • 4.2 进程上下文切换做什么
    • 4.3 switch_to:切寄存器(架构相关)
    • 4.4 对照你 RISC-V trap 切栈
    • 4.5 为什么只存 callee-saved
  • 五、CFS 调度器(Completely Fair Scheduler)⭐ 核心
    • 5.1 CFS 的核心思想
    • 5.2 vruntime 怎么算
    • 5.3 nice 值与权重
    • 5.4 CFS 数据结构:红黑树
    • 5.5 调度时序
    • 5.6 sched_latency 与 min_granularity
    • 5.7 对照 FreeRTOS 优先级调度
  • 六、EEVDF(新调度器,Linux 6.6+)
    • 6.1 为什么 CFS 升级到 EEVDF
    • 6.2 EEVDF 核心思想
    • 6.3 对你影响
  • 七、调度策略(Scheduling Policy)
    • 7.1 三类调度策略
    • 7.2 RT 优先级
    • 7.3 chrt 改策略
    • 7.4 SCHED_DEADLINE(硬实时)
  • 八、抢占(Preemption)
    • 8.1 抢占点
    • 8.2 抢占等级(CONFIG)
    • 8.3 关抢占 vs 关中断
  • 九、fork / exec / wait(进程生命周期)
    • 9.1 fork()
    • 9.2 exec()
    • 9.3 wait()
    • 9.4 孤儿进程与僵尸
  • 十、进程关系与会话
    • 10.1 进程组 / 会话
    • 10.2 守护进程(daemon)
  • 十一、常用进程调试
    • 11.1 工具
    • 11.2 关键 /proc 字段
  • 十二、本篇小结
  • 速查表

一、task_struct:宇宙中心的数据结构

1.1 task_struct 有多大

  • FreeRTOS TCB:几百字节(栈指针/优先级/状态/链表节点)
  • Linuxtask_struct:约 8000 字节(ARM64/RISC-V 64)

为什么这么大?因为 Linux 进程要独立地址空间 + 文件上下文 + 信号 + cgroup + namespace + 审计…全挂这里。

1.2 task_struct 核心字段(分组)

structtask_struct{// === 标识 ===pid_tpid;// 进程 ID(线程组里是线程 ID)pid_ttgid;// 线程组 ID(进程 ID,用户看到的)structtask_struct*group_leader;// 线程组首领charcomm[TASK_COMM_LEN];// 可执行文件名(16 字符)// === 状态 ===volatilelongstate;// 进程状态(见 §二)intexit_state;intprio,static_prio,normal_prio;// 优先级unsignedintpolicy;// 调度策略(SCHED_NORMAL/FIFO/RR)// === 调度 ===conststructsched_class*sched_class;// 调度类structsched_entityse;// CFS 调度实体(vruntime 等)structsched_rt_entityrt;// RT 调度实体unsignedintrt_priority;cpumask_tcpus_allowed;// 允许跑的 CPU// === 家族 ===structtask_struct*parent;// 父进程structlist_headchildren;// 子进程链表structlist_headsibling;// === 内存(★对照 FreeRTOS 最大差异)===structmm_struct*mm;// 进程地址空间(线程共享)structmm_struct*active_mm;// 内核线程用(借来的 mm)// === 文件 ===structfiles_struct*files;// 打开的文件表structfs_struct*fs;// 文件系统根/root// === 信号 ===structsignal_struct*signal;structsighand_struct*sighand;sigset_tblocked,real_blocked;// === 栈 ===void*stack;// 内核栈(union with thread_info)// === 时间统计 ===u64 utime,stime;// 用户态/内核态时间u64 nvcsw,nivcsw;// 自愿/非自愿切换次数// === namespace/cgroup(容器)===structnsproxy*nsproxy;structcss_set__rcu*cgroups;// ... 还有几百个字段};

1.3 对照 FreeRTOS TCB

字段FreeRTOS TCBLinux task_struct差异
栈指针pxTopOfStacksp(在 stack 里)同
优先级uxPriorityprio/static_prioLinux 更复杂(多套)
状态eTaskStatestateLinux 状态更多
链表xStateListItem 等list_head 多个同思路
地址空间❌ 无mm_struct ★Linux 最大增量
文件表❌ 无files_struct ★Linux 最大增量
信号❌ 无signal/sighand ★Linux 有
时间统计❌ 无utime/stimeLinux 有

嵌入式视角:
Linux task_struct 是"胖版 TCB"——多了 mm(地址空间)/files(文件)/signals(信号)/namespace(容器)。因为 Linux 进程要独立地址空间和文件上下文,FreeRTOS 任务全在一个地址空间共享一切。字段虽多,核心思路同(标识/状态/调度/栈),增量在 mm 和 files。


二、进程状态

2.1 Linux 进程状态

#define TASK_RUNNING 0 // 可运行(就绪或正在跑) #define TASK_INTERRUPTIBLE 1 // 可中断睡眠(信号能唤醒) #define TASK_UNINTERRUPTIBLE 2 // 不可中断睡眠(信号不唤醒,等 IO) #define __TASK_STOPPED 4 // 停止(信号 SIGSTOP) #define __TASK_TRACED 8 // 被跟踪(gdb) #define EXIT_ZOMBIE 16 // 僵尸(死了等父收尸) #define EXIT_DEAD 32 // 最终死亡

2.2 状态机

fork() │ ▼ ┌─────────┐ 调度选中 ┌─────────┐ │ RUNNING │ ─────────> │ RUNNING │ │ (就绪) │ <───────── │ (运行) │ └────┬────┘ 时间片到 └────┬────┘ │ │ │ 等 IO/信号量 │ 信号/IO 完成 ▼ ▼ ┌──────────────┐ ┌─────────┐ │INTERRUPTIBLE │ ───────>│ RUNNING │ │ (可中断睡) │ 唤醒 │ (就绪) │ └──────────────┘ └─────────┘ │ │ 等 磁盘 IO(D 状态) ▼ ┌────────────────┐ │UNINTERRUPTIBLE │ 信号唤不醒,只能 IO 完成 │ (不可中断睡) │ └────────────────┘

2.3 关键状态解读

  • TASK_RUNNING:就绪(在运行队列)或正在跑(单 CPU 同时只有一个"跑")
  • TASK_INTERRUPTIBLE:可中断睡眠,等事件(信号量/IO/信号),信号能打断
  • TASK_UNINTERRUPTIBLE:D状态,等磁盘 IO 等,信号唤不醒——这是"杀不掉"的进程(kill -9 也没用)
  • TASK_ZOMBIE:死了但父没wait(),占 PID 和退出码,等收尸

2.4 ps 看状态

ps-eopid,stat,comm# STAT 列:# R=running S=interruptible sleep D=uninterruptible sleep# T=stopped Z=zombie X=dead# + 前台 s 会话首 l 多线程 < 高优先级 N 低优先级

2.5 对照 FreeRTOS 状态

FreeRTOSLinux差异
RunningRUNNING(运行)同
ReadyRUNNING(就绪)Linux 不分就绪/运行(都 RUNNING)
BlockedINTERRUPTIBLE/UNINTERRUPTIBLELinux 分两种睡眠
SuspendedSTOPPED类似
DeletedZOMBIELinux 要父收尸

D 状态是 Linux 的"杀不掉"
FreeRTOS 任务阻塞 = 等 signal/queue,能被删。LinuxD状态(等磁盘 IO)信号唤不醒,kill -9 也杀不掉——因为杀它要它响应信号,它在 D 不响应。这是 Linux 比 FreeRTOS 复杂的点:IO 等待有"不可打断"语义,防止 IO 中途被打断导致数据不一致。你调"进程杀不掉"先看是不是 D 状态。


三、进程 vs 线程 vs 内核线程

3.1 Linux 不区分进程线程(都是 task_struct)

概念mm 是否共享files 是否共享创建方式
进程不共享(独立)不共享fork()
线程共享共享clone(CLONE_VM|CLONE_FILES|…)
内核线程无 mm(借 active_mm)共享 init_fileskthread_create()

3.2 fork / clone / vfork

Linux 进程创建底层都是clone系统调用,靠 flag 控制共享什么:

// fork:不共享(子独立)clone(SIGCHLD);// pthread_create:共享 mm/files/信号等clone(CLONE_VM|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|...);// vfork:共享 mm,父阻塞到子 exec/exitclone(CLONE_VM|CLONE_VFORK);

3.3 内核线程(kthread)

内核线程:只在内核态跑,没用户态地址空间。用途:后台活(kworker/ksoftirqd/kthreadd/migration)。

// 创建内核线程structtask_struct*k=kthread_create(my_thread_fn,arg,"mythread");wake_up_process(k);// 唤醒让它跑// 或一步到位structtask_struct*k=kthread_run(my_thread_fn,arg,"mythread");

内核线程的mm为 NULL,跑时active_mm借前一个进程的 mm(省切页表)。

嵌入式视角:内核线程 = FreeRTOS 任务
你 FreeRTOS 任务 = 函数死循环。Linux内核线程最像 FreeRTOS 任务——一个函数在内核态循环跑。你写驱动要后台活(轮询/处理),用kthread_run起一个,和 FreeRTOSxTaskCreate同构。区别:内核线程在内核态(全权),FreeRTOS 任务在"内核态"(FreeRTOS 无分层)。用户态线程( pthread)是另一回事,在 U 态跑。

3.4 PID vs TGID vs TID

  • PID(Process ID):用户看到的进程号,实际是TGID(线程组 ID)
  • TID(Thread ID):线程 ID,内核task_struct->pid(每个 task_struct 唯一)
  • 单线程进程:PID == TID
  • 多线程进程:同组 task_struct 共享 TGID,各 TID 不同
ps-eLf# 看 PID(=TGID)/ TID# 多线程进程:N 行同 PID,不同 TID(LWP)

四、上下文切换(对照 RISC-V trap)

4.1 两种"切换"

切换是什么频率
用户态↔内核态syscall/中断,同进程内高(每次 syscall)
进程上下文切换从进程 A 切到进程 B中(调度时)

4.2 进程上下文切换做什么

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

staticvoidcontext_switch(structrq*rq,structtask_struct*prev,structtask_struct*next){structmm_struct*mm=next->mm;structmm_struct*oldmm=prev->active_mm;// 1. 切地址空间(若不同)if(unlikely(!mm)){// next 是内核线程next->active_mm=oldmm;// 借 prev 的 mm}elseif(prev->mm!=mm){// 不同进程switch_mm(oldmm,mm,next);// 切页表(写 satp + flush TLB)}// 2. 切寄存器(栈指针/ra/...)-> switch_to()switch_to(prev,next,prev);}

4.3 switch_to:切寄存器(架构相关)

switch_to最终调__switch_to(arch/riscv/kernel/entry.S 或类似):

// 简化的 RISC-V 版 __switch_to: # 1. 保存 prev 的 callee-saved 寄存器到 prev 栈 addi sp, sp, -FRAME sd ra, 0(sp) sd s0, 8(sp) sd s1, 16(sp) ... # s2-s11 sd sp, TASK_THREAD_SP(a0) # 存 prev->thread.sp # 2. 切栈:加载 next 的 sp ld sp, TASK_THREAD_SP(a1) # next->thread.sp # 3. 切 TP(percpu 指针) ld tp, TASK_THREAD_TP(a1) # 4. 切 CSR(thread.fcsr 浮点 CSR 等) ... # 5. 恢复 next 的 callee-saved ld s1, 16(sp) ld s0, 8(sp) ld ra, 0(sp) addi sp, sp, FRAME ret # 跳到 next 上次被切走的地方(ra)

4.4 对照你 RISC-V trap 切栈

你 mscratch 切栈(中断栈)Linux switch_to(进程切换)
触发中断/trap调度器决定
切什么sp(中断栈↔任务栈)sp + 页表 + percpu + CSR
保存啥全部通用寄存器(中断可能破坏)只存 callee-saved(s0-s11/ra)
返回mretret
频率每次中断每次调度

你的 mscratch 切栈是 Linux switch_to 的简化版
你 [[04-trap 机制详解]] 的 mscratch 切栈:trap 时 swap sp 和 mscratch。Linux switch_to 同构——切 prev/next 的 sp。增量是:① 还切页表(mm);② 只存 callee-saved(调度点已知 caller-saved 不重要);③ 还切 percpu(tp)。你已经懂"切栈",Linux 是放大版,加了切地址空间。

4.5 为什么只存 callee-saved

调度点是自愿的(调度器在明确位置调用 schedule),不是中断打断。所以调度点之后,编译器只保证 callee-saved 寄存器(s0-s11/ra/sp)跨函数调用保留,caller-saved(t0-t6/a0-a7)编译器已处理。只存 callee-saved 足够——比中断保存全部寄存器省。


五、CFS 调度器(Completely Fair Scheduler)⭐ 核心

5.1 CFS 的核心思想

不是按优先级抢,是按"谁跑得少"轮。

  • 每个进程有vruntime(虚拟运行时间):表示它"已经跑了多少"
  • 调度器选vruntime最小的跑(跑得最少的优先)
  • 跑一会儿,vruntime 增加,变成不是最小,换别人跑
  • 结果:每个进程公平分享 CPU

5.2 vruntime 怎么算

vruntime += 实际运行时间 × (NICE_0_LOAD / 进程权重)
  • 权重(weight):由 nice 值算出,nice 0 权重 1024,nice -1 权重 1171(高 10%),nice +1 权重 920(低 10%)
  • 高权重进程(nice 低):vruntime 增长慢 → 更久保持"跑得少" → 更多 CPU
  • 低权重进程(nice 高):vruntime 增长快 → 很快被换下 → 少 CPU

5.3 nice 值与权重

nice权重CPU 占比(nice 0 为基准)
-2088761极高
-109548~10x
010241x(基准)
+10110~0.1x
+1915极低
  • nice 范围 -20 ~ +19,默认 0
  • nice 不是优先级,是权重——影响 vruntime 增长率,进而影响 CPU 份额
  • 普通用户只能调高 nice(降权),root 能调低(升权)

5.4 CFS 数据结构:红黑树

  • 每个 CPU 一个运行队列struct rq
  • CFS 部分用红黑树(rbtree),按 vruntime 排序
  • 最左节点 = vruntime 最小 = 下一个跑
  • 进程入队:插入红黑树(O(log n))
  • 进程出队:取最左(O(log n))
CPU0 rq.cfs │ 红黑树(按 vruntime) │ [vruntime=100] ← 最左,下一个跑 / \ [200] [150] / \ / [180][210] [130]

5.5 调度时序

1. tick 中断 → scheduler_tick() 2. 当前进程 vruntime 更新 3. 检查:当前 vruntime 是否 > 红黑树最左 + sched_latency? - 否:继续跑 - 是:设 need_resched,稍后 schedule() 4. schedule(): - 当前进程入队(回红黑树) - 取最左 = next - context_switch(prev, next)

5.6 sched_latency 与 min_granularity

  • sched_latency(目标延迟,~6ms):在这个窗口内,所有进程都应跑一遍
  • min_granularity(最小粒度,~0.75ms):每个进程至少跑这么久,避免频繁切换
  • 时间片 = sched_latency / 进程数,但不小于 min_granularity
4 个进程,sched_latency=6ms: 每个时间片 = 6ms / 4 = 1.5ms(> min_granularity 0.75ms,OK) 每 6ms 每进程跑 1.5ms,公平

5.7 对照 FreeRTOS 优先级调度

FreeRTOS 优先级抢占Linux CFS
选谁跑最高优先级vruntime 最小
同优先级轮转(time slicing)自然公平(vruntime 趋同)
高优先级抢占低优先级(可能饿死低)权重高 CPU 多,但不饿死
饥饿低优先级可能饿死不会(都跑)
实时性高(优先级绝对)低(公平,延迟不定)
适合实时控制交互/吞吐

CFS 不会饿死进程,但实时性弱
FreeRTOS 高优先级任务永远先跑,低优先级可能饿死——对实时控制是优点(关键任务必须先跑)。CFS 不让任何进程饿死——对交互/吞吐是优点(都响应),但实时性弱。Linux 实时需求用 SCHED_FIFO/RR(见 §六),或 RT-Linux 补丁。这是你 MCU 转 Linux 要适应的:Linux 默认不硬实时。


六、EEVDF(新调度器,Linux 6.6+)

6.1 为什么 CFS 升级到 EEVDF

CFS 用了十几年,问题:

  • 公平性在某些场景不完美(特别是延迟敏感任务)
  • 启发式多,难调

EEVDF(Earliest Eligible Virtual Deadline First)是 6.6 引入的新算法,理论更扎实。

6.2 EEVDF 核心思想

  • 每个进程有lag(滞后量):应得 CPU - 实得 CPU
  • Eligible(合格):lag ≤ 0(没多占)的进程才能跑
  • 在合格进程里,选虚拟截止时间(vdeadline)最早的跑
  • 结果:比 CFS 更精确的公平 + 更好的延迟控制

6.3 对你影响

  • 接口不变(还是 nice/policy)
  • 行为更公平,延迟敏感任务响应更好
  • 你看 6.6+ 内核代码,kernel/sched/fair.c是 EEVDF 实现了

嵌入式视角:EEVDF 是 CFS 的演进,不是革命
CFS → EEVDF ——核心思路(公平/vruntime)保留,机制优化(加 lag/vdeadline)。


七、调度策略(Scheduling Policy)

7.1 三类调度策略

策略调度类适合行为
SCHED_NORMAL(SCHED_OTHER)CFS/EEVDF普通进程公平调度,nice 加权
SCHED_BATCHCFS/EEVDF批处理CPU 密集,少交互,降优先级
SCHED_IDLECFS/EEVDF极低优先级只在空闲跑(nice 比所有都低)
SCHED_FIFORT实时优先级抢占,无时间片,跑到让/阻塞
SCHED_RRRT实时优先级抢占 + 同优先级轮转
SCHED_DEADLINEDL硬实时EDF(最早截止期优先),指定周期/运行时间/截止期

7.2 RT 优先级

  • RT 优先级范围 1-99(99 最高)
  • RT 优先级 > 普通进程(任何 RT 都抢普通)
  • SCHED_FIFO/RR 的优先级 = rt_priority
  • RT 进程能饿死普通进程(全占 CPU)→ 内核有rt_throttling默认让 RT 最多占 95%,留 5% 给普通

7.3 chrt 改策略

chrt-f80./my_realtime_app# SCHED_FIFO 优先级 80chrt-r50./my_rr_app# SCHED_RR 优先级 50chrt-o0./my_normal_app# SCHED_OTHERchrt-p$(pidof myapp)# 查看策略

7.4 SCHED_DEADLINE(硬实时)

structsched_attrattr={.size=sizeof(attr),.sched_policy=SCHED_DEADLINE,.sched_runtime=10000000,// 每周期需 10ms.sched_period=100000000,// 周期 100ms.sched_deadline=20000000,// 截止期 20ms};sched_setattr(0,&attr,0);
  • 内核保证每 period 内给 runtime,在 deadline 前完成
  • 用 EDF 算法调度
  • 比 FIFO/RR 更精确的实时保证

嵌入式视角:Linux 实时性
FreeRTOS 是硬实时(优先级抢占,us 级响应)。Linux 默认(SCHED_NORMAL)不是实时,ms 级延迟。要用 Linux 做实时:

  • SCHED_FIFO/RR:内核抢占,软实时,但可能被中断/驱动阻塞
  • SCHED_DEADLINE:更精确,但仍非硬实时
  • PREEMPT_RT 补丁(主线化中):全内核抢占,接近硬实时
  • Xenomai:双核(Linux + 实时核),硬实时
    边缘 AI/控制若要硬实时,选 PREEMPT_RT 或 Xenomai。

八、抢占(Preemption)

8.1 抢占点

Linux 内核可抢占(CONFIG_PREEMPT),在安全点检查need_resched,调schedule():

抢占点何时
中断返回内核态IRQ 返回,检查 need_resched
系统调用返回syscall 返回用户态前
显式 schedule()主动睡眠/阻塞
preempt_enable() 后临界区退出
互斥锁释放mutex_unlock 后

8.2 抢占等级(CONFIG)

配置抢占性用途
PREEMPT_NONE不抢占(除非阻塞)服务器(吞吐)
PREEMPT_VOLUNTARY自愿抢占(显式点)桌面(平衡)
PREEMPT全内核抢占低延迟(嵌入式/桌面)
PREEMPT_RT实时(几乎全可抢占)硬实时

8.3 关抢占 vs 关中断

preempt_disable();// 关抢占(本 CPU 不切走)// 临界区(不能睡!)preempt_enable();// 开抢占(检查 need_resched)local_irq_save(flags);// 关中断(本 CPU 中断不来)// 临界区local_irq_restore(flags);spinlock_tlock;spin_lock(&lock);// 自旋锁隐含关抢占(SMP)+ 可能关中断spin_unlock(&lock);
  • 关抢占:本核不切走,但中断还来(中断里可能 schedule)
  • 关中断:中断不来,绝对安静
  • 自旋锁:隐含关抢占(07 篇详讲)

关抢占/持锁时不能睡眠
FreeRTOS 临界区里能调阻塞 API(会切走)。Linux 关抢占/持自旋锁时绝对不能睡眠——睡眠会 schedule(),但你关了抢占,schedule 切不出去,死锁/panic。这是 Linux 比 FreeRTOS 严格的点。能睡的临界区用 mutex(07 篇详讲)。


九、fork / exec / wait(进程生命周期)

9.1 fork()

pid_tpid=fork();if(pid==0){// 子进程}elseif(pid>0){// 父进程}
  • fork()创建子进程,复制父的 task_struct
  • 用COW(Copy-On-Write):页表复制,物理页共享,写时才复制(惰性,01 篇哲学)
  • 子继承父的 mm/files/signals 副本

9.2 exec()

execl("/bin/ls","ls","-l",NULL);
  • 替换当前进程的代码/数据/堆/栈(丢弃旧 mm,建新 mm)
  • PID 不变,但变成新程序
  • file_operations 保持(打开的 fd 默认保留,除 FD_CLOEXEC 标记的)

9.3 wait()

pid_tpid=wait(&status);// 等任一子进程
  • 父 wait 收尸子进程
  • 子 exit 后变 ZOMBIE,父 wait 才彻底释放
  • 父不 wait → 子变僵尸,占 PID;父死了 → init 收养收尸

9.4 孤儿进程与僵尸

  • 孤儿:父死了,子被 init(PID1)收养,init wait 收尸
  • 僵尸:子死了父没 wait,占 PID 和退出码,父 wait 或父死后 init 收尸

嵌入式视角:fork 比 FreeRTOS 创建任务重
FreeRTOSxTaskCreate立即分配栈和 TCB。Linuxfork用 COW,看似复制 mm,实际只复制页表(快),物理页写时才复制。但 fork 仍比 xTaskCreate 重——要复制 files/signals/页表。所以 Linux 服务器用进程池/线程,不频繁 fork。你写服务别无脑 fork。


十、进程关系与会话

10.1 进程组 / 会话

  • 进程组:相关进程集合(如 pipeline),组 ID = PGID
  • 会话:进程组集合,会话首控制终端,会话 ID = SID
  • 用途:信号广播(kill -PGID)、作业控制

10.2 守护进程(daemon)

// daemon 化典型步骤pid=fork();if(pid>0)exit(0);// 父退出setsid();// 子建新会话,脱离控制终端fork();// 再 fork,防再获控制终端if(pid>0)exit(0);chdir("/");// 改根umask(0);// 关标准 fd,重定向到 /dev/null
  • 守护进程:后台运行,无控制终端
  • BSP 启动后台服务常 daemon 化

十一、常用进程调试

11.1 工具

工具看什么
ps aux/ps -ef进程列表
top/htop实时进程状态/CPU
pstree进程树
cat /proc/<pid>/status进程详情(状态/内存/信号)
cat /proc/<pid>/sched调度信息(vruntime/policy)
cat /proc/<pid>/maps地址空间布局
cat /proc/<pid>/stack内核栈(WARNING)
strace -p <pid>跟踪系统调用
perf top热点函数
chrt/nice/renice改调度策略/优先级

11.2 关键 /proc 字段

# /proc/<pid>/status State: R (running) Pid: 1234 PPid: 1 Uid: 0 voluntary_ctxt_switches: 1500 # 主动睡眠切换 nonvoluntary_ctxt_switches: 30 # 被抢占切换
  • voluntary 多 = 频繁等 IO/睡眠(正常)
  • nonvoluntary 多 = 频繁被抢占(可能 CPU 紧张)

嵌入式视角:
Linux 的perf统计任意函数/CPU 周期。/proc/<pid>/sched看 vruntime 判断进程是否被公平调度。16 篇详讲 perf/ftrace。


十二、本篇小结

  • task_struct是宇宙中心,比 FreeRTOS TCB 胖十倍(mm/files/signals/namespace)
  • 进程状态:RUNNING/INTERRUPTIBLE/UNINTERRUPTIBLE(D,杀不掉)/ZOMBIE 等
  • Linux 不区分进程线程(都 task_struct,clone flag 控制共享);内核线程无 mm
  • 上下文切换context_switch:切页表(switch_mm)+ 切寄存器(switch_to,只存 callee-saved),对照你 mscratch 切栈是放大版
  • CFS:vruntime 最小优先跑,红黑树,权重由 nice 决定;不会饿死进程但实时性弱
  • EEVDF(6.6+):CFS 演进,加 lag/vdeadline,更公平
  • 调度策略:SCHED_NORMAL(CFS)/SCHED_FIFO/RR(RT)/SCHED_DEADLINE(硬实时)
  • 抢占:关抢占/持自旋锁时不能睡眠(FreeRTOS 临界区能睡,Linux 不能,严格)
  • fork 用 COW(惰性),exec 换程序,wait 收尸;孤儿归 init,僵尸等收尸
  • PREEMPT_RT 补丁让 Linux 接近硬实时

速查表

想干啥怎么做
看进程列表ps aux / top / htop
看进程详情cat /proc//status
看地址空间cat /proc//maps
看调度信息cat /proc//sched
改优先级nice -n 10 ./app / renice -n -5 -p PID
改调度策略chrt -f 80 ./app
创建内核线程kthread_run(fn, arg, “name”)
跟踪 syscallstrace -p PID
看进程树pstree -p
杀进程kill -9 PID(D 状态杀不掉)
看僵尸ps aux | grep Z
实时性SCHED_FIFO/RR/DEADLINE 或 PREEMPT_RT
关抢占preempt_disable(不能睡)
睡眠等待TASK_INTERRUPTIBLE(可被信号唤醒)

💡技术之路漫漫,分享是为了更好地交流。如果本文的内容对你有启发,希望能得到你的点赞 👍和收藏 ⭐。

如果你在调试过程中遇到了其他问题,欢迎在评论区 💬留言,我们一起探讨。也欢迎关注 👀我,一起交流底层开发的那些事儿。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 17:51:01

家庭聚餐一句不服气,老技工靠口述搭建作坊库存系统

让完全不懂电脑的家人试试AI&#xff0c;结果出乎意料 我舅退休前是车床工&#xff0c;跟机器打了一辈子交道&#xff0c;跟电脑的交情仅限于会在家庭群里发早上好的图片。上周末家庭聚餐&#xff0c;表弟起哄说现在AI能自己生成管理系统&#xff0c;我舅不服&#xff1a;「机器…

作者头像 李华
网站建设 2026/10/4 17:50:49

插件加载失败排查指南:从plugins机制到did not activate实战

搞开发这些年&#xff0c;我几乎每天都在跟“plugins”打交道。很多人在群里甩一张 “failed to load plugins web boot: 2 entries did not activate linxin666/dsh-p” 的截图&#xff0c;紧接着就是一句“这啥意思&#xff1f;”。说实话&#xff0c;插件系统在不同工具里长…

作者头像 李华
网站建设 2026/10/4 17:45:59

接口自动化框架代码生成工具:从YAML配置到Java测试代码

1. 为什么要给接口自动化框架配一个代码生成工具做接口自动化测试这些年&#xff0c;从最早用Postman手动点接口&#xff0c;到后来写Python脚本&#xff0c;再到现在搭建Java TestNG的自动化框架&#xff0c;我一直在跟“写测试代码”这件事打交道。后来逐渐发现一个现实&…

作者头像 李华
网站建设 2026/10/4 17:39:21

Cursor插件机制深度解析:CLI驱动、事件激活与SDK工程化

1. “plugins”不是功能菜单&#xff0c;而是Cursor生态的神经中枢你点开Cursor右下角那个小齿轮图标&#xff0c;翻遍Settings里所有选项&#xff0c;却始终找不到“Plugins”这个独立入口——这恰恰是绝大多数新用户踩进的第一个认知陷阱。它不像VS Code那样把插件市场做成显…

作者头像 李华