[Linux系统编程]进程间通信---管道/FIFO/存储映射IO(MMAP)

在进程间完成数据传递需要借助操作系统提供特殊的方法,如:文件、管道、信号、共享内存、消息队列、套接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信方式有:
①管道(使用最简单)
②信号(销最小)
③共享映射区(无血缘关系)~
④本地套接字(最稳定)~

一.管道

优点:简单,相比信号,套接字实现进程间通信,简单很多。
缺点:1.只能单向通信,双向通信需建立两个管道。
2.只能用于父子、兄弟进程(有共同祖先)间通信。该问题后来使用fifo有名管道解决。#
特质:

1.本质是一个伪文件(实为内核缓冲区(4k)),利用环形队列机制实现
2.由两个文件描述符引用,一个表示读端fd[0],一个表示写端fd[1],数据在管道中单向流动。
3.只能一次读取

局限性:

① 数据不能进程自己写,自己读。。
②)管道中数据不可反复读取。一旦读走,管道中不再存在。
③采用半双工通信方式,数据只能在单方向上流动。
④只能用在有血缘关系的进程之间。

pipe函数:
功能: 创建,并打开管道。
管道实际上是一个缓冲区,作为一个伪文件,它有两个文件描述符,其中fd[0]表示读端,fd[1]表示写端

int pipe(int fd[2]).

参数:
fd[0]:读端。
fd[1]:写端。
返回值:成功:0
失败:-1 errno

由于父子进程共享文件操作符表,故而使得父子进程间可一起操作管道,利用pipe函数返回的fd文件描述符,操作写端读端,实现进程间通信。
管道只能单向流动,所以,对于父子进程,必须确保只有一个写,一个读。(半双工通信方式)

管道的读写行为:
读管道:
1.管道有数据,read返回实际读到的字节数
2.管道无数据: ①若无写端,read返回0(类似读到文件尾) ②有写端,read阻塞等待。
写管道:
1.无读端,异常终止。(SIGPIPE异常信号导致)
2.有读端:①管道已满, 阻塞等待 ② 管道未满, write返回写入的字节个数。

使用父子进程间通信实现ls |wc -l 的操作:
(通过父子进程,execl函数族,dup重定向实现)

原理:
父进程执行ls操作,默认ls的内容输入到标准输出STDOUT_FILENO中,使用重定向dup2将STDOUT_FILENO重定向到管道的写端fd[1]
子进程执行wc -l 操作,wc -l是对标准输入的内容进行处理,将其重定向到管道的读端fd[0]
在这里插入图片描述

兄弟间进程通信:

一个父进程fork出两个子进程,如果想要使用管道,必须使得管道单向流动。
对于父进程和它的两个子进程,对于同一个管道,最开始都会打开它的读端和写端。
想要使兄弟进程通信,就需要关闭父进程的读端和写端,同时使得兄弟进程各持一端,保证单向通信。
这次使用兄弟进程间通信来实现ls |wc -l 的操作
在这里插入图片描述可以使用 ulimit-a命令来査看当前系统中创建管道文件所对应的内核缓冲区大小。通常为4K:

在这里插入图片描述

二.命名管道FIFO

FIFO可用于实现无血缘关系进程间通信
上文所述,管道可以看做一个文件,进程的内核空间被加载到内存的同一区域,借助管道来实现进程通信,所以无血缘关系的进程也可使用管道来实现通信,FIFO是一种命名管道,可以使用mkfifo命令或者mkfifo函数来创建。

在这里插入图片描述
FIFO与pipe管道有所不同,不分写端和读端,两个进程可以同时open这个FIFO文件,执行write和read操作,只需要保证管道单向流动即可,通过常规的文件read和write操作,就能够实现进程通信。
FIFO允许有多个写端,多个读端,但每次管道内容被读取时与PIPE一样,内容都会被清空。所以尽量避免这种情况。
根据这种原理,不使用FIFO,直接使用文本文件,也可以完成进程间通信。
问题在于,使用文本文件时,不会像管道一样在特定的情况下阻塞,有可能会出现读操作先于写操作,所以这种通信方式并不适用。

三.存储映射I/O

存储映射,是将磁盘上的文件映射到内存,方便我们直接对文件操作。
1.MMAP函数创建存储映射
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset).
作用:为文件创建存储映射,返回这个映射区的指针。
参数:

addr:指定映射区的首地址。通常传NULL,表示让系统自动分配
length:共享内存映射区的大小。(<=文件的实际大小)
prot:共享内存映射区的读写属性。PROT_READ、PROT_WRITE、PROT_READ | PROT_WRITE
flags:标注共享内存的共享属性。MAP_SHARED(对内存的操作会写入磁盘)、MAP_PRIVATE(不会写入磁盘)
fd:用于创建共享内存映射区的那个文件的 文件描述符
offset:偏移位置,默认0,表示映射文件全部。需是 4的整数倍。

返回值:

成功: 映射区的首地址
失败: 返回MAP FAILED,是一个宏,实际上是一个(void*)-1

MUNMAP释放映射
munmap(" 映射区地址p “,” 地址空间长度len ")

MMAP使用注意事项:

1.创建映射区的文件大小为 0,指定非0大小创建映射区,出“总线错误”
2.指定0大小创建映射区,出“无效参数”错误
3.用于创建映射区的文件读写属性为只读。映射区属性为 读写。出“无效参数 (仅在使用MAP_SHARED时)”
4.创建映射区,需要文件的read权限。故 mmap的创建权限,应该<=文件的open权限。且必须有文件的读权限
5.文件描述符fd,在mmap创建映射区完成即可关闭。后续访问文件,用地址访问。
6.offset 必须是 4K的整数倍。(MMU 映射的最小单位4K)
7.对申请的映射区内存,不能越界访问。(即只能在申请的内存空间上进行操作)
8.munmap用于释放的地址,必须是mmap申请返回的地址。否则回收失败。
9.映射区访问权限为“私有”MAP_PRIVATE,对内存所做的所有修改,只在内存有效,不会反应到物理磁盘上。

mmap函数的保险调用方式:
1.fd=open(“文件名”,RDWR): --------文件,用读写方式打开
2.当文件为空时,扩展文件大小:
①使用lseek: lseek(fd,len,SEEK_END),扩展len的大小,然后必须write数据进入文件,否则扩展失效。write(fd,str,sizeof(str));
②使用ftruncate: ftruncate(fd,len) 直接扩展len大小,需要文件的读权限。
3.map(NULL,有效文件大小,PROT_READIPROT_WRITE,IMAP_SHARED, fd,0)------创建映射,权限<=OPEN权限,MAP_SHARED方式,offset偏移量为0,文件大小不越界。

利用存储映射,实现父子进程间通信
在这里插入图片描述 在这里插入图片描述利用存储映射,实现无血缘关系进程间通信

写入进程
在这里插入图片描述
读出进程
在这里插入图片描述

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值