一、实验目的
加深对进程调度概念的理解,体验进程调度机制的功能,了解
Linux
系统中进程
调度策略的使用方法, 练习进程调度算法的编程和调试技术。
二、前置知识:linux中的调度策略-policy
在
linux
系统中调度策略(
policy
)可以是以下
3
种:
SCHED_OTHER 默认的分时调度策略( 值等于 0)SCHED_FIFO 先进先先出调度策略 ( 值等于 1)SCHED_RR 时间片轮转调度策略( 值等于 2)
后两种专用于对响应时间有特殊要求的进程,会抢先于
SCHED_OTHER调度策略的进程而执行。具有 SCHED_FIFO
调度策略的进程只能被更高优先级的进程抢先。
具有 SCHED_RR
调度策略的进程必要时可以与同级进程共享时间片。
进程优先数
(prio)
由静态优先数和动态优先数两部分组成,值越小调度优先级越高 。
具有 SCHED_OTHER
策略的进程静态优先数总是
0
。
动态优先数与进程的执行状态有关,但可以使用 nice
命令或系统调用加大进程优先数使其优先级降低,或用系统调用 setpriority
分别按进程或进程组或用户号设置介于
-20
到
+20
之间的动态优先数。
与进程调度策略有关的系统调用函数原型都声明在以下文件中:
#include <sched.h>
#include <sys/time.h>
#include <sys/resource.h>
设置进程调度策略的系统调用语法为:
int sched_setscheduler(pid_t pid,int policy,const struct sched_param *sp);
pid
进程号
policy
以上说明的
3
种调度策略之一
sp
调度参数结构指针
,
调度参数结构主要存有调度优先数
struct sched_param {
...
int sched_priority;
...
};
返回值: 执行成功后返回
0
获取进程调度策略的系统调用语法为
:
int sched_getscheduler(pid_t pid);
pid 进程号
返回值: 进程当前的调度策略
设置进程动态优先数的系统调用语法为:
int getpriority(int which,int who);
which 设置的对象,可以是:
进程 PRIO_PROCESS
进程组 PRIO_PGRP
用户 PRIO_USER
who
对应设置对象的进程号或组号或用户号
返回值
:
所有匹配进程中的最高优先数
设置进程动态优先数的系统调用语法为:
int setpriority(int which,int who,int prio);
which 设置的对象,可以是:
进程 PRIO_PROCESS
进程组 PRIO_PGRP
用户 PRIO_USER
who
对应设置对象的进程号或组号或用户号
prio
要设置的进程优先数
返回值
:
所有匹配进程中的最高优先数
三、示例实验分析
该示例实验测试在 linux 系统中不同调度策略和不同优先数的调度效果。
1.pached.c 父进程创建3个子进程为他们设置不同的优先数的调度策略
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(int argc, char *argv[])
{
int i,j,status;
int pid[3]; //存放进程号
struct sched_param p[3]; //设置调度策略时使用的数据结构
for(i=0; i<3;i++){
//循环创建 3 个子进程
if((pid[i]=fork()) >0)
{
//取进程优先数放在调度策略数据结构中
//如果 argv[i+1] 非 NULL,则将其转换为整数。
p[i].sched_priority = (argv[i+1] != NULL) ? atoi(argv[i+1]):10;
//父进程设置子进程的调度策略.如果命令行第 4,5,6 参数指定了 3 个策略值则按指定的数设置,否则都为默认策略
sched_setscheduler(pid[i],(argv[i+4] != NULL) ? atoi(argv[i+4]) : SCHED_OTHER,&p[i]);
//父进程设置子进程的优先数,如果命令行第 1,2,3 参数指定了 3 个优先数则按指定的数设置,否则都为 10
setpriority(PRIO_PROCESS,pid[i],(argv[i+1] != NULL) ? atoi(argv[i+1]):10);
}
//各子进程循环报告其优先数和调度策略
else{
sleep(1);
//每隔 1 妙报告一次进程号和优先级
for(i=0; i<10; i++){
printf("Child PID = %d priority = %d\n",getpid(),getpriority(PRIO_PROCESS,0));
sleep(1);
}
exit( EXIT_SUCCESS);
}
}
//父进程报告子进程调度策略后先行退出
printf("My child %d policy is %d\n",pid[0],sched_getscheduler(pid[0]));
printf("My child %d policy is %d\n",pid[1],sched_getscheduler(pid[1]));
printf("My child %d policy is %d\n",pid[2],sched_getscheduler(pid[2]));
return EXIT_SUCCESS;
}
argv: 这是一个命令行参数数组,通常是从 main
函数的参数中传入的,main函数中的 argc
是参数的个数,argv
是参数值的数组。
atoi( ): atoi
是一个标准库函数,将字符串转换为整数。
2.makefile文件
srcs = psched.c
objs = psched.o
opts = -g -c
all: psched
psched: $(objs)
gcc $(objs) -o psched
psched.o: $(srcs)
gcc $(opts) $(srcs)
clean:
rm psched *.o
3.生成可执行文件psched
$gmake
gcc -c psched.c
gcc psched.o -o psched
4. 运行 psched,指定 3 个子进程的优先数为 10, 5, -10;调度策略都是默认策略
$./psched 10 5 -10 0 0 0
child 10771 policy is 0
child 10772 policy is 0
30
child 10773 policy is 0
Child PID = 10773 priority = -10
Child PID = 10772 priority = 5
Child PID = 10771 priority = 10
......
Child PID = 10773 priority = -10
Child PID = 10772 priority = 5
Child PID = 10771 priority = 10
可以看出在相同的调度策略下优先数小的进程先得到了执行。
5. 再次运行 psched,指定 3 个子进程的优先数为 10, 5, 18;调度策略分别是 0,0,1
./psched 10 5 18 0 0 1
My child 11306 policy is 0
My child 11307 policy is 0
My child 11308 policy is 1
Child PID = 11308 priority = 18
Child PID = 11307 priority = 5
Child PID = 11306 priority = 10
Child PID = 11308 priority = 18
Child PID = 11307 priority = 5
Child PID = 11306 priority = 10
可以看出虽然 11308 进程其优先数最大,但由于其调度策略为先进先出,因此
总是首先得到调度。
四、独立实验分析
设有两个并发执行的父子进程,不断循环输出各自进程号、优先数和调度策略。
进程初始调度策略均为系统默认策略和默认优先级。
当某个进程收到 SIGINT信号时会自动将其优先数加 1
,收到
SIGCSTP
信号时会自动将其优先数减
1
。请编程实现以上功能。
1.sched.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
// 信号处理函数
void adjust_priority(int sig)
{
int which = PRIO_PROCESS;
int who = 0; // 0表示当前进程
int prio = getpriority(which, who);
if (sig == SIGINT)
{
prio++;
}
else if (sig == SIGTSTP)
{
prio--;
}
setpriority(which, who, prio);
}
int main()
{
pid_t pid;
struct sched_param sp;
// 设置信号处理函数
signal(SIGINT, adjust_priority);
signal(SIGTSTP, adjust_priority);
pid = fork();
if (pid < 0)
{
perror("fork failed");
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
// 子进程
while (1)
{
int policy = sched_getscheduler(0);
int prio = getpriority(PRIO_PROCESS, 0);
printf("Child PID = %d, Priority = %d, Policy = %d\n", getpid(), prio, policy);
sleep(1);
}
}
else
{
// 父进程
while (1)
{
int policy = sched_getscheduler(0);
int prio = getpriority(PRIO_PROCESS, 0);
printf("Parent PID = %d, Priority = %d, Policy = %d\n", getpid(), prio, policy);
sleep(1);
}
}
return 0;
}
signa函数
void (*signal(int sig, void (*func)(int)))(int);
signa函数将指定信号(SIGINT
和 SIGTSTP
)与处理函数 adjust_priority
关联起来。当进程收到 SIGINT
或 SIGTSTP
信号时,操作系统会调用 adjust_priority
函数,并将相应的信号编号作为参数传递给它。
2.makefile
srcs = priority_adjust.c
objs = priority_adjust.o
opts = -g -c
all: priority_adjust
priority_adjust: $(objs)
gcc $(objs) -o priority_adjust
priority_adjust.o: $(srcs)
gcc $(opts) $(srcs)
clean:
rm priority_adjust *.o