lab5 Linux进程及并发程序设计 2.docx
《lab5 Linux进程及并发程序设计 2.docx》由会员分享,可在线阅读,更多相关《lab5 Linux进程及并发程序设计 2.docx(19页珍藏版)》请在冰点文库上搜索。
lab5Linux进程及并发程序设计2
华南师范大学实验报告
学生姓名学号
专业多媒体与网络技术年级、班级12级多媒体2班
课程名称操作系统原理
实验项目实验5Linux进程及并发程序设计
实验类型验证设计综合实验时间2014年4月22日
实验指导老师实验评分
实验5Linux进程及并发程序设计
一.实验目的:
掌握Linux环境下的进程并发程序及管道应用程序的编写要点。
二.实验内容:
1编写一个并发程序,父进程打印“TheParentisrunning”,子进程打印“TheChildisrunning”。
2编写一个并发程序,父进程打印“TheParentisrunning”,子进程打印“TheChildisrunning”,并保证子进程输出在前,父进程输出在后。
3编写一个管道应用程序,父进程通过管道提供字符串“putthestringintothepipe.”给子进程,子进程通过管道接收这条信息,然后打印输出。
4调试并运行3.10的并发程序设计实例,显示结果是什么,并分析之。
5提高题:
编写一个管道应用程序,使父进程接受用户从键盘输入的数据信息并通过管道传送给子进程,子进程打印输出通过管道接收到的数据信息。
3.实验过程步骤与结果:
(一)编写一个并发程序
1.根据要求编写程序:
#include
main()
{
intp;//存放子进程pid号
while((p=fork())==-1);//创建子进程直到成功为止
if(p==0)//返回值=0表示子进程返回
{
printf("TheChildisrunning\n");
}
else//返回值>0表示父进程返回
{
printf("TheParentisrunning\n");
}
}
2.存放在Linux中的bingfa.c文档中:
3.使用gcc命令对bingfa.c进行编译:
4.多次使用./a.out执行该命令,查看结果:
输出:
TheParentisrunning
TheChildisrunning
或者
TheChildisrunning
TheParentisrunning
5.运行结果分析:
通过实验结果可看出,这是一个并发程序,子进程和父进程在不同的情况下先后出现的顺序可能不同,具有随机性。
(2)编写一个并发程序(子先父后)
1.根据要求编写程序:
#include
#include
main()
{
intp;//存放子进程pid号
while((p=fork())==-1);//创建子进程直到成功为止
if(p==0)//返回值=0表示子进程返回
{
printf("TheChildisrunning\n");
exit(0);
//在子进程结束时调用exit(0),使子进程自我终止,并发终止信号给其父进程;
}
else//返回值>0表示父进程返回
{
wait(0);
//父进程等待子进程终止,再执行下方程序
printf("TheParentisrunning\n");
}
}
2.存放在Linux中的Childfirst.c文档中:
3.使用gcc命令对Childfirst.c进行编译:
4.多次使用./a.out执行该命令,查看结果:
输出:
TheChildisrunning
TheParentisrunning
5.运行结果分析:
从运行结果可看出此程序实现了父等子的功能,始终是子进程先执行。
从代码上看,是用wait(0)和exit(0)实现的,在子进程(p=0)运行后使用exit(0)终止进程,在父进程(p>0)内容程序执行前加上wait(0),使父进程接收到子进程结束的信号后才执行内容程序,从而实现了后执行父进程的效果。
(3)编写一个管道应用程序(父提供给子字符串)
1.根据要求编写程序:
#include
#include
#include//printf函数的头文件
#include//exit函数的头文件
#defineLINESIZE1024
intmain(void)
{
intn,fd[2];
pid_tpid;
charline[LINESIZE];
if(pipe(fd)<0)//调用pipe()函数建立管道;
{
printf("pipeerror");//建立不成功则返回“pipeerror",并退出;
exit
(1);
}
if((pid=fork())<0)//创建进程,创建不成功则返回"forkerror"并退出
{
printf("forkerror");
exit
(1);
}
elseif(pid>0)//父进程的fork()返回
{
close(fd[0]);//关闭管道读指针;
printf("I'mfather~\n");//声明它是父进程
write(fd[1],"putthestringintothepipe.\n",29);
//将"putthestringintothepipe.\n"写入管道
close(fd[1]);//关闭管道写指针;
}
else//父进程的fork()返回
{
close(fd[1]);//关闭管道写指针;
printf("I'mson~\n");//声明它是子进程
n=read(fd[0],line,LINESIZE);
//读取输入的信息,存放到字符串line中
printf("string:
%s\n",line);//输出”string:
“和输入的信息
close(fd[0]);//关闭管道读指针;
}
exit(0);//退出程序;
}
2.存放在Linux中的father_to_son.c文档中:
3.使用gcc命令对father_to_son.c进行编译:
4.使用./a.out执行该命令,查看结果:
输出:
I'mfather~
I'mson~
string:
putthestringintothepipe.
5.运行结果及程序思路分析:
通过pipe()函数建立管道;用close(fd[0])关闭父进程管道读指针,利用write(fd[1],"字符串内容",字符串长度);写入字符串,最后关闭管道写指针;用close(fd[1])关闭子进程管道写指针,利用read(fd[0],"字符串内容",字符串长度);读取管道中字符串,最后关闭管道读指针;实现父进程向子进程传递字符串的功能;
(四)调试运行3.10并发程序并进行分析
1.程序源代码如下:
【father1.c】:
【child1.c】:
【pipeline.c】:
2.执行程序:
输入:
cc-ochild1child1.c
cc-ofather1father1.c
ccpipeline.c
./a.out
输出:
_Parentisusingpipewrite.
child,child.
3.代码与运行结果分析:
【father1.c】:
#include
#include
main()
{
staticcharstring[]="Parentisusingpipewrite.";
intlen;
len=sizeof(string);
write(1,string,len);
//将"Parentisusingpipewrite."写入字符串len中
printf("parent,parent,parent\n\n\n");
//输出显示是parent进程在执行
exit(0);
}
【child1.c】:
#include
main()
{
charoutput[30];
read(0,output,30);//读output中的内容
printf("_%s\nchild,child.\n",output);
//输出output中的内容加上child,child声明是子进程
return(0);
}
【pipeline.c】:
#include
#include
#include
#defineSTD_INPUT0//定义标准输入设备描述符
#defineSTD_OUTPUT1//定义标准输出设备描述符
intfd[2];
main()
{
staticcharprocess1[]="father1",process2[]="child1";
pipe(fd);//定义管道,返回文件描述符fd[0],fd[1]
pipeline(process1,process2);//调用自定义函数pipeline();
exit
(1);//程序结束
}
pipeline(process1,process2)
char*process1,*process2;
{
inti;
while((i=fork())==-1);
//创建进程,创建失败则反复创建,直到创建成功
if(i)//父进程的fork()返回
{
close(fd[0]);//关闭管道读端
close(STD_OUTPUT);
//关闭标准输出描述符1(原来是写到显示器的指针)
dup(fd[1]);
//指定标准输出描述符1为管道写指针,语句后调用printf函数,那么内容并不会输出到显示器,而是写入了管道中。
close(fd[1]);//关闭原始管道写指针
execl(process1,process1,0);//用程序father1覆盖当前程序
printf("——fatherfailed.\n");//execl()执行失败时才执行这一行
}
else
{
close(fd[1]);//关闭管道写端
close(STD_INPUT);//关闭标准输出描述符0
dup(fd[0]);//指定标准输入描述符0为管道读指针,语句后调用scanf函数,那么不会从键盘读取内容,而是从管道中读取。
close(fd[0]);//关闭原始管道读指针
execl(process2,process2,0);//用程序child1覆盖当前程序
printf("——childfailed.\n");//execl()执行失败时才执行这一行
}
exit
(2);//程序结束
}
4.运行结果分析:
输出结果为:
_Parentisusingpipewrite.child,child.
Pipeline.c中调用了dup(fd[1]);指定标准输出描述符1为管道写指针,语句后调用printf函数,那么内容并不会输出到显示器,而是写入了管道中。
而从结果可看出,父进程通过管道传给了子进程“Parentisusingpipewrite.”(从而才会前面有_,后面有child,child),同时pipeline.c中使用execl(process1,process1,0);用父进程覆盖原进程,如果没有发挥dup(fd[1])的作用(将printf中的内容“parent,parent,parent”写入管道中),也应在界面中打印出来。
所以我进行了以下几个测试:
(1)测试printf是否因write而没有输入到管道中(注释掉write行)
输出了:
_parent,parent,parent(3行)child,child
出现了“_”和“child,child”,说明dup(fd[1])的作用实现了,prinf的内容的确输入到管道中。
说明是write导致的。
(2)测试是否由于write和printf的顺序导致无法写入(调换位置)
仍然无法输出,说明不是顺序问题;
(3)测试是否发送接收字符串的问题(是否结束符\0的问题)
修改发送字符串长度:
后面出现的pa说明的确是字符串长度的问题;
接下来修改接收字符串长度:
成功实现prinf内容输入管道并从子进程同时读取出用write和prinf写入的内容。
分析字符串长度为何会导致这种问题:
通过上网了解到,用sizeof计算变量的空间大小时,它会把数组末尾的\0符号也计算进去,在你往管道内输入后,读取时遇见'\0'符号就会认为字符串结束,就不再读取prinf中的内容,而prinf中的内容,不管与write命令的顺序如何,都只会把内容输入到管道中write的部分。
从而,整个程序利用execl();实现father1和child1在同一程序中实现,father1通过管道通信传给了child1字符串;同时利用close(fd[])的方式实现进程的互斥。
使用dup(fd[])实现将外部输入输出设备的信息置于管道中,进行读取。
(五)编写一个管道应用程序(父接收键盘输入传给子进程)
1.根据要求编写程序:
#include
#include
#include
#include
#include
#include
#defineLINESIZE1024
intmain(void)
{
intn,fd[2];
pid_tpid;
charline[LINESIZE];
fgets(line,sizeof(line),stdin);//获得键盘输入,存放在字符串line中
if(pipe(fd)<0)//调用pipe()函数建立管道;
{
printf("pipeerror");//建立不成功则返回“pipeerror",并退出;
exit
(1);
}
if((pid=fork())<0)//创建进程,创建不成功则返回"forkerror"并退出
{
printf("forkerror");
exit
(1);
}
elseif(pid>0)
{
close(fd[0]);//关闭管道读指针
printf("I'mfather~\n");//声明它是父进程
write(fd[1],line,sizeof(line));//将line中的内容(即键盘输入)写入管道中
close(fd[1]);//关闭管道写指针
}
else//子进程的fork()返回
{
close(fd[1]);//关闭管道写指针;
printf("I'mson~\n");//声明它是子进程
n=read(fd[0],line,LINESIZE);
//读取输入的信息,存放到字符串line中
printf("string:
%s\n",line);//输出”string:
“和输入的信息
close(fd[0]);//关闭管道读指针
}
exit(0);//退出程序;
}
2.存放在Linux中的key_father.c文档中:
3.使用gcc命令对key_father.c进行编译:
4.使用./a.out执行该命令,查看结果:
输入:
I’mYidan!
!
!
yeyeye
输出:
I'mfather~
I'mson~
string:
I’mYidan!
!
!
Yeyeye
输入:
yes!
输出:
I'mfather~
I'mson~
string:
yes!
5.运行结果分析:
通过fgets(字符数组名,字符数字长度,stdin)获取键盘输入;通过pipe()函数建立管道;用close(fd[0])关闭父进程管道读指针,利用write(fd[1],要写入的字符串,字符串长度);写入字符串,最后关闭管道写指针;用close(fd[1])关闭子进程管道写指针,利用read(fd[0],要写入的字符串字符串长度);读取管道中字符串,最后关闭管道读指针;实现父进程向子进程传递字符串的功能;
与实验三直接在代码中加入字符串的区别在于用fgets(字符数组名,字符数字长度,stdin)获取键盘输入,并将其存放在该字符数组中,在进行传递字符时,直接使用字符数组名进行传递。
4.实验总结与反思
通过这次实验了解到wait();exit();fget(;;stdin);pipe(fd[2]);close(fd[0]);dup(files);等句子在进程间通信的使用,知道通信间的一些逻辑关系,学会如何改变父子进程执行顺序的先后、如何实现父子进程间信息的传递,受益匪浅。
关于每个程序的分析已附在每个程序代码注释和运行结果分析。
同时,实验过程多与别人讨论,多输入不同的数据,稍微改动程序进行尝试,都会使自己从更多的角度来看待问题,解决问题,