Linux进程调度器基础讲解
Linux2 调度进程

Linux2 调度进程在Fedora Core Linux系统中,允许多个进程并发执行。
但是,通常情况下,系统中的资源总是有限的,如果系统中并发执行的进程数量过多,就会造成系统的整体性能下降,尤其是当系统中存在黑客进程或者病毒进程时,甚至可能会造成系统瘫痪。
因而,有必要根据一定的策略对系统中的进程进行调度,例如,将可疑的进程终止,将不紧急的进程挂起或者降低其优先级等。
系统管理员或者普通用户都可以对进程进行调度,但是执行这种操作时要非常小心,因为有些进程是与系统运行相关联的,不能对它们有错误的调度。
可以使用如下所示的任一种方法进行进程调度:●在系统监视器的“进程”选项页中进行调度。
●在top命令的执行过程中使用交互命令进行调度。
●在命令行中直接执行各种调度进程的命令。
下面将介绍如何使用命令行命令调度进程。
1.更改进程的优先级在fedora系统中,各个进程都是具有特定的优先级的,系统在为某个进程分配CPU使用时间时是根据进程的优先级进行判定的。
有些进程比较重要,需要先执行,以提高整个程序的执行效率,而有些不太重要的进程,其优先级可以低一些。
通常情况下,大多数用户进程的优先级是相同的,但是可以使用Fedora提供的某些命令改变进程的优先级。
通过执行“ps -l”命令可以查看当前用户进程的优先级,如下所示。
PRI表示进程的优先级,它是由操作系统动态计算的,是实际的进程优先级;NI所表示的是请求进程执行优先级,它可由进程拥有者或者超级用户进行设置,NI会影响到实际的进程优先级。
下面介绍两个可以改变进程优先级的命令。
(1)nice命令格式:nice 【选项】命令功能:在启动进程时指定请求进程执行优先级。
该命令较常用的一个选项是“-n”,n值(即NI值)的范围是从-20到19。
-20代表最高的NI优先级,19代表最低的NI优先级。
如果没有该参数,将自动设置NI值为10。
默认情况下,只有超级用户才有权提高请求进程的优先级,而普通用户只能降低请求进程的优先级。
请描述linux下常见的调度策略及调度原理

请描述linux下常见的调度策略及调度原理在Linux下,常见的进程调度策略包括:1.CFS(Completely Fair Scheduler)完全公平调度器:CFS是Linux内核默认的调度策略。
它通过使用红黑树数据结构来维护进程队列,以确保公平分配CPU时间片。
CFS基于进程的虚拟运行时间(vruntime)进行调度,根据进程的优先级和历史执行情况来分配CPU时间。
2.实时调度策略:Linux提供了多种实时调度策略,包括先来先服务(FIFO)和轮转(Round Robin)调度策略。
实时任务具有较高的优先级,可以实时响应系统事件,适用于对时间敏感的应用,如嵌入式系统和实时视频处理等。
3.基于优先级的调度策略:Linux还支持基于静态优先级和动态优先级的调度策略。
这些策略根据进程的优先级决定调度顺序,优先级较高的进程将获得更多的CPU时间。
调度原理是指操作系统如何决定哪个进程获得CPU资源的分配。
Linux的调度器使用时间片轮转和优先级调度等策略来实现公平和高效的调度。
调度器会根据不同的调度策略和优先级,分配给每个进程一定的CPU时间片。
时间片指定了进程能够运行的时间段。
当一个进程的时间片用完或发生了阻塞事件时,调度器会将CPU 分配给下一个就绪状态的进程。
CFS调度器基于虚拟运行时间(vruntime)来分配CPU时间。
vruntime表示进程所需的实际运行时间,CFS通过比较进程的vruntime来决定下一个运行的进程。
较长时间没有运行的进程会被赋予更长的时间片,以实现公平调度。
实时调度策略将优先级更高的实时任务放在优先级队列的前面,以确保它们及时地响应系统事件。
在实时任务运行期间,其他普通优先级的任务将被暂时挂起。
总的来说,Linux的调度器通过多种调度策略和优先级,根据不同类型的任务和进程的要求,合理分配CPU资源,以实现公平、高效和响应及时的调度。
这样可以确保系统的正常运转并提高性能。
操作系统原理-进程调度

