一、优先级分别以及任务抢占
Neutrino 提供了一个基于优先级驱动的抢占式的设计理念。优先级驱动意味着,我们可以为每个线程分配一个优先级,它将可以根据优先级调度策略获取CPU资源。如果一个低优先级线程和一个高优先级线程同时像获取CPU使用权,那么高优先级线程将会运行。抢占式意思是说,如果一个低优先级线程在运行,这个时候一个高优先级运行条件得到满足,将要运行,那么它将获取CPU使用权。
线程的优先级从1-255(最高)。普通线程的优先级范围从1-63(默认)。root用户线程优先级允许设置在63之( procmgr_ability())接口。系统有一个空闲线程(位于进程管理器)有最低的优先级(0),这个空闲任务总是处于就绪状态。
默认情况下子线程从父线程继承优先级。一个线程有两个优先级,一个称为真实优先级,一个成为有效优先级。系统通过有效优先级完成调度。一个线程自己本身可以修改两个优先级,但是有效优先级可能会因为系统调度策略或者优先级继承发生改变。正常情况下,有效优先级与真实优先级相等。
中断处理器的优先级比任何线程优先级都要搞,但是它不像线程一样被调度。如果一个中断发生了,那么:
1.当前正在运行的线程失去CPU占用权,开始中断异常处理(SMP issues)
2.硬件运行内核
3.内核调用中断处理程序
1.1任务状态分析
如果想要完全了解调度器的工作,首先必须了解任务在程序运行过程中的几种状态以及知道就绪队列的原理。
当一个任务从运行态转变为阻塞状态可能的原因有:
1.线程主动休眠
2.线程在等待其他线程的消息
3.线程在等待互斥锁
当我们设计一个应用程序的时候,必须考虑到当一个线程在等待某些事情的发生,确保不是让CPU空转。必须使用一些策略保证低优先级任务可以获取CPU使用权。
我们把各种类型阻塞统称为阻塞状态,这些阻塞类型包含:等待应答阻塞、等待消息阻塞、互斥锁阻塞、中断阻塞、休眠阻塞。
当一个线程想要湖区CPU使用权的时候我们称它为就绪状态,但是突然被其他任务打断。这个任务将会运行我们称它为运行状态。
1.2就绪队列分析
就绪队列是一个简化版本的内核数据结构,根据优先级排序的队列。每个队列节点挂着其他优先级相同的就绪的任务。
如上图所示,线程B-F都处于就绪状态。线程A正在运行。其他线程G-Z被阻塞。线程A/B/C拥有最高优先级,所以他们将基于调度策略分享处理器执行任务。
活动的线程是唯一一个处于运行状态的。内核利用一个数组(每个处理器有一个入口)去追踪当前在运行的线程。
每个线程被分配一个优先级。调度器选取处于就绪状态的最高优先级的任务准备下一次运行。
1.3挂起一个正在运行的线程
当内核进行系统调用的时候,例如:异常、硬件中断,当前正在运行的线程被短暂挂起。当任何线程的执行状态发生改变的时候调度器会进行裁决。线程将会被全局性的调度,横跨所有进程。
通常情况下,被挂起状态下的可执行程序会重新执行,但是调度器会进行上下文切换,从一个线程转到另外一个线程,当一个正在运行的线程发生:
1.被阻塞
2.被抢占
3.主动放弃CPU使用权
1.3.1当一个线程被阻塞
当一个线程在等待其他事情的发生的时候,它将会被阻塞(IPC需要,等待互斥锁等)。被阻塞的线程从运行数组移除,同时最高优先级的任务运行。当一个阻塞的线程编程非阻塞状态时,它将会按照优先级被放置在相应队列尾部。
1.3.2当一个线程被抢占
当一个高优先级的线程就绪的时候,当前正在运行的线程会被抢占。被抢占的线程按照优先级放置在对应队列的开始位置,同时高优先级任务开始执行。
一个被抢占的线程在相同优先级水平的队列中的位置不会改变。
1.3.3线程主动放弃CPU
一个正在运行的线程主动放弃处理器使用权(sched_yield()),那么将会按照优先级水平放置在对应就绪队列的尾部。最高优先级的线程继续运行。
二、调度策略
为了适应各种应用程序,Neutrino提供了以下三种调度策略:
1.FIFO 调度方式
2.循环式调度法
3.适应式调度法
系统中每个线程都可以利用任意方式调度。调度方式基于每个线程为基础,而不是基于同一节点上的所有线程和进程。记住,这种调度策略适用于仅当两个或多个线程有相同的优先级都处于就绪状态下。
线程可以利用 pthread_attr_setschedparam() 或则 pthread_attr_setschedpolicy() 设置调度参数和策略,对于所有线程在创建的时候都有效。
虽然子线程继承父线程优先级,线程可以调用pthread_setschedparam()去请求内核调度策略和算法,或者调用 pthread_setschedprio()仅仅修改优先级。一个线程可以调用 pthread_getschedparam()获取当前调度策略和算法,可以调用pthread_self()获取线程ID。例如:
struct sched_param param; int policy, retcode; /* Get the scheduling parameters. */ retcode = pthread_getschedparam( pthread_self(), &policy, ¶m); if (retcode != EOK) { printf ("pthread_getschedparam: %s.\n", strerror (retcode)); return EXIT_FAILURE; } printf ("The assigned priority is %d, and the current priority is %d.\n", param.sched_priority, param.sched_curpriority); /* Increase the priority. */ param.sched_priority++; retcode = pthread_setschedparam( pthread_self(), policy, ¶m); if (retcode != EOK) { printf ("pthread_setschedparam: %s.\n", strerror (retcode)); return EXIT_FAILURE; }
当你获取调度器参数,sched_param结构体里面的sched_priority 参数用于修改指定的优先级,而sched_curpriority由于修改线程当前优先级(这个优先级可能会因为优先级继承发生改变)。
1.1FIFO调度策略
在FIFO调度策略中,一个在运行的线程将会持续运行直到:
1.主动放弃CPU
2.被高优先级线程抢占
1.2循环式调度策略
1、如果一个线程用完时间片就会被中断运行,移到队列尾部。
1.3适应式调度策略
1、如果一个线程用完时间片就会被中断运行,降低一个优先级。(只降低一次)。