嘿,我们来聊聊Linux里的进程和线程,这其实就是多任务系统里程序跑起来的样子。Linux把所有的线程都归在进程族里,统一用task_struct来描述,你看fork和do_exit这两个步骤,就能发现内核眼里它们俩本来就密不可分。 系统刚上电时,reset_init会先启动kernel_thread这个入口,其实它就是个黑箱,藏在kernel/fork.c里,直接调用_do_fork。它一口气生了两个内核火焰,一个叫kernel_init,另一个叫kthreadd。这时候kthreadd还没影儿呢,它是靠着链表kthread_create_list被挂起来的。你顺着这条线找,会看到kthread_create和kthread_run这两个宏,一个负责注册信息,一个负责马上把它唤醒执行。这一套流程其实就是内核线程从孵化到唤醒的闭环。 咱们再看看内核对外的三个接口。kernel_thread是核心层私用的接口,藏得深;kthread_create是公开的宏,但它得让kthread进程帮着干活;kthread_run也是宏,一创建出来就能直接跑。总结一下就是:kernel_thread是个起点,kthread家族负责扩散开去,最后它们都落在了_do_fork身上。 在用户空间写程序时,大家只能通过系统调用这道门进去。最经典的fork是直接复制出来的进程,父子从同个地址开始跑;vfork有点特殊,父进程得等子进程干完活儿或者退出才继续执行;clone就比较灵活了,能自定义一大堆参数,像文件描述符、寄存器状态什么的。 不管是在内核层还是用户层,创建进程这事儿最终都得靠_do_fork这个总开关。它内部会调用copy_process一次性克隆出一大堆家当,比如进程描述符、内存表、信号量、计时器等等。 那身份证是谁发的呢?就是struct task_struct这张单子。里面塞满了调度、资源、状态这些信息,结构体特别长有上千行代码呢。以后每一次调度、处理信号、回收资源都得回头看看这张身份证。 到了进程要退出的时候咋办?用户态调exit系统调用就行了。内核态直接就跳到do_exit()里去了。这事儿跟创建的时候正好反着来:先把内存页、文件句柄这些资源释放掉;再把跟父进程的链接给剪断;通知调度器把这个实体删掉;最后才把task_struct本身给释放掉。干完这一套活儿,内核资源图上就少了一颗节点了。 咱们就不啰嗦源码路径和具体细节了,光看这些函数名就知道大致是怎么个画面感了吧。