1. 概述
- 系统中每个线程都关联了一个调度策略和优先级,调度器根据调度策略和优先级进行线程调度,从而决定那个线程将在下一个调度中得到CPU时间。
- 调度器为每个优先级维护了一个待调度线程的列表,当需要进行调度时,调度器访问最高优先级的非空列表,然后从列表头选择一个线程调度运行。
- Linux的调度都是支持抢占的,如果有高优先级的线程准备好运行了,那么它将抢占当前运行的线程,这使得当前线程被重新加入到等待调度的链表中;调度策略决定了在同一个优先级列表中的可调度线程的顺序。
2. 调度策略
2.1 SCHED_FIFO:先入先出调度
- SCHED_FIFO线程的优先级必须大于0,当它运行时,一定会抢占正在运行的普通策略的线程(SCHED_OTHER, SCHED_IDLE, SCHED_BATCH)。SCHED_FIFO策略是没有时间片的算法,需要遵循以下规则:
- ① 如果一个SCHED_FIFO线程被高优先级线程抢占了,那么它将会被添加到该优先级等待列表的首部,以便当所有高优先级的线程阻塞的时候得到继续运行;
- ② 当一个阻塞的SCHED_FIFO线程变为可运行时,它将被加入到同优先级列表的尾部;
- ③ 如果通过系统调用改变线程的优先级,则根据不同情况有不同的处理方式:
- a. 如果优先级提高了,那么线程会被添加到所对应新优先级的尾部,因此,这个线程有可能会抢占当前运行的同优先级的线程;
- b. 如果优先级没变,那么线程在列表中的位置不变;
- c. 如果优先级降低了,那么它将被加入到新优先级列表的首部;
- ④ 根据POSIX.1-2008规定,除了使用pthread_setschedprio(3)以外,通过使用其他方式改变策略或者优先级会使得线程加入到对应优先级列表的尾部;
- ⑤ 如果线程调用了sched_yield(2),那么它将被加入到列表的尾部;
- ⑥ SCHED_FIFO会一直运行,直到它被IO请求阻塞,或者被更高优先级的线程抢占,亦或者调用了sched_yield();
2.2 SCHED_RR:轮转调度
- SCHED_RR是SCHED_FIFO的简单增强,除了对于线程占用的时间总量之外,SCHED_FIFO适用的规则对于SCHED_RR同样适用;
- 如果SCHED_RR线程的运行时间大于等于时间总量,那么它将被加入到对应优先级列表的尾部;如果SCHED_RR线程被抢占了,当它继续运行时它只运行剩余的时间量;时间总量可以通过sched_rr_get_interval()函数获取。
2.3 SCHED_OTHER:默认Linux时间共享调度
- SCHED_OTHER只能用于优先级为0的线程,SCHED_OTHER策略是所有不需要实时调度线程的统一标准策略;
- 调度器通过动态优先级来决定调用哪个SCHED_OTHER线程,动态优先级是基于nice值的,nice值随着等待运行但是未被调度执行的时间总量的增长而增加;这样的机制保证了所有SCHED_OTHER线程调度的公平性;
SCHED_FIFO, SCHED_RR的线程如果内部是一个非阻塞的死循环,那么它将一直占用CPU,使得其它线程没有机会运行。
可以使用RLIMIT_RTTIME来限制实时线程的CPU占用时间;Linux也提供了两个proc文件,用于控制为非实时线程运行预留CPU时间。