1、华 南 师 范 大 学 实 验 报 告学生姓名学号专业多媒体与网络技术年级、班级12 级多媒体 2 班课程名称操作系统原理实验项目实验 5 Linux 进程及并发程序 设计 实验类型验证设计综合实验时间2014年4 月 22 日实验指导老师实验评分实验 5 Linux 进程及并发程序设计 一实验目的:掌握 Linux 环境下的进 程并发程序及管道应 用程序的编写要点。二实验内容:1 编写一个并发程序,父进程打印“The Parent is running”,子进程打印“The Child is running”。2 编写一个并发程序,父进程打印“The Parent is running”,子
2、进程打印“The Child is running”,并保证子进程输出在前,父 进程输 出在后。3 编写一个管道应用程序,父进程通过管道提供字符串“put the string into the pipe.”给子进程,子进程通过管道接收这条信息,然后打印输出。4 调试并运行 3.10 的并发程序设计实例,显示结果是什么,并分析之。5 提高题:编写一个管道应用程序,使父进程接受用户从键盘输入的数据信息并通过管道传送给子进程,子进程打印输出通过管道接收到的数据信息。3(实验过程步骤与结果:(一)编写一个并发程序1.根据要求编写程序:#includemain( ) int p; /存放子进程 pid
3、 号while( ( p=fork( ) ) =-1); /创建子进程直到成功为止if (p = 0) /返回值=0 表示子进程返回 printf(“The Child is runningn“);else /返回值0 表示父进程返回 printf(“The Parent is runningn“);2.存放在 Linux 中的 bingfa.c 文档中:3.使用 gcc 命令对 bingfa.c 进行编译:4.多次使用./a.out 执行该命令,查看结果:输 出:The Parent is runningThe Child is running或者The Child is runningTh
4、e Parent is running5.运行结果分析:通过实验结果可看出,这是一个并发程序,子进程和父进程在不同的情况下先后出现的顺序可能不同,具有随机性。(2(编写一个并发程序(子先父后)1.根据要求编写程序:#include#include main( ) int p; /存放子进程 pid 号while( ( p=fork( ) ) =-1); /创建子进程直到成功为止if (p = 0) /返回值=0 表示子进程返回 printf(“The Child is runningn“);exit(0); / 在子进程结束时调用 exit(0),使子 进程自我终止,并发终止信号给其父进程;e
5、lse /返回值0 表示父进程返回 wait(0); /父进程等待子进程终止,再执行下方程序printf(“The Parent is runningn“); 2.存放在 Linux 中的 Childfirst.c 文档中:3.使用 gcc 命令对 Childfirst.c 进行编译:4.多次使用./a.out 执行该命令,查看结果:输 出:The Child is runningThe Parent is running5.运行结果分析:从运行结果可看出此程序实现了父等子的功能,始终是子进程先执行。从代码上看,是用 wait(0)和 exit(0)实现的,在子 进程(p=0)运行后使用 ex
6、it(0)终止进程,在父进程(p0)内容程序执行前加上 wait(0),使父进程接收到子进程结束的信号后才执行内容程序,从而实现了后执行父进程的效果。(3(编写一个管道 应用程序(父提供 给子字符串)1.根据要求编写程序:#include #include #include /printf 函数的头文件#include /exit 函数的头文件#define LINESIZE 1024int main(void)int n, fd2;pid_t pid;char lineLINESIZE;if(pipe(fd)0) /父进程的 fork()返回close(fd0); /关闭管道读指针;prin
7、tf(“Im fathern“); /声明它是父进程write(fd1, “put the string into the pipe.n“,29);/将“put the string into the pipe.n“写入管道close(fd1); /关闭管道写指针;else /父进程的 fork()返回 close(fd1); /关闭管道写指针;printf(“Im sonn“); /声明它是子进程n=read(fd0,line, LINESIZE);/读取输入的信息,存放到字符串 line 中printf(“string: %sn“, line); /输出”string:“和输入的信息clo
8、se(fd0); /关闭管道读指针;exit(0); /退出程序;2.存放在 Linux 中的 father_to_son.c 文档中:3.使用 gcc 命令对 father_to_son.c 进行编译:4.使用./a.out 执行该命令,查看结果:输 出:Im fatherIm sonstring:put the string into the pipe.5.运行结果及程序思路分析:通过 pipe()函数建立管道;用 close(fd0)关闭父进程管道读指针,利用write(fd1, “字符串内容“,字符串长度);写入字符串,最后关闭管道写指针;用 close(fd1)关闭子进程管道写指针,
9、利用 read(fd0, “字符串内容“, 字符串长度);读取管道中字符串,最后关闭管道读指针;实现父进程向子进程传递字符串的功能;(四)调试运行 3.10 并发程序并 进行分析1.程序源代码如下:【father1.c】:【child1.c】:【pipeline.c】:2.执行程序:输 入: cc -o child1 child1.c cc -o father1 father1.ccc pipeline.c./a.out 输 出:_Parent is using pipe write.child,child.3.代码与运行结果分析:【father1.c】:#include#includemai
10、n()static char string=“Parent is using pipe write.“;int len;len=sizeof(string);write(1,string,len); /将“Parent is using pipe write.“写入字符串 len 中printf(“parent,parent, parentnnn“);/输出显示是 parent 进程在执行exit(0);【child1.c】:#includemain()char output30;read(0,output,30); /读 output 中的内容printf(“_%sn child,child
11、.n“,output);/输出 output 中的内容加上 child,child 声明是子进程return(0);【pipeline.c】:#include#include#include#define STD_INPUT 0 /定义标准输入设备描述符#define STD_OUTPUT 1 /定义标准输出设备描述符int fd2;main()static char process1=“father1“,process2=“child1“;pipe(fd); /定义管道,返回文件描述符 fd0,fd1pipeline(process1,process2); /调用自定义函数 pipeline
12、();exit(1); /程序结束pipeline(process1,process2)char *process1,*process2;int i;while(i=fork()=-1); /创建进程,创建失败则反复创建,直到 创建成功if(i) /父进程的 fork()返回close(fd0); /关闭管道读端close(STD_OUTPUT); /关闭标准输出描述符 1(原来是写到显示器的指针)dup(fd1); /指定标准输出描述符 1 为管道写指针,语句后调用 printf 函数,那么内容并不会输出到显示器,而是写入了管道中。close(fd1); /关闭原始管道写指针execl(pr
13、ocess1,process1,0); /用程序 father1 覆盖当前程序printf(“father failed.n“);/execl()执行失败时才执行这一行elseclose(fd1); /关闭管道写端close(STD_INPUT); /关闭标准输出描述符 0dup(fd0); /指定标准输入描述符 0 为管道读指针, 语句后调用 scanf 函数,那么不会从键盘读取内容,而是从管道中读取。close(fd0); /关闭原始管道读指针execl(process2,process2,0); /用程序 child1 覆盖当前程序printf(“child failed.n“);/ex
14、ecl()执行失败时才执行这一行exit(2); /程序结束4.运行结果分析:输出结果为:_Parent is using pipe write.child,child.Pipeline.c 中调用了 dup(fd1); 指定标准输出描述符 1 为管道写指针,语句后调用 printf 函数,那么内容并不会输出到显示器,而是写入了管道中。而从结果可看出,父进程通过管道传给了子进程“Parent is using pipe write.”(从而才会前面有_,后面有 child,child),同时 pipeline.c 中使用execl(process1,process1,0); 用父进程覆盖原进程
15、,如果没有发挥dup(fd1)的作用(将 printf 中的内容“ parent,parent,parent”写入管道中),也应在界面中打印出来。所以我进行了以下几个测试:(1(测试 printf 是否因 write 而没有输入到管道中(注释掉 write 行)输出了:_parent,parent,parent(3 行) child,child出现了“_”和“child,child ”,说明 dup(fd1)的作用实现了,prinf 的内容的确输入到管道中。说明是 write 导致的。(2(测试是否由于 write 和 printf 的顺序导致无法写入(调换位置)仍然无法输出,说明不是顺序问题
16、;(3(测试是否发送接收字符串的问题(是否结束符0 的问题)修改发送字符串长度:后面出现的 pa 说明的确是字符串 长度的问题;接下来修改接收字符串长度:成功实现 prinf 内容输入管道并从子进程同时读取出用 write 和prinf 写入的内容。分析字符串长度为何会导致这种问题:通过上网了解到,用 sizeof 计算变量的空间大小时,它会把数组末尾的0 符号也计算进去,在你往管道内输入后,读取时遇见0符号就会认为字符串结束,就不再读取 prinf 中的内容,而 prinf 中的内容,不管与 write 命令的顺序如何,都只会把内容 输入到管道中 write 的部分。从而,整个程序利用 ex
17、ecl();实现 father1 和 child1 在同一程序中实现,father1 通过管道通信传给了 child1 字符串;同时利用 close(fd)的方式实现进程的互斥。使用 dup(fd)实现将外部 输入输出设备的信息置于管道中,进行读取。(五)编写一个管道应用程序(父接收键盘输 入传给子进程)1.根据要求编写程序:#include #include #include #include #include #include#define LINESIZE 1024int main(void)int n, fd2;pid_t pid;char lineLINESIZE;fgets(lin
18、e, sizeof(line), stdin); /获得键盘输 入,存放在字符串 line 中if(pipe(fd)0) close(fd0); /关闭管道读指针printf(“Im fathern“); /声明它是父进程write(fd1, line,sizeof(line); /将 line 中的内容(即键盘输入)写入管道中close(fd1); /关闭管道写指针else /子进程的 fork()返回 close(fd1); /关闭管道写指针;printf(“Im sonn“); /声明它是子进程n=read(fd0,line, LINESIZE);/读取输入的信息,存放到字符串 line
19、 中printf(“string: %sn“, line); /输出”string:“和输入的信息close(fd0); /关闭管道读指针exit(0); /退出程序;2.存放在 Linux 中的 key_father.c 文档中:3.使用 gcc 命令对 key_father.c 进行编译:4.使用./a.out 执行该命令,查看结果:输 入: Im Yidan!yeyeye输 出:Im fatherIm sonstring:Im Yidan!Yeyeye输 入: yes!输 出:Im fatherIm sonstring:yes!5.运行结果分析:通过 fgets(字符数组名, 字符数字长
20、度, stdin)获取 键盘输入; 通过 pipe()函数建立管道;用 close(fd0)关闭父进程管道读指针,利用 write(fd1, 要写入的字符串,字符串长度);写入字符串,最后关闭管道写指针;用close(fd1)关闭子进程管道写指针,利用 read(fd0, 要写入的字符串字符串长度);读取管道中字符串,最后关闭管道读指针;实现父进程向子进程传递字符串的功能;与实验三直接在代码中加入字符串的区别在于用 fgets(字符数组名, 字符数字长度, stdin)获取键盘输入,并将其存放在该字符数组中,在进行传递字符时,直接使用字符数组名进行传递。4(实验总结与反思通过这次实验了解到 wait();exit();fget(;stdin);pipe(fd2);close(fd0);dup(files);等句子在进程间通信的使用,知道通信间的一些逻辑关系,学会如何改变父子进程执行顺序的先后、如何实现父子进程间信息的传递,受益匪浅。关于每个程序的分析已附在每个程序代码注释和运行结果分析。同时,实验过程多与别人讨论,多输入不同的数据,稍微改动程序进行尝试,都会使自己从更多的角度来看待问题,解决问题,