6.3.1 进程调度的功能
6.3.1 进程调度的功能
进程调度的功能 根据一定的调度策略或指标遍历所有的就绪进程, 从中选择一个最合适的进程。 选择该进程的过程实际是用户按特定指标对所有进 程进行排队的过程。
6.3.2 进程调度的时机
6.3.2 进程调度的时机
1.时钟中断 时钟中断是最频繁且周期性地引发进程调度的事件之一。
作业
大小 进入时刻 开始时刻 结束时刻
周转 时间
带权周 转时间
平均周 转时间
平均带权周 转时间
A 20
0
0
20
20 1.00
B 40
10
50
90
80 2.00
C 30
15
20
50
35 1.17 43.75
2.04
D 10
60
90
100
40 4.00
6.4.3 响应比高者优先调度算法
6.4.3 响应比高者优先调度算法
6.3.3 进程调度的方式
进程调度的方式
非抢占方式又称非剥夺式调度
它是指进程调度程序一旦把CPU分配给某进程后,该进程 可以一直运行下去,在它的时间片用完之前,或任务完成 之前,或因为I/O请求被阻塞之前,决不允许其他进程抢走 它的CPU。
抢占方式又称剥夺式调度
抢占方式允许进程调度程序根据某种策略终止当前正在运 行的进程,将其移入就绪队列,再根据某种调度算法选择 另一个进程投入运行。
6.4.1 先来先服务调度算法
先来先服务(First Come First Service,FCFS)
例子:假设系统中有4个作业先后投入,它们的作业 大小和进入时间如表(作业大小和时间单位分钟)
linux的任务调度机制

linux的任务调度机制摘要:1.Linux任务调度机制简介2.Linux任务调度器的工作原理3.调度策略和队列4.进程优先级和调度算法5.总结正文:Linux任务调度机制是操作系统中负责分配处理器时间片给各个进程的核心组件。
它依据特定的策略和算法,确保公平、高效地管理进程的执行。
本文将详细介绍Linux任务调度机制的各个方面。
1.Linux任务调度机制简介Linux采用基于优先级的抢占式调度算法,以确保处理器资源得到充分利用。
调度器通过周期性地在就绪队列中选择一个或多个进程,将它们分配给处理器执行。
调度器主要依据进程的优先级和当前的负载情况来决定哪个进程获得处理器资源。
2.Linux任务调度器的工作原理Linux任务调度器的核心组件是调度实体(scheduler entity),它包括进程队列、调度策略和调度算法。
调度实体根据系统的当前状态,按照策略和算法来选择下一个要执行的进程。
调度实体的工作过程分为以下几个步骤:- 进程创建:当一个新进程被创建时,调度器会为其分配一个初始优先级,并将其加入就绪队列。
- 进程执行:调度器从就绪队列中选择一个或多个进程,将它们分配给处理器执行。
执行过程中,进程可能因时间片用完或被阻塞而放弃处理器资源。
- 进程更新:调度器周期性地更新进程的优先级和状态,以反映其当前的执行情况。
- 进程退出:当进程完成执行或被终止时,调度器会将其从进程队列中移除。
3.调度策略和队列Linux调度器支持多种调度策略,如FIFO(先进先出)、SJF(短作业优先)和RR(时间片轮转)。
调度策略决定了进程在队列中的排列顺序,从而影响了调度器选择下一个进程的依据。
Linux中有两个主要的进程队列:就绪队列和运行队列。
就绪队列包含了所有等待处理器资源的进程,而运行队列则存放了当前正在执行的进程。
调度器会根据策略从就绪队列中选择一个或多个进程,将其加入运行队列。
4.进程优先级和调度算法Linux中的进程优先级是一个0-139的整数,优先级数值越低,进程获得处理器资源的机会越高。
Linux编程:模拟进程调度算法

