实验二Linux进程、线程及编程实验一、实验目的1、通过编写一个完整的守护进程,掌握守护进程编写和调试的方法2、进一步熟悉如何编写多进程程序二、实验环境硬件:PC机一台,JXARM9-2410教学实验平台。
软件:Windows98/XP/2000系统,虚拟机环境下的Linux系统。
三、预备知识1、fork()fork()函数用于从已存在的进程中创建一个新进程。
新进程称为子进程,而原进程称为父进程。
使用fork()函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程上下文、代码段、进程堆栈、存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制和控制终端等,而子进程所独有的只有它的进程号、资源使用和计时器等2、exit()和_exit()的区别_exit()函数的作用最为简单:直接使进程停止运行,清除其使用的存空间,并销毁其在核中的各种数据结构;exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。
exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的容写回文件,就是图中的"清理I/O缓冲"一项。
3、wait()和waitpid()wait()函数是用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或者该进程接到了一个指定的信号为止。
如果该父进程没有子进程或者他的子进程已经结束,则wait()就会立即返回。
四、实验容在该实验中,读者首先创建一个子进程1(守护进程),然后在该子进程中新建一个子进程2,该子进程2暂停10s,然后自动退出,并由子进程1收集子线程退出的消息。
在这里,子进程1和子进程2的消息都在系统日志文件(例如“/var/log/messages”,日志文件的全路径名因版本的不同可能会有所不同)中输出。
在向日志文件写入消息之后,守护进程(子进程1)循环暂停,其间隔时间为10s。
五、实验步骤1.分析下面的代码,注意它的运行会产生几个进程以及掌握fork()调用的具体过程。
/* multi_proc_wrong.c */#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>#include <sys/wait.h>int main(void){pid_t child1, child2, child;/*创建两个子进程*/child1 = fork();child2 = fork();/*子进程1的出错处理*/if (child1 == -1){printf("Child1 fork error\n");exit(1);}else if (child1 == 0) /*在子进程1中调用execlp()函数*/{printf("In child1: execute 'ls -l'\n");if (execlp("ls", "ls", "-l", NULL) < 0){printf("Child1 execlp error\n");}}if (child2 == -1) /*子进程2的出错处理*/{printf("Child2 fork error\n");exit(1);}else if( child2 == 0 ) /*在子进程2中使其暂停5s*/{printf("In child2: sleep for 5 seconds and then exit\n");sleep(5);exit(0);}else /*在父进程中等待两个子进程的退出*/{printf("In father process:\n");child = waitpid(child1, NULL, 0); /* 阻塞式等待 */if (child == child1){printf("Get child1 exit code\n");}else{printf("Error occured!\n");}do{child = waitpid(child2, NULL, WNOHANG);/* 非阻塞式等待 */if (child == 0){printf("The child2 process has not exited!\n");sleep(1);}} while (child == 0);if (child == child2){printf("Get child2 exit code\n");}else{printf("Error occured!\n");}}return 0;}此段代码会是我们所希望的结果,如果child1 = fork();child2 = fork();都成功则会有4个进程,child1 = fork();后会才生一个新的进程:主进程、child1,child2 = fork()后主进程和child1有分别产生一个新的进程。
2.分析下面代码,体会与上一个代码之间的区别,/* multi_proc.c */#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>#include <sys/wait.h>int main(void){pid_t child1, child2, child;/*创建两个子进程*/child1 = fork();/*子进程1的出错处理*/if (child1 == -1){printf("Child1 fork error\n");exit(1);}else if (child1 == 0) /*在子进程1中调用execlp()函数*/{printf("In child1: execute 'ls -l'\n");if (execlp("ls", "ls", "-l", NULL) < 0){printf("Child1 execlp error\n");}}else /*在父进程中再创建进程2,然后等待两个子进程的退出*/{child2 = fork();if (child2 == -1) /*子进程2的出错处理*/{printf("Child2 fork error\n");exit(1);}else if(child2 == 0) /*在子进程2中使其暂停5s*/{printf("In child2: sleep for 5 seconds and then exit\n");sleep(5);exit(0);}printf("In father process:\n");child = waitpid(child1, NULL, 0); /* 阻塞式等待 */if (child == child1){printf("Get child1 exit code\n");}else{printf("Error occured!\n");}do{child = waitpid(child2, NULL, WNOHANG);/* 非阻塞式等待 */if (child == 0){printf("The child2 process has not exited!\n");sleep(1);}} while (child == 0);if (child == child2){printf("Get child2 exit code\n");}else{printf("Error occured!\n");}}return 0;}3.在宿主机上编译调试该程序gcc multi_proc.c –o multi_proc4. 在确保没有编译错误后,使用交叉编译该程序arm-linux-gcc multi_proc.c –o multi_proc5.将生成的可执行程序下载到目标板上运行。
6.分析实验结果主要是要理解FORK()函数的意义:在语句child1=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的代码部分完全相同。
两个进程中,原先就存在的那个被称作“父进程”,新出现的那个被称作“子进程”。
父子进程的区别除了进程标志符(process ID)不同外,变量child1的值也不相同,child1存放的是fork的返回值。
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:在父进程中,fork返回新创建子进程的进程ID;在子进程中,fork返回0;如果出现错误,fork返回一个负值;所以掌握了fork的含义后就不难分析结果了。
第二段代码将child2放在了父进程中创建,这样child1就不会在创建child2,此时child1、child2都是主进程的子进程,共有3个进程存在。
六、实验总结通过实验过程中编写一个完整的守护进程,掌握守护进程编写和调试的方法,进一步熟悉如何编写多进程程序,对Linux的进程和线程有了更多的了解。