进程间通信-管道

进程间通信的目的

  1. 数据传输:一个进程需要将它的数据发送给另一个进程
  2. 资源共享:多个进程之间共享同样的资源
  3. 通知事件:一个进程需要向另一个或者另一组进程发送消息,通知发生了某种事件
  4. 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程)

管道 分为:匿名管道pipe和命名管道
也是一个文件
什么是管道???
5. 管道是unix中最为古老的 进程间通信的形式
6. 把从一个进程连接到另一个进程的一个数据流称为一个 “管道”
匿名管道

#include <unistd.h>
功能:创建⼀一⽆无名管道
原型
int pipe(int fd[2]);
参数
fd:⽂文件描述符数组,其中fd[0]表⽰示读端, fd[1]表⽰示写端
返回值:成功返回0,失败返回错误代码

看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想”
管道读写规则
当没有数据可读时
O_NONBLOCK disable:read 调用阻塞,即进程暂停执行,一直等到有数据来为止。
O_NONBLOCK enable:read 调用返回-1,errno值为EAGAIN。
当管道满时
O_NONBLOCK disable: write调用阻塞,直到有进程读走数据。
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN。
如果所有管道写端对应的文件描述符被关闭,read返回0。
如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出。
当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。
当要写入的数据量大于PIPE_BUF时,Linux不在保证写入的原子性。
原子性
栗子:
A想要从自己的帐户中转1000块钱到B的帐户里。那个从A开始转帐,到转帐结束的这一个过程,称之为一个事务。在这个事务里,要做如下操作:
7. 从A的帐户中减去1000块钱。如果A的帐户原来有3000块钱,现在就变成2000块钱了。
8. 在B的帐户里加1000块钱。如果B的帐户如果原来有2000块钱,现在则变成3000块钱了。
如果在A的帐户已经减去了1000块钱的时候,忽然发生了意外,比如停电什么的,导致转帐事务意外终止了,而此时B的帐户里还没有增加1000块钱。那么,我们称这个操作失败了,要进行回滚。回滚就是回到事务开始之前的状态,也就是回到A的帐户还没减1000块的状态,B的帐户的原来的状态。此时A的帐户仍然有3000块,B的帐户仍然有2000块。
我们把这种要么一起成功(A帐户成功减少1000,同时B帐户成功增加1000),要么一起失败(A帐户回到原来状态,B帐户也回到原来状态)的操作叫原子性操作。
如果把一个事务可看作是一个程序,它要么完整的被执行,要么完全不执行。这种特性就叫原子性。
管道特点

  1. 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间的通信;通常,一个进程由一个进程创建,然后该进程调用fork,此后父子进程之间就可以应用该管道
  2. 管道提供流式服务
  3. 一般而言,进程退出,管道释放,所以管道的生命周期 随进程
  4. 一般而言,内核会对管道操作进行同步与互斥
  5. 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立两个管道

命名管道
管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信,我们可以使用FIFO文件(常被称为命名管道)在两个不相关的进程中进行数据交换。命名管道是一种特殊类型的文件。
创建一个命名管道:$ mkfifo filename
命名管道也可以从程序里创建,相关函数有:
int mkfifo(const char *filename,mode_t mode);
创建命名管道:

int main(int argc, char *argv[])
{
mkfifo("p2", 0644);
return 0;
}

匿名管道与命名管道的区别

  1. 匿名管道由pipe函数创建并打开
  2. 命名管道由mkfifo函数创建,打开用open
  3. FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一旦这些工作完成之后,它们具有相同的语义

猜你喜欢

转载自blog.csdn.net/zhangjie1315/article/details/80150402