暨南大学本科实验报告专用纸
一、实验目的
通过进程的创建、撤销和运行加深对进程概念和进程并发执行的理解,明
确进程与程序之间的区别。
二、实验环境及设备
(一)实验室名称:计算机实验室
(二)主要仪器设备:PC机、Linux操作系统环境
三、实验内容
(1)编写一段程序,使用系统调用fork()来创建两个子进程,并由父进
程重复显示字符某字符串和自己的标识数,而子进程则重复显示某字符串
和自己的标识数。
(2)编写一段程序,使用系统调用fork()来创建一个子进程。
子进程通过系统调用exec()更换自己的执行代码,显示新的代码后,调用exit()结束。
而父进程则调用waitpid()等待子进程结束,并在子进程结束后显示子进程的标识符,然后正常结束。
四、实验调试分析
1、实验函数说明
(1)fork()创建新进程
要创建一个进程,最基本的系统调用是fork。
系统调用fork用于派生一个进程,
头文件:#include <>
函数定义:int fork( void );
返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1
函数说明:一个现有进程可以调用fork函数创建一个新进程。
由fork创建的新进程被称为子进程(child process)。
fork函数被调用一次但返回两次。
两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。
注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间,它们之间共享的存储空间只有代码段。
(2)exec函数族
头文件:#include <>
函数族:
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, const char *envp[]);
int execv(const char *path, const char *argv[]);
int execve(const char *path, const char *argv[], const char *envp[]; int execvp(const char *file, const char *argv[]);
参数说明:
execl的第一个参数是包括路径的可执行文件,后面是列表参数,列表的第一个为命令path,接着为参数列表,最后必须以NULL结束。
execlp的第一个参数可以使用相对路径或者绝对路径。
execle最后包括指向一个自定义环境变量列表的指针,此列表必须以NULL 结束。
execv,v表示path后面接收的是一个向量,即指向一个参数列表的指针,注意这个列表的最后一项必须为NULL。
execve,path后面接收一个参数列表向量,并可以指定一个环境变量列表向量
execvp,第一个参数可以使用相对路径或者绝对路径,v表示后面接收一个参数列表向量。
exec被调用时会替换调用它的进程的代码段和数据段(但是文件描述符不变),直接返回到调用它的进程的父进程,如果出错,返回-1并设置errno。
(3)exit()终止进程函数
头文件:#include <>
exit()函数的作用是:直接使进程停止运行,清除其使用的内存空间,并清除其在内核中的各种数据结构。
(4)waitpid()父进程等待子进程中等或结束后才执行
头文件:#include<sys/> 或 #include<sys/>
定义函数: pid_t waitpid(pid_t pid,int * status,int options);函数说明:waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
如果在调用 waitpid()时子进程已经结束,则 waitpid()会立即返回子进程结束状态值。
子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一起返回。
如果不在意结束状态值,则参数status可以设成NULL。
参数pid为欲等待的子进程识别码, 其他数值意义如下:
pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。
pid=-1 等待任何子进程,相当于 wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为 pid 的子进程。
参数options提供了一些额外的选项来控制waitpid,参数 option 可
以为 0 或可以用"|"运算符把它们连接起来使用。
2、实验调试
(1)实验一调试
刚开始在写两个fork()程序时,是将两个fork()函数写在一起,如果是这样的话,第二次调用fork()语句时就可能不满足实验的要求了,因为父子进程的执行顺序是任意的,在第二次调用fork()函数时,可能是第一次调用产生的子进程在调用这个fork()函数,就会返回0值,不符合实验要求创建两个子进程的条件,因此应在确定当前进程时父进程时才调用fork()语句创建第二个子进程。
(2)实验二调试
创建子进程后,调用execl()将参数中指定的新进程代替原有的进程,若能够成功替换该进程就不会执行该函数后面的exit(0),转向执行新进程,待所有的子进程执行完毕后,就会转向执行waitpid()函数,再返回子进程的识别码,并输出;若没有替换成功就会执行execl()后面的语句exit(0),终止当前的子进程,再转向执行waitpid()语句。
备注:实验调试过程中未能将调试结果截图是本次实验的一大败笔。
下次会注意的。
五、实验结果
六、实验源程序
/*实验二程序*/
#include<>
#include<>
#include<>
int main()
{
pid_t pid1,pid2;
/*先创建一个子进程*/
pid1=fork();
if(pid1<0){
/*pid1没创建子进程成功*/
printf("fail to fork.\n");
exit(1);
}else if(pid1 == 0){
printf("child,pid is : %u\n",getpid()); }else
{
printf("parent,pid is :%u\n",getpid());
/*若是父进程,则再新创建其子进程*/
pid2=fork();
if(pid2<0){
printf("fail to fork.\n");
exit(1);
}else if(pid2 == 0){
printf("child,pid is : %u\n",getpid());
}else
{
printf("parent,pid is :%u\n",getpid());
}
}
return 0;
}
/*实验二程序*/
#include<>
#include<sys/>
#include<>
#include<>
int main(){
pid_t pid;
int result;
/*创建子进程*/
pid = fork();
if(pid <0)
printf("fork error");
else if (pid == 0)
{
/*若是子进程*/
printf("new program.\n");
/*替换当前的进程*/
execl("/root/","",0);
/*若没有替换成功,终止该子进程*/ exit(0);
}
else{
/*若子进程全部执行结束,*/
/*则返回子进程的进程标识码*/
int e=waitpid(pid,&result,0);
printf("Child process PID:%d.\n",e);
exit(0);
}
}
七、实验心得
通过做这次实验,了解了子进程和父进程的调用顺序,以及调用的结果,而且通过调用fork()可以知道存在三种情况下,子进程发生调用的变化。