Part4. 管道通信

往期回顾:
Part0. 实验环境
Part1-1.熟悉UKylin环境
Part1-2.熟悉UKylin环境
Part2.进程控制
Part3.进程通信

一、实验目的

1.了解管道的概念。
2.掌握Linux支持的管道通信方式。

二、实验内容

  1. 编写一段程序,实现进程的管道通信。使用pipe()建立一个管道。子进程p1向管道写一句话:
    Child process is sending message!
    而父进程则从管道中读取来自于子进程的信息,显示在屏幕上。
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<stdlib.h>
int pid1;   //存储子进程的PID号;/******************主函数*****************/
int main()
{int fd[2];                     //打开文件的文件描述符,fd[1]是写入端,fd[0]是读出端char OutPipe[100],InPipe[100];  //存储要写入管道的字符串pipe(fd);                      //创建管道---建立一无名管道while((pid1 = fork()) == -1);   //创建子进程if(pid1 == 0)                   //子进程{ sprintf(OutPipe,"Child process is sending message!");    //将字符串输出到OutPipe(目的字符串)中write(fd[1],OutPipe,50);    //写进程从管道的写入端(句柄1)将50个字节数据写入管道sleep(1);                   //延时1秒   exit(0);                    //子进程结束}else{wait(0);                    //等待子进程执行完毕read(fd[0],InPipe,50);      //读进程从管道的读出端(句柄0)读出50个字节数据printf("%s\n",InPipe);      //父进程将字符串显示在屏幕上exit(0);                    //或者 return 0;}return  0;
}

代码修改:
要求:修改程序实现10次读、写管道文件的操作,应如何实现,请编写代码,并运行程序分析结果。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<stdlib.h>
int pid1;   //存储子进程的PID号;
/**************************主函数***********************/
int main()
{int fd[2];                     //打开文件的文件描述符,fd[1]是写入端,fd[0]是读出端char OutPipe[100],InPipe[100];  //存储要写入管道的字符串int cnt = 10;             pipe(fd);                       //创建管道---建立一无名管道while((pid1 = fork()) == -1);   //创建子进程if(pid1 == 0)                   //子进程{ for(int i=0;i<cnt;i++){sprintf(OutPipe,"Child process is sending message!");    //将字符串输出到OutPipe(目的字符串)中write(fd[1],OutPipe,50);  //从管道的写入端(句柄1)将50个字节数据写入管道printf("#%d:Message sent from child process successfully!\n",i);sleep(2);                  //延时2秒}}else                               //父进程{for(int i=0;i<cnt;i++){read(fd[0],InPipe,50);       //从管道的读出端(句柄0)读出50个字节数据printf("#%d:%s\n",i,InPipe); //父进程将字符串显示在屏幕上}wait(0);exit(0);}return  0;
}

2.编写一段程序,实现进程的管道通信。使用pipe()建立一条管道线。两个子进程p1和p2分别向管道各写一句话:
Child 1 is sending message!
Child 2 is sending message!
而父进程则从管道中分别读出来自于两个子进程的信息,显示在屏幕上。

#include<unistd.h>
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>int pid1,pid2;    //存储两个子进程的PID号;
/*************************主函数**********************/
int main()
{int fd[2];                     //打开文件的文件描述符char OutPipe[100],InPipe[100]; //存储要写入管道的字符串pipe(fd);                      //创建管道---建立一无名管道while((pid1 = fork()) == -1);  //创建子进程if(pid1 == 0)                  //子进程{lockf(fd[1],1,0);          //lockf(fd,1,0)给文件上锁,使实现互斥访问sprintf(OutPipe,"Child 1 is sending message!");  //将字符串输出到OutPipe(目的字符串)中write(fd[1],OutPipe,50);   //从管道的写入端(句柄1)将50个字节数据写入管道lockf(fd[1],0,0);          //lockf(fd,0,0)是文件解锁sleep(1);                  //延时1秒 exit(0);                   //子进程结束}else {while((pid2 = fork()) == -1);  //创建子进程if(pid2 == 0){sleep(0);lockf(fd[1],1,0);          //lockf(fd,1,0)给文件上锁,使实现互斥访问sprintf(OutPipe,"Child 2 is sending message!");    //将字符串输出到OutPipe(目的字符串)中write(fd[1],OutPipe,50);  //从管道的写入端(句柄1)将50个字节数据写入管道lockf(fd[1],0,0);         //lockf(fd,0,0)是文件解锁sleep(1);exit(0);                  //子进程结束}else                          //父进程{wait(0);                  //等待子进程执行完毕wait(0);                  //等待子进程执行完毕read(fd[0],InPipe,50);    //从管道的读出端(句柄0)读出50个字节数据printf("%s\n",InPipe);    //父进程将字符串显示在屏幕上//wait(0);read(fd[0],InPipe,50);printf("%s\n",InPipe);exit(0);                  //或者 return 0;}}     return 0;
}


