IPC通信之管道

管道

Linux管道(pipe)提供一种单向(半双工)的进程间通讯机制。管道有一个读端和一个写端。从写端写入的数据可以重读端读出来。函数pipe()创建一个管道,返回两个文件描述符,fd[0]是读端,f[1]是写端。如下图

管道的读写可以使用Linux标准IO操作接口进行,例如read、write等。从图1不难看出,数据一直缓存在内核中直到被读取出来。

单个进程的管道几乎没有任何用处。通常调用pipe的进程接着调用fork,这样就创建了从父进程到子进程的IPC通道。如下图

缺省情况下,管道工作在阻塞方式下。进程读取管道时,如果管道为空,则读操作一直阻塞直到数据到达;进程写管道时,如果管道已满,写操作会阻塞进程直到足够数据被读出。

可以使用fcntl(fd, F_SETFL, O_NONBLOCK)设置管道工作为非阻塞IO方式。管道为空时,read接口会立即返回-1,errno设置为EAGAIN;管道满时,write接口会立即返回-1;errno设置为EAGAIN。

如果所有写端关闭,读端read操作会返回0(EOF)。如果所有读端关闭,write操作会给调用进程发送SIGPIPE信号,如果调用进程忽略SIGPIPE信号,则write操作会返回失败,并设置errno为EPIPE。使用pipe和fork的进程需要正确使用close关闭多于的fd,以确保EOF和SIGPIPE/EPIPE传递正确。

其他注意事项:

1、管道是字节流通信,没有消息边界的概念;

2、管道只能在具有共同祖先的进程间使用;

3、不能对管道进行lseek操作;

实际使用中,还有两个概念对理解管道至关重要。一个是管道容量。另一个是管道操作原子性。

管道容量有限。如果管道满,阻塞方式下write操作会阻塞,非阻塞方式下会返回失败。不同的系统有不同的管道容量限制。应用模块不应该依赖特定的容量限制,正确的设计是:一旦数据到达进程应尽快消费数据,避免写进程长时间阻塞。从linux 2.6.11版本开始,管道容量是65536字节。

POSIX 1-2001规定向管道写小于PIPE_BUF字节长度的数据时原子操作;写超过PIPE_BUF字节长度的数据不是原子操作。Linux上PIPE_BUF是4096字节,更细致的描述:

1、阻塞方式,n<=PIPE_BUF(n为写入的字节数,下同):写操作是原子操作,如果pipe空间不足则阻塞。

2、非阻塞方式,n<=PIPE_BUF:写操作是原子操作,如果pipe空间不足,则失败,errno设置为EAGAIN。

3、阻塞方式,n>PIPE_BUF:写操作不是原子操作,写入的数据可能与其他进程写入的交叉排列,写操作阻塞直到所有数据写完。

4、非阻塞方式,n>PIPE_BUF:写操作不是原子操作,如果pipe空间不足,则失败,errno设置为EAGAIN。写入的数据可能与其他进程写入的数据交叉排列。同时实际写入可能小于n(部分写入);调用者应该检查write实际写入的长度。

命名管道

FIFO又称命名管道。它使用mkfifo创建,使用open打开。只要有权限任何进程都可以打开一个管道,读端使用O_RDONLY标准打开FIFO,写端使用O_WRONLY标准打开FIFO。

FIFO与管道的唯一差别是创建和打开方式不同,在它们之上的IO操作时完全相同。一个给定的FIFO有多个写进程是很常见的。这就意味着如果不希望多进程所写的的数据互相穿插,则需要考虑原子写操作(如管道相同)。

应用场景

管道的有点是编程简单易用。

管道也存在一些不足:

1、需要通过内核传递数据

2、管道容量(64k)和PIPE_BUF不能修改

3、管道传递的是流。同步方式下编程比较复杂

因此管道适用于那些数据量少,性能要求不高的场合。

举例:

管道

#include <stdio.h>
#include <unistd.h>
int
main(void)
{
	int		n;
	int		fd[2];
	pid_t	pid;
	char	line[MAXLINE];

	if (pipe(fd) < 0)
		printf("pipe error");
	if ((pid = fork()) < 0) {
		printf("fork error");
	} else if (pid > 0) {		/* parent */
		close(fd[0]);
		write(fd[1], "hello world\n", 12);
	} else {					/* child */
		close(fd[1]);
		n = read(fd[0], line, MAXLINE);
		write(STDOUT_FILENO, line, n);
	}
	exit(0);
}

命名管道

读端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>

#define FIFO_NAME "myfifo"
#define BUF_SIZE 1024

int main(void)
{
	int fd;
	char buf[BUF_SIZE];

	umask(0);    //设置允许当前进程创建文件或者目录最大可操作的权限
	fd = open(FIFO_NAME,O_RDONLY);
	read(fd,buf,BUF_SIZE);
	printf("Read content: %s\n",buf);
	close(fd);

	return 0;
}
写端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "myfifo"
#define BUF_SIZE 1024

