进程的创建与并发执行-带答案版
- 格式:doc
- 大小:361.00 KB
- 文档页数:14
实验二进程管理2.1 进程的创建与并发执行1.实验目的(1) 加深对进程概念的理解,理解进程和程序的区别。
(2) 认识并发进程的实质。
分析进程争用资源的现象,学习解决进程互斥的方法。
(3) 理解系统调用和用户命令的区别。
2.实验类型:验证型3.实验学时:24.实验原理和知识点(1) 实验原理:程序的并发执行具有随机性和不可再现性。
程序并发执行会导致资源共享和资源竞争,各程序向前执行的速度会受资源共享的制约。
程序的动态执行过程用进程这个概念来描述。
由于向前推进的速度不可预知,所以多个进程并发地重复执行,整体上得到的结果可能不同。
但要注意,就其中某单个进程而言,其多次运行结果是确定的。
(2) 知识点:进程、子进程、并发执行的特性;5.实验环境(硬件环境、软件环境):(1)硬件环境:Intel Pentium III 以上CPU,128MB以上存,2GB以上硬盘(2)软件环境:linux操作系统。
6. 预备知识(1) fork()系统调用头文件:#include <unistd.h> unix standard header/*是POSIX标准定义的unix类系统定义符号常量的头文件,包含了许多UNIX系统服务的函数原型,例如read函数、write函数和getpid函数*/函数原型:pid_t fork(void);/*是Linux下的进程号类型,也就是Process ID _ Type 的缩写。
其实是宏定义的unsigned int类型*/函数功能:fork的功能是创建子进程。
调用fork的进程称为父进程。
如图2.1所示。
子进程是父进程的一个拷贝,它继承了父进程的用户代码、组代码、环境变量、已打开的文件代码、工作目录及资源限制。
fork语句执行后,核向父进程返回子进程的进程号,向子进程返回0。
父子进程都从fork()的下一句开始并发执行。
返回值:返回值==-1:创建失败。
返回值==0:程序在子进程中。
返回值>0:程序在父进程中。
(该返回值是子进程的进程号)编程提示:虽然子进程是父进程的一个复制品,但父子的行为是不同的。
编程时要抓住核的返回值。
通过返回值,可以知道是父进程还是子进程,因而可以编写不同行为的代码。
图2.1 fork()创建进程示意图(2) wait()系统调用头文件:#include <sys/wait.h>函数原型:pid_t wait(int *status);函数功能:wait的功能是等待子进程结束。
发出wait调用的进程只要有子进程,就会睡眠直到子进程中的一个终止为止。
若没有子进程,则该调用立即返回。
函数参数:status是子进程退出时的状态信息。
返回值:成功则返回子进程号,否则返回-1。
(3) getpid()系统调用头文件:unistd.h,在VC++6.0下可以用process.h函数原型:pid_t getpid(void);函数功能:wait的功能是将父进程挂起,等待子进程终止。
getpid函数用来取得目前进程的进程ID,许多程序利用取到的此值来建立临时文件,以避免临时文件相同带来的问题。
返回值:目前进程的进程ID。
(4) 其他系统调用exit –终止进程exec –执行一个应用程序nice –改变进程的优先7. 实验容与步骤(将所有截图换成用户名为你们自己的拼音,并回答所有的问题)(1)运行图2.1所示程序将图2.1中的源程序保存为e201.c在终端中编译gcc –o e201 e201.c运行./e201此时,程序为死循环,在该终端中无法继续输入命令执行,于是,我们可以暂时不管这一个终端,而是再新建一个终端,然后键入ps –e,列出当前所有进程观察屏幕,是否有两个名为e201的进程。
比较它们的进程号,判别哪个是父进程,哪个是子进程。
程序中的while(1);语句是为了不让程序退出来,以便于你观察进程状态。
用kill命令把这两个进程终止。
可见,第一个终端如下图所示,已经从死循环中解救出来了。
当然,最简单粗暴的方法不是关闭相关进程,而是直接通过ctrl+c 强制结束。
对图2.1的程序稍加改进,可看见两个进程的进程号。
(2)编写一段名为e202.c的源程序,使用系统调用fork()创建两个子进程p1和p2。
p1的功能为显示字符'b',p2的功能为显示字符'c',父进程的功能为显示字符'a',父进程和两个子进程并发运行。
不停的运行e202,观察并记录屏幕上的显示结果。
程序设计过程:用while语句控制fork()直到创建成功。
用if语句判别是在子进程中还是在父进程中。
程序运行后会创建三个进程,它们分别是子进程p1、p2、父进程。
这三个进程并发运行。
假定子进程有些任务要做,完成这些任务要花一定时间,因此,可以用一个延时函数简单地模拟这些任务。
//e202.c#include <stdio.h>void delay(int x) /* 延时函数*/{int i,j;for(i=0;i<x;i++)for(j=0;j<x;j++) ;}int main(){int p1,p2;while((p1=fork())==-1); /* 创建子进程p1 */if(p1==0) /* 子进程p1创建成功*/{delay(4096); /* 子进程p1延时*/putchar('b'); /* 子进程p1显示字符'b' */}else{while((p2=fork())==-1); /* 创建子进程p2 */if(p2==0) /* 子进程p2创建成功*/{delay(2048); /* 子进程p2延时*/putchar('c'); /* 子进程p2显示字符'c' */}else{putchar('a'); /* 父进程显示字符'a' */}}return 0;}按向上的光标键、回车,运行刚才的程序。
快速重复这个步骤,观察并记录结果。
请回答问题:屏幕上是否有时显示bac,有时显示bca,…。
为什么会这样呢?(3) 参考(1)完成下列程序设计父子进程同步实验:编写一段程序, 使用系统调用fork()创建一个子进程,子进程求1+2+……+100的和并打印出来,使用系统调用wait()让父进程等待子进程结束。
请贴出源代码截图:请贴出正确运行截图:(4) 已知下列Linux程序执行后, 运行结果如下,请画出进程家族树(以进程号标示进程,注意,每次运行进程号都不一样)。
//Linux程序#include "stdio.h"#include "unistd.h"int main(){int p1,p2,p3;p1=fork();p2=fork();p3=fork();//注:getpid()获取当前进程pidif(p1>0 && p2>0 && p3>0) printf("A:%d\n",getpid());if(p1==0 && p2>0 && p3>0) printf("B:%d\n",getpid());if(p1==0 && p2==0 && p3>0) printf("C:%d\n",getpid());if(p1==0 && p2==0 && p3==0)printf("D:%d\n",getpid());if(p1==0 && p2>0 && p3==0) printf("E:%d\n",getpid());if(p1>0 && p2==0 && p3>0) printf("F:%d\n",getpid());if(p1>0 && p2==0 && p3==0) printf("G:%d\n",getpid());if(p1>0 && p2>0 && p3==0) printf("H:%d\n",getpid());sleep(10);return 0;}这些进程构成的进程树为(要画出进程树才有加分哦!):ps f 命令可以查看当前终端进程的进程数关系图8. 心得体会附录pstree的用法格式:pstree以树状图显示进程,只显示进程的名字,且相同进程合并显示。
格式:pstree -p以树状图显示进程,还显示进程PID。
格式:pstree <pid>格式:pstree -p <pid>以树状图显示进程PID为<pid>的进程以及子进程,如果有-p参数则同时显示每个进程的PID。
附录Linux系统调用列表以下是Linux系统调用的一个列表,包含了大部分常用系统调用和由系统调用派生出的的函数。
其中有一些函数的作用完全相同,只是参数不同。
(有点像C++函数重载,但是Linux核心是用C语言写的,所以只能取成不同的函数名)。
还有一些函数已经过时,被新的更好的函数所代替了(gcc在这些函数时会发出警告),但因为兼容的原因还保留着,这些函数在前面标上“*”号以示区别。
各系统调用的使用方法,可以通过man命令获得。
二、文件系统控制1、文件读写操作2、文件系统操作1、信号2、消息3、管道4、信号量5、共享存。