进程管理实验

一.实验目的及实验环境

实验目的:
通过观察、分析实验现象,深入理解进程及进程在调度执行和内存空间等方面的特点,掌握在POSIX 规范中fork和kill系统调用的功能和使用。
实验环境:

  1. 硬件
    (1) 主机:win7;
    (2) 内存:8G ;
    (3) 硬盘空间:500G。
  2. 软件
    Linux 操作系统,ubuntu16,安装在虚拟机下,预装有X-Window 、vi、gcc、gdb 和火狐浏览器。

二. 实验内容

通读下列代码:

/* 
* POSIX 下进程控制的实验程序残缺版 
*/ 
#include <stdio.h> 
#include <sys/types.h>
#include <unistd.h> 
#include <signal.h>
#include <ctype.h> 
/* 允许建立的子进程个数最大值 */
#define MAX_CHILD_NUMBER 10 
/* 子进程睡眠时间 */
#define SLEEP_INTERVAL 2 
int proc_number=0; /* 子进程的自编号,从0开始 */
void do_something(); 
main(int argc, char* argv[])
{ 
int child_proc_number = MAX_CHILD_NUMBER; /* 子进程个数 */
int i, ch; 
pid_t child_pid; 
pid_t pid[10]={0}; /* 存放每个子进程的id */ 
if (argc > 1)
{ 
/* 命令行参数中的第一个参数表示建立几个子进程,最多10个 */ 
child_proc_number = atoi(argv[1]); 
child_proc_number
= (child_proc_number > 10) ? 10 : child_proc_number;
} 
for (i=0; i<child_proc_number; i++)
{ 
/* 在这里填写代码,建立child_proc_number个子进程
* 子进程要执行
* proc_number = i; 
* do_something();
* 父进程把子进程的id保存到pid[i] */
} 

/* 让用户选择杀死哪个进程。输入数字(自编号)表示杀死该进程
* 输入q退出 */
while ((ch = getchar()) != 'q')
{ 
if (isdigit(ch)) 
{ 
/* 在这里填写代码,向pid[ch-'0']发信号SIGTERM, 
* 杀死该子进程 */ 

 }
} 

/* 在这里填写代码,杀死本组的所有进程 */ 
return;
} 
void do_something()
{ 
for(;;)
{ 
/* 打印子进程自编号。为清晰,在每个号码前加“号码+3”个空格
* 比如号码是1,就打印" 1" */
printf("This is process No.%*d\n",
proc_number+3, 
proc_number); 
sleep(2); /* 主动阻塞两秒钟 */
} 
} 

先猜想一下这个程序的运行结果。假如运行“./process 20”,输出会是什么样?
然后按照注释里的要求把代码补充完整,运行程序。可以多运行一会儿,并在
此期间启动、关闭一些其它进程,看process 的输出结果有什么特点,记录下这个结果。
开另一个终端窗口,运行“ps aux|grep process”命令,看看process 究竟启动了多少个进程。回到程序执行窗口,按“数字键+回车”尝试杀掉一两个进程,再到另一个窗口看进程状况。
按q 退出程序再看进程情况。

三.实验问题

  1. 你最初认为运行结果会怎么样?
    从0到9顺序打印,按下数字回车后杀死相对应进程,输入q回车后,则退出程序。
  2. 实际的结果什么样?有什么特点?试对产生该现象的原因进行分析。
    实验结果与预期结不全相同,随机输出0~9号进程,循环输出。输入数字键回车后,杀死该数字所对应的进程,输入q回车后,杀死所有进程,退出程序。
  3. proc_number 这个全局变量在各个子进程里的值相同吗?为什么?
    相同,子进程的资源独立不影响。
  4. kill 命令在程序中使用了几次?每次的作用是什么?执行后的现象是什么?
    kill命令在程序中使用了2次。第一次是杀死输入的线程,输入进程号回车后,执行后接下来的结果中不会有该进程号;第二次是杀死本组所有进程。即主进程以及它创建的所有子进程。
  5. 使用kill 命令可以在进程的外部杀死进程。进程怎样能主动退出?这两种退出方式哪种更好一些?
    return或exit()函数都可以正常退出。而使用kill命令则是异常退出。
    正常退出比较好,若在子进程退出前使用kill命令杀死其父进程,则系统会托管子进程。当用kill命令使得子进程先于父进程退出时,而父进程又没有调用wait函数等待子进程结束,子进程处于僵尸状态,并且会一直保持下去,直到系统重启。子进程处于僵尸状态时,内核只保存该进程的必要信息以被父进程所需,此时子进程始终占着资源。使用return或exit()函数正常退出更好一些。

四.测试数据及运行结果

1. 正常测试数据(3组)及运行结果;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2. 非正常测试数据及运行结果。
在这里插入图片描述

五.总结

