1、实验一、进程控制实验 1.1 实验目的 加深对于进程并发执行概念的理解。实践并发进程的创建和控制方法。观察和体验进程的动态特性。进一步理解进程生命期期间创建、变换、撤销状态变换的过程。掌握进程控制的方法,了解父子进程间的控制和协作关系。练习 Linux 系统中进程创建与控制有关的系统调用的编程和调试技术。 1.2 实验说明 1)与进程创建、执行有关的系统调用说明 进程可以通过系统调用fork()创建子进程并和其子进程并发执行.子进程初始的执行映像是父进程的一个复本.子进程可以通过 exec()系统调用族装入一个新的执行程序。父进程可以使用 wait()或 waitp
2、id()系统调用等待子进程的结束并负责收集和清理子进程的退出状态。
fork()系统调用语法:
#include
3、径名字符串.
argv[] 要传递给新执行程序的完整的命令参数列表(可以为空).
envp[] 要传递给新执行程序的完整的环境变量参数列表(可以为空).
Exec 执行成功后将用一个新的程序代替原进程,但进程号不变,它绝不会再返回到调用进程了。如果 exec 调用失败,它会返回-1。
wait() 系统调用语法:
#include
4、d,int *status,int option); status 用于保留子进程的退出状态 pid 可以为以下可能值: -1 等待所有 PGID 等于 PID 的绝对值的子进程 1 等待所有子进程 0 等待所有 PGID 等于调用进程的子进程 >0 等待 PID 等于 pid 的子进程 option 规定了调用 waitpid 进程的行为: WNOHANG 没有子进程时立即返回 WUNTRACED 没有
5、报告状态的进程时返回
wait 和 waitpid 执行成功将返回终止的子进程的进程号,不成功返回-1。
getpid()系统调用语法:
#include
6、件引发的,如:键盘中断、定时器中断、非法内存引用等。信号的名字都以 SIG 开头,例如 SIGTERM、SIGHUP。可以使用 kill -l 命令查看系统当前的信号集合。 信号可在任何时间发生,接收信号的进程可以对接收到的信号采取3种处理措施之一: • 忽略这个信号 • 执行系统默认的处理 • 捕捉这个信号做自定义的处理信号从产生到被处理所经过的过程: 产生 (generate)-> 挂起 (pending)-> 派送 (deliver)-> 部署 (disposition) 或忽略 (igore) 一个信号集合是一个 C 语言的 sigset_t 数据类型的对象
7、sigset_t 数据类型定义在
8、可以响应的信号到来后继续执行。
每个进程都能使用 signal 函数定义自己的信号处理函数,捕捉并自行处理接收的除 SIGSTOP 和 SIGKILL 之外的信号。以下是有关的系统调用的语法说明。
kill 系统调用语法:
#include
9、
pause 系统调用语法:
#include
10、返回 0。
signal 系统调用语法为:
#include
11、新的子进程并执行一个新的命令和其并发执行. 1) 打开一终端命令行窗体,新建一个文件夹,在该文件夹中建立以下名为pctl.c 的C语言程序: /* * Filename : pctl.c * copyright : (C) 2006 by zhanghonglie * Function : 父子进程的并发执行 */ #include "pctl.h" int main(int argc, char *argv[]) { int i; int pid; //存放子进程号
12、 int status; //存放子进程返回状态 char *args[] = {"/bin/ls","-a",NULL}; //子进程要缺省执行的命令 signal(SIGINT,(sighandler_t)sigcat); //注册一个本进程处理键盘中断的函数 pid=fork() ; //建立子进程 if(pid<0) // 建立子进程失败? { printf("Create Process fail!\n"); exit(EXIT_FAILURE); } if(pid == 0) // 子进程执行代码段
13、 { //报告父子进程进程号 printf("I am Child process %d\nMy father is %d\n",getpid(),getppid()); pause(); //暂停,等待键盘中断信号唤醒 //子进程被键盘中断信号唤醒继续执行 printf("%d child will Running: \n",getpid()); // if(argv[1] != NULL){ //如果在命令行上输入了子进程要执行的命令 //则执行输入的命令
14、 for(i=1; argv[i] != NULL; i++) printf("%s ",argv[i]); printf("\n"); //装入并执行新的程序 status = execve(argv[1],&argv[1],NULL); } else{ //如果在命令行上没输入子进程要执行的命令 //则执行缺省的命令 for(i=0; args[i] != NULL; i++) printf("%s ",args[
15、i]); printf("\n"); //装入并执行新的程序 status = execve(args[0],args,NULL); } } else //父进程执行代码段 { printf("\nI am Parent process %d\n",getpid()); //报告父进程进程号 if(argv[1] != NULL){ //如果在命令行上输入了子进程要执行的命令 //则父进程等待子进程执行结束 printf("%
16、d Waiting for child done.\n\n",pid); waitpid(pid,&status,0); //等待子进程结束 printf("\nMy child exit! status = %d\n\n",status); } else{ //如果在命令行上没输入子进程要执行的命令 //唤醒子进程,与子进程并发执行不等待子进程执行结束, if(kill(pid,SIGINT) >= 0) printf("%d Wakeup %d chil
17、d.\n",getpid(),pid) ; printf("%d don't Wait for child done.\n\n",getpid());
} }
return EXIT_SUCCESS;
}
2) 再建立以下名为 pctl.h 的 C 语言头文件:
#include
18、进程自定义的键盘中断信号处理函数 typedef void (*sighandler_t) (int); void sigcat(){ printf("%d Process continue\n",getpid()); } 3) 建立以下项目管理文件 Makefile head = pctl.h srcs = pctl.c objs = pctl.o opts = -g -c all: pctl pctl: $(objs) gcc $(objs) -o pctl pctl.o: $(srcs) $(head)
19、 gcc $(opts) $(srcs) clean: rm pctl *.o 4) 输入 make 命令编译连接生成可执行的 pctl 程序 $ gmake gcc -g -c pctl.c gcc pctl.o -o pctl 5) 执行 pctl 程序(注意进程号是动态产生的,每次执行都不相同) $ ./pctl I am Child process 4113 My father is 4112 I am Parent process 4112 Wakeup 4113 child. 4112 don't
20、 Wait for child done. 4113 Process continue 4113 child will Running: /bin/ls -a . .. Makefile pctl pctl.c pctl.h pctl.o $ 以上程序的输出说明父进程 4112 创建了一个子进程 4113,子进程执行被暂停。父进程向子进程发出键盘中断信号唤醒子进程并与子进程并发执行。父进程并没有等待子进程的结束继续执行先行结束了(此时的子进程成为了孤儿进程,不会有父进程为它清理退出状态了)。而子进程继续执行,它变成了列出当前目录所有文件名的命令 ls
21、 -a。在完成了列出文件名命令之后,子进程的执行也结束了。此时子进程的退出状态将有初始化进程为它清理。 6) 再次执行带有子进程指定执行命令的 pctl 程序: $ ./pctl /bin/ls -l I am Child process 4223 My father is 4222 I am Parent process 4222 4222 Waiting for child done. 可以看到这一次子进程仍然被挂起,而父进程则在等待子进程的完成。为了检测父子进程是否都在并发执行,请输入 ctrl+z 将当前进程放入后台并输入 ps 命令查看当前
22、系统进程信息,显示如下: [1]+ Stopped ./pctl /bin/ls -l $ ps -l F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 0 S 0 4085 4083 0 76 0 - 1413 wait pts/1 00:00:00 bash 0 T 0 4222 4085 0 76 0 - 360 finish pts/1 00:00:00 pctl 1 T 0 4223
23、 4222 0 76 0 - 360 finish pts/1 00:00:00 pctl 0 R 0 4231 4085 0 78 0 - 1302 - pts/1 00:00:00 ps 可以看到当前系统中同时有两个叫 pctl 的进程,它们的进程号分别是 4222 和 4223。它们的状态都为―T‖,说明当前都被挂起。4223 的父进程是 4222,而 4222 的父进程是 4085,也就是 bash-shell。为了让 pctl 父子进程继续执行,请输入 fg 命令让 pctl 再次返回前台,显示如下:
24、 $ fg ./pctl /bin/ls -l 现在pctl父子进程从新返回前台。我们可以通过键盘发键盘中断信号来唤醒 pctl 父子进程继续执行,输入 ctrl+c,将会显示: 4222 Process continue 4223 Process continue 4223 child will Running: /bin/ls -l total 1708 -rw-r--r-- 1 root root 176 May 8 11:11 Makefile -rwxr-xr-x 1 root root 8095 May 8 14:08 pctl
25、 -rw-r--r-- 1 root root 2171 May 8 14:08 pctl.c -rw-r--r-- 1 root root 269 May 8 11:10 pctl.h -rw-r--r-- 1 root root 4156 May 8 14:08 pctl.o My child exit! status = 0 以上输出说明了子进程在捕捉到键盘中断信号后继续执行了指定的命令,按我们要求的长格式列出了当前目录中的文件名,父进程在接收到子进程执行结束的信号后将清理子进程的退出状态并继续执行,它报告了子进程 的退出编码(0 表示子进程正常结束)最后父进程也结束执行。 1.4 实验结果 参考以上示例程序中建立并发进程的方法,编写一个多进程并发执行程序。父进程首先创建一个执行ls命令的子进程然后再创建一个执行ps命令的子进程,并控制 ps 命令总在 ls 命令之前执行。 1.5. 实验小结 第一次实验的操作是比较简单的,通过老师的讲解,很快就能掌握要点,但是在执行过程中,很容易打错字符或漏掉标点符号,导致实验结果不正确,通过不断查找与更改,最后得出正确结论。此次实验很需要耐心和毅力,能不断加强我们的动手能力后调试能力。






