命名管道通信

什么是命名管道

一个主要的限制是,它是匿名管道的应用还没有名字,因此,只有它可以用于进程间通信的方式与亲缘关系。在命名管道(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. Github Atom

    码代码新神器-Github Atom   周末闲着没事,逛论坛发现了一个新的编辑器,由github发布的Atom编辑器.瞬间被吸引了,所以就去尝试着折腾了一下,后来发现这个编辑器确实很不错,他的特点就 ...

  2. 利用SQL语句实现分页

    1.概述 在网页中如果显示的数据太多就会占据过多的页面,而且显示速度也会很慢.为了控制每次在页面上显示数据的数量,就可以利用分页来显示数据. 2.技术要点 在SQL Server中要实现SQL分页,需 ...

  3. vlan 以及 Linux实现的IEEE 802.1Q VLAN

    vlan 以及 Linux实现的IEEE 802.1Q VLAN Vlan的概念 VLAN技术介绍 VLANVLAN概述 以太网是一种基于CSMA/CD(Carrier Sense Multiple ...

  4. DataReader和DataSet的区别以及使用

    DataReader和DataSet这两个对象都可以将检索的关系数据存储在内存中.它们在功能使用方面非常相似,但是它们不可以相互替换. 主要区别如表所示:   DataReader DataSet 数 ...

  5. Delphi XE中类成员的访问权限(新增了strict private和strict protected,还有automated)

    Delphi XE中类成员的访问权限共提供了6个关键词来用于限定访问权限:public.private.protected.published.automated strict private . s ...

  6. OCA读书笔记(10) - 管理UNDO数据

    Undo自动管理与手动管理 undo段自动管理SQL> show parameter undo_management 将undo段改为手工管理SQL> alter system set u ...

  7. MySQL分区技术 (一)

    4:MySQL 分区技术(是mysql 5.1以版本号后開始用->是甲骨文mysql技术团队维护人员以插件形式插入到mysql里面的技术) 眼下,针对海量数据的优化主要有2中方法: 1:大表拆成 ...

  8. 不起眼的 z-index 却能牵扯出这么大的学问(转)

    z-index在日常开发中算是一个比较常用的样式,一般理解就是设置标签在z轴先后顺序,z-index值大的显示在最前面,小的则会被遮挡,是的,z-index的实际作用就是这样. 但是你真的了解z-in ...

  9. SICP 解题集 — SICP 解题集

    SICP 解题集 — SICP 解题集 SICP 解题集¶ 这个文档的目标是成为中文化的.完整的<计算机程序的构造和解释>一书的解题集. 这个解题集的特色是: 对于每道习题,除了习题答案之 ...

  10. [品质生活] 舒适 Schick HYDRO 5剃须刀

    [品质生活] 舒适 Schick HYDRO 5剃须刀 [品质生活] 舒适 Schick HYDRO 5剃须刀