输出结果如上图。其中在代码中增加了两对lockf。当子进程在向管道中写数据时,加锁;写完数据后,解锁。这样可以防止多端输入发生冲突,保证写入数据的稳定性和准确性,实现资源的互斥与同步访问。其中lockf(fd,0,0)表示文件解锁;lockf(fd,1,0)表示给文件加锁。

其实,管道本身具有同步互斥的机制,即在进行管道读写操作时不会被打断,所以即使没有加锁也是可以正常执行的。当某一个子进程被调度,向管道中写入数据后,等一段时间,等待管道中数据被读,读完管道之后,才允许下一次写入。

附链接:https://blog.csdn.net/jnu_simba/article/details/11746217
https://blog.csdn.net/qq_36829091/article/details/80138836

Point1:pipe的特点:

  • 只能单向通信
  • 只能血缘关系的进程进行通信
  • 依赖于文件系统
  • 生命周期随进程
  • 面向字节流的服务
  • 管道内部提供了同步机制

Point2:四个特殊情况:

  1. 如果所有指向管道写端的文件描述符都关闭了,而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样
  2. 如果有指向管道写端的文件描述符没关闭,而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
  3. 如果所有指向管道读端的文件描述符都关闭了,这时有进程指向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。
  4. 如果有指向管道读端的文件描述符没关闭,而持有管道写端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再write会阻塞,直到管道中有空位置了才写入数据并返回。

3.编写一段程序,实现:在父进程中用pipe()建立一条管道,往管道里写入字符串,两个子进程分别接收来自父进程写入的两个字符串。

#include<unistd.h>
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>int pid1,pid2;    //存储两个子进程的PID号;
/*************************主函数**********************/
int main()
{int fd[2];                     //打开文件的文件描述符char OutPipe[100],InPipe[100]; //存储要写入管道的字符串pipe(fd);                      //创建管道---建立一无名管道while((pid1 = fork()) == -1);  //创建子进程if(pid1 == 0)                  //子进程{read(fd[0],InPipe,50);     //从管道的读出端(句柄0)读出50个字节数据printf("#pid1 received: %s\n",InPipe);          //子进程将字符串显示在屏幕上exit(0);}else {while((pid2 = fork()) == -1);if(pid2 == 0){//sleep(1);read(fd[0],InPipe,50);   //从管道的读出端(句柄0)读出50个字节数据printf("#pid2 received: %s\n",InPipe);      //子进程将字符串显示在屏幕上sleep(1);                 //延时1秒exit(0);}else                          //父进程{sprintf(OutPipe,"Parent is sending first message!");       //将字符串输出到OutPipe(目的字符串)中write(fd[1],OutPipe,50);                                   //从管道的写入端(句柄1)将50个字节数据写入管道sleep(1);wait(0);                  //等待子进程执行完毕            sprintf(OutPipe,"Parent is sending second message!");      //将字符串输出到OutPipe(目的字符串)中write(fd[1],OutPipe,50);//wait(0);                  //等待子进程执行完毕//wait(0);                  //等待子进程执行完毕exit(0);}}       return 0;
}


结果分析:
由父进程创建一个管道pipe,向管道中写入两个字符串,子进程读取管道中的字符串并显示到屏幕,多数情况下是pid2先打印输出(父进程写入管道的字符串),但从逻辑上分析,进程pid1和pid2谁先读管道内容具有随机性。如果希望强制pid1先读取,可以在pid2读取数据前加sleep函数,输出结果如截图所示。
其他几种输出显示:
1)去掉pid2子进程的sleep函数,输出如下图:

2)去掉父进程的一个wait函数或者“写—wait—写”模式下,输出如下图:

3)父进程“写—wait—写—wait”模式下,输出:

【实验相关资料】
一、什么是管道
UNIX系统在OS的发展上,最重要的贡献之一便是该系统首创了管道(pipe)。这也是UNIX系统的一大特色。
所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。

句柄fd[0]

句柄fd[1]
读出端

写入端

