进程
Linux的进程调度
CFS 完全公平调度算法
权重和nice值
- 权重:权重越大,分配的时间比例越大,就相当于进程的优先级越高。进程的时间 = C P U 总时间 ∗ 进程的权重 / 就绪队列所有进程权重之和 进程的时间 = CPU总时间 * 进程的权重/就绪队列所有进程权重之和 进程的时间=CPU总时间∗进程的权重/就绪队列所有进程权重之和
- nice值:进程对外的编程接口中使用的是一个nice值 ,大小范围是
(-20~19)
,数值越小优先级越大,意味着权重值越大,nice值和权重之间可以转换的。一个进程每降低一个nice值,就能多获得10% 的CPU时间。1024权重对应nice值为0,被称为NICE_0_LOAD
。默认情况下,大多数进程的权重都是NICE_0_LOAD
虚拟时间
虚拟时间vruntime和实际时间(wtime)转换公式如下:
v r u n t i m e = w t i m e ∗ ( N I C E _ 0 _ L O A D / w e i g h t ) vruntime = wtime * (NICE\_0\_LOAD / weight) vruntime=wtime∗(NICE_0_LOAD/weight)
CFS调度主要保证每个进程运行的虚拟时间一致即可。在选择下一个即将运行的进程时,只需要找到虚拟时间最小的进程就行了
在运行队列中用红黑树结构组织进程的调度实体,这里进程虚拟时间正是红黑树的key,这样进程就以进程的虚拟时间被红黑树组织起来了。红黑树的最左子节点,就是虚拟时间最小的进程,随着时间的推移进程会从红黑树的左边跑到右,然后从右边跑到左边,
CFS调度进程
让虚拟时间最小的进程最先运行, 一旦进程运行虚拟时间就会增加,最后尽量保证所有进程的虚拟时间相等,谁小了就要多运行,谁大了就要暂停运行。
僵尸进程及守护进程?
守护进程
守护进程就是在后台运⾏,不与任何终端关联的进程,⼀个守护进程的⽗进程是init进程通常情况下守护进程在系统启动时就在运⾏,它们以root⽤⼾运⾏,并能处理⼀些系统级的任务
孤⼉进程
如果⽗进程先退出,⼦进程还没退出,那么⼦进程的⽗进程将变为init进程。(注:任何⼀个进程都必须有⽗进程)。孤⼉进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集⼯作。因此孤⼉进程并不会有什么危害
僵⼫进程
⼦进程⽐⽗进程先结束,而⽗进程⼜没有回收⼦进程,释放⼦进程占⽤的资源,此时⼦进程将成为⼀个僵⼫进程。
⼀个进程在调⽤exit命令结束⾃⼰的⽣命的时候,其实它并没有真正的被销毁, 而是留下⼀个称为僵⼫进程(Zombie)的数据结构
在Linux进程的状态中,僵⼫进程是⾮常特殊的⼀种,它已经放弃了⼏乎所有内存空间,没有任何可执⾏代码,也不能被调度,仅仅在进程列表中保留⼀个位置,记载该进程的退出状态等信息供其他进程收集。
系统所能使⽤的进程号是有限的,如果⼤量的产⽣僵⼫进程,将因为没有可⽤的进程号而导致系统不能产⽣新的进程。 此即为僵⼫进程的危害,应当避免。
⭐️如何避免僵⼫进程?
-
⽗进程通过wait 和waitpid等函数等待⼦进程结束,但是这会导致⽗进程挂起。
-
忽略SIGCHLD信号,这常⽤于并发服务器的性能的⼀个技巧
因为并发服务器常常fork很多⼦进程,⼦进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理⽅式设为忽略,可让内核把僵⼫⼦进程转交给init进程去处理,省去了⼤量僵⼫进程占⽤系统资源。
-
如果⽗进程很忙可以⽤signal 函数为SIGCHLD 注册handler 信号处理函数,在信号处理函数调⽤wait/waitpid等待⼦进程退出。
-
通过两次调⽤fork 。⽗进程⾸先调⽤fork 创建⼀个⼦进程然后waitpid 等待⼦进程退出,⼦进程再fork ⼀个孙进程后退出。这样⼦进程退出后会被⽗进程等待回收,而对于孙⼦进程其⽗进程已经退出所以孙进程成为⼀个孤⼉进程,孤⼉进程由init 进程接管,孙进程结束后,init会等待回收。
⼦进程结束后为什么要进⼊僵⼫状态**?**
设置僵⼫进程的⽬的是维护⼦进程的信息,⽗进程可能要取得⼦进程的退出状态等信息。这些信息⾄少包括进程ID ,进程的终⽌状态,以及该进程使⽤的CPU 时间,当⽗进程调⽤wait 或waitpid时就可以得到这些信息。
僵⼫状态是每个⼦进程必经的状态吗?
是的。 任何⼀个⼦进程(init除外)在exit()之后,并⾮⻢上就消失掉,而是留下⼀个称为僵⼫进程(Zombie)的数据结构,等待⽗进程处理。这是每个⼦进程在结束时都要经过的阶段。如果⼦进程在exit()之后,⽗进程没有来得及处理,这时⽤ps -aux命令就能看到⼦进程的状态是"Z"。如果⽗进程能及时 处理,可能⽤ps -aux命令就来不及看到⼦进程的僵⼫状态,但这并不等于⼦进程不经过僵⼫状态。
僵尸进程不能将它看成是一个正常的进程,这个进程已经死亡了,用户区资源已经被释放了,只是还占用着一些内核资源(PCB)。 僵尸进程就相当于是一副已经腐烂只剩下骨头的尸体。
僵尸进程的出现是由于这个已死亡的进程的父进程不作为造成的。
消灭僵尸进程的方法是,杀死这个僵尸进程的父进程,这样僵尸进程的资源就被系统回收了。通过kill -9 僵尸进程PID的方式是不能消灭僵尸进程的,这个命令只对活着的进程有效,僵尸进程已经死了,鞭尸是不能解决问题的。
上下文切换?
CPU上下文切换
根据任务的不同,CPU 的上下文切换就可以分为几个不同的场景,也就是进程上下文切换 、线程上下文切换 以及中断上下文切换。
系统调用
系统调用过程中,不会涉及到虚拟内存等进程用户态的资源,也不会切换进程。
- 进程上下文切换,是指从一个进程切换到另一个进程运行。
- 而系统调用过程中一直是同一个进程在运行。
所以,系统调用过程通常称为特权模式切换,而不是上下文切换。
进程上下文切换
进程是由内核来管理和调度的,进程的切换只能发生在内核态。所以,进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。
因此,进程的上下文切换就比系统调用 时多了一步:在保存当前进程的内核状态和CPU寄存器之前,需要先把该进程的虚拟内存、栈等保存下来;而加载了下一进程的内核态后,还需要刷新进程的虚拟内存和用户栈。(与系统调用的区别?)
线程上下文切换
- 前后两个线程属于不同进程。此时,因为资源不共享,所以切换过程就跟进程上下文切换是一样。
- 前后两个线程属于同一个进程。此时,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据。
中断上下文切换
为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件。而在打断其他进程时,就需要将进程当前的状态保存下来,这样在中断结束后,进程仍然可以从原来的状态恢复运行。
-
中断上下文切换并不涉及到进程的用户态。
-
所以,即便中断过程打断了一个正处在用户态的进程,也不需要保存和恢复这个进程的虚拟内存、全局变量等用户态资源。
-
中断上下文,其实只包括内核态中断服务程序执行所必需的状态,包括CPU 寄存器、内核堆栈、硬件中断参数等。
-
对同一个 CPU 来说,中断处理比进程拥有更高的优先级
-
进程上下文切换一样,中断上下文切换也需要消耗CPU
参考:
极客时间