ImageVerifierCode 换一换
格式:DOCX , 页数:19 ,大小:32.75KB ,
资源ID:3676102      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/3676102.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Linux进程通信 之 管道.docx)为本站会员(b****3)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Linux进程通信 之 管道.docx

1、Linux进程通信 之 管道管道无名管道管道包括无名管道和有名管道两种,前者在父子进程中流行,后者由于可以独立成为磁盘文件而存在,因为能够被无血缘关系的进程共享.无名管道通常直接称为管道,它占用两个文件描述符,不能被非血缘关系的进程共享,一般应用于父子进程.UNIX中一切皆为文件,管道也是一种文件,称为管道文件.当系统中创建一个管道时,它返回两个文件描述符:一个文件以只写打开,作为管道的输入端;另一个文件以只读打开,作为管道的输出端.#include int pipe(int fildes2);函数pipe在内核中创建一个管道,并分配两个文件描述符标识管道的两端,这两个文件描述符存储与fild

2、es0和fildes1中.一般约定fildes0描述管道和输出端,进程向此文件描述符中读取数据,fildes1描述管道的输入端,进程向此文件描述符写入数据.fildes0:只读文件描述符.fildes1:只写文件描述符.Pipe调用成功返回0,否则返回-1.单向管道流模型管道的两端(输入端和输出端)被一个进程控制没有太大的意义,如果管道的两端分别控制在不同的进程中,这两个进程之间就能够进行通信.拥有管道输入端的进程,可以向管道发送数据,拥有管道输出端的进程,可以从管道中接受前一个进程发送来的消息.1) 从父进程流向子进程的管道在父进程创建无名管道并产生子进程后,父子进程均拥有管道两端的访问权.

3、此时关闭父进程的管道输出端,关闭子进程的管道输入端,就形成一个从父进程到子进程的管道流,数据由父进程写入,从子进程读出.2) 从子进程流向父进程的管道在父进程中创建无名管道并产生子进程后,父子进程均拥有两端的访问权.此时关闭父进程的管道输入端,关闭子进程的管道输出端,就形成了一个从子进程到父进程的管道流.数据由子进程写入,从父进程读出.#include #include #include #include int main() pid_t pid; int fildes2; char buf256; int i,j; if( pipe(fildes) 0 ) printf(pipe error

4、n); return -1; if ( (pid = fork() 0) printf(fork error!n); return -1; if(0 = pid) /子进程 close(fildes1); j = read(fildes0,buf,sizeof(buf); bufj = 0; printf(childlength=%d buf=%sn,j,buf); return 0; /父进程 close(fildes0); write(fildes1,Hello,strlen(Hello); return 0; 在进程的通信中,我们无法判断每次通信中报文的字节数,即无法对数据流进行自动拆分

5、,从而发生了子进程一次性读取父进程两次通信的报文,为了能正常拆分发送报文,常常采用以下几种方法:a) 固定长度.b) 显式长度.每条报文由长度域和数据域组成.长度域大小固定,储存了数据域长度.分为字符串型和整形两种.数据域是传输的实际报文数据.接收进程先获取长度域的数据,转换成数据域的长度,再读取相应长度的信息即为数据域内容.c) 短连接.每当进程间需要通信时,创建一个通信线路,发生一条报文后立即废弃这调通线路.这种方式在socket通信中很常用.#include #include #include #include int main() pid_t pid; int fildes2; cha

6、r buf256; int i,j; if( pipe(fildes) 0 ) printf(pipe errorn); return -1; if ( (pid = fork() 0) printf(fork error!n); return -1; if(0 = pid) /子进程 close(fildes1); j = read(fildes0,buf,5); bufj = 0; printf(childlength=%d buf=%sn,j,buf); memset(buf,0,sizeof(buf); j = read(fildes0,buf,5); bufj = 0; printf

7、(childlength=%d buf=%sn,j,buf); memset(buf,0,sizeof(buf); j = read(fildes0,buf,5); bufj = 0; printf(childlength=%d buf=%sn,j,buf); return 0; /父进程 close(fildes0); write(fildes1,12345,5); write(fildes1,67890,5); write(fildes1,abcde,5); return 0; 双向管道流模型管道是进程之间的一种单向交流方式,要实现进程间的双向交流,就必须通过两个管道来完成.1) 创建管道

8、,返回两个无名管道文件描述符fildes1和fildes2,fildes1为管道1,fildes2为管道2.int fildes12,fildes22;pipe(fildes1);pipe(fildes2);2) 创建子进程,子进程继承管道1和管道2.3) 管道1从父进程流程子进程,管道2从子进程流程父进程.连接标准I/O的管道模型dup2(fd0,0); /复制fd0到文件描述符0中,更改标准输入为fd0dup2(fd1,1); /复制fd1到文件描述符1中,更改标准输出为fd1dup2(fd2,2); /复制fd2到文件描述符2中,更改标准错误输出为fd2示例一:#include #inc

