调度算法
在操作系统中调度是指一种资源分配,因而调度算法是指:根据系统的资源分配策略所规定的资源分配算法。对于不同的的系统和系统目标,通常采用不同的调度算法,目前存在的多种调度算法中,有的算法适用于作业调度,有的算法适用于进程调度;但也有些调度算法既可以用于作业调度,也可以用于进程调度。
通常将作业或进程归入各种就绪或阻塞队列。
调度算法要求:高资源利用率、高吞吐量、用户满意等原则。
进程调度所采用的算法是与整个系统的设计目标相一致的:
1.批处理系统:增加系统吞吐量(单位时间内CPU完成作业的数量)和提高系统资源的利用率;
2.分时系统:保证每个分时用户能容忍的响应时间。
3.实时系统:保证对随机发生的外部事件做出实时响应。
调度算法的分类
1、先来先服务 (FCFS,first come first served)
在所有调度算法中,最简单的是非抢占式的FCFS算法。
——原理:进程按照它们请求CPU的顺序使用CPU.不考虑等待时间和执行时间,会产生饥饿现象。
——优点:易于理解且实现简单,且相当公平
——缺点:比较有利于长进程,而不利于短进程,有利于CPU 繁忙的进程,而不利于I/O 繁忙的进程
2、最短作业优先(SJF, Shortest Job First)
又称为“短进程优先”SPN(Shortest Process Next);这是对FCFS算法的改进,其目标是减少平均周转时间。
——原理:对预计执行时间短的进程优先分派处理机。通常后来的短进程不抢先正在执行的进程。
——优点:相比FCFS 算法,该算法可改善平均周转时间和平均带权周转时间,缩短进程的等待时间,提高系统的吞吐量。
——缺点:对长进程不利,可能长时间得不到执行(可能造成长进程的饥饿问题),且未能依据进程的紧迫程度来划分执行的优先级,以及难以准确估计进程的执行时间,从而影响调度性能。
3、最高响应比优先法(HRRN,Highest Response Ratio Next)
最高响应比优先法是对FCFS方式和SJF方式的一种综合平衡。最高响应比优先法调度策略同时考虑每个作业的等待时间长短和估计需要的执行时间长短,从中选出响应比最高的作业投入执行。这样,即使是长作业,随着它等待时间的增加,W / T(进程等待时间/ 进程执行时间)也就随着增加,也就有机会获得调度执行。这种算法是介于FCFS和SJF之间的一种折中算法。
——原理:响应比R定义如下: R =(W+T)/T = 1+W/T (“响应比=(进程执行时间+进程等待时间)/ 进程执行时间”)
其中T为执行时间,W为等待时间。每当要进行作业调度时,系统计算每个作业的响应比,选择其中R最大者投入执行。
——优点:兼顾长短作业
——缺点:计算响应比开销大,适用于批处理系统。
4、时间片轮转算法(RR,Round-Robin)
该算法采用剥夺策略。时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法,又称RR调度。每个进程被分配一个时间段(固定的执行时间),称作它的时间片,即该进程允许运行的时间。
——原理:让就绪进程以FCFS 的方式按时间片轮流使用CPU 的调度方式,即给每个进程固定的执行时间,根据进程到达的先后顺序让进程在单位时间片内执行,在一个时间片结束时,发生时钟中断。调度程序据此暂停当前进程的执行,将其送到就绪队列的末尾,后便调度下一个进程执行,时间片轮转调度不考虑进程等待时间和执行时间,属于抢占式调度。
——优点:是简单易行、兼顾长短作业。
——缺点:不利于处理紧急作业,平均等待时间较长,上下文切换较费时。适用于分时系统。由于时间片的大小对系统性能的影响很大,因此时间片的大小应选择恰当
——时间片长度的确定
1.系统对响应时间的要求 T(响应时间)=N(进程数目)*q(时间片)
2.就绪队列中进程的数目:数目越多,时间片越小
3.系统的处理能力:应当使用户输入通常在一个时间片内能处理完,否则使响应时间,平均周转时间和平均带权周转时间延长。
5、多级反馈队列(Multilevel Feedback Queue)
多级反馈队列调度算法是一种CPU处理机调度算法,UNIX操作系统采取的便是这种调度算法。
——算法描述:
1、进程在进入待调度的队列等待时,首先进入优先级最高的Q1等待。
2、首先调度优先级高的队列中的进程。若高优先级中队列中已没有调度的进程,则调度次优先级队列中的进程。例如:Q1,Q2,Q3三个队列,只有在Q1中没有进程等待时才去调度Q2,同理,只有Q1,Q2都为空时才会去调度Q3。
3、在低优先级的队列中的进程在运行时,又有新到达的作业,那么在运行完这个时间片后,CPU马上分配给新到达的作业(抢占式)。
在多级反馈队列调度算法中,如果规定第一个队列的时间片略大于多数人机交互所需之处理时间时,便能够较好的满足各种类型用户的需要。
实时进程将得到优先调用,实时进程根据实时优先级决定调度权值,分时进程则通过nice和counter值决
定权值,nice越小,counter越大,被调度的概率越大,也就是曾经使用了cpu最少的进程将会得到优先调度。
RR和FIFO的不同:
当采用RR策略的进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。
FIFO一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃。
如果有相同优先级的实时进程(根据优先级计算的调度权值是一样的)已经准备好,FIFO时必须等待该进程主动放弃后才可以运行这个优先级相同的任务。而RR可以让每个任务都执行一段时间。
RR和FIFO的相同点:
RR和FIFO都只用于实时任务。
创建时优先级大于0(1-99)。
按照可抢占优先级调度算法进行。
就绪态的实时任务立即抢占非实时任务。
task_struct结构体
1.进程状态,将记录进程在等待,运行或死锁。
2.调度信息,由哪个调度函数调度,怎样调度等。
3.进程的通讯情况
4.因为要插入进程树,必须有联系父子兄弟的指针,当然是task_struct型
5.时间信息,比如计算好执行的时间,以便cpu分配。
6.标号,决定改进进程归属
7.可以读写打开文件的一些信息
8.进程上下文和内核上下文
9.处理器上下文
10.内存信息
僵尸进程
僵尸进程是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵尸进程。· 僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。· 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入僵尸状态。僵尸进程的危害· 进程的退出状态必须被维持下去,因为他要告诉关心他的进程(父进程),你交给我的任务,我办的怎么样了,父进程如果一直不读取,那子进程就一直处于Z状态。· 维护退出状态本身就是要用数据维护,也属于进程基本信息,所有保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB就要一直维护。· 一个父进程创建了很多子进程,都没有回收的话,就会造成资源的浪费。· 会造成内存泄漏
#include<stdio.h>
#include<stdlib.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 1;
}
else if(id > 0){
//parent
printf("parent[%d] is sleeping...\n",getpid());
}
else{
printf("child[%d] is begin Z..\n",getpid());
sleep(5);
exit(EXIT_SUCCESS);
}
return 0;
}
孤儿进程
父进程如果提前退出,那么子进程后退出,进入z之后,被称为孤儿进程,孤儿进程被1号init进程领养,当然要由init进程回收
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 1;
}
else if(id == 0){
printf("I am child,pid:%d\n",getpid());
sleep(10);
}
else{
printf("I am parent,pid:%d\n",getpid());
sleep(3);
exit(0);
}
return 0;
}
setenv函数
int setenv(const char * name,const char * value,int overwrite)
注释:
(1)函数说明:用来改变或增加环境变量的内容
(2)参数解释:
name:环境变量名称字符串
value:环境变量内容
overwrite:用来决定是否要改变已存在的环境变量。如果overwrite不为0,则改变环境变量原有内容,原有内容会被改为参数value所指的变量内容。如果overwrite为0,且该环境变量已有内容,则参数value会被忽略。
(3)成功时返回0,有错误失败返回-1
getenv函数
char * getenv(const char * name)
注释:
(1)函数说明:获取环境变量的值
(2)参数说明:name为环境变量的名字
(3)返回值:执行成功则返回指向该内容的指针,找不到符合的环境变量名称则返回NULL
unsetenv函数
char * unsetenv(const char * name)
注释:
(1)函数说明:用来删除一个环境变量
(2)参数解释:name表示环境变量的名称
(3)返回值:成功返回0,失败返回-1并记录错误信息
export命令(用于将shell变量输出为环境变量,或者将shell函数输出为环境变量。)
一个变量创建时,它不会自动地为在它之后创建的shell进程所知。而命令export可以向后面的shell传递变量的值。当一个shell脚本调用并执 行时,它不会自动得到原为脚本(调用者)里定义的变量的访问权,除非这些变量已经被显式地设置为可用。export命令可以用于传递一个或多个变量的值到任何后继脚本。
(1)语法
export(选项)(参数)
(2)选项
-f:代表[变量名称]中为函数名称;
-n:删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中;
-p:列出所有的shell赋予程序的环境变量。
(3)参数
变量:指定要输出或者删除的环境变量
(4)测试模块
直接输入export命令查看已经存在的环境变量