命名管道通信

什么是命名管道

一个主要的限制是,它是匿名管道的应用还没有名字,因此,只有它可以用于进程间通信的方式与亲缘关系。在命名管道(named pipe或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。

这样,即使与FIFO的创建进程不存在亲缘关系的进程,仅仅要可以訪问该路径,就行彼此通过FIFO相互通信

有名管道创建

int mkfifo(const char * pathname, mode_t mode)

和普通文件创建一样pathname为文件名,mode为权限

有名管道通信规则

管道关闭规则

intclose (int __fd);

1.当最后一个读进程管道关闭时。写进程不管是堵塞还是非堵塞,都会将管道写满(假设能写满)并退出

2.当最后一个写进程管道关闭时,向管道写入一个结束标识,当读进程从管道读到这个结束标识时。假设是堵塞读进程将结束堵塞返回读入数据个数为0.(对于未堵塞读进程假设管道内没有数据则返回-1,假设读到结束标识则返回读入数据个数为0)

规则分析1

写进程:

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<limits.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h> #define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 80000
intmain(int argc,char *argv[])
{
unlink(FIFO_NAME);
int pipe_fd;
int res;
char buf[BUF_SIZE];
memset(buf,3,BUF_SIZE);
if(access(FIFO_NAME,F_OK)==-1)
{
res=mkfifo(FIFO_NAME,0766);
if(res!=0)
{
fprintf(stderr,"不能创建管道文件 %s\n",FIFO_NAME);
exit(1);
}
} printf("进程PID %d 打开管道 O_WRONLY\n",getpid());
pipe_fd=open(FIFO_NAME,O_WRONLY); if(pipe_fd!=-1)
{
res=write(pipe_fd,buf,sizeof(buf));
printf("写入数据的大小是%d \n",res);
close(pipe_fd);
sleep(1000);
}
else
exit(1);
exit(1);
}

读进程

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<limits.h>
#include<sys/types.h>
#include<sys/stat.h> #define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 20 intmain(int argc, char *argv[]) {
char buf[BUF_SIZE];
memset(buf, 0, BUF_SIZE);
int pipe_fd;
int res;
int bytes_read = 0; printf("进程PID %d 打开管道 O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_RDONLY); if (pipe_fd != -1) {
bytes_read = read(pipe_fd, buf, sizeof(buf));
printf("读入数据的大小是%d \n", bytes_read);
sleep(10);
close(pipe_fd);
} else
exit(1);
exit(1);
}

控制台信息

读进程:

进程PID 10930打开管道 O_RDONLY

读入数据的大小是20

写进程:

进程PID 10918打开管道 O_WRONLY

(10S后输出…..)

写入数据的大小是65536

分析:当读进程运行到close(pipe_fd);时,写进程一次性将数据写满缓冲区(65536)并退出。

规则分析2

写进程

#define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 80000
int main(int argc,char *argv[])
{
unlink(FIFO_NAME);
int pipe_fd;
int res;
char buf[BUF_SIZE];
memset(buf,3,BUF_SIZE);
if(access(FIFO_NAME,F_OK)==-1)
{
res=mkfifo(FIFO_NAME,0766);
if(res!=0)
{
fprintf(stderr,"不能创建管道文件 %s\n",FIFO_NAME);
exit(1);
}
} printf("进程PID %d 打开管道 O_WRONLY\n",getpid());
pipe_fd=open(FIFO_NAME,O_WRONLY); if(pipe_fd!=-1)
{
res=write(pipe_fd,buf,sizeof(buf));
printf("写入数据的大小是%d \n",res);
sleep(10);
close(pipe_fd); }
else
exit(1);
exit(1);
}  

读进程

#define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 4000 int main(int argc, char *argv[]) {
char buf[BUF_SIZE];
memset(buf, 0, BUF_SIZE);
int pipe_fd; int bytes_read = 0; printf("进程PID %d 打开管道 O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_RDONLY); if (pipe_fd != -1) { do {
bytes_read = read(pipe_fd, buf, sizeof(buf));
printf("读入数据的大小是%d \n", bytes_read); } while (bytes_read != 0); close(pipe_fd);
} else
exit(1);
exit(1);
}

控制台输出:

 

读进程

进程PID 12240打开管道 O_RDONLY

读入数据的大小是4000.

……

(10S后)

读入数据的大小是0

写进程

进程PID 12227打开管道 O_WRONLY

写入数据的大小是80000

分析:

假设读进程为堵塞的,当写进程关闭管道时。读进程收到写进程发来的结束符,读进程结束堵塞(此时bytes_read =0)

假设读进程为非堵塞的,首先将全部数据读取出来,然后在读进程未收到写进程发来的结束符时。由于管道没有数据读进程不会堵塞且返回-1,由于此例WHILE退出条件是bytes_read
=0。因此在未读到结束符之前返回值一直是-1,直到读取到结束符才返回0

管道写端规则

对于设置了堵塞标志的写操作:

1.      
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。假设此时管道空暇缓冲区不足以容纳要写入的字节数。则进入睡眠。直到当缓冲区中可以容纳要写入的字节数时,才開始进行一次性写操作。