1. 实验过程中遇到的问题及解决办法;
遇到的问题是对进程的表示不清晰,导致if判断中的要执行的语句写错,将进程的代码理解透彻之后就发现错误了,之后就解决了。刚开始,对fork创建进程的原理不是很清楚,以至于在父子进程之间的关系不能很好的搞清楚,在知道子进程创建之后和父进程拥有一套代码,并且了解dosomething的作用之后,就对调试有了很大的帮助。
2. 对设计及调试过程的心得体会。
这次进程实验,让我清晰的看到了父进程创建子进程,手动杀死子进程,对于进程的概念也不再觉得特别抽象,也对进程的理解更加深刻,并且对系统的创建进程的方式方法更加了解,也对杀死进程的方法更加了解,也知道,而子进程和父进程是两套代码,是一样的代码,但由于dosomething函数将子进程一直运行,所以,那个while的是父进程调用的,而非子进程调用的。还有,若是已经通过输入字符杀死进程,而最后又通过q来杀死全部进程,那么有的进程就杀了两次,这一点需要优化。并且清楚,return方法或者调用exit方法是正常退出,在执行程序或者其他进程时,要注意使用正常退出的方式,减少使用异常退出。

六.附录:源代码

#include <stdio.h> 
#include <sys/types.h>
#include <unistd.h> 
#include <signal.h>
#include <ctype.h> 
/* 允许建立的子进程个数最大值 */
#define MAX_CHILD_NUMBER 10 
/* 子进程睡眠时间 */
#define SLEEP_INTERVAL 2 
int proc_number=0; /* 子进程的自编号,从0开始 */
void do_something(); 
int main(int argc, char* argv[])
{ 
int child_proc_number = MAX_CHILD_NUMBER; /* 子进程个数 */
int i, ch; 
pid_t child_pid; 
pid_t pid[10]={0}; /* 存放每个子进程的id */ 
if (argc > 1)
{ 
/* 命令行参数中的第一个参数表示建立几个子进程,最多10个 */ 
child_proc_number = atoi(argv[1]); 
child_proc_number
= (child_proc_number > 10) ? 10 : child_proc_number;
} 


for (i=0; i<child_proc_number; i++)
{ 
/* 在这里填写代码,建立child_proc_number个子进程
* 子进程要执行
* proc_number = i; 
* do_something();
* 父进程把子进程的id保存到pid[i] */
child_pid=fork();

if(child_pid==0){
	proc_number = i;
	do_something();
    }
else if(child_pid>0){
	pid[i] = child_pid;
    }
else printf("error\n");
} 

/* 让用户选择杀死哪个进程。输入数字(自编号)表示杀死该进程
* 输入q退出 */
while ((ch = getchar()) != 'q')
{ 
if (isdigit(ch)) 
{ 
/* 在这里填写代码,向pid[ch-'0']发信号SIGTERM, 
* 杀死该子进程 */ 
if(ch-'0'<child_proc_number)
{
i=kill(pid[ch-'0'],SIGTERM);
if(i==0)
printf("kill success\n");
else if(i==-1)
printf("kill error\n");
}
else 
  printf("the main process don`t have this child\n");
} 

}

/* 在这里填写代码,杀死本组的所有进程 */
kill(0,SIGTERM); 
return 0;
} 
void do_something()
{ 
for(;;)
{ 
/* 打印子进程自编号。为清晰,在每个号码前加“号码*/
printf("This is process No.%*d\n,pid=%d\n",
proc_number+3, 
proc_number,getpid()); 
sleep(2); /* 主动阻塞两秒钟 */
} 
}
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
通过优先级调度算法的模拟,加深进程概念和进程调度过程的理解。设计原理及方案:1)在Linux下用C语言编程模拟优先级程调度算法。为了清楚地观察每个进程的调度过程,程序将每个时间片内的进程情况显示出来。2)进程控制块是进程存在的唯一标志,因此,在模拟算法中每一个进程用一个进程控制块PCB来代表,PCB用一结构体表示。3)进程在运行过程中其状态将在就绪、执行、完成几种状态之间转换,同时进程可能处于不同的队列中,如就绪队列。在优先级调度算法中,选择单向队列,入队既是将进程控制块插入队尾,出队既是按优先级重新排列的队,删除队头元素。4)为了便于处理,程序中的某进程运行时间以时间片为单位计算。各进程的优先级认为输入,运行所需时间随机产生。5)优先权调度算法采用动态优先权,进程每运行一个时间片,优先数减1;进程在就绪队列等待一个时间单位,优先数加1。6)对于遇到优先权一致的情况,采用FCFS策略解决。7)由于是模拟进程调度,所以,对被选中的进程并不实际启动运行,而是修改进程控制块的相关信息来模拟进程的一次运行。 分别用两种调度算法对伍个进程进行调度。每个进程可有三种状态;执行状态(R)、就绪状态(W,包括等待状态)和完成状态(F,并假定初始状态为就绪状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值