细节基础(不是大框架,是一些细的东西)
进程创建:通常由一个父进程fork创建,包含页表复制和pid申请。
线程创建:和进程差不多,但可以灵活选择共享进程的哪些资源。
线程切换只需要保存寄存器,开销小,进程切换需要页表的切换,涉及到io,开销大
进程在Linux中用task_struct 描述,包含了内存管理、调度、信号、id、状态等等
对比其他OS,Linux没有就绪态,它将就绪态和运行态合并成一个‘具备运行条件的状态’
调度策略:(这里不讲优先级调度、FCFS、最短优先、RR等基础的,只介绍Linux 标准内核实现的两个调度类)
实时调度:使用静态优先级的优先级调度
默认调度:不管优先级如何都能被调度,但优先级低的获得的时间片短,比例用nice值表
旧的调度器:
On调度器:遍历队列,找到需要的
O1调度器:同样优先级的放在同一个队列里,每次从最高优先级的队列里拿一个。对于时间片,优先级高一级,则时间片+5ms。这种调度方式也适用于实时调度。
目前linux采用的调度器
CFS(完全公平)调度器:和O1原理一样,但计算时间片的时候采用动态的调整
1. nice -1则weight*110%(多10%), nice +1则weight*90%(少10%)
2. CFS给cfs_rq中的每一个进程安排一个虚拟时钟vruntime。如果一个进程得以执行,随着时间的增长,其vruntime将不断增大。没有得到执行的进程vruntime不变。
3. vruntime = 实际时间*weight的比值;nice=0时,vruntime == 实际时间。
4. 每个cpu对应一个由cfs_rq构成的红黑树,并且以vruntime为key,调度器总是选择vruntime最小的那个进程来执行。
关于优先级
优先级反转现象:A>B>C, C持有资源R并占用cpu。此时A想抢占,但由于A也要R,因此A被阻塞;此时B抢占,B不要R,B先运行完,然后C、最后A。B优先级比A低但先运行完毕了,这种称为优先级反转
优先级继承:为了解决上述问题,A被阻塞后,C由于持有共享资源R,可以继承A的优先级,最后运行的顺序为C--A--B
静态优先级/动态优先级:略。
柔性优先级:不像动态/静态那样,优先级低的就拿不到cpu,而是按照优先级的高低,按比例分时共享cpu
调度的时机
cpu空闲的时候(废话)
时间片用完的时候(废话+2)
当某个进程io的时候(block),可以直接调度
通常是在系统调用返回用户态的时候,或者在中断返回内核态的时候允许调度(or抢占)
等待队列机制
等待和唤醒是linux基础机制,等待中的队列由双向链表连接。
一个队列等待多个事件:
假如一个页正在换进换出,则任务需要等待io完成,那么如果每一个页都设置一个等待队列开销会很大,因此Linux设定为一个队列等待一组页。当某一页完成io时,通过hash查找等待该页的任务
一个任务等待多个队列:poll/select/epoll 多路复用
阻塞io:数据没准备好就等着,有数据了再返回
非阻塞io:请求完直接返回,如果数据没好就轮询
io多路复用:专门拿一个线程负责监控,减少内核态与用户态切换
select-数组:O(n), 有io时醒来,轮询所有连接,查找哪个有io发生。轮询时将每个fd拷贝到内核
poll-链表:和select差不多,但没有最大连接数量的限制
epoll-红黑树:O(1), 不用轮询,用回调,fd带回调函数