Linux管道pipe -- C和Python两种实现方案解析
一:什么是管道管道是Linux 支持的最初Unix IPC形式之一,具有以下特点:管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道; 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程); 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在于内存中。数据的
·
一:什么是管道
管道是Linux 支持的最初Unix IPC形式之一,具有以下特点:
管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道; 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程); 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在于内存中。数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
二:C管道函数
NAME
pipe - create an interprocess channel
pipe - create an interprocess channel
SYNOPSIS
#include <unistd.h>
#include <unistd.h>
int pipe(int fildes[2]);
RETURN VALUE
Upon successful completion, 0 shall be returned; otherwise, -1 shall be returned and errno set to indicate the error.
Upon successful completion, 0 shall be returned; otherwise, -1 shall be returned and errno set to indicate the error.
三:C/C++和Python区别
在C/C++中,我们是利用一个含有两个文件描述符的数组来表示一根管道,通常使用fd[1]来读,fd[2]来写。而Python中则是由multiprocessing.Pipe()直接创建两个描述符,直接用就行。刚开始用Python时,我误以为Python中的管道是全双工的,这是错误的。管道都是半双工的,你在一端读的时候,一端只能写。不可能同时进行。
四:代码说明
本文代码实现父进程和子进程对话,对话顺序是通过C中的read或者Python中的recv函数阻塞特性实现的。你一句我一句,不会乱顺序。
五:C代码如下
头文件:
#ifndef _UTILI_H
#define _UTILI_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define err_quit(m) \
do{ \
perror(m); \
exit(1); \
}while(0)
#define MAXLINE 512
#endif
实现:
#include "utili.h"
void child_handler(int *fd1, int *fd2, char **chls);
void parent_handler(int *fd1, int *fd2, char **pars);
int main()
{
char* parent_say[] = {"hello", "how old are you", "me too,bye", NULL};
char* child_say[] = {"hi", "i'm fine,and you?", "byebye", NULL};
int fd1[2]; //use fd[1] which is for reading, another is for writing
int fd2[2]; //like the above
if(pipe(fd1) == -1 || pipe(fd2) == -1)
err_quit("create pipe err.\n");
pid_t pid;
if( (pid = fork()) < 0) //C还是很坑的,第一次在这写成了==,这个错误愣是害我找了半天
err_quit("fork err.\n");
else if(pid == 0){
child_handler(fd1, fd2, child_say);
exit(0);
}
parent_handler(fd1, fd2, parent_say);
int status;
wait(&status);
return 0;
}
void child_handler(int *fd1, int *fd2, char **chls)
{
close(fd1[1]);
close(fd2[0]);
char buff[MAXLINE];
for(int i=0; *(chls+i) != NULL; ++i){
read(fd1[0], buff, sizeof(buff));
printf("parent say:>%s\n", buff);
write(fd2[1], *(chls+i), strlen(*(chls+i))+1);
}
close(fd1[0]);
close(fd2[1]);
}
void parent_handler(int *fd1, int *fd2, char **pars)
{
close(fd1[0]);
close(fd2[1]);
char buff[MAXLINE];
for(int i=0; *(pars+i) != NULL; ++i){
write(fd1[1], *(pars+i), strlen(*(pars+i))+1);
read(fd2[0], buff, sizeof(buff));
printf("child say:>%s\n", buff);
}
close(fd1[1]);
close(fd2[0]);
}
六:Python代码如下
import multiprocessing
parent_say = ('hello', 'how old are you', 'me too,bye')
child_say = ('hi', 'i am fine,and you?', 'bye')
def child_handler(read_fd, write_fd):
for word in child_say:
print 'child say:>' + (read_fd.recv())
write_fd.send(word)
def parent_handler(read_fd, write_fd):
for word in parent_say:
write_fd.send(word)
print 'parent say:>' + (read_fd.recv())
if __name__ == '__main__':
(read_fd1, write_fd1) = multiprocessing.Pipe()
(read_fd2, write_fd2) = multiprocessing.Pipe()
child = multiprocessing.Process(target=child_handler, args=(read_fd1, write_fd2))
child.start()
parent_handler(read_fd2, write_fd1)
read_fd1.close()
read_fd2.close()
write_fd1.close()
write_fd2.close()
child.join()
七:结果如图:
update:
管道操作的原子性:
一个管道的容量是有限的。POSIX规定,少于 PIPE_BUF 的写操作必须原子完成:要写的数据应被连续的写到管道;大于 PIPE_BUF 的写操作可能是非原子的: 内核可能会把此数据与其它进程的对此管道的写操作交替起来。POSIX规定PIPE_BUF至少为512B(linux中为4096B),具体的语义如下: 其中n为要写的字节数
- n <= PIPE_BUF, O_NONBLOCK无效:原子的写入n个字节。如果管道当前的剩余空间不足以立即写入n个字节,就阻塞直到有足够的空间。
- n <= PIPE_BUF, O_NONBLOCK有效:写入具有原子性,如果有足够的空间写入n个字节,write立即成功返回。否则一个都不写入,返回错误,并设errno为EAGAIN。
- n > PIPE_BUF, O_NONBLOCK无效:非原子写。可能会和其它的写进程交替写。write阻塞直到将n个字节写入管道。
- n > PIPE_BUF, O_NONBLOCK有效:如果管道满,则write失败,返回错误,并将errno设置为 EAGIN。如果不满,则返回写入的字节数为1~n,即部分写入,写入时可能有其他进程穿插写入。
结论:
- 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
- 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
系统加于管道和FIFO的唯一限制为:
- OPEN_MAX 一个进程在任意时刻打开的最大描述符数(Posix要求至少为16)。
- PIPE_BUF 可原子性地写往一个管道或FIFO的最大数据量(Posix要求至少为512)。
更多推荐
已为社区贡献2条内容
所有评论(0)