Linux编程:模拟进程调度算法稍稍有点操作系统基础的朋友应该知道进程的调度算法,在这⾥Koala还是给⼤家略微介绍⼀下接下来将要⽤到的⼏种算法:1. 先来先服务(FCFS)采⽤FCFS调度,先请求CPU的进程会先分配到CPU。
使⽤FCFS调度的等待时间通常较长,CPU利⽤率也会较低2. 最短作业优先调度(SJF)采⽤SJF调度会选择具有最短CPU运⾏时间的进程分配CPU使⽤权。
如果两个进程的CPU区间相同,则按照FCFS来进⾏选择。
SJF调度可以证明是最佳的,它降低了平均等待时间。
3. 轮转法调度(RR)RR调度将CPU时间分为较⼩的时间⽚,调度程序循环就绪队列。
为每⼀个进程分配不超过⼀个时间⽚的CPU。
RR调度专门⽤于分时系统。
4. 优先级调度每⼀个进程都有⼀个优先级与其关联,具有最⾼优先级的进程会分配到CPU。
优先级调度的⼀个主要问题是优先级较低的进程会产⽣饥饿现象。
整个编程思路按照如下进⾏:创建主线程,主线程创建⼦线程,⼦线程有⼀个虚拟PCB主线程创建20个⼦线程,分别实现FCFS调度、SJF调度、RR调度、优先级调度,并且计算每个调度的平均等待时间。
对于每个⼦线程,在其运⾏期间,输出其占⽤的时间标号(例如,第3个线程占⽤了第10秒的CPU时间,输出为:“Thread3:10”)。
下⾯是整个的代码(仅供参考):#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<unistd.h>#include<pthread.h>#include<time.h>#include<iostream>#define Thread_Num 20using namespace std;pthread_mutex_t Device_mutex ;//Virtual PCB of threadsstruct VirtualPCB{int tid;int priority;int waittime;int runtime;int arrivetime;int visited;int tempruntime;public:int gettid(){return tid;}int getwaittime(){return waittime;}int getpriority(){return priority;}int getruntime(){return runtime;}int getarrivetime(){return arrivetime;}void setvisit(int a){visited=a;}int getvisit(){return visited;}int gettempruntime(){return tempruntime;}void setwaittime(int n){waittime = n;}void settempruntime(int n){tempruntime = tempruntime - n;}}TCB[Thread_Num];//Function to initial virtual PCBvoid t_init(){int n;srand(time(NULL));for(n =0;n<Thread_Num;n++){TCB[n].tid = n + 1;//⽤线程创建序号作为虚拟进程id//⽤随机数随机产⽣虚拟PCB的值TCB[n].priority = 1 + rand()%19;TCB[n].runtime = 1 + rand()%19;TCB[n].arrivetime = 0;//模拟时,默认进程按创建顺序依次在0时刻到达TCB[n].waittime = 0;TCB[n].visited =0;TCB[n].tempruntime = TCB[n].runtime;}}//Threads run functionvoid *t_print(void *arg){int n = *(int *)arg;//get argumentwhile(1){pthread_mutex_lock(&Device_mutex);printf("Thread_%-2d: ",n);printf("tid:%-2d priority:%-2d runtime:%-2d \n",TCB[n-1].gettid(),TCB[n-1].priority,TCB[n-1].runtime); pthread_mutex_unlock(&Device_mutex);sleep(1);break;}//printf("Error %d\n",n);pthread_exit(0);}//First come first service schedule functionvoid FCFS(){cout<<"-----------FCFS:"<<endl;int i,j;int start = 0;float waittime = 0;float avwait = 0;for(i=0;i<Thread_Num/2;i++){for(j=0;j<Thread_Num;j++){if(TCB[j].getarrivetime()==i && TCB[j].getvisit()==0){printf("Thread: %-2d Start: %-3d Runtime: %-2d\n",TCB[j].gettid(),start,TCB[j].getruntime()); waittime = waittime + (float)start;start = start + TCB[j].getruntime();TCB[j].setvisit(1);}}}avwait = waittime / (float)Thread_Num;printf("Total waitting time : %f\n",waittime);printf("Average waitting time : %f\n",avwait);}//Shortest job first schedule functionvoid SJF(){for(int k=0 ;k<Thread_Num;k++){TCB[k].setvisit(0);}cout<<"-------------SJF:"<<endl;int i,j;int start = 0;float waittime = 0;float avwait = 0;for(i=1;i<Thread_Num;i++){for(j=0;j<Thread_Num;j++){if(TCB[j].getruntime()==i && TCB[j].getvisit()==0){printf("Thread: %-2d Start: %-3d Runtime: %-2d\n",TCB[j].gettid(),start,TCB[j].getruntime()); waittime = waittime + (float)start;start = start + TCB[j].getruntime();TCB[j].setvisit(1);}}}avwait = waittime / (float)Thread_Num;printf("Total waitting time : %f\n",waittime);printf("Average waitting time : %f\n",avwait);}//Round R schedule functionvoid RR(int r){cout<<"--------------RR:"<<endl;int start = 0;float waittime = 0;float avwait = 0;for(int i=0;i<Thread_Num;i++){int totaltime = totaltime + TCB[i].getruntime();TCB[i].setvisit(0);}for(int j=0;j<20*Thread_Num;j=j+r){int k = (j%(20*r))/r;if(TCB[k].gettempruntime() > 0){int tepruntime = r;if(TCB[k].gettempruntime()-r<=0){tepruntime = TCB[k].gettempruntime();TCB[k].setwaittime(start + tepruntime - TCB[k].getruntime());}printf("Thread: %-2d Start: %-3d Runtime:%-2d \n",TCB[k].gettid(), start,tepruntime);start = start + tepruntime;TCB[k].settempruntime(r) ;}}for(int m=0;m<Thread_Num;m++){waittime += TCB[m].getwaittime();//printf("TCB[%d].getwaittime():%d\n",m+1,TCB[m].getwaittime());}avwait = waittime / (float)Thread_Num;printf("Total waitting time : %f\n",waittime);printf("Average waitting time : %f\n",avwait);}//Priority schedule functionvoid Priority(){for(int k=0 ;k<Thread_Num;k++){TCB[k].setvisit(0);}cout<<"-----------Priority:"<<endl;int i,j;int start = 0;float waittime = 0;float avwait = 0;for(i=1;i<Thread_Num;i++){for(j=0;j<Thread_Num;j++){if(TCB[j].getpriority()==i && TCB[j].getvisit()==0){printf("Thread: %-2d Start: %-3d Runtime: %-2d\n",TCB[j].gettid(),start,TCB[j].getruntime()); waittime = waittime + (float)start;start = start + TCB[j].getruntime();TCB[j].setvisit(1);}}}avwait = waittime / (float)Thread_Num;printf("Total waitting time : %f\n",waittime);printf("Average waitting time : %f\n",avwait);}//Main thread execute function to create 20 children threadsvoid *Children(void*){int ret[Thread_Num];t_init();pthread_t tid[Thread_Num];pthread_mutex_init(&Device_mutex,NULL);int i,j;for(i=0;i<Thread_Num;i++){int k =i+1;ret[i] = pthread_create(&tid[i],NULL,&t_print, &k);if(ret[i] == 0) {sleep(1);}else{printf("Thread_%-2d failed!\n",i+1);}}for(j=0;j<Thread_Num;j++)pthread_join (tid[i], NULL);pthread_mutex_destroy(&Device_mutex);pthread_exit(0);}int main(){int ret1;pthread_t tid1;//Declare main threadret1 = pthread_create(&tid1,NULL,&Children,NULL);//Create main threadif(ret1 == 0){printf("Main Thread ok!\n");sleep(20);}else{printf("Thread failed!\n");}FCFS();SJF();cout<<"Please enter RR time:\n";//Request RR timeint rr;scanf("%d",&rr);RR(rr);Priority();return 0;}OK!此代码的运⾏结果如下(部分):第⼀张图打印了⼀下虚拟PCB的部分内容:第⼆张图⽚打印了FCFS调度算法运⾏结果:第三张图⽚打印了SJF调度算法运⾏结果:第四张图⽚打印了RR调度算法运⾏结果(部分):第五张图⽚打印了Priority调度算法运⾏结果:注意看每张图下⾯的两⾏数据,分别是不同算法对应的总的进程的等待时间以及平均等待时间的⼤⼩,印证了SJF算法通常是最少平均等待时间的调度算法最后希望⼤家能够积极提建议,指出纰漏!。
进程调度