2.      
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空暇区域,写进程就会试图向管道写入数据,写操作在写全然部请求写的数据后返回。

对于没有设置堵塞标志的写操作:

3.      
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

在写满全部FIFO空暇缓冲区后,写操作返回。

4.      
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。

假设当前FIFO空暇缓冲区可以容纳请求写入的字节数,写完后成功返回;假设当前FIFO空暇缓冲区不可以容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写

管道读端规则

对于设置了堵塞标志的写操作:

1.      
假设有进程写打开FIFO。且当前FIFO内没有数据,将一直堵塞。

 

对于没有设置堵塞标志的写操作:

2.      
假设有进程写打开FIFO,且当前FIFO内没有数据。则返回-1,当前errno值为EAGAIN,提醒以后再试。

管道读写规则代码举例

写进程

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<limits.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <errno.h> #define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 88888
int main(int argc,char *argv[])
{
int pipe_fd;
int res;
char buf[BUF_SIZE];
memset(buf,3,BUF_SIZE);
if(access(FIFO_NAME,F_OK)==-1)
{
res=mkfifo(FIFO_NAME,0766);
if(res!=0)
{
fprintf(stderr,"不能创建管道文件 %s\n",FIFO_NAME);
exit(1);
}
} printf("进程PID %d 打开管道 O_WRONLY\n",getpid());
pipe_fd=open(FIFO_NAME,O_WRONLY|O_TRUNC);//1
// pipe_fd=open(FIFO_NAME,O_WRONLY|O_TRUNC|O_NONBLOCK);//2
if(pipe_fd!=-1)
{
res=write(pipe_fd,buf,sizeof(buf));
printf("写入数据的长度是%d \n",res);
close(pipe_fd);
}
else
exit(1);
exit(1);
}

读进程

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<limits.h>
#include<sys/types.h>
#include<sys/stat.h> #define FIFO_NAME "/tmp/my_fifo"
#define BUF_SIZE 4000 int main(int argc, char *argv[]) { int res;
if(access(FIFO_NAME,F_OK)==-1)
{
res=mkfifo(FIFO_NAME,0766);
if(res!=0)
{
fprintf(stderr,"不能创建管道文件 %s\n",FIFO_NAME);
exit(1);
}
}
char buf[BUF_SIZE];
memset(buf, 0, BUF_SIZE);
int pipe_fd;
int num=0;
int bytes_read = 0; printf("进程PID %d 打开管道 O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_RDONLY);//3
//pipe_fd = open(FIFO_NAME, O_RDONLY|O_NONBLOCK);//4
if (pipe_fd != -1) { do {
num++;
bytes_read = read(pipe_fd, buf, sizeof(buf));
printf("第%d次读入数据,数据的长度是%d \n",num, bytes_read); } while (bytes_read != 0); close(pipe_fd);
} else
exit(1);
exit(1);
}

上面两段代码各自是管道的读端进程与写端进程。当中有4个凝视行。分别代表

1.       pipe_fd=open(FIFO_NAME,O_WRONLY|O_TRUNC);//1堵塞写端

2.       pipe_fd= open (FIFO_NAME,O_WRONLY|O_TRUNC|O_NONBLOCK);//2非堵塞写端

3.       pipe_fd =open(FIFO_NAME, O_RDONLY);//3堵塞读端

4.       pipe_fd =open(FIFO_NAME, O_RDONLY|O_NONBLOCK);//4非堵塞读端

能够分下面3种情况分析:

说明:写端与读端不能同一时候都不堵塞

写端堵塞。读端不堵塞

控制台输出例如以下:

 

读端进程:

进程PID 5919打开管道 O_RDONLY

第1次读入数据。数据的长度是-1

…………..

第5次读入数据,数据的长度是4000

…………..

第26次读入数据,数据的长度是4000

第27次读入数据,数据的长度是888

第28次读入数据,数据的长度是0

写端进程:

进程PID 5906打开管道 O_WRONLY

写入数据的长度是88888

分析:读端满足读端规则2。前面因为写进程还未開始写入数据到管道因此返回-1

写端满足写端规则2

写端不堵塞,读端堵塞

运行流程:先运行读端程序,在运行写端

控制台输出例如以下:

 

读端进程:

进程PID 6046打开管道 O_RDONLY

第1次读入数据。数据的长度是4000

…………

第16次读入数据,数据的长度是4000

第17次读入数据,数据的长度是1536

第18次读入数据,数据的长度是0

 

写端进程:

进程PID 6056打开管道 O_WRONLY

写入数据的长度是65536

分析:读端满足读端规则1,读进程在为读取到管道数据时一直处于等待堵塞状态

写端满足写端规则3。写端写满管道后推出,因此写入数据长度是65535,而不是88888

写端堵塞,读端堵塞

控制台输出例如以下:

 

读端进程:

进程PID 8386打开管道 O_RDONLY

第1次读入数据。数据的长度是4000

…………

第22次读入数据。数据的长度是4000

第23次读入数据,数据的长度是888

第24次读入数据,数据的长度是0

 

写端进程:

