嵌入式Linux核心课程课文章目录嵌入式Linux核心课程课一、课程目标二、进程调度核心概述2.1 调度核心目的2.2 调度器核心概念三、Linux核心调度策略详解样例代码3.1 调度策略一CFS调度Completely Fair Scheduler完全公平调度核心原理适用场景3.2 调度策略二RT调度Real-Time Scheduler实时调度核心原理适用场景3.3 样例代码1查看进程调度策略与优先级用户态3.4 样例代码2内核态查看调度类验证调度机制四、进程调度触发时机五、进程销毁机制详解样例代码5.1 进程销毁的两种方式方式1主动退出正常销毁方式2被动终止异常销毁5.2 进程销毁完整流程5.3 关键概念僵尸进程与孤儿进程5.4 样例代码3进程主动退出与父进程回收用户态5.5 样例代码4内核态释放进程资源简化版六、课堂练习七、课后作业八、本章总结九、核心关键词第10课 课程回顾总结上一节课答案第9课 进程管理子系统(一)进程概念与创建 实战作业代码代码功能说明注意事项一、课程目标理解进程调度的核心意义与设计原则掌握调度器的作用掌握Linux内核两种核心调度策略CFS、RT的原理与适用场景理解进程优先级、时间片的分配逻辑以及调度触发时机掌握进程销毁的完整流程理解僵尸进程、孤儿进程的产生与处理能通过代码分析进程调度机制排查调度相关异常衔接进程创建知识形成完整的进程生命周期管理认知二、进程调度核心概述进程调度是内核的核心功能之一本质是合理分配CPU资源决定多个就绪进程中哪个能获得CPU执行权确保系统高效、公平、实时地运行。2.1 调度核心目的提高CPU利用率避免CPU空闲让CPU始终处于有效工作状态保证系统公平性为每个进程分配合理的CPU时间避免饥饿满足实时性需求嵌入式场景中确保高优先级实时进程快速响应优化系统响应速度缩短进程等待时间提升用户体验2.2 调度器核心概念Linux内核通过**调度器类sched_class**管理不同类型的调度策略每个进程都属于某一个调度类调度器根据调度类的优先级决定调度顺序。核心调度器Linux 2.6.23后引入CFS调度器替代传统O(1)调度器成为默认调度器。三、Linux核心调度策略详解样例代码Linux内核支持多种调度策略核心分为两大类普通进程调度CFS和实时进程调度RT分别对应不同的应用场景。3.1 调度策略一CFS调度Completely Fair Scheduler完全公平调度核心原理CFS是Linux默认调度策略适用于普通用户进程非实时核心思想是“公平分配CPU时间”——为每个进程分配一个“虚拟运行时间”调度器始终选择虚拟运行时间最少的进程执行。关键机制虚拟运行时间vruntime根据进程优先级动态调整优先级越高vruntime增长越慢获得CPU的机会越多。时间片CFS不固定时间片大小而是根据就绪进程数量动态调整避免短进程等待过长时间。调度触发进程时间片耗尽、进程主动放弃CPU、高优先级进程进入就绪态时触发。适用场景普通应用程序如Shell、浏览器、后台服务对实时性要求不高追求公平性和CPU利用率。3.2 调度策略二RT调度Real-Time Scheduler实时调度核心原理RT调度适用于实时进程采用“优先级抢占式调度”——实时进程优先级高于所有普通进程高优先级实时进程可抢占低优先级进程的CPU执行权。RT调度的两种子策略SCHED_FIFO先进先出同优先级实时进程按就绪顺序执行一旦执行直到主动放弃CPU或被更高优先级进程抢占。SCHED_RR时间片轮转同优先级实时进程按时间片轮转执行时间片耗尽后切换到下一个同优先级进程。适用场景嵌入式实时场景如工业控制、车载系统、医疗设备对响应时间有严格要求如毫秒级响应。3.3 样例代码1查看进程调度策略与优先级用户态查看当前进程的调度策略、优先级验证CFS调度#include stdio.h #include unistd.h #include sys/types.h #include sched.h int main() { pid_t pid getpid(); int policy; struct sched_param param; // 获取当前进程的调度策略 policy sched_getscheduler(pid); if (policy -1) { perror(sched_getscheduler failed); return -1; } // 获取当前进程的调度参数优先级 if (sched_getparam(pid, param) -1) { perror(sched_getparam failed); return -1; } // 打印调度策略 printf(当前进程PID%d\n, pid); switch (policy) { case SCHED_OTHER: printf(调度策略CFSSCHED_OTHER\n); break; case SCHED_FIFO: printf(调度策略RT-FIFOSCHED_FIFO\n); break; case SCHED_RR: printf(调度策略RT-RRSCHED_RR\n); break; default: printf(调度策略未知\n); } // 打印优先级CFS优先级范围0-139RT优先级范围1-99 printf(进程优先级%d\n, param.sched_priority); return 0; }运行结果当前进程PID1234调度策略CFSSCHED_OTHER进程优先级0说明普通用户进程默认使用CFS调度策略优先级为0CFS优先级0对应nice值0nice值范围-20~19值越小优先级越高。3.4 样例代码2内核态查看调度类验证调度机制内核模块查看当前进程的调度类区分CFS与RT调度 #include linux/module.h #include linux/sched.h #include linux/init.h static int __init sched_class_demo_init(void) { // 判断当前进程的调度类 if (current-sched_class fair_sched_class) { printk(当前进程使用 CFS 公平调度\n); } else if (current-sched_class rt_sched_class) { printk(当前进程使用 RT 实时调度\n); } else { printk(当前进程使用其他调度类\n); } // 打印进程优先级prio为动态优先级static_prio为静态优先级 printk(进程静态优先级%d动态优先级%d\n, current-static_prio, current-prio); return 0; } static void __exit sched_class_demo_exit(void) { printk(调度类演示模块卸载\n); } module_init(sched_class_demo_init); module_exit(sched_class_demo_exit); MODULE_LICENSE(GPL);运行结果当前进程使用 CFS 公平调度进程静态优先级120动态优先级120说明CFS进程的静态优先级默认120对应nice值0动态优先级与静态优先级一致RT进程的静态优先级范围为1~99高于CFS进程。四、进程调度触发时机进程调度不会随机触发内核在特定时机触发调度确保系统稳定高效核心触发时机分为以下4种进程时间片耗尽CFS调度中进程的虚拟运行时间达到阈值调度器触发切换。进程主动放弃CPU进程调用sleep()、wait()等函数进入睡眠状态主动释放CPU。高优先级进程就绪高优先级进程尤其是RT进程从睡眠或创建状态进入就绪态抢占当前运行进程的CPU。内核态返回用户态时内核处理完系统调用、中断后返回用户态前检查是否需要调度。五、进程销毁机制详解样例代码进程销毁是进程生命周期的最后一步核心是释放进程占用的所有资源内存、文件描述符、PCB等分为“主动退出”和“被动终止”两种方式。5.1 进程销毁的两种方式方式1主动退出正常销毁进程通过调用exit()、_exit()系统调用主动退出释放资源常见场景用户程序执行完毕main函数return底层调用exit()进程主动调用exit()终止自身运行。方式2被动终止异常销毁进程被其他进程或内核终止常见场景其他进程调用kill()系统调用发送终止信号如SIGKILL进程执行非法操作如除以0、访问非法内存内核触发异常终止进程父进程终止子进程被init进程PID1收养若子进程退出后父进程未回收成为僵尸进程。5.2 进程销毁完整流程触发退出进程调用exit()或被信号终止进入退出流程关闭资源关闭进程打开的所有文件描述符、网络连接、信号处理等释放内存释放进程地址空间、页表、堆栈等内存资源更新PCB将进程状态设置为EXIT_ZOMBIE僵尸态保存退出状态码通知父进程向父进程发送SIGCHLD信号告知父进程自身已退出资源回收父进程调用wait()/waitpid()读取子进程退出状态释放子进程PCB进程彻底销毁若父进程未回收子进程保持僵尸态直到父进程退出后被init进程回收。5.3 关键概念僵尸进程与孤儿进程僵尸进程子进程退出父进程未调用wait()/waitpid()回收子进程PCB未释放状态为EXIT_ZOMBIE。危害占用PID资源长期积累会导致系统PID耗尽。孤儿进程父进程先于子进程退出子进程被init进程PID1收养子进程退出后由init进程回收不会成为僵尸进程。5.4 样例代码3进程主动退出与父进程回收用户态创建子进程子进程主动退出父进程调用waitpid()回收避免僵尸进程 #include stdio.h #include unistd.h #include sys/types.h #include sys/wait.h #include stdlib.h int main() { pid_t pid, wpid; int status; // 创建子进程 pid fork(); if (pid 0) { perror(fork failed); return -1; } if (pid 0) { // 子进程执行任务后主动退出 printf(子进程PID%d执行任务完毕主动退出\n, getpid()); exit(0); // 主动退出退出状态码0 } else { // 父进程等待子进程退出并回收 printf(父进程PID%d等待子进程退出...\n, getpid()); // waitpid() 等待指定子进程退出获取退出状态 wpid waitpid(pid, status, 0); if (wpid -1) { perror(waitpid failed); return -1; } // 判断子进程退出状态 if (WIFEXITED(status)) { printf(父进程子进程PID%d正常退出退出状态码%d\n, wpid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf(父进程子进程PID%d被信号终止终止信号%d\n, wpid, WTERMSIG(status)); } } return 0; }运行结果父进程PID1234等待子进程退出…子进程PID1235执行任务完毕主动退出父进程子进程PID1235正常退出退出状态码0说明父进程通过waitpid()回收子进程避免子进程成为僵尸进程WIFEXITED(status)判断子进程是否正常退出WEXITSTATUS(status)获取退出状态码。5.5 样例代码4内核态释放进程资源简化版内核模块模拟进程退出时释放资源的核心逻辑 #include linux/module.h #include linux/sched.h #include linux/init.h #include linux/slab.h static int __init proc_exit_demo_init(void) { struct task_struct *task current; printk(当前进程PID%d模拟退出前释放资源\n, task-pid); // 模拟释放进程资源实际内核由do_exit()函数完成 if (task-mm) { printk(释放进程地址空间mm_struct\n); // 内核中实际调用mmput()释放mm_struct } printk(模拟进程状态设置为僵尸态EXIT_ZOMBIE\n); printk(通知父进程等待回收\n); return 0; } static void __exit proc_exit_demo_exit(void) { printk(进程退出演示模块卸载\n); } module_init(proc_exit_demo_init); module_exit(proc_exit_demo_exit); MODULE_LICENSE(GPL);运行结果当前进程PID1236模拟退出前释放资源释放进程地址空间mm_struct模拟进程状态设置为僵尸态EXIT_ZOMBIE通知父进程等待回收说明内核中进程退出的核心函数是do_exit()负责释放进程资源、设置僵尸态、通知父进程本代码模拟了该过程的核心逻辑。六、课堂练习简述CFS调度与RT调度的核心区别以及各自的适用场景。Linux进程调度的触发时机有哪些什么是僵尸进程如何避免僵尸进程产生CFS调度中虚拟运行时间vruntime的作用是什么进程主动退出与被动终止的区别是什么各自的触发场景有哪些七、课后作业画图描述进程调度的完整流程标注CFS与RT调度的优先级关系。编写程序创建两个RT进程SCHED_RR策略设置不同优先级验证高优先级进程抢占低优先级进程。编写程序创建子进程后父进程不调用wait()观察子进程是否成为僵尸进程并用ps命令查看。简述进程销毁的完整流程说明init进程在进程回收中的作用。查阅资料说明Linux内核中do_fork()创建进程与do_exit()销毁进程的核心关联。八、本章总结本章承接上一课进程创建的内容详细讲解了进程管理子系统的核心后续流程——进程调度与销毁。重点介绍了CFS完全公平调度和RT实时调度两种核心策略明确了两者的原理、适用场景及优先级差异解析了进程调度的触发时机说明内核如何合理分配CPU资源同时讲解了进程销毁的两种方式、完整流程以及僵尸进程、孤儿进程的产生与处理方法。进程调度是保障系统高效、实时运行的关键进程销毁是释放系统资源、避免资源泄漏的核心环节。通过本课学习形成了完整的进程生命周期创建→调度→运行→销毁认知掌握了进程调度与销毁的底层逻辑为后续学习进程通信、内核调试、嵌入式实时系统优化打下坚实基础。九、核心关键词进程调度、CFS调度、RT调度、调度器、虚拟运行时间、时间片、进程销毁、僵尸进程、孤儿进程、exit()、waitpid()第10课 课程回顾总结本课作为进程管理子系统的第二部分重点讲解了进程调度与销毁的核心机制衔接上一课进程创建的内容形成了完整的进程生命周期管理体系。课程首先明确了进程调度的核心目的的是合理分配CPU资源保障系统公平性、高效性和实时性介绍了调度器类的核心概念。随后详细解析了CFS完全公平调度和RT实时调度两种核心策略对比了两者的原理、关键机制和适用场景通过用户态与内核态样例代码直观展示了调度策略的应用与验证方法。课程还讲解了进程调度的四大触发时机明确了调度器何时触发进程切换。在进程销毁部分详细说明了主动退出与被动终止两种方式拆解了进程销毁的完整流程重点解释了僵尸进程与孤儿进程的产生原因、危害及处理方法通过代码演示了父进程回收子进程的核心操作。通过本课学习掌握了进程调度与销毁的底层逻辑理解了CFS与RT调度的区别能通过代码分析调度机制、避免僵尸进程。本课知识是嵌入式Linux内核开发的重要基础对理解系统资源管理、优化实时性、排查进程异常具有重要意义为后续进程通信、驱动开发等内容奠定了基础。上一节课答案第9课 进程管理子系统(一)进程概念与创建 实战作业代码进程创建综合实战fork写时复制验证进程信息打印 #include stdio.h #include unistd.h #include sys/types.h #include string.h int main() { pid_t pid; char buf[32] 父进程初始数据; printf(【父进程】PID%d准备创建子进程buf%s\n, getpid(), buf); // 调用fork创建子进程验证写时复制 pid fork(); if (pid 0) { perror(fork创建子进程失败); return -1; } if (pid 0) { // 子进程修改buf验证写时复制修改后才复制页 strcpy(buf, 子进程修改后数据); printf(【子进程】PID%dPPID%dbuf%s\n, getpid(), getppid(), buf); // 子进程执行完毕主动退出 sleep(2); printf(【子进程】执行完毕退出\n); return 0; } else { // 父进程不修改buf查看数据是否被影响验证COW sleep(1); // 等待子进程修改数据 printf(【父进程】PID%d子进程PID%dbuf%s\n, getpid(), pid, buf); // 等待子进程退出避免僵尸进程 wait(NULL); printf(【父进程】子进程已退出父进程执行完毕\n); } return 0; }代码功能说明该程序是第9课进程概念与创建的实战作业核心验证fork创建子进程及写时复制COW机制。程序中父进程创建子进程后子进程修改共享数据缓冲区父进程不修改通过打印缓冲区内容验证写时复制的核心逻辑——只有当子进程修改数据时内核才会复制对应内存页父进程数据不受影响。同时程序打印父子进程PID、PPID展示进程关系父进程通过wait()回收子进程避免僵尸进程。代码贴合课程重点巩固进程创建、写时复制、进程关系等核心知识点为后续进程调度、销毁学习打基础。注意事项编译命令gcc fork_cow_demo.c -o fork_cow_demo运行时需确保父进程等待子进程执行sleep()函数否则可能出现打印顺序混乱。写时复制验证关键子进程修改数据后父进程数据应保持初始值若两者数据一致说明COW机制未生效需检查内核配置。父进程必须调用wait()/waitpid()回收子进程否则子进程会成为僵尸进程可通过ps -ef | grep defunct查看。fork创建子进程后父子进程执行顺序由调度器决定sleep()函数仅用于控制打印顺序不影响进程执行逻辑。若fork失败大概率是系统PID资源耗尽或权限不足可通过ps -ef查看进程数量结束无用进程后重试。可使用strace ./fork_cow_demo 查看fork系统调用的执行过程观察COW机制的底层调用。