Linux调度程序提高交互式程序的优先级,让它们运行更频繁。因此, 调度程序提供较长的默认时间片给交互式程序。此外,调度程序还能根 据进程的优先级动态调整分配给它的时间片,从而保证了优先级高的进 程,执行的频率高,执行时间长。通过动态掉正优先级和时间片的长度 机制,Linux调度性能不但非常稳定而且也很强健。
计算优先级和时间片
进程拥有一个初始的nice值(优先级),范围是-20~19,默认 是0,进程task_struct的static_prio域存放这个值,因为它 从一开始由用户指定,不能修改,所以是静态优先级。 调度程序用到的动态优先级存放在prio域里,动态优先级 是通过一个关于静态和进程交互性的函数关系计算而来。 effective_prio()函数返回一个进程的动态优先级。 调度程序通过一些推断来获取准确反映进程时I/O消耗型还 是处理器消耗型。 为了支持这种推断,Linux记录了一个进程用于休眠和用于 执行的时间。该值存放在task_struct的sleep_avg域中,范 围是从0到MAX_SLEEP_AVG。默认值是10毫秒, sleep_avg会根据它的休眠时间的长短而增长,直到最大值 为止,进程没运行一个节拍,sleep_avg就相应减少,直到 0为止。
可运行队列
调度程序中最基本的数据结构是运行队列。 可执行队列定义于kernel/sched.c中,由结构runqueue 表示,可执行队列是给定处理器上可执行进程的链表 ,每个处理器一个。每一个可投入运行的进程都惟一 的归属于一个可执行队列。此外,可执行队列中还包 含每个处理器的调度信息。因此,可执行队列是每一 个处理器最重要的数据结构。
重新计算时间片
操作系统在所有的进程的时间片都用完时,会重新计 算每个进程的时间片。 典型的实现是循环访问每个进程: for(系统中的每个人物){ 重新计算优先级 重新计算时间片 }
linux下常见的调度策略及调度原理

