实验四:《操作系统》之管道通信
Part4. 管道通信
往期回顾:
Part0. 实验环境
Part1-1.熟悉UKylin环境
Part1-2.熟悉UKylin环境
Part2.进程控制
Part3.进程通信
一、实验目的
1.了解管道的概念。
2.掌握Linux支持的管道通信方式。
二、实验内容
- 编写一段程序,实现进程的管道通信。使用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:四个特殊情况:
- 如果所有指向管道写端的文件描述符都关闭了,而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样
- 如果有指向管道写端的文件描述符没关闭,而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
- 如果所有指向管道读端的文件描述符都关闭了,这时有进程指向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。
- 如果有指向管道读端的文件描述符没关闭,而持有管道写端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再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)了解windows系统环境下的进程通讯机制. (2)熟悉Windows系统提供的进程通信API. 二. 实验准备 相关API函数介绍 ...
- linux管道通信题目,操作系统实训(Linux)——习题解答、例题解析、实验指导-王红-实验实验7软中断及管道通信课案.ppt...
操作系统实训(Linux)--习题解答.例题解析.实验指导-王红-实验实验7软中断及管道通信课案.ppt 实验7 软中断及管道通信 一.实验目的(1)掌握linux系统软中断通信的实现方法.(2)掌握 ...
- Linux系统无名管道通信实验,Linux进程间通信(二)---管道通信之无名管道及其基础实验...
管道简介 管道是Linux中进程间通信的一种方式,它把一个程序的输出直接连接到另一个程序的输入(其实我更愿意将管道比喻为农村浇地的管子).Linux的管道主要包括两种:无名管道和有名管道.这一节主要讲 ...
- 实验四:进程同步与通信
一.实验目的: 1.掌握基本的同步与互斥算法,理解P,V操作. 2.理解生产者消费者模型,了解其它典型的同步互斥模型,如哲学家就餐.读者-写者模型等. 3.学习使用Windows中基本的同步对象,掌握 ...
- Linux操作系统实验系列之实验四管道通信
一.实验目的 1.了解什么是管道 2.熟悉UNIX/LINUX支持的管道通信方式 二.实验内容: 编写程序实现进程的管道通信.用系统调用pipe( )建立一管道,二个子进程P1和P2分别向管道各写一句 ...
- 操作系统真象还原实验记录之实验三十四:实现管道
操作系统真象还原实验记录之实验三十四:实现管道 1.管道相关知识总结 先说我们操作系统的管道实现: 上述图中,管道缓冲区就是一页内存,这一页内存被我们当成了环形缓冲区结构, 当这页管道被创建出来后,全 ...
- 【操作系统实验】Linux进程通信—共享内存通信、管道通信
Linux进程通信-共享内存通信.管道通信 一.实验目的: 二.实验题目: 1. 试设计程序利用共享内存完成如下进程通信 1.shmget函数 2.shmat函数 3.shmdt函数 4.shmctl ...
- 2020-10-29 实验四 进程同步与通信
实验四 进程同步与通信 一.实验目的: 二.实验环境: 三.实验内容: 四.心得体会: 一.实验目的: 1. 掌握基本的同步与互斥算法,理解P,V操作. 2. 理解生产者消费者模型,了解其它典型的同步 ...
- 广州大学2020操作系统实验四:文件系统
相关资料 广州大学2020操作系统实验一:进程管理与进程通信 广州大学2020操作系统实验二:银行家算法 广州大学2020操作系统实验三:内存管理 广州大学2020操作系统实验四:文件系统 广州大学2 ...
最新文章
- python绘制直方图根据不同分类_如何在python中绘制具有多个类别的直方图
- Kindeditor中上传本地照片后需要带域名的绝对路径实际获取为相对路径
- locate 和 find
- 用python计算准确率_Python中计算模型精度的几种方法,Pytorch,中求,准确率
- appium for java教程_appium自动化测试入门(java版)
- PHP中cURL错误号对照[转]
- centos7 安装lsb_Docker安装与配置
- 随想录(内核模块的测试方法)
- java 静态方法 非静态变量_深度分析:Java 静态方法/变量,非静态方法/变量的区别,今天一并帮你解决!...
- java电脑控制对方手机_电脑控制大师手机专家多控系统-Total Control电脑控制手机助手下载V7.0.0官方电脑端32位/64位最新版-西西软件下载...
- hprose php,hprose和swoole区别
- python实现爬取网易云音乐评论,并且将评论信息存储到pymysql
- 聊聊技术人的“绩效考核”
- java 字符串像素_如何在JavaFX中计算字符串的像素宽度?
- Linux /usr/src/kernels 目录为空的解决方法
- 华为服务器安装系统流程图,华为a安装手册.docx
- 【毕设】基于HTML的零食商城网站大前端开发设计(含文档)
- twig 之基本语法
- EndNote中英文混排
- Hadoop安全之Kerberos