在Linux里面,无论是进程,还是线程,到了内核里面,我们统一都叫任务(Task),由一个统一的结构task_struct进行管理。
在内核中,它们虽然都是任务,但是应该加以区分。其中,pid是process id,tgid是thread group ID。
如果一个进程创建了其他线程。线程有自己的pid,tgid就是进程的主线程的pid,group_leader指向的就是进程的主线程。
任务状态:
调度
在线程、进程的数据结构task_struct中有一个成员变量
unsigned int policy;
unsigned int rt_priority;
#define SCHED_NORMAL 0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3
#define SCHED_IDLE 5
#define SCHED_DEADLINE 6
对于实时进程,优先级的范围是0~99;对于普通进程,优先级的范围是100~139。数值越小,优先级越高。
SCHED_FIFO、SCHED_RR、SCHED_DEADLINE是实时调度策略
FIFO先进先出,SCHED_RR轮流调度,相同优先级任务使用完时间片会被放入队列尾部。
SCHED_DEADLINE,按照任务deadLine进行调度。
SCHED_NORMAL、SCHED_BATCH、SCHED_IDLE普通调度
CFS完全公平调度算法
首先,你需要记录下进程的运行时间。CPU会提供一个时钟,过一段时间就触发一个时钟中断。就像咱们的表滴答一下,这个我们叫Tick。CFS会为每一个进程安排一个虚拟运行时间vruntime。如果一个进程在运行,随着时间的增长,也就是一个个tick的到来,进程的vruntime将不断增大。没有得到执行的进程vruntime不变。
对vruntime进行排序的数据结构是红黑树。
主动调度:
计算机主要处理计算、网络、存储三个方面。计算主要是CPU和内存的合作;网络和存储则多是和外部设备的合作;在操作外部设备的时候,往往需要让出CPU,调用schedule()函数。
进程上下文切换,一是切换进程空间,也即虚拟内存;二是切换寄存器和CPU上下文。
抢占调度:一个进程执行时间太长了,是时候切换到另一个进程了。
在计算机里面有一个时钟,会过一段时间触发一次时钟中断,通知操作系统,时间又过去一个时钟周期,这是个很好的方式,可以查看是否是需要抢占的时间点。时钟中断处理函数会调用scheduler_tick()
用户态的抢占时机:
对于用户态的进程来讲,从系统调用中返回的那个时刻,是一个被抢占的时机。
对于用户态的进程来讲,从中断中返回的那个时刻,也是一个被抢占的时机。
内核态的抢占时机:
对内核态的执行中,被抢占的时机一般发生在preempt_enable()中。
在内核态的执行中,有的操作是不能被中断的,所以在进行这些操作之前,总是先调用preempt_disable()关闭抢占,当再次打开的时候,就是一次内核态代码被抢占的机会。