一 Linux进程间通信( 三 )

注意:O_NONBLOCK是非阻塞的标志位,指定管道对我们的操作要么成功,要么立刻返回错误,不被阻塞 。
管道特点(了解)

  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork , 此后父、子进程之间就可应用该管道 。
  • 管道提供流式服务 。也就是你想往管道里读写多少数据是根据自身来定的
  • 一般而言 , 进程退出 , 管道释放,所以管道的生命周期随进程
  • 一般而言,内核会对管道操作进行同步与互斥
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
  • 半双工是指传输过程中同时只能向一个方向传输,一方的数据传输结束之后 , 另外一方再回应 。双方传输数据是不可以同时进行的
  • 全双工是指两方能同时发送和接受数据 。在这种情况下就没有拥堵的危险 , 数据的传输也就更快
命名管道概念:无名管道,由于没有名字,所以只能用于亲缘关系的进程通信 。为了克服这个缺点,提出了命名管道(FIFO) 。
命名管道不同于无名管道之处在于它提供了一个路径名与之关联 , 以FIFO的文件形式存在于文件系统中,这样,即使与FIFO的创建进程不存在亲缘关系的进程 , 只要可以访问该路径 , 就能够彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据 。
  • FIFO在文件系统(磁盘上)中作为一个特殊文件而存在 , 但是FIFO中的内容却存放在内存中 。
  • 当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用 。
  • FIFO有名字,不相关的进程可以通过打开命名通道进行通信 。
创建命名管道1.通过命令创建命名管道
mkfifo filename
2.通过函数创建命名管道
int mkfifo(const char *pathname, mode_t mode);
功能:创建命名管道
参数:pathname:普通的路径名,也就是创建后FIFO的名字 。
?mode:文件的权限,与打开普通文件的open函数中的mode参数类似 。
返回值:成功:0 (状态码)失败:如果文件已经存在,则会出错返回-1
代码示例:
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#define FIFO "./fifo"int main(){umask(0);// 创建管道int ret = mkfifo(FIFO, 0666);if (ret == -1){perror("make fifo");exit(-1);}}运行结果如下:
一 Linux进程间通信

文章插图
上面说过 , 管道其实就是一种特殊的文件,管道文件大小是0,因为上面介绍过 , 管道文件的内容都存放在内存当中 。
命名管道读写操作以及注意事项一旦创建了一个FIFO,就可以用open打开它,常见的文件I/O都可以作用于FIFO文件 。
FIFO严格的遵循先进先出的原则 , 对管道以及FIFO的读总是从开始处返回数据,对它们的写则是把数据添加到末尾 。
  • 一个为只读而打开一个管道的进程会阻塞直到另外一个进程为只写打开该管道
  • 一个为只写而打开一个管道的进程会阻塞直到另外一个进程为只读打开该管道
读写规则:
读管道
  • 管道中有数据,read返回返回实际读到的字节数
  • 管道中无数据:(1)若管道写端被全部关闭 , read返回0
?(2)若写端没有全部关闭,read阻塞等待
写管道
  • 管道读端全部被关闭,进程异常终止
  • 管道读端没有全部关闭:(1)若管道已经满了 。write阻塞
?(2)若管道没满 , write将数据写入,并返回实际写入的字节数
使用命名管道进行通信接下来我会使用命名管道实现简单的版本聊天 。
一 Linux进程间通信

文章插图
talkA.c
#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<stdlib.h>#include<fcntl.h>//先读后写//以只读的方式打开管道1//以只写的方式打开管道2define SIZE 1024int main() {int fdr = -1;int fdw = -1;int ret = -1;char buf[SIZE];//以只读的方式打开管道1fdr = open("fifo1",O_RDONLY);if(-1==fdr){perror("open");return 1;}printf("以只读的方式打开管道1....\n");//以只写的方式打开管道2fdw = open("fifo2",O_WRONLY);if(-1==fdw){perror("open");return 1;}printf("以只写的方式打开管道2....\n");//循环读写while(1){//读管道1memset(buf,0,SIZE);ret = read(fdr,buf,SIZE);if(ret<=0){perror("read");break;}printf("read:%s\n",buf);//写管道2memset(buf,0,SIZE);fgets(buf,SIZE,stdin);//去掉最后一个换行符if('\n'==buf[strlen(buf)-1])buf[strlen(buf)-1]=0;//写管道ret = write(fdw,buf,strlen(buf));if(ret<=0){perror("write");break;}printf("write ret:%d\n",ret);}//关闭文件描述符close(fdr);close(fdw);}

推荐阅读