Linux管道pipe -- C和Python两种实现方案解析
2016-09-19 20:22
357 查看
一:什么是管道
管道是Linux 支持的最初Unix IPC形式之一,具有以下特点:管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道; 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程); 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在于内存中。数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
二:C管道函数
NAMEpipe - create an interprocess channel
SYNOPSIS
#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.
三: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)。
相关文章推荐
- Linux有名管道namepipe --C和Python两种实现方式解析
- Linux下实现秒级定时任务的两种方案(crontab 每秒运行)
- Linux下实现秒级定时任务的两种方案(Crontab 每秒运行)
- Python中使用PIPE操作Linux管道
- linux 管道实现解析
- Android 截取手机屏幕两种实现方案解析
- Python中使用PIPE操作Linux管道
- Android 截取手机屏幕两种实现方案解析
- Linux下实现秒级定时任务的两种方案(crontab 每秒运行)
- Linux下实现秒级定时任务的两种方案(crontab 每秒运行)
- Android 截取手机屏幕两种实现方案解析
- 解析Android截取手机屏幕两种实现方案
- Linux系统编程:pipe匿名管道的使用,实现linux命令下管道命令
- Python 3 利用 subprocess 实现管道( pipe )交互操作读/写通信
- Python 3 利用 subprocess 实现管道( pipe )交互操作读/写通信
- Linux下实现秒级定时任务的两种方案(crontab 每秒运行)
- Linux下实现秒级定时任务的两种方案
- linux下利用pipe函数实现类似 “cat a.txt | grep a”的管道命令
- linux下的多进程通信(IPC)原理及实现方案(管道、队列、信号量、共享内存)
- Linux下实现秒级定时任务的两种方案(crontab 每秒运行)