linux下常见的调度策略及调度原理Linux是一种开源的操作系统,广泛应用于服务器和嵌入式设备中。
在Linux系统中,进程调度策略是操作系统的核心组成部分之一,它决定了进程的执行顺序和时间分配。
本文将介绍Linux下常见的调度策略及其调度原理。
在Linux系统中,常见的进程调度策略包括先来先服务(FCFS)、最短作业优先(SJF)、时间片轮转(RR)和优先级调度(Priority Scheduling)等。
先来先服务(FCFS)是一种简单而直观的调度策略,它按照进程到达的先后顺序进行调度。
即当一个进程到达系统时,它将被放入就绪队列的末尾,并等待CPU的分配。
当CPU空闲时,系统将选择就绪队列中的第一个进程分配给CPU执行。
这种调度策略的优点是公平性强,但缺点是无法处理长作业和短作业的差异,容易产生"饥饿"现象。
最短作业优先(SJF)调度策略是根据进程的执行时间来决定优先级的调度策略。
即系统会选择执行时间最短的进程先执行,以减少平均等待时间。
这种调度策略的优点是能够最大程度地减少平均等待时间,但缺点是可能会出现长作业等待时间过长的问题。
时间片轮转(RR)是一种基于时间片的调度策略,每个进程被分配一个固定长度的时间片。
当一个进程的时间片用完时,系统将把CPU分配给下一个进程。
这种调度策略的优点是能够有效地平衡进程之间的响应时间,但缺点是可能会导致频繁的上下文切换。
优先级调度(Priority Scheduling)是一种根据进程优先级来决定调度顺序的策略。
每个进程被分配一个优先级,优先级越高的进程越容易被调度执行。
这种调度策略的优点是能够根据不同进程的需求进行灵活调度,但缺点是可能会导致低优先级进程的"饥饿"问题。
在Linux系统中,调度算法的实现是通过内核的进程调度器来完成的。
内核中的调度器会根据不同的调度策略来选择下一个要执行的进程,并将其上下文切换到CPU中执行。
深入解读Linux进程调度Schedule【转】

