操作系统实验报告-Linux系统下进程的创建与调度
实验一 Linux系统中进程的创建与调度算法模拟实验
一、实验目的和要求
1.加深对进程概念的理解,进一步认识并发执行的实质。
2.掌握Linux 操作系统中进程的创建和终止操作。
3.掌握在Linux 操作系统中创建子进程并加载新映像的操作。
4.加深对进程概念的理解,明确进程和程序的区别。
5. 深入理解系统如何组织进程。
6. 理解常用进程调度算法的具体实现。
三、进程创建实验:实验内容
编写一个C 程序,并使用系统调用fork()创建一个子进程。要求如下:
1、 在子进程中分别输出当前进程为子进程的提示、当前进程的PID 和父进程的PID 、根据用户输入确定当前进程的返回值、退出提示等信息;
2、在父进程中分别输出当前进程为父进程的提示、当前进程的PID 和子进程的PID 、等待子进程退出后获得的返回值、退出提示等信息。
编写另一个C 程序,使用系统调用fork()以创建一个子进程,并使用这个子进程调用exec 函数族以执行系统命令ls 。
四、进程创建实验:实验步骤
(1)在根目录下用vi命令创建一个test1_1.c文件 进行程序编辑
(2)输入gcc test1_1.c -o test1_1,将test1_1.c文件编译输出成test1_1文件
(4)运行可执行文件并输出程序运行结果
输入ls,发现目录下多了一个test1_1文件
再输入./ test1_1,即可运行该文件,并得到输出结果
(5)用vi命令创建test1_2.c文件,并编辑
(6)gcc编译test1_2.c为test1_2,并输入./ test1_2运行文件,得到结果
五、进程创建实验:代码及注释
实验一第一问
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<sys/wait.h>
#include<stdio.h>
int main(){
pid_t haizi; /*存储子进程的pid*/
int retval; /*用户提供的子进程返回值*/
int status; /*父进程的子进程退出状态*/
/*创建新的进程*/
haizi = fork();
if (haizi >= 0)
{
if (haizi == 0)
{
printf("haizi:I am the haizi process\n");
printf("haizi:My PID is %d\n",getpid()); /*输出当前进程的pid*/
printf("haizi:My fuqin's PID is %d\n",getppid()); /*输出当前父进程的pid*/
printf("haizi:The value of fork return is %d\n",haizi);
printf("haizi:Sleep for one second...\n");
sleep(1); //让当前进程睡眠1s
printf("haizi:Enter an exit value (0~255): ");
scanf("%d",&retval);
printf("haizi:Goodbye! \n");
exit(retval); /*子进程退出,退出值为用户提供的返回值*/
}
else
{
printf("fuqin:I am the fuqin process!\n");
printf("fuqin:My PID is %d\n",getpid());
printf("fuqin:The value of my haizi's PID is %d\n", haizi);
printf("fuqin:I will now wait for my haizi to exit.\n");
wait(&status);
printf("fuqin:haizi's exit code is %d\n",WEXITSTATUS(status));/*输出子进程的返回值*/
printf("fuqin:Goodbye!\n");
exit(0); /*父进程退出*/
}
}
else
{
perror("fork error !\n"); /*显示错误信息*/
exit(0);
}
return 0;
}
实验一第二问
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
int main() {
int rtn; /*子进程的返回值*/
if (fork() == 0) {
/*The son process execution this process */
execlp("/bin/ls","ls -al",(char *)0);
exit(21); /*显示打印失败信息*/
}
else {
/*父进程等待子进程结束并打印子进程返回值*/
wait(&rtn);
printf("child process return %d\n", rtn);
}
return 0;
}
六、进程创建实验:实验结果
实验一第一问
首先在主程序中通过 forkO 创建子进程,并根据fork()的返回值确定所处的进程是子进程还是父进程,然后分别在子进 程和当前进程(父进程)中调用 getpidO getppid()、 wait()等函数
图1.1 进程创建的运行结果
实验一第二问
图1.2 进程调用 exec 函数族以执行系统命令 ls
七、进程调度实验:实验内容
编写C 程序,模拟实现单处理器系统中的进程调度算法,实现对多个进程的模拟调度,要求采用常见的进程调度算法(如先来先服务、时间片轮转和优先级等调度算法)进行模拟调度。
八、进程调度实验:实验步骤
开始运行程序后,出现三个算法选择:1.优先级调度算法;2.先来先服务算法;3.时间轮转算法,选择其中任意一个算法之后,提示输入进程的个数,进程的名称,进程的优先级,进程的运行时间
每个进程的状态可以是就绪W(Wait)、运行R(Run)、或阻塞B(Block)三种状态之一。 每进行一次调度程序都打印一次运行进程、就绪队列、阻塞队列以及各个进程的PCB,以便进行检查。重复以上过程,直到所要进程都完成为止。
输入完成后开始调度
1) 优先级调度算法
每个进程的状态可以是就绪W(Wait)、运行R(Run)、或阻塞B(Block)三种状态之一。 每进行一次调度程序都打印一次运行进程、就绪队列、阻塞队列以及各个进程的PCB,以便进行检查。重复以上过程,直到所要进程都完成为止。进程的相关参数即运行时间、I/O需求等自行设定。
实现动态优先级调度算法:可指定进程的初始优先级(优先级与优先数成反比,优先级最高为0),优先级改变遵循下列原则:进程在就绪队列中每停留一个时间片,优先级加1,进程每运行一个时间片,优先级减3。(说明:本算法在优先级相同的情况下,选择到达时间早的进程进行运行)
2) 先来先服务算法
每个进程的状态可以是就绪W(Wait)、运行R(Run)、或阻塞B(Block)三种状态之一。 每进行一次调度程序都打印一次运行进程、就绪队列、阻塞队列以及各个进程的PCB,以便进行检查。重复以上过程,直到所要进程都完成为止。进程的相关参数即运行时间、I/O需求等自行设定。
3) 时间片轮转算法
进程调度程序总是选择就绪队列中第一个进程,允许其占有处理机一个时间片的时间。当执行的时间片用完时,调度程序便停止该进程的执行,并将它送就绪队列的末尾,等待分配下一时间片再执行。然后把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程,在一给定的时间内,均能获得一时间片处理机执行时间。
九、进程调度实验:代码及注释改进后的代码:
#include <stdio.h>
#include <stdlib.h>
// 定义进程控制块PCB
struct pcb {
char name[10];
char state;//进程状态
int super;//优先级
int ntime;//需要时间
int rtime;//已经执行时间
struct pcb* link;
}*ready = 0, * p;
typedef struct pcb PCB;
int runningTime = 0;
//求进程数
int PCB_Len() {
int pcb_len = 0;
PCB* pr = ready;
while (pr != 0) {
pcb_len++;
pr = pr->link;
}
return pcb_len;
}
//建立对进程进行最高优先级调度算法,同级条件下进行先来先服务算法函数
void sort() {
PCB* temp;
p->super -= 1;
temp = ready;
if (temp != 0) {
if (p->super > temp->super) {//插入队首
p->link = ready;
ready = p;
}
else {
while (temp != 0) {
if (temp->link == 0) {//插入队尾
temp->link = p;
break;
}
if (p->super > temp->link->super) {
p->link = temp->link;
temp->link = p;
break;
}
temp = temp->link;
}
}
}
else {
ready = p;
}
}
//连接pcb
void linkpcb() {
PCB* first;
if (ready == 0) {//插入队首
p->link = ready;
ready = p;
}
else {//按从大到小插入到合适位置
first = ready;
if (p->super > first->super) { //插入到队首
p->link = first;
ready = p;
}
else {
while (first->link != 0 && p->super <= first->link->super) {
first = first->link;
}
if (first->link != 0) {
p->link = first->link;
first->link = p;
}
else {
first->link = p;
p->link = 0;
}
}
}
}
// 建立进程控制块输入函数
void input() {
int i, num;
printf("请输入即将运行的进程总数目:");
scanf("%d", &num);
for (i = 0; i < num; i++) {
p = (PCB*)malloc(sizeof(PCB));//申请空间,指针
if (p == 0) {
printf("内存分配不成功!\n");
}
else {
printf("\n请输入第%d个进程名称:", i + 1);
scanf("%s", p->name);
printf("请输入第%d个进程的优先级:", i + 1);
scanf("%d", &p->super);
printf("请输入第%d个进程运行时间:", i + 1);
scanf("%d", &p->ntime);
p->rtime = 0;
p->state = 'W';
p->link = 0;
linkpcb();
}
}
}
//建立进程显示函数,用于显示当前进程
void disp(PCB* pr) {
printf("Name\tState\tSuper\tNTime\tRunTime\n");
printf("%s\t", pr->name);
printf("%c\t", pr->state);
printf("%d\t", pr->super);
printf("%d\t", pr->ntime);
printf("%d\t\n", pr->rtime);
}
//建立函数查看进程
void check() {
PCB* temp;
printf("\n **** 当前正在运行的进程是:%s\n", p->name); //显示当前运行进程
disp(p);
temp = ready;
printf("\n ****当前就绪队列状态为:\n"); //显示就绪队列状态
if (temp == 0)printf("\n 就绪队列为空。\n ");
while (temp != 0) {
disp(temp);
temp = temp->link;
}
free(temp);
}
//建立进程就绪函数(进程运行时间到,置运行状态)
void running() {
if (p->rtime == p->ntime) {//进程已完成
printf("\n 进程 [%s] 已完成.\n", p->name);
}
else {
sort(); //调用sort函数
}
}
//先来先服务和时间片轮转算法的连接pcb
void linkpcb_r() {
PCB* first;
if (ready == 0) {//插入队首
p->link = ready;
ready = p;
}
else {//按先后顺序插入到合适位置
first = ready;
while (first->link != 0 ) {
first = first->link;
}
first->link = p;
p->link = 0;
}
}
//先来先服务算法的输入
void input_f() {
int i, num;
printf("请输入即将运行的进程总数目:");
scanf("%d", &num);
for (i = 0; i < num; i++) {
p = (PCB*)malloc(sizeof(PCB));//申请空间,指针
if (p == 0) {
printf("内存分配不成功!\n");
}
else {
printf("\n请输入第%d个进程名称:", i + 1);
scanf("%s", p->name);
printf("请输入第%d个进程的优先级:", i + 1);
scanf("%d", &p->super);
printf("请输入第%d个进程运行时间:", i + 1);
scanf("%d", &p->ntime);
p->rtime = 0;
p->state = 'W';
p->link = 0;
linkpcb_r();
}
}
}
//先进先出算法的查看进程
void check_f(PCB* temp) {
printf("\n **** 当前正在运行的进程是:%s\n", temp->name); //显示当前运行进程
disp(temp);
printf("\n ****当前就绪队列状态为:\n"); //显示就绪队列状态
if (temp->link == 0)printf("\n 就绪队列为空。\n ");
while (temp->link != 0) {
disp(temp->link);
temp = temp->link;
}
}
//建立先来先服务算法函数
int running_f(int n) {
PCB* temp;
temp = ready;
while (temp!= 0 &&temp->rtime<temp->ntime) {
printf("\n\nThe execute number:%d\n", ++n);
temp->state = 'R';
check_f(temp);
temp->rtime += 1;//运行时间加1;
if (temp->rtime == temp->ntime) {//进程已完成
printf("\n 进程 [%s] 已完成.\n", temp->name);
}
}
return n;
}
//时间片轮转算法查看进程
void check_r(PCB* temp,PCB* temp1) {
int flag=0;
printf("\n **** 当前正在运行的进程是:%s\n", temp->name); //显示当前运行进程
disp(temp);
printf("\n ****当前就绪队列状态为:\n"); //显示就绪队列状态
while (temp1 != 0 ) {
if(temp1->ntime!=temp1->rtime && temp1!=temp)
{
disp(temp1);
flag=1;
}
temp1 = temp1->link;
}
if (flag == 0)printf("\n 就绪队列为空。\n ");
}
//建立时间片轮转算法函数
int running_r(int n) {
PCB* temp;
PCB* temp1;
temp = ready;
temp1=ready;
while (temp!=0) {
if(temp->rtime<temp->ntime)
{
printf("\n\nThe execute number:%d\n", ++n);
temp->state = 'R';
check_r(temp,temp1);
temp->rtime += 1;//运行时间加1
}
if(temp->rtime == temp->ntime)
{//进程已完成
printf("\n 进程 [%s] 已完成.\n", temp->name);
}
temp=temp->link;
}
return n;
}
//时间片轮转算法输入
int input_r() {
int i, num,k=0;
printf("请输入即将运行的进程总数目:");
scanf("%d", &num);
for (i = 0; i < num; i++) {
p = (PCB*)malloc(sizeof(PCB));//申请空间,指针
if (p == 0) {
printf("内存分配不成功!\n");
}
else {
printf("\n请输入第%d个进程名称:", i + 1);
scanf("%s", p->name);
printf("请输入第%d个进程的优先级:", i + 1);
scanf("%d", &p->super);
printf("请输入第%d个进程运行时间:", i + 1);
scanf("%d", &p->ntime);
p->rtime = 0;
p->state = 'W';
p->link = 0;
linkpcb_r();
if(p->ntime>k)
{
k=p->ntime;
}
}
}
return k;
}
int main() {
int len, h = 0;
int k,a=0;
printf("***请选则调度算法***\n");
printf("1.优先度调度算法\n");
printf("2.先进先出调度算法\n");
printf("3.时间片轮转调度算法\n");
scanf("%d",&a);
switch (a) {
case 1:
input();//输入进程
len = PCB_Len();
while ((len != 0) && (ready != 0)) {
printf("\n\nThe execute number:%d\n", ++h);
p = ready;
ready = p->link;
p->link = 0;
p->state = 'R';
check();//查看进程
p->rtime += 1;//运行时间加1;
p->state = 'W';//置为就绪状态
running();
}
break;
case 2:
input_f();//输入进程
len = PCB_Len();
while ((len != 0) && (ready != 0)) {
h=running_f(h) ;
p = ready;
ready = p->link;
}
case 3:
k=input_r();//输入进程
for(int i=0;i<k;i++){
h=running_r(h);
}
default:
break;
}
printf("\n\n 进程已经完成.\n");
return 0;
}
十、进程调度实验:实验结果
图1.3优先级调度程序的运行结果1
图1.4优先级调度程序的运行结果2
图1.5优先级调度程序的运行结果3
图1.6优先级调度程序的运行结果4
改进后的代码(优先级+FIFO+RR)
图1.7优先级调度程序的运行结果
图1.8FIFO调度程序的运行结果
图1.9RR调度程序的运行结果
十一、实验思考
1、 总结调用fork()函数后的三种返回情况。
当fork函数返回值为 小于0,表示没有成功创建子进程。原来的进程仍在执行
当fork函数返回值为 0,表示子进程创建成功,且当前进程为 子进程
当fork函数返回值为 大于0,表示返回值为父进程的返回
2、 总结fork()和wait()配合使用的清况,并尝试在父进程中取消wait()函数,观察进程的运行情况。
进程一旦调用了wait(),就立即阻塞自己,由wait()自动分析是否当前进程的某个子
进程已经退出,如果让它找到了这样一个已经变成僵尸态的子进程,wait ()就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait ()就会一直阻塞在这里,直到有一个出现为止。wait()要与fork()配套出现,如果在使用fork()之前调用wait(),wait()的返回值则为-1,正常情况下wait()的返回值为子进程的PID
3、 验证、总结exec 函数族的具体使用方法。
六个函数的用法和相应参数
int execl(const char *path, const char *arg, …)
int execv(const char *path, char *const argv[])
int execle(const char *path, const char *arg, …, char *const envp[])
int execve(const char *path, char *const argv[], char *const envp[])
int execlp(const char *file, const char *arg, …)
int execvp(const char *file, char *const argv[])
-
在你实现的进程调度程序中,调度模式是抢占式还是非抢占式?
-
若调度算法中,每运行一次就将优先数减2, 同时将运行时间加1,其他条件不变,则在程序中如何修改?
将每一次rtime运行时间由+1变为+2,将每一次super优先度r-1变为-2,增加判断进程是否会在时间片结束前完成,若会,则直接进入下一个时间片。
- 如何将调度算法改为固定优先数调度算法?
在动态优先数上进行改变即可,只需要排一次序,不需要后面优先级改变后的排序,在运行时不改变进程的优先级
- 如何基于上述调度算法来实现先来先服务和时间片轮转调度算法?
具体实现如上代码所示,
在main主函数中编写switch case语句,让用户选择使用何种算法,先来先服务算法与时间片轮转调度算法相比优先度调度算法,在pcb连接时不需要重新排序,按进入顺序即可,在查看进程时,先来先服务与优先度调度算法大致相同,可由当前进程开始顺序向后查找,时间片轮转调度算法则需考虑其他所有进程的状态。先来先服务调度算法的实现为从前到后依次执行完所有进程,时间片轮转调度算法的实现则为每个进程运行一个时间片的时间,直到完成该进程。