int main(void)
{
	int fd;
	char buf[BUF_SIZE] = "Hello Reader,I come from process named Writer!";

	umask(0);             //设置允许当前进程创建文件或者目录最大可操作的权限

	if (mkfifo(FIFO_NAME,S_IFIFO|0666) == -1)
    {
      perror("mkfifo error!");
      exit(1);
    }

	if ((fd = open(FIFO_NAME,O_WRONLY)) == -1)
    {
      perror("fopen error!");
      exit(1);
    }
	write(fd,buf,strlen(buf)+1);
	close(fd);

	return 0;
}
时间: 2024-10-06 13:11:42

IPC通信之管道的相关文章

进程通信——匿名管道

有的时候在程序的开发过程中,两个进程之间会有数据的交互.信号的机制能够实现进程通信.它通过 “中断--响应” 的异步模式来工作.但作为通信来讲,信号还是远远不够的.因为它不能够携带任何其他的信息.只利用信号机制来实现进程通信显得捉襟见肘,并且信号的优势并不此.所以必须开发新的进程间通信的方法.本文所学习的 "匿名管道" 便是其中的一种最简单的方法. 基本概念 在讲管道的基本概念之前,首先提一个问题.如果让你来设计进程通信的方式,你会怎么做?最简单的方式莫过于两个进程打开同一个文件,这样

进程间的通信:管道

进程间的通信:管道 Linux中将命令联系到一起使用实际上就是把一个进程的输出通过管道传递给另一个进程的输入,这些都是shell封装好的,对标准输入和输出流进行了重新连接,使数据流从键盘输入经过两个程序最终输出到屏幕上.如下: cmd1|cmd2 进程管道 在两个程序之间传递数据最简单的方法就是使用popen()和pclose()了.原型如下: #include <stdio.h> FILE *popen(const char *command, const char *open_mode);

一个基于共享内存的ipc通信框架

一个基于共享内存的ipc通信框架 与共享内存相关的操作主要包括共享内存的初始化, 共享内存的释放, 共享内存的锁的相关操作, 在这里操作共享内存的环境是: 1 多个进程没有亲缘关系, 也没有server/client关系, 是多个不相关进程并发操作共享内存 2 共享内存一开始不存在, 由第一个访问他的进程创建 3 当共享内存退出时, 由最后一个离开的进程释放共享内存, 并清除信号量 在这个问题之中, 主要有两个比较大的问题: 1 怎样新建并初始化共享内存 新建共享内存的数据都可以用信号量来控制,

Android Service IPC通信之Messenger机制

概述 之前我写过一篇博客介绍Service:Android Service全面解析,里面讲过如何实现Service的跨进程(IPC)通信,主要是通过编写AIDL接口文件来实现的.本篇我们来讲讲Service IPC通信的另外一种方式-Messenger. Messenger,也称为信使,通过它可以在不同的进程间传递message对象,在message中放入我们需要传递的数据你就可以实现跨进程通信和传递数据了.所以说Messenger机制是基于消息的跨进程通信方式. 可以看到,我们可以在客户端发送

android IPC通信(下)-AIDL

android IPC通信(上)-sharedUserId&&Messenger android IPC通信(中)-ContentProvider&&Socket 这篇我们将会着重介绍AIDL的使用方式和原理,要介绍AIDL先要简单介绍一下Binder,而且Messenger,ContentProvider和AIDL的最底层都是使用的Binder. Binder 直观来说,Binder是Android中的一个类,它实现了IBinder接口.从IPC角度来说,Binder是A

Android使用Messenger进行Service IPC通信分析

如果想要进行IPC通信,一般写一个AIDL接口,再写一个Service子类,然后实现AIDL接口 当做IBinder返回给Activity界面层. 如果不想写AIDL接口文件,只是单I线程中与Service进行通信 我们可以用Android写好的Messenger类来处理,一样能将消息传递给Service进行通信. 先写上基本代码: public class MyService extends Service { Messenger messenger = null; public MyServ

android IPC通信(上)-sharedUserId&amp;amp;&amp;amp;Messenger

看了一本书,上面有一章解说了IPC(Inter-Process Communication,进程间通信)通信.决定结合曾经的一篇博客android 两个应用之间的通信与调用和自己的理解来好好整理总结一下这块的知识.因为内容较多,这部分会分上中下三篇博客来细致分析解说,第一篇上篇要解说的是sharedUserId和Messenger的使用方式. android IPC通信(中)-ContentProvider&&Socket android IPC通信(下)-AIDL sharedUserI

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

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

python—day29 守护进程、互斥锁、模拟抢票、IPC通信机制、生产者消费者模型

1.守护进程: 什么是守护进程,假如你是皇帝,每日每夜守护你的就是太监,守护进程就相当于太监,当皇帝驾崩后太监也需要陪葬,所以守护进程当父进程销毁时就一起销毁: 1 from multiprocessing import Process 2 3 import time 4 5 def task(name): 6 7 time.sleep(0.5) 8 print('%s' %name) 9 10 11 if __name__ == '__main__': 12 p = Process(targe