Linux进程通信-管道(pipe)
来源:互联网 发布:阿里云电话客服 编辑:程序博客网 时间:2024/05/21 07:59
简述
在Linux系统中,管道是一种最基本的IPC机制,要理解管道的机制,我们可以举一个简单的例子,在linux系统下的进程都是独立,如果两个进程需要交流也就是通信,我们需要开辟一个他们两个可以共同访问的区域,而管道,简单来讲就是为了实现这一目的而建立的,管道由pipe函数创建,调用pipe函数时,在内核中会开辟一块缓冲区(简称管道)用来通信,由两个文件描述符引用,一个表示读端,一个表示写端。
创建过程
我们用一幅图来描述一下管道的创建过程
简单来概括一下这几个步骤
1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。数据从写端流入管道,从读端流出,这样就实现了进程间通信。
下面我们来具体实现一下:
#inlcude<stdio.h>#include<unistd.h>#include<errno.h>#include<string.h>int main(){ int _pipe[2]; int ret = pipe(_pipe); if(ret == -1) { printf("create erro cod : %d\n",errno); return 1; } pid_t id = fork(); if(id < 0) { printf("fork erro"); return 2; } else if(id == 0)//子进程 { close(_pipe[0])//关闭输入 int i = 0; char * _mesg = NULL; while(i < 100) _mesg = " child "; write(_pipe[1],_mesg,strlen(_mesg)+1); sleep(1); i++; } else { close(_pipe[1]);//父进程读 char _mesgF[100]; int j = 0; while(j < 100) { memset(_mesgF,'\0',sizeof(_mesgF)); read(_pipe[0],_mesgF,sizeof(_mesgF)); printf("%s\n",_mesgF); j++; } } return 0;}
管道在使用的同时也存在一些局限性:
① 数据自己读不能自己写。
② 数据一旦被读走,便不在管道中存在,不可反复读取。
④ 只能在有公共祖先的进程间使用管道。(父子间)
命名管道
由于基于fork机制,所以管道只能用于父进程和子进程之间,或者拥有相同祖先的两个子进程之间 (有亲缘关系的进程之间)。为了解决这一问题,Linux提供了FIFO方式连接进程。FIFO又叫做命名管道(named PIPE)。
FIFO (First in, First out)为一种特殊的文件类型,它在文件系统中有对应的路径。当一个进程以读(r)的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道,所以FIFO实际上也由内核管理,不与硬盘打交道。之所以叫FIFO,是因为管道本质上是一个先进先出的队列数据结构,最早放入的数据被最先读出来,从而保证信息交流的顺序。FIFO只是借用了文件系统(file system,命名管道是一种特殊类型的文件,因为Linux中所有事物都是文件,它在文件系统中以文件名的形式存在。)来为管道命名。写模式的进程向FIFO文件中写入,而读模式的进程从FIFO文件中读出。当删除FIFO文件时,管道连接也随之消失。FIFO的好处在于我们可以通过文件的路径来识别管道,从而让没有亲缘关系的进程之间建立连接。
命名管道有两种创建方式,一是在Shell下交互地建立一个命名管道,二是在程序中使用系统函数建立命名管道。
Shell方式下建立命名管道使用系统函数mknod和mkfifo.函数调用如下。
#include <sys/types.h>#include <sys/stat.h>int mkfifo(const char *path, mode_t mode,dev_t dev);int mknode(const char *path,mode_t mode);
其中pathname是被创建的文件名称,mode表示将在该文件上设置的权限位,dev是当创建设备特殊文件时使用的一个值。这两个函数调用成功都返回0,失败都返回-1
下面我们来创建一个管道
因为现在我们创建的是一个命名管道,所以我们需要创建两个非亲缘进程来实验我们的管道
我们要创建一个读端和一个写端
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#define _PATH_ "/tmp/file.tmp"#define _SIZE_ 100int main()//读端{ int fd = open(_PATH_, O_RDONLY); if(fd < 0){ printf("open file error!\n"); return 1; } char buf[_SIZE_]; memset(buf, '\0', sizeof(buf)); while(1){ int ret = read(fd, buf, sizeof(buf)); if (ret <= 0)//error or end of file { printf("read end or error!\n"); break; } printf("%s\n", buf); if( strncmp(buf, "quit", 4) == 0 ){ break; } } close(fd); return 0;}
下面是写端
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#define _PATH_ "/tmp/file.tmp"#define _SIZE_ 100int main(){ int ret = mkfifo(_PATH_,0666|S_IFIFO); if(ret == -1){ printf("mkfifo error\n"); return 1; } int fd = open(_PATH_, O_WRONLY); if(fd < 0){ printf("open error\n"); } char buf[_SIZE_]; memset(buf, '\0', sizeof(buf)); while(1){ scanf("%s", buf); int ret = write(fd, buf, strlen(buf)+1); if(ret < 0){ printf("write error\n"); break; } if( strncmp(buf, "quit", 4) == 0 ){ break; } } close(fd); return 0;}
总结
管道是进程间通信的一种方式,而一般的管道我们称之为匿名管道,但是匿名管道有一个问题在于只能在亲缘进程之间使用,因此诞生出了一种新的不受亲缘约束的命名管道。
- Linux进程通信-管道(pipe)
- Linux--进程间通信(一)-管道(pipe)通信
- linux管道通信(pipe)
- linux 线程或进程之间通过管道通信(pipe)
- linux进程间通信之管道(pipe与fifo)
- 深入理解Linux进程间通信(IPC)-- 管道pipe
- 【Linux系统编程】进程间通信--无名管道(pipe)
- Linux进程通信(一)——pipe管道
- Linux进程间通信之管道(pipe)
- Linux环境进程间通信 --- 管道Pipe
- Linux 进程间管道pipe通信
- Linux进程间通信之管道(pipe)
- Linux进程间通信之管道(pipe)
- 进程通信----管道(pipe)
- Linux进程通信IPC--管道Pipe/Named Pipe
- 进程间通信:管道(pipe)
- 进程间通信--无名管道(pipe)
- 进程通信之管道(PIPE)
- spring+quartz定时器
- gdb远程调试示例
- 34_面向对象_07_构造器(构造方法)
- 蓝桥杯 ALGO-148 算法训练 5-1最小公倍数
- WeX5向data组件动态添加数据格式
- Linux进程通信-管道(pipe)
- Eclipse下更新android sdk慢的解决办法
- 方便人类——信息学训练专用库
- 缓冲区溢出——远程代码执行问题
- JVM结构
- 蓝桥杯 ALGO-122 算法训练 未名湖边的烦恼
- 网络安全工程师就是网管吗?
- 基本VLAN特性配置与管理——4
- 如何删除\200, \343特殊字符