文档库 最新最全的文档下载
当前位置:文档库 › 东北大学操作系统实验报告4-2017

东北大学操作系统实验报告4-2017

东北大学操作系统实验报告4-2017
东北大学操作系统实验报告4-2017

实验四进程的管道通信

一、实验目的

1、加深对进程概念的理解,明确进程和程序的区别。

2、学习进程创建的过程,进一步认识进程并发执行的实质。

3、分析进程争用资源的现象,学习解决进程互斥的方法。

4、学习解决进程同步的方法。

5、掌握Linux系统中进程间通过管道通信的具体实现

二、实验内容

使用系统调用pipe()建立一条管道,系统调用fork()分别创建两个子进程,它们分别向管道写一句话,如:

Child process1 is sending a message!

Child process2 is sending a message!

父进程分别从管道读出来自两个子进程的信息,显示在屏幕上注:实际要求最好创建两个以上子进程,但不需要太多

三、实验要求

1、这是一个设计型实验,要求自行、独立编制程序。

2、两个子进程要并发执行。

3、实现管道的互斥使用。当一个子进程正在对管道进行写操作时,另一个欲写入管道的子进程必须等待。使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对管道的锁定。

4、实现父子进程的同步,当父进程试图从一空管道中读取数据

时,便进入等待状态,直到子进程将数据写入管道返回后,才将其唤醒。

四、程序流程图

父进程

子进程

五、程序代码及注释

#include

#include//写管程用到的那些函数用到的头文件

#include

#include//wait用到这个

int main()

{

int fd[2];

pipe(fd); //父进程创建管道

char outpipe[50],inpipe[50]; //存放字符串

int pid1,pid2,pid3; //存放返回值

while((pid1 = fork()) == -1); //创建不成功就跳不出来了

if(pid1 == 0) //子进程1

{

sleep(1); //sleep函数可以决定子进程的顺序,等的时间长的话进入就晚,输出也就晚

lockf(fd[1], 1, 0); //对管道写入端口加锁

sprintf(outpipe,"\n child process 1 is sending message !\n");

write(fd[1], outpipe, 50); //把字符串内容写入管道写入口

lockf(fd[1], 0, 0); //对管道写入口解锁

exit(0);

}

else//父进程

{

while((pid2 = fork()) == -1);

if(pid2 == 0) //子进程2

{

sleep(2);

lockf(fd[1], 1, 0);

sprintf(outpipe,"\n child process 2 is sending message !\n");

write(fd[1], outpipe, 50);

lockf(fd[1], 0, 0);

exit(0);

}

else//父进程

{

while((pid3 = fork()) == -1);

if(pid3 == 0) //子进程3

{

sleep(3);

lockf(fd[1], 1, 0);

sprintf(outpipe,"\n child process 3 is sending message !\n");

write(fd[1], outpipe, 50);

lockf(fd[1], 0, 0);

exit(0);

}

else//父进程

{

wait(0); //阻塞,等待子进程

read(fd[0], inpipe, 50); //从管道读出口读出信息放到数组

printf("%s\n",inpipe);

wait(0);

read(fd[0], inpipe, 50);

printf("%s\n",inpipe);

wait(0);

read(fd[0], inpipe, 50);

printf("%s\n",inpipe); //三个子进程,要输出三次

exit(0);

}

}

}

}

六、运行结果及说明

因为加了锁,不会出现子进程同时往管道里写的情况,所以,子进程互斥地往管道里写,父进程从管道里读并输出,就显示成了如上图模样

注:一个之前不明白的流程

由fork创建的新进程被称为子进程(child process)。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程id。将子进程id返回给父进程的理由是:因为一个进程的子进程可以多于一个,没有一个函数使一个进程可以获得其所有子进程的进程id。对子进程来说,之所以fork返回0给它,是因为它随时可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。(进程id 0总是由交换进程使用,所以一个子进程的进程id不可能为0 )。

fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置(两进程的程序计数器pc值相同,也就是说,子进程是从fork返回处开始执行的),但有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork 的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。

可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。至于那一个最先运行,可能与操作系统(调度算法)有关,而且这个问题在实际应用中并不重要,如果需要父子进程协同,可以通过原语的办法解决

七、课后习题

1、指出父进程与两个子进程并发执行的顺序,并说明原因。

子进程先执行,然后父进程才执行。这是由进程的同步机制决定的,因为只有子进程向管道中写入信息后,父进程才能读取;否则父进程自己调用wait()系统调用将自己阻塞,将处理机交由子进程。至于各子进程哪个先执行,这是由操作系统决定的,不过我们也可以人为的用sleep函数设定。

2、若不对管道加以互斥控制,会有什么后果?

管道进行互斥控制,是为防止两个子进程对管道资源进行争夺而产生信息丢失或覆盖。如果不加控制,那么可能一个子进程写入的信息还没来得及被父进程读出,另一个子进程又先写入信息,那么之前的进程写入的信息将被覆盖,父进程也就读不到之前进程传递来的信息了。

3、说明你是如何实现父子进程之间的同步的。

1)父进程读出前确认管道中有数据,否则阻塞自己,用wait()实现;

2)子进程写入前确认管道中数据已被父进程读出,否则阻塞自己;

相关文档