二、管道的类型:
1.有名管道
一个可以在文件系统中长期存在的、具有路径名的文件。用系统调用mknod( )建立。它克服无名管道使用上的局限性,可让更多的进程也能利用管道进行通信。因而其它进程可以知道它的存在,并能利用路径名来访问该文件。对有名管道的访问方式与访问其他文件一样,需先用open( )打开。
2.无名管道
一个临时文件。利用pipe( )建立起来的无名文件(无路径名)。只用该系统调用所返回的文件描述符来标识该文件,故只有调用pipe( )的进程及其子孙进程才能识别此文件描述符,才能利用该文件(管道)进行通信。当这些进程不再使用此管道时,核心收回其索引结点。二种管道的读写方式是相同的,本次实验使用无名管道。
3.pipe文件的建立
分配磁盘和内存索引结点、为读进程分配文件表项、为写进程分配文件表项、分配用户文件描述符
4.读/写进程互斥
内核为地址设置一个读指针和一个写指针,按先进先出顺序读、写。为使读、写进程互斥地访问pipe文件,需使各进程互斥地访问pipe文件索引结点中的直接地址项。因此,每次进程在访问pipe文件前,都需检查该索引文件是否已被上锁。若是,进程便睡眠等待,否则,将其上锁,进行读/写。操作结束后解锁,并唤醒因该索引结点上锁而睡眠的进程。

三、本次实验所涉及的系统调用
1.pipe( )创建无名管道
建立一无名管道。
系统调用格式
pipe(filedes)
参数定义
int pipe(filedes);
int filedes[2];
其中,filedes[1]是写入端,filedes[0]是读出端。
该函数使用头文件如下:

#include <unistd.h>
#include <signal.h>
#include <stdio.h>

2.lockf 文件锁
允许将文件区域用作信号量(监视锁),或用于控制对锁定进程的访问(强制模式记录锁定)。
系统调用格式
int lockf(int fd, int cmd, off_t len);
参数说明:
①fd 是打开文件的文件描述符。
为通过此函数调用建立锁定,文件描述符必须使用只写权限(O_WRONLY)或读写权限(O_RDWR)打开。如果调用进程是具有PRIV_LOCKRDONLY 权限的组的成员,它也可以使用lockf()来锁定使用只读权限(O_RDONLY)打开的文件。
②cmd 是指定要采取的操作的控制值,允许的值在中定义。
如下所示:

# define F_ULOCK 0 //解锁
# define F_LOCK 1 //互斥锁定区域
# define F_TLOCK 2 //测试互斥锁定区域
# define F_TEST 3 //测试区域

F_TEST 用于检测在指定的区域中是否存在其他进程的锁定。如果该区域可访问,lockf()将返回 0,否则返回−1;在这种情况下,errno 设置为[EACCES]。F_LOCK 和 F_TLOCK 都用于锁定文件的某个区域(如果该区域可用)。F_ULOCK 用于删除文件区域的锁定。
③len是要锁定或解锁的连续字节数。
要锁定的资源从文件中当前偏移量开始,对于正 len 将向前扩展,对于负 len 则向后扩展(直到但不包括当前偏移量的前面的字节数)。如果 len 为零,则锁定从当前偏移量到文件结尾的区域(即从当前偏移量到现有或任何将来的文件结束标志)。要锁定一个区域,不需要将该区域分配到文件中,因为这样的锁定可以在文件结束标志之后存在。
3.read( )
系统调用格式
read( fd ,buf ,nbyte )
功能:从fd(一般是fd[0])所指示的文件中读出nbyte个字节的数据,并将它们送至由指针buf所指示的缓冲区中。如该文件被加锁,等待,直到锁打开为止。
参数定义
int read(fd,buf,nbyte);
int fd;
char *buf;
unsigned nbyte;
4.write( )
系统调用格式
write(fd,buf,nbyte)
功能:把nbyte 个字节的数据,从buf所指向的缓冲区写到由fd(一般是fd[1])所指向的文件中。如文件加锁,暂停写入,直至开锁。
参数定义同read( )。

————
The End

那写看似毫无波澜的日复一日,会在某一天 让你突然发现努力的意义。 “小忙”加油!
无悔昨天 & 感谢今天 & 喜欢明天~