进程PID 8373打开管道 O_WRONLY

写入数据的长度是88888

分析:读端满足读端规则1,读取处理用于读取数据已在管道等待堵塞状态

写满足最终写的 - 侧规则2

Linux学习记录--命名管道通信的更多相关文章

  1. C#命名管道通信

    C#命名管道通信 最近项目中要用c#进程间通信,以前常见的方法包括RMI.发消息等.但在Windows下面发消息需要有窗口,我们的程序是一个后台运行程序,发消息不试用.RMI又用的太多了,准备用管道通 ...

  2. C++和C#进程之间通过命名管道通信(上)

    C++和C#进程之间通过命名管道通信(上) "命名管道"是一种简单的进程间通信(IPC)机制.命名管道可在同一台计算机的不同进程之间,或在跨越一个网络的不同计算机的不同进程之间,支 ...

  3. c# c++通信--命名管道通信

    进程间通信有很多种,windows上面比较简单的有管道通信(匿名管道及命名管道) 最近做个本机c#界面与c++服务进行通信的一个需求.简单用命名管道通信.msdn都直接有demo,详见下方参考. c+ ...

  4. linux命名管道通信过程

    前一个道,这节学习命名管道. 二命名管道 无名管道仅仅能用来在父子进程或兄弟进程之间进行通信,这就给没有亲缘关系的进程之间数据的交换带来了麻烦.解决问题就是本节要学习的还有一种管道通信:命名管道. 命 ...

  5. Linux下进程间管道通信小作业

    在进行这次作业之前,我们先来看看什么是管道吧! 管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入,常说的管道多是指无名管道,无名管道只能用于具有亲缘关系的进程之间, ...

  6. Linux进程间通信——使用命名管道

    在前一篇文章——Linux进程间通信——使用匿名管道中,我们看到了如何使用匿名管道来在进程之间传递数据,同时也看到了这个方式的一个缺陷,就是这些进程都由一个共同的祖先进程启动,这给我们在不相关的的进程 ...

  7. 《Linux 进程间通信》命名管道:FIFO

    命名管道的主要用途:不相关的进程之间交换数据. 命令行上创建命名管道: $ mkfifo filename  程序中创建命名管道: #include <sys/types.h> #incl ...

  8. windows10使用VS(VC++)创建c++多进程命名管道通信

    代码可以在 这里 下载 代码主要涉及到: 管道通信 多线程(含临界区) 多进程通信 创建的子进程独立运行 更新日志: 04-12-2020 1. 去除自定义函数返回值,改为int作为函数返回值并增加相 ...

  9. Linux 学习记录 一(安装、基本文件操作).

         Linux distributions主要分为两大系统,一种是RPM方式安装软件的系统,包括Red Hat,Fedora,SuSE等都是这类:一种则是使用Debian的dpkg方式安装软件的 ...

随机推荐

  1. Contact类解析

    Contact类 public static class Contacts implements BaseColumns, ContactsColumns, ContactOptionsColumns ...

  2. Java的一些基础小知识之JVM与GC (转)

    一.JVM是什么 Java虚拟机(英语:Java Virtual Machine,缩写为JVM),又名爪哇虚拟器,一种能够运行Java bytecode的虚拟机,以堆栈结构机器来进行实做.最早由太阳微 ...

  3. How to get the source code of the chromium of the specified revision

    I'd like to get the source code of the chromium 34.0.1847.9. gclient config http://src.chromium.org/ ...

  4. WM_PARENTNOTIFY的作用(不完全)

    VCL里源码如下: procedure TWinControl.WMParentNotify(var Message: TWMParentNotify); begin with Message do ...

  5. [Android学习笔记]ListView中含有Button导致无法响应onItemClick回调的解决办法

    转自:http://www.cnblogs.com/eyu8874521/archive/2012/10/17/2727882.html 问题描述: 当ListView的Item中的控件只是一些展示类 ...

  6. [Android学习笔记]View的draw过程学习

    View从创建到显示到屏幕需要经历几个过程: measure -> layout -> draw measure过程:计算view所占屏幕大小layout过程:设置view在屏幕的位置dr ...

  7. EA强大功能之代码凝视

    前面讲了EA怎样方便我们生成代码,这次讲一下,怎样生成具体的凝视. 1.文件表头凝视 (1)点击工具----选项 在常规项里改动作者: 在代码project中改动代码project的默认语言. (2) ...

  8. swift 笔记2

    swift交流群:342581988,欢迎增加. 今天真郁闷啊,把mac升级到10.10了.如今好了,曾经的程序都跑不了了.哎,不说了,让我郁闷会再. 说说条件推断吧,事实上这些基本的语法大家都知道肯 ...

  9. uva315(求割点数目)

    传送门:Network 题意:给出一张无向图,求割点的个数. 分析:模板裸题,直接上模板. #include <cstdio> #include <cstring> #incl ...

  10. 第一章. ActionScript 语言基础

    第一章. ActionScript 语言基础 1.0. ActionScript 3.0 Cookbook 概述 1.1. 新建一个 ActionScript project 1.2. 自己定义应用程 ...