管道,你造吗??
前言:
进程间通讯方式有:消息队列、管道、信号量、共享内存、信号(信号值)、套接字(socket)。
管道:在多个进程之间传递数据的通道。分为无名管道和有名管道,他们都是一种半双工通讯方式。这种方式效率低,进程操作文 件相互独立,不能实现同步通讯。
一、有名管道
1. 定义: 在磁盘上会存在一个管道文件,但在进程通讯时,数据不会保存到磁盘上。(管道文件并不会占用磁盘的block空间)
2.相关操作:
- 创建: 命令方式: mkfifo filename 函数方式:int mkfifo(char *filename,mode_t mode);
- 打开管道文件:int open (char *filename,int flag);
- 写入数据:int write(int flag,void *buff,size_t size);
- 读数据:int read(int flag,void *buff,size_t size);
- 关闭管道文件:int close(int fd);
eg: 进程A将用户输入的数据传递给进程B,B进程将数据显示出来。
有名管道操作时,创建管道对应的内存空间是在有读有写时创建;
打开管道文件时,open如果只以只读/只写的方式打开,则会进入阻塞状态,知道另一个进程以只读/只写被打开;
如果读端或写端关闭,则对应的写端/读端也会直接退出;
所以,综上所述要想通过管道通讯,必须保证有读有写。
管道文件仅仅缓存发送的数据,并不能区分数据的分割点! 写了两次,一次退出。——>子节点
- 一次接收的数据长度可能不等于一次发送的数据长度。
- 如果有多个读端,写端发送的数据具体有那个数据读取,由系统当前的运行状态决定。
二、无名管道
1.定义:没有管道文件,借助于父子进程之间共享fork之前打开的文件描述符,父子进程通过相同的文件描述符操作同一块内存空间。父子进程fork之前打开管道文件,如果父进程写入数据,则在子进程写入数据时,是接着父进程操作位置继续写入。
2.相关操作
创建和打开: int pipe(fd[2]); //表示有两个文件描述符。fd[0]指向管道的读端,fd[1]指向管道的写端。
父进程关闭读端,子进程关闭写端。同样,父进程关闭写端,子进程关闭读端。
在管道文件正式通讯前,应该关闭一对读、写端。因为它是一种半双工通讯方式
3.有名管道和无名管道的区别:
- 有名管道用于任意两个进程之间,而无名管道只能用于父子进程之间。
- 有名管道通讯是双向的,任意一端可读可写。但在同一时间段,只能一端读一端写;无名管道不能再网络间通讯,单 向,只能一端读一端写。
三、有关管道的思考
1.写入管道的数据会乱序吗??
管道是个流式的,依次写入,依次顺序读取。不会乱序,但多线程中如果两个线程 同时写入,数据可能会交替在一起。
2.管道为什么是个半双工模式???
linux系统下一个命令只能实现一个功能。如果想要实现较复杂的任务,就需要多个进程共同完成。将第一个进程处理结果传递给第二个进程,然后依次传递,这就好像一条完整的生产链。我们需要做的就是把数据传递下去,所以为半双工模式。也可以设置为全双工模式,但这样就麻烦多了。如果使用全双工模式建议使用双向管道(socketpair),但它的本质是两个套接字。
附加:
同步:需要内核通知机制(信号 中断)支持。当调用一个可能阻塞的函数时,函数立即返回,进程继续执行,当收到通知机制时,才处理相应事件;
异步:程序完全按照代码的顺序执行,如果调用一个阻塞函数,进程一直等待该函数返回。
父子进程fork之前共享文件描述符以及地址偏移量。