深⼊解读Linux进程调度Schedule【转】调度系统是现代操作系统⾮常核⼼的基础⼦系统之⼀,尤其在多任务并⾏操作系统(Multitasking OS)上,系统可能运⾏于单核或者多核CPU上,进程可能处于运⾏状态或者在内存中可运⾏等待状态。
如何实现多任务同时使⽤资源并且提供给⽤户及时的响应实现实时交互以及提供⾼流量并发等对现代操作系统的设计实现带来了巨⼤挑战,⽽Linux调度⼦系统的设计同样需要实现这些看似⽭盾的要求,适应不同的使⽤场景。
我们看到Linux是⼀个复杂的现在操作系统,各个⼦系统之间相互合作才能完成⾼效的任务。
本⽂从围绕调度⼦系统,介绍了调度⼦系统核⼼的概念,并且将其与Linux各个相关组件的关系进⾏探讨,尤其是与调度⼦系统息息相关的中断(softirq和irq)⼦系统以及定时器Timer,深⼊⽽全⾯地展⽰了调度相关的各个概念以及相互联系。
由于笔者最近在调试PowerPC相关的芯⽚,因此相关的介绍会以此为例提取相关的内核源代码进⾏解读展⽰。
涉及的代码为Linux-4.4稳定发布版本,读者可以查看源码进⾏对照。
1. 相关概念要理解调度⼦系统,⾸先需要总体介绍调度的流程,对系统有⼀个⾼屋建瓴的认识之后,再在整体流程中对各个节点分别深⼊分析,从⽽掌握丰富⽽饱满的细节。
在系统启动早期,会注册硬件中断,时钟中断是硬件中断中⾮常重要的⼀种,调度过程中需要不断地刷新进程的状态以及设置调度标志已决定是否抢占进程的执⾏进⾏调度。
时钟中断就是周期性地完成此项⼯作。
这⾥⼜引出另外⼀个现代OS的调度设计思想即抢占(preempt),⽽与其对应的概念则为⾮抢占或者合作(cooperate),后⾯会给出两者的详细区别。
时钟中断属于硬件中断,Linux系统不⽀持中断嵌套,所以在中断发⽣时⼜会禁⽌本地中断(local_irq_disable),⽽为了尽快相应其他可能的硬件事件,必须要尽快完成处理并开启中断,因此引出了中断下半部,也就是softirq的概念。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.1 进程•从教科书上,我们都能知道:进程是资源分配的最小单位,而线程是CPU 调度的的最小单位。
•进程不仅包括可执行程序的代码段,还包括一系列的资源,比如:打开的文件、内存、CPU时间、信号量、多个执行线程流等等。
而线程可以共享进程内的资源空间。
•在Linux内核中,进程和线程都使用struct task_struct结构来进行抽象描述。
•进程的虚拟地址空间分为用户虚拟地址空间和内核虚拟地址空间,所有进程共享内核虚拟地址空间,没有用户虚拟地址空间的进程称为内核线程。
Linux内核使用task_struct结构来抽象,该结构包含了进程的各类信息及所拥有的资源,比如进程的状态、打开的文件、地址空间信息、信号资源等等。
task_struct结构很复杂,下边只针对与调度相关的某些字段进行介绍。
struct task_struct{/* ... *//* 进程状态*/volatile long state;/* 调度优先级相关,策略相关*/int prio;int static_prio;int normal_prio;unsigned int rt_priority;unsigned int policy;/* 调度类,调度实体相关,任务组相关等*/const struct sched_class *sched_class;struct sched_entity se;struct sched_rt_entity rt;#ifdef CONFIG_CGROUP_SCHEDstruct task_group *sched_task_group;struct sched_dl_entity dl;/* 进程之间的关系相关*//* Real parent process: */struct task_struct __rcu *real_parent;/* Recipient of SIGCHLD, wait4() reports: */struct task_struct __rcu *parent;/* * Children/sibling form the list of natural children: */struct list_head children;struct list_head sibling;struct task_struct *group_leader;/* ... */}1.2 进程状态•上图中左侧为操作系统中通俗的进程三状态模型,右侧为Linux对应的进程状态切换。
每一个标志描述了进程的当前状态,这些状态都是互斥的;•Linux中的就绪态和运行态对应的都是TASK_RUNNING标志位,就绪态表示进程正处在队列中,尚未被调度;运行态则表示进程正在CPU上运行;内核中主要的状态字段定义如下/* Used in tsk->state: */#define TASK_RUNNING 0x0000#define TASK_INTERRUPTIBLE 0x0001#define TASK_UNINTERRUPTIBLE 0x0002/* Used in tsk->exit_state: */#define EXIT_DEAD 0x0010#define EXIT_ZOMBIE 0x0020#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)/* Used in tsk->state again: */#define TASK_PARKED 0x0040#define TASK_DEAD 0x0080#define TASK_WAKEKILL 0x0100#define TASK_WAKING 0x0200#define TASK_NOLOAD 0x0400#define TASK_NEW 0x0800#define TASK_STATE_MAX 0x1000/* Convenience macros for the sake of set_current_state: */#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) #define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED)#define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED)#define TASK_IDLE (TASK_UNINTERRUPTIBLE | TASK_NOLOAD)1.3 scheduler 调度器•所谓调度,就是按照某种调度的算法,从进程的就绪队列中选取进程分配CPU,主要是协调对CPU等的资源使用。
进程调度的目标是最大限度利用CPU时间。
内核默认提供了5个调度器,Linux内核使用struct sched_class来对调度器进行抽象:1.Stop调度器, stop_sched_class:优先级最高的调度类,可以抢占其他所有进程,不能被其他进程抢占;2.Deadline调度器, dl_sched_class:使用红黑树,把进程按照绝对截止期限进行排序,选择最小进程进行调度运行;3.RT调度器, rt_sched_class:实时调度器,为每个优先级维护一个队列;4.CFS调度器, cfs_sched_class:完全公平调度器,采用完全公平调度算法,引入虚拟运行时间概念;5.IDLE-Task调度器, idle_sched_class:空闲调度器,每个CPU都会有一个idle线程,当没有其他进程可以调度时,调度运行idle线程;Linux内核提供了一些调度策略供用户程序来选择调度器,其中Stop调度器和IDLE-Task调度器,仅由内核使用,用户无法进行选择:•SCHED_DEADLINE:限期进程调度策略,使task选择Deadline调度器来调度运行;•SCHED_RR:实时进程调度策略,时间片轮转,进程用完时间片后加入优先级对应运行队列的尾部,把CPU让给同优先级的其他进程;•SCHED_FIFO:实时进程调度策略,先进先出调度没有时间片,没有更高优先级的情况下,只能等待主动让出CPU;•SCHED_NORMAL:普通进程调度策略,使task选择CFS调度器来调度运行;•SCHED_BATCH:普通进程调度策略,批量处理,使task选择CFS调度器来调度运行;•SCHED_IDLE:普通进程调度策略,使task以最低优先级选择CFS调度器来调度运行;1.4 runqueue 运行队列•每个CPU都有一个运行队列,每个调度器都作用于运行队列;•分配给CPU的task,作为调度实体加入到运行队列中;•task首次运行时,如果可能,尽量将它加入到父task所在的运行队列中(分配给相同的CPU,缓存affinity会更高,性能会有改善);Linux内核使用struct rq结构来描述运行队列,关键字段如下:/** This is the main, per-CPU runqueue data structure. ** Locking rule: those places that want to lock multiple runqueues* (such as the load balancing or the thread migration code), lock* acquire operations must be ordered by ascending &runqueue.*/struct rq {/* runqueue lock: */raw_spinlock_t lock;/** nr_running and cpu_load should be in the same cacheline because* remote CPUs use both these fields when doing load calculation.*/unsigned int nr_running;/*三个调度队列:CFS调度,RT调度,DL调度*/struct cfs_rq cfs;struct rt_rq rt;struct dl_rq dl;/* stop指向迁移内核线程,idle指向空闲内核线程*/struct task_struct *curr, *idle, *stop;/* ... */}1.5 task_group 任务分组•利用任务分组的机制,可以设置或限制任务组对CPU的利用率,比如将某些任务限制在某个区间内,从而不去影响其他任务的执行效率;•引入task_group后,调度器的调度对象不仅仅是进程了,Linux内核抽象出了sched_entity/sched_rt_entity/sched_dl_entity描述调度实体,调度实体可以是进程或task_group;•使用数据结构struct task_group来描述任务组,任务组在每个CPU上都会维护一个CFS调度实体、CFS运行队列,RT调度实体,RT运行队列;Linux内核使用struct task_group来描述任务组,关键的字段如下:/*task group related information*/struct task_group{/* ... *//* 为每个CPU都分配一个CFS调度实体和CFS运行队列*/#ifdef CONFIG_FAIR_GROUP_SCHED/* schedulable entities of this group on each cpu */struct sched_entity **se;/* runqueue "owned" by this group on each cpu */struct cfs_rq **cfs_rq;unsigned long shares;#endif/* 为每个CPU都分配一个RT调度实体和RT运行队列*/#ifdef CONFIG_RT_GROUP_SCHEDstruct sched_rt_entity **rt_se;struct rt_rq **rt_rq;struct rt_bandwidth rt_bandwidth;#endif/* task_group之间的组织关系*/struct rcu_head rcu;struct list_head list;struct task_group *parent;struct list_head siblings;struct list_head children;/* ... */};3. 调度程序调度程序依靠几个函数来完成调度工作的,下边将介绍几个关键的函数。