linux进程管理概览
进程与线程
pcb
进程通过进程描述符(pcb)描述。在linux中pcb的结构体是 task_stack
(在include/linux/sched.h
) 包含了进程的状态信息、地址空间等进程的全部信息。通过pcb就可以恢复进程的状态。所以这里可以稍微说一下,进程切换其实就是将current_thread_info
切换到对应的pcb,然后按照pcb中的信息恢复从而切换进程。
pcb一般被存储在内核空间的堆中,每个进程的task_stack
由内核负责维护和管理。
进程创建与状态
进程通过 fork
(内部调用clone
) 从父进程创建出子进程。进程中有明显的继承关系。
fork
的过程:
- 子进程完全复制父进程的pcb
- 子进程设置成 uninterruptible状态
- 修改部分子进程的pcb的参数:某些flags、PID
- 根据
clone
传入的参数拷贝/共享 文件、文件系统、信号、地址空间等 - 返回子进程指针
可以看到,子进程是"拷贝"了父进程的pcb,但其实此时只是将该pcb"只读"共享了。只有当涉及到需要写入的时候才真正的将数据拷贝,让各个进程有自己的拷贝。这是利用了 写时拷贝 的优化方式。
终结过程 exit
- 设置状态
- 删除定时器、输出记账信息
- 释放占用的mm
- 减少使用的文件的引用计数,如果为0直接释放
- 通知父进程
- 变成僵尸态,
do_exit
切换到别的进程再也不返回
子进程终结的时候会通知父进程,父进程通过 wait
来回收子进程的资源。
- 如果父进程没有回收->子进程为僵尸进程,无法被回收
- 如果父进程先一步终结,子进程会被
init
进程收养
线程
线程和进程在linux中其实没有严格区分,甚至可以看到线程的创建方式也是调用 clone
线程对于进程来说就是:和父进程共用虚拟内存,文件系统信息,打开的文件,信号的子进程
内核线程是由 kthread
进程中创建出来
进程调度
进程调度的方式:实时调度和非实时调度
进程切换的过程:
使用 schedule
- 将虚拟内存映射切换(MMU的页表地址切换)
- 从pcb中恢复新进程的状态
进程切换耗时(linux已经是轻量级了)的原因:pcb的切换消耗,需要更新MMU,可能会造成TLB flash等耗时操作。
CFS原因
一般进程有 IO密集型(例如:按键等需要读取IO)和处理器密集型(例如:视频编解码完全由CPU处理)两种。 但是这两种的需求和特点又十分不同:
- IO密集型:处理时间短,但是需要快速响应,不然会感觉卡顿
- 处理器密集型:处理时间长,不需要快速响应
linux就做了优化,依据nice(越小权重越高,可以获得更多的时间片)来为每个进程分配时间片,当时间片运行完自动切换到下一进程。这样就可以优先切换IO密集型的进程,这样可以做到快速响应,但是如何去判断什么时候优先切换,又如何判断是否是IO密集型就是问题了,并且nice分配时间片也会有很多问题(1.绝对值会造成0 1 与 18 19分配的时间差很多 2.不同的nice映射时间片不同并且时间片的时间还会随着定时器节拍改变)
所以linux对于非实时的普通进程使用CFS(完全公平调度)。
普通进程(SCHED_NORMAL)
CFS的含义:运行每一个进程都运行一段时间,循环轮转,然后选择调度运行时间最少的程序运行
对于普通进程,使用CFS来做调度策略。CFS不适用nice来分配时间片,而是使用vruntime来记录进程实际运行的时间,vruntime越小那么优先级越高,越优先调度。
CFS用红黑树来管理所有要执行的进程,这样每次只需要调度最左下角的进程(为了加速会缓存)。当进程暂停会从红黑树移除,放置到等待队列,当进程就绪后又会从等待队列删除添加回红黑树的运行队列中。
在等待队列中,会用while循环一直等待就绪的条件达成,当被唤醒后会再次的确认条件是否为真(查看是不是伪唤醒),如果确认条件满足了,才会退出循环。
实时进程(SCHED_FIFO | SCHED_RR)
这两个都和优先级强相关,不允许低优先级的抢占。如果只有低优先级,那么这两种都会一直执行不会切换。
SCHED_FIFO
就像名字所描述的,只要不自己主动放弃,或者自己收到阻塞,或者被优先级更高的打断,就可以一直执行下去
SCHED_RR
= SCHED_FIFO
+时间片。大部分和FIFO一样,但是对于同优先级的进程,还是会有时间片来管理切换进程。
实时进程类型 | 低优先级 | 同优先级 | 高优先级 |
---|---|---|---|
SCHED_FIFO | 不允许打断 | 不允许打断 | 打断 |
SCHED_RR | 不允许打断 | 时间片切换 | 打断 |
抢占
linux是支持用户抢占和内核抢占的。但是抢占必须在安全的时候,在调度的时候会有标志位来记录。
当从一个进程被抢占后,标志位 need_resched
会置1,这样返回后,会认为要返回到原来的进程执行。
用户抢占
发生时机:
- 从内核返回到用户进程
- 从中断返回到用户进程
当返回的时候如果 need_resched
为1,就会触发调度。并且从内核返回到用户认为是安全的,所以到用户的时候就可以直接再被调度抢占
内核抢占
对于内核线程来说,需要一个标志位preempt_count
来标志用来计数(锁),如果计数不是0,那么切换的时候就是不安全的就不运行抢占,当计数是0的时候才运行抢占。
所以当 need_resched
为1,并且 preempt_count
为0,那么就认为有一个更需要调度的进程,就会触发schedule
调度
调度时机:
- 中断返回内核前
- 内核线程被阻塞
- 再一次可以被抢占
- 手动触发
schedule
(需要自己保证安全)
__EOF__

本文链接:https://www.cnblogs.com/alanli07/p/18173951.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程