实验四:《操作系统》之管道通信相关推荐

  1. 操作系统实验四——使用命名管道实现进程通信

    操作系统实验四--使用命名管道实现进程通信 一. 实验目的 (1)了解windows系统环境下的进程通讯机制. (2)熟悉Windows系统提供的进程通信API. 二. 实验准备 相关API函数介绍 ...

  2. linux管道通信题目,操作系统实训(Linux)——习题解答、例题解析、实验指导-王红-实验实验7软中断及管道通信课案.ppt...

    操作系统实训(Linux)--习题解答.例题解析.实验指导-王红-实验实验7软中断及管道通信课案.ppt 实验7 软中断及管道通信 一.实验目的(1)掌握linux系统软中断通信的实现方法.(2)掌握 ...

  3. Linux系统无名管道通信实验,Linux进程间通信(二)---管道通信之无名管道及其基础实验...

    管道简介 管道是Linux中进程间通信的一种方式,它把一个程序的输出直接连接到另一个程序的输入(其实我更愿意将管道比喻为农村浇地的管子).Linux的管道主要包括两种:无名管道和有名管道.这一节主要讲 ...

  4. 实验四:进程同步与通信

    一.实验目的: 1.掌握基本的同步与互斥算法,理解P,V操作. 2.理解生产者消费者模型,了解其它典型的同步互斥模型,如哲学家就餐.读者-写者模型等. 3.学习使用Windows中基本的同步对象,掌握 ...

  5. Linux操作系统实验系列之实验四管道通信

    一.实验目的 1.了解什么是管道 2.熟悉UNIX/LINUX支持的管道通信方式 二.实验内容: 编写程序实现进程的管道通信.用系统调用pipe( )建立一管道,二个子进程P1和P2分别向管道各写一句 ...

  6. 操作系统真象还原实验记录之实验三十四:实现管道

    操作系统真象还原实验记录之实验三十四:实现管道 1.管道相关知识总结 先说我们操作系统的管道实现: 上述图中,管道缓冲区就是一页内存,这一页内存被我们当成了环形缓冲区结构, 当这页管道被创建出来后,全 ...

  7. 【操作系统实验】Linux进程通信—共享内存通信、管道通信

    Linux进程通信-共享内存通信.管道通信 一.实验目的: 二.实验题目: 1. 试设计程序利用共享内存完成如下进程通信 1.shmget函数 2.shmat函数 3.shmdt函数 4.shmctl ...

  8. 2020-10-29 实验四 进程同步与通信

    实验四 进程同步与通信 一.实验目的: 二.实验环境: 三.实验内容: 四.心得体会: 一.实验目的: 1. 掌握基本的同步与互斥算法,理解P,V操作. 2. 理解生产者消费者模型,了解其它典型的同步 ...

  9. 广州大学2020操作系统实验四:文件系统

    相关资料 广州大学2020操作系统实验一:进程管理与进程通信 广州大学2020操作系统实验二:银行家算法 广州大学2020操作系统实验三:内存管理 广州大学2020操作系统实验四:文件系统 广州大学2 ...

最新文章

  1. SAP MM 盘点流程中上不了台面却很实用的方案建议
  2. ibmmq 通道命令_IBM MQ 从接收通道获取数据
  3. pytorch 常用问题解决
  4. docker-compose up volumes 调用外部文件,权限问题 cannot open directory xxxxx .: Permission denied
  5. python array的应用
  6. 【推荐系统】深入理解推荐系统:无需人工特征工程的xDeepFM
  7. ubuntu12.4上安装minigui3.0.12
  8. 创建工程并测试RedisTemplate
  9. 1555C. Coin Rows
  10. vue本地没事放到服务器上无限循环,解决vue中的无限循环问题
  11. 使用自定义annotation接口进行aspectj动态缓存
  12. 谈谈你对oracle,对Oracle的优化
  13. 【Redis】Redis 事务
  14. 《C++ Primer Plus》14.2 私有继承 学习笔记
  15. 来,我们一起学Hibernate
  16. CentOS之安装docker
  17. DatabaseMetaData的用法(转)
  18. 谭浩强 c语言 swap,C语言谭浩强完整教案.ppt
  19. 乱谈那些个著名的科技互联网公司和产品名字
  20. 地铁综合监控系统网络方案,简化工作轻松应对客流高峰

热门文章

  1. 红孩儿编辑器的渲染子系统的渲染模块的函数依赖关系图
  2. python创建ppt_ppt自动化创建工具——python-pptx
  3. 95 费解的开关(递推)
  4. GeeM2引擎弹出装备显示框的设置方法
  5. Java源码——一个简单的洗牌(shuffling)程序 (Card shuffling and dealing with Collections method shuffle)
  6. vue-seamless-scroll大屏抽奖滚动动画实例
  7. C/C++ fabs 函数 - C语言零基础入门教程
  8. 数据分析项目实战——Airbnb数据分析
  9. Python 中 ‘unicodeescape’ codec can’t decode bytes in position X-X: trun错误
  10. 关于innerText和value的区别 2020-10-21