9、lude #include using namespace std; int main(int argc,char *argv) int fd; fd = open(./outlog,O_RDWR|O_CREAT,0755); dup2(fd,1); /更改标准输出为outlog cout Hollo Worldn; return 0; 示例二:父进程的输出连接子进程的输入通信实例#include #include #include #include using namespace std; int main(int argc,char *argv) int fildes2; pid_t pi

10、d; int i,j; char buf256; if( pipe(fildes) 0 | (pid = fork() 0 ) cout error n; return -1; if(0 = pid) /子进程 close(fildes1); dup2(fildes0,0); close(fildes0); gets(buf); cout childbuf=bufendl; return 0; /父进程 close(fildes0); dup2(fildes1,1); close(fildes1); coutHello Worldendl; return 0; Popen模型创建连接标准I/O

11、的管道需要多个步骤,需要使用大量的代码,UNIX提供了一组函数简化了这个复杂的过程.#includeFILE *popen(const char *command,char *type);Int pclose(FIEL *stream);函数popen类似于函数system,它首先fork一个子进程,然后调用exec执行参数command中给定的shell命令.不同的是,函数popen自动在父进程和exec创建的子进程之间建立了一个管道,这个管道可以连接子进程的标准输入,也可以连接子进程的标准输出,参数type决定了管道的I/O类型参数type的取值情况r 创建与子进程的标准输出连接的管道(管

12、道数据由子进程流程父进程)w 创建与子进程的标准输入连接的管道(管道数据由父进程流程子进程)#include int main(int argc,char * argv) FILE *in,*out; char buf256; if ( NULL = (out = popen(grep init,w) ) printf(error n); return -1; if( NULL = (in = popen(ps -ef,r) printf(error n); return -1; while(fgets(buf,sizeof(buf),in) fputs(buf,out); pclose(in

13、); pclose(out); return 0; 有名管道管道如果无名,只能在共同血缘进程中使用,管道如果有名,就可以在整个系统中使用.FIFO管道,有名的管道,它以一种特殊的文件类型储存与文件系统中,以供无血缘的关系进程访问.Shell命令和C程序都可以创建有名管道.1) Shell命令创建有名管道mknod name b|c major minor /创建块设备或字符设备文件mknod name p /创建管道文件 mknod name s /创建信号量 mknod name m /创建共享内存 参数name为创建的文件名称,参数major和minor分别代表主次设备号.2) 命令mkf

14、ifo创建管道mkfifo m mode FILE其中mode是管道文件创建后的访问权限,FILE是管道文件创建后的名称eg.创建一个用户本身可读写,其他任何用户只读的管道文件k2.#mkfifo m 644 k2;3) C函数mkfifo创建管道UNIX的C语言中,也提供了创建有名管道的函数#include #include Int mkfifo(char *path,mode_t mode); 函数mkfifo创建有名管道,字符串path指定了管道的文件路径和名称,参数mode决定了管道文件的访问权限,它的取值类似于open函数的第三个参数,并且自带了O_CREAT和O_EXCL选项,因此

15、本函数只能创建一个不存在的管道文件,或者返回”文件以存在”错误.如果只是希望打开而不创建文件,请使用函数open或者函数fopen.有名管道的应用管道本身就是文件,因此对普通文件的操作也适合于管道文件,可以按照以下步骤应用管道.1) 创建管道文件(应用命令mknod或者mkfifo,或者函数mkfifo)2) 读进程a) 只读打开管道文件(应用函数open或fopen)b) 读管道(应用函数read或者fread)3) 写进程a) 只写打开管道b) 写管道(应用函数write或者fwrite)4) 关闭管道.低级文件编程库和标准文件编程库都可以操作管道,在打开管道文件前请务必先确认该管道是否存

16、在和是否具备访问权限.管道在执行读写操作前,两端必须同时打开,否则执行打开管道某端操作的进程将一直阻塞直到某个进程以相反方向打开管道为止.示例:写有名管道#include #include #include #include using namespace std; int main(int argc,char *argv) FILE *fp = NULL; char buf256; if ( mkfifo(./fifodate,S_IFIFO|0666) 0 ) cout system errorendl; return -1; while(1) /打开和关闭管道操作,要在循环里面 if(

17、(fp = fopen(./fifodate,w) = NULL ) cout fopen file failedendl; return -1; cout buf; fputs(buf,fp); fclose(fp); fp = NULL; if(0 = strcmp(buf,quit) break; return 0; 读有名管道#include #include #include #include using namespace std; int main(int argc,char *argv) char buf256; FILE *fp = NULL; while(1) /打开和关闭

18、管道操作,要在循环里面 if( (fp = fopen(./fifodate,r) = NULL ) cout fopen failed endl; return -1; memset(buf,0,sizeof(buf); fgets(buf,sizeof(buf),fp); cout read fifo msg:bufendl; fclose(fp); fp = NULL; if(0 = strcmp(buf,quit) break; return 0; 管道的模型1) “1-1”模型本模型应用于两个进程之间的双向通信,设置两个FIFO.进程A的数据从管道1流程流程B,进程B的数据通过管道2

19、流程进程A.2) “N-1”模型本进程适用于非交互式服务系统,客户端掌握了公共FIFO的输入端,将消息写入管道,后台服务进程掌握了公共FIFO的输出口,它读取管道中得信息.3) “N-1-N”模型本进程适合于交互式服务系统,客户进程掌握了一个总所周知的可以向后台服务进程传递消息的有名管道外,每个客户进程均还拥有一个私有的FIFO.为了使服务进程正确找到客户进程的私有管道,客户进程务必在其发送的请求消息中增加专用FIFO标识.管道小结:管道是进程之间最古老的通信方式,它在UNIX系统中是以一种特殊的文件形式-管道文件.它占用磁盘i节点块和数据块,在目录中记载了文件和i节点对应关系的管道时有名管道

20、,没有记载的是无名管道.在UNIX中,创建无名管道有pipe,popen等,关闭无名管道的函数有pclose等.创建有名管道的命令有mknod,mkfifo等,创建有名管道的函数有mkfifo.无名管道应用于父子进程间,实现从父进程到子进程或者从子进程到父进程之间的单向交流.常常实现进程之间的输入输出重定向.有名管道以管道文件的形式存储与磁盘等外部设备中,可再任意两个进程之间应用,常常实现进程之间的数据交换.管道还有以下特性1) 虽然无名管道没有路径,不在任何目录文件中记录该目录项,但是它仍然是文件,占用物理上的i节点和数据块.2) 无名管道没有路径,因此无法实现open调用,从而导致无名管道

21、只能在血缘进程中以继承的方式获取访问所需的文件描述符.3) 无名管道文件一旦关闭,就不能再次使用,即使是管道的创建进程也一样.4) 如果管道一次性写入小于PIPE_BUF字节的数据,该写入操作是原子操作,否则数据将先拆分再写入,此时只能保证各个拆分块的原子操作,拆分了之间的写操作不再具备原子性.例如当多个进程同时写超过PIPE_BUF字节的数据到管道时,写入的数据将被拆分成块,这时进程写入到管道的数据中可能插有其他进程的数据.5) 管道最大能够存储PIPE_BUF字节的数据,一旦管道达到最大容量,写管道操作将阻塞,直到管道中数据被读出为止.6) 管道以FIFO(先进先出)方式处理数据.7) 管

22、道数据一旦读出,就从管道中删除,具有不可再现性.Example:客户端发送客户端id消息域,服务端接收并放回结果/服务端 接收客户端通过有名管道发送的消息 /消息格式11+2 1:表示客户端1,固定一个字节,1+2:数据域,固定3个字节/服务端返回结果3到特定的有名管道(./data/data_client1) #include #include #include #include #include #include using namespace std; void Operation(char *_data,char *Child) string _1,_2; string strIn =

23、 _data; int pos = strIn.find(+); if(pos != string:npos) _1 = strIn.substr(0,pos); _2 = strIn.substr(pos+1,1); int iRet = atoi(_1.c_str() + atoi(_2.c_str(); char strRet3; sprintf(strRet,%d,iRet); int fClient = open(Child,O_RDWR|O_CREAT,0755); write(fClient,strRet,strlen(strRet); close(fClient); cout

24、send msgstrRetChildendl; return; int main() int fSrv; /管道data_serv,服务端读取 if( -1 = mkfifo(./data/data_serv,S_IFIFO|0666) ) cout mkfifo data_serv error!endl; return -1; /管道data_client1,返回给客户端1的管道 if( -1 = mkfifo(./data/data_client1,S_IFIFO|0666) ) cout mkfifo data_client1 error!endl; return -1; /管道dat

25、a_client2,返回给客户端2的管道 if( -1 = mkfifo(./data/data_client2,S_IFIFO|0666) ) cout mkfifo data_client2 error!endl; return -1; while(1) char whichClient2; /用于储存客户端编号,客户端发送的编号 1 固定1个字节 char data12; /用于存储数据域,客户端发送的数据域 1+2 固定3个字节 char Client50; /服务器端读取的内容格式为 11+2 表示客户端编号数据域: 1客户端编号 1+2数据域 fSrv = open(./data/data_serv,O_RDWR|O_CREAT,0755); read(fSrv,whichClient,1); /读取客户端进程编号,占一个字节 1 read(fSrv,data,3); /读取该客户端对应的数据域 eg 1+2 whichClient1 = 0; data3 = 0; cout recv msg whichClient dataendl; if( strcmp(whichClient,1

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1