linux中的pipe和java fifo pipe的区别

博客访问: 117788
博文数量: 35
博客积分: 2146
博客等级: 大尉
技术积分: 370
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
linux中的pipe和fifo的区别
在linux进程间通信(IPC)可以通过信号量、文件系统、消息队列、共享内存还有管道来实现的。其中消息队列、内存管理是在System
V中提出的。
进程通信间涉及到了管道,而且管道在shell命令中也大有用处。那就简要说说管道:
管道顾名思义,你可以将其理解为日常生活中的管子,一边流入,一边流出。它可以有半双工和全双工。半双工就是只能一边流入,另一边流出;全双工则是一边可以流入,也可以流出。
pipe就是一种半双工的管道。其中,fd[1]
用来向管道中写入数据,而fd[0]在另一端用来读出数据。如果现有两个进程要利用pipe进行通信。此时,就要保证只能有一个写入端和一个读出端,即:fd[1]和fd[0]只能有一个。
如下程序:
/*实现子进程向管道中写入数据,父进程读出数据*/
#include&unistd.h&
#include&sys/types.h&
#include&stdio.h&
#include&stdlib.h&
#include&string.h&
string[]="data from child process\n";
if(pipe(fd)&0)
perror("pipe");
if((childpid=fork())==-1)
perror("fork");
if(childpid==0)
close(fd[0]);
printf("childpid
=%2d\n",getpid());
write(fd[1],string,strlen(string));
close(fd[1]);
printf("parentpid
=%2d\n",getppid());
nbytes=read(fd[0],buf,sizeof(buf));
printf("Received
string:%s\n",buf);
下面来说道fifo:
fifo是一种全双工,即:它的一端既可以进行读取fd[0],也可以进行写入fd[1]。
正因为它的这种通信方式,使其可以用来涉及基于C/S模式的网络通信。具体做法:
首先让服务器产生一个服务器端的FIFO,然后让各个客户端产生以其PID为名称的客户端的FIFO,在客户于服务器进行通信时,客户端向服务器端发送自己的PID,以使服务器对客户的请求进行响应时,向其客户端的FIFO写入响应信息。代码实现客户端和服务器进行各自的名称和PID交换。
具体代码见:
/index.php
阅读(4520) | 评论(0) | 转发(2) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。Linux系统编程(9)
一、进程间通信
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。如下图所示。
二、管道是一种最基本的IPC机制:
& &管道是linux进程间通信的一种方式,其是利用管道“文件”作为不同进程之间的传输数据的媒介,而实现进程间的数据交换。
& &而无名管道pipe则是利用内核虚拟出来的管道“文件”来作为不同进程间数据传输通道,而并非实际存在真正意义上的文件。&
& &&& &&&1、无名管道只能用于具有亲缘关系的进程之间的通信,并且要由父进程来创建。(eg:父子进程间通信)
2、通信方式是单工的,意味着要实现两个进程间的双向通信,必须建立两个管道。
& &&& &&&3、其存在于内核为其分配的内核空间在。
管道读写注意事项:
1.必须在系统调用fork()中调用pipe(),否则子进程将不会继承文件描述符;
2.当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。
2、匿名管道的用法:
#include &unistd.h&
int pipe(int filedes[2]);
1.pipe()会建立管道,并将文件描述词由参数filedes数组返回。
2.&filedes[0]为管道里的读取端
& & filedes[1]则为管道的写入端。
3.若成功则返回零,否则返回-1,错误原因存于errno中。
&错误代码:&
&&&&&&&& EMFILE 进程已用完文件描述词最大量
&&&&&&& &ENFILE 系统已无文件描述词可用。
&&&&&&&& EFAULT 参数 filedes 数组地址不合法。
调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。pipe函数调用成功返回0,调用失败返回-1。
开辟了管道之后如何实现两个进程间的通信呢?比如可以按下面的步骤通信。
#include&sys/types.h&
#include&sys/stat.h&
#include&unistd.h&
#include&fcntl.h&
#include&stdio.h&
#include&stdlib.h&
#include&errno.h&
#include&string.h&
#include&signal.h&
int main(int argc, char *argv[])
int pipefd[2];
if (pipe(pipefd) == -1)
perror(&pipe error&);
pid = fork();
if (pid == -1)
perror(&fork error&);
if (pid == 0)
close(pipefd[0]);
write(pipefd[1], &hello&, 5);
close(pipefd[1]);
exit(EXIT_SUCCESS);
close(pipefd[1]);
char buf[10] = {0};
read(pipefd[0], buf, 10);
printf(&buf=%s\n&, buf);
}1. 父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。
2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
3. 父进程关闭管道写端,子进程关闭管道读端。子进程可以往管道里写,父进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。
使用管道有一些限制:
两个进程通过一个管道只能实现单向通信,比如最上面的例子,父进程读子进程写,如果有时候也需要子进程读父进程写,就必须另开一个管道。
管道的读写端通过打开的文件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那里继承管道文件描述符。上面的例子是父进程把文件描述符传给子进程之后父子进程之间通信,也可以父进程fork两次,把文件描述符传给两个子进程,然后两个子进程之间通信,总之需要通过fork传递文件描述符使两个进程都能访问同一管道,它们才能通信。
进程间通信必须通过内核提供的通道,而且必须有一种办法在进程中标识内核提供的某个通道,前面讲过的是用打开的文件描述符来标识的。如果要互相通信的几个进程没有从公共祖先那里继承文件描述符,它们怎么通信呢?内核提供一条通道不成问题,问题是如何标识这条通道才能使各进程都可以访问它?文件系统中的路径名是全局的,各进程都可以访问,因此可以用文件系统中的路径名来标识一个IPC通道。
FIFO和UNIX
Domain Socket这两种IPC机制都是利用文件系统中的特殊文件来标识的。
FIFO文件在磁盘上没有数据块,仅用来标识内核中的一条通道.
2.命名管道
& &与无名管道的不同之处在于,有名管道fifo的管道文件是真正意义上的文件,其由用户自定义分配。& &
& &&& &&&1、有名管道fifo可以用于任意不同进程间的通信,不仅仅局限于具有亲缘关系的进程之间。
& &&& &&&2、其存储路径是用户定义的,有名管道文件是真正意义上的文件。
2、命名管道的用法:
命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
$ mkfifo filename或者
& & 1、创建有名管道:&
int mkfifo(const char *pathname, mode_t mode);
& & 2、打开管道 & &:&
int open(const char *pathname, int flags);
& & 3、读写、关闭有名管道与操作无名管道一致。
3、创建有名管道(mkfifo)时错误返回信息:
& & & &EACCES One &of &the &directories in pathname did not allow search (exe‐
& & & & & & & cute) permission.
& & & &EEXIST pathname already exists. &This includes the case where &pathname
& & & & & & & is a symbolic link, dangling or not.
& & & &ENAMETOOLONG
& & & & & & & Either the total length of pathname is greater than PATH_MAX, or
& & & & & & & an individual filename &component &has &a &length &greater &than
& & & & & & & NAME_MAX. &In the GNU system, there is no imposed limit on over‐
& & & & & & & all filename length, but some file systems may place &limits &on
& & & & & & & the length of a component.
& & & &ENOENT A &directory &component &in pathname does not exist or is a dan‐
& & & & & & & gling symbolic link.
&&&&以上只列出几种,其中需要稍加注意第2种,其用于判断创建的管道是否已经存在.
命名管道的打开规则
如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
O_NONBLOCK enable:立刻返回成功
如果当前打开操作是为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
需要注意的是打开的文件描述符默认是阻塞的.
系统加于管道和FIFO的唯一限制是:
1、OPEN_MAX 一个进程在任意时刻打开的最大描述符数。可以通过调用sysconf函数查询。
2、PIPE_BUF 可原子地写往一个管道或FIFO的最大数据量。Posix任务它是一个路径名变量,它的值可以随指定的路径名而变化,因为不同的路径名可以落在不同文件系统上,而这些文件系统可能有不同的特征。所以PIPE_BUF可通过pathconf函数取得。
pipeconf.c
#include &stdio.h&
#include &stdlib.h&
#include &unistd.h&
main(int argc, char **argv)
if(argc != 2)
printf(&usage:pipeconf &pathname&&);
printf(&PIPE_BUF = %ld, OPEN_MAX = %ld\n&,
pathconf(argv[1], _PC_PIPE_BUF), sysconf(_SC_OPEN_MAX));
对管道或FIFO能以两种方式设置成非阻塞:
1、调用open时可指定O_NONBLOCK标志。
writefd = open(FIFO1, O_WRONLY | O_NONBLOCK, 0);
2、如果一个描述符已经打开,可调用fcntl启用O_NONBLOCK标志。对于管道来说,必须使用这种技术,因为管道没哟open调用,在pipe调用中也无法指定O_NONBLOCK标志。使用fcntl时,先使用F_GETFL命令取得当前文件状态标志,将它与O_NONBLOCK标志按位或,再使用F_SETFL命令存储这些文件状态标志。
if((flags = fcntl(fd, F_GETFL, 0) & 0))
err_sys(&F_GETEFL error&);
flags |= O_NONBLOCK;
if((flags = fcntl(fd, F_SETFL, 0) & 0))
err_sys(&F_SETEFL error&);
下面用一个简单程序,演示FIFO IPC的用法。
该程序分为2端:
server程序创建一个FIFO,并从FIFO读取字符,转换成大写后输出到屏幕。
#include &stdio.h&
#include &sys/types.h&
#include &sys/stat.h&
#include &fcntl.h&
#define FIFO_PATH &/tmp/myfifo&
int main()
/*建立FIFO*/
ret = mkfifo(FIFO_PATH, 0777);
/*打开FIFO*/
fd = open(FIFO_PATH, O_RDONLY);
if(-1 == fd)
printf(&error/n&);
return -1;
nread = read(fd, &buffer, 1);
if(nread & 0)
buffer = toupper(buffer); //将字符c转换为大写英文字母.如果c为小写英文字母.
//则返回对应的大写字母;否则返回原来的值。
printf(&%c&, buffer);
运行server后,可看到创建了文件/tmp/myfifo,这是mkfifo函数指定的命名管道的路径(名字)。
当然,系统不会真的在磁盘上创建这个文件。
client程序读取用户输入并写入FIFO。
#include &stdio.h&
#include &sys/types.h&
#include &sys/stat.h&
#include &fcntl.h&
#define FIFO_PATH &/tmp/myfifo&
int main()
fd = open(FIFO_PATH, O_WRONLY);
if(-1 == fd)
printf(&error/n&);
return -1;
while(c = getchar())
write(fd, &c, 1);
先启动server程序,再运行client,随便输入些字符。
server端将在屏幕上显示转换为大写后的输入字符。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:42596次
排名:千里之外
原创:51篇
转载:18篇
(3)(1)(14)(6)(17)(6)(11)(1)(1)(3)(3)(3)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'2015年3月(71)
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:193910次
积分:2637
积分:2637
排名:第14651名
原创:17篇
转载:325篇
(10)(22)(56)(186)(72)
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'1408人阅读
细说linux IPC系列(10)
【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流,请勿用于商业用途】
在unix系统上最早的IPC形式为管道,管道的创建使用pipe函数:
#include &unistd.h&
int pipe(int pipefd[2]);
该函数创建一个单向的管道,返回两个描述符 pipefd[0],和pipefd[1],pipefd[0]用于读操作,pipefd[1]用于写操作。该函数一般应用在父子进程(有亲缘关系的进程)之间的通信,先是一个进程创建管道,再fork出一个子进程,然后父子进程可以通过管道来实现通信。
管道具有以下特点:
管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
函数pipe一般使用步骤如下:
1.pipe创建管道;
2.fork创建子进程;
3.父子进程分别关闭掉读和写(或写和读)描述符;
4.读端在读描述符上开始读(或阻塞在读上等待写端完成写),写端开始写,完成父子进程通信过程。
一个简单的通信实现(来自linux man手册的修改):
#include &sys/wait.h&
#include &stdio.h&
#include &stdlib.h&
#include &unistd.h&
#include &string.h&
#include &errno.h&
int main(int argc, char *argv[])
int pipefd[2];
char buf[128];
if (argc != 2) {
fprintf(stderr, &Usage: %s &string&\n&, argv[0]);
return -1;
if (pipe(pipefd) & 0) {
fprintf(stderr, &pipe: %s\n&, strerror(errno));
return -1;
cpid = fork();
if (cpid & 0) {
fprintf(stderr, &fork: %s\n&, strerror(errno));
return -1;
if (0 == cpid) { /* 子进程 */
close(pipefd[1]); /* 子进程关闭写端 */
readlen = read(pipefd[0], buf, 128); //子进程阻塞在读上,等待父进程写
if (readlen & 0) {
fprintf(stderr, &read: %s\n&, strerror(errno));
return -1;
write(STDOUT_FILENO, buf, readlen);
write(STDOUT_FILENO, &\n&, 1);
close(pipefd[0]); //读完之后关闭读描述符
} else { /* 父进程 */
close(pipefd[0]); /*父进程关闭没用的读端 */
write(pipefd[1], argv[1], strlen(argv[1])); //父进程开始写
close(pipefd[1]); /* 父进程关闭写描述符 */
wait(NULL); /* 父进程等待子进程退出,回收子进程资源 */
运行时将打印命令行输入参数,打印将在父进程睡眠2秒之后,子进程将阻塞在读,直到父进程写完数据,可见管道是有同步机制的,不需要自己添加同步机制。如果希望两个进程双向数据传输,那么需要建立两个管道来实现。
管道最大的劣势就是只能在拥有共同祖先进程的进程之间通信,在无亲缘关系的两个进程之间没有办法使用,不过有名管道FIFO解决了这个问题。FIFO类似于pipe,也是只能单向传输数据,不过和pipe不同的是他可以在无亲缘关系的进程之间通信,它提供一个路径与之关联,所以只要能访问该路径的进程都可以建立起通信,类似于前面的共享内存,都提供一个路径与之关联。
#include &sys/types.h&
#include &sys/stat.h&
int mkfifo(const char *pathname, mode_t mode);pathname 为系统路径名,mode为文件权限位,类似open函数第二个参数。
打开或创建一个新的fifo是先调用mkfifo,当指定的pathname已存在fifo时,mkfifo返回EEXIST错误,此时再调用open函数。
下面来使用mkfifo实现一个无亲缘关系进程间的双向通信,此时需要建立两个fifo,分别用于读写。服务进程循环的读并等待客户进程写,之后打印客户进程传来数据并向客户进程返回数据;客户进程向服务器写数据并等待读取服务进程返回的数据。
server process:
#include &stdio.h&
#include &string.h&
#include &errno.h&
#include &sys/stat.h&
#include &sys/types.h&
#include &fcntl.h&
#include &slnipc.h&
int main(int argc, const char *argv[])
int wr_fd, rd_
char sendbuf[128];
char recvbuf[128];
rc = mkfifo(SLN_IPC_2SER_PATH, O_CREAT | O_EXCL); //建立服务进程读的fifo
if ((rc & 0 ) && (errno != EEXIST)) {
fprintf(stderr, &mkfifo: %s\n&, strerror(errno));
return -1;
rc = mkfifo(SLN_IPC_2CLT_PATH, O_CREAT | O_EXCL); //建立服务进程写的fifo
if ((rc & 0 ) && (errno != EEXIST)) {
fprintf(stderr, &mkfifo: %s\n&, strerror(errno));
return -1;
wr_fd = open(SLN_IPC_2CLT_PATH, O_RDWR, 0);
if (wr_fd & 0) {
fprintf(stderr, &open: %s\n&, strerror(errno));
return -1;
rd_fd = open(SLN_IPC_2SER_PATH, O_RDWR, 0);
if (rd_fd & 0) {
fprintf(stderr, &open: %s\n&, strerror(errno));
return -1;
for (;;) {
rc = read(rd_fd, recvbuf, sizeof(recvbuf)); //循环等待接受客户进程数据
if (rc & 0) {
fprintf(stderr, &read: %s\n&, strerror(errno));
printf(&server recv: %s\n&, recvbuf);
snprintf(sendbuf, sizeof(sendbuf), &Hello, this is server!\n&);
rc = write(wr_fd, sendbuf, strlen(sendbuf));
if (rc & 0) {
fprintf(stderr, &write: %s\n&, strerror(errno));
close(wr_fd);
close(rd_fd);
client process:
#include &stdio.h&
#include &sys/stat.h&
#include &sys/types.h&
#include &fcntl.h&
#include &string.h&
#include &errno.h&
#include &slnipc.h&
int main(int argc, const char *argv[])
int rd_fd, wr_
char recvbuf[128];
char sendbuf[128];
if (argc != 2) {
fprintf(stderr, &Usage: %s &string&\n&, argv[0]);
return -1;
snprintf(sendbuf, sizeof(sendbuf), &%s&, argv[1]);
wr_fd = open(SLN_IPC_2SER_PATH, O_RDWR, 0);
if (wr_fd & 0) {
fprintf(stderr, &open: %s\n&, strerror(errno));
return -1;
rd_fd = open(SLN_IPC_2CLT_PATH, O_RDWR, 0);
if (rd_fd & 0) {
fprintf(stderr, &open: %s\n&, strerror(errno));
return -1;
rc = write(wr_fd, sendbuf, strlen(sendbuf));
if (rc & 0) {
fprintf(stderr, &write: %s\n&, strerror(errno));
return -1;
rc = read(rd_fd, recvbuf, sizeof(recvbuf));
if (rc & 0) {
fprintf(stderr, &write: %s\n&, strerror(errno));
return -1;
printf(&client read: %s\n&, recvbuf);
close(wr_fd);
close(rd_fd);
服务器先启动运行,之后运行客户端,运行结果
# ./server
server recv: hi,this is fifo client
# ./client &hi,this is fifo client&
client read: Hello, this is server!
这里有一些类似于socket实现进程间通信过程,只是fifo的读写描述符是两个,socket的读写使用同一个描述符。fifo的出现克服了管道的只能在有亲缘关系的进程之间的通信。和其他的进程间通信一直,fifo传送的数据也是字节流,需要自己定义协议格式来解析通信的数据,可以使用socket章节介绍的方式来实现的通信协议。
本节源码下载:
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:316316次
积分:5263
积分:5263
排名:第5537名
原创:165篇
译文:14篇
评论:58条
文章:14篇
阅读:21827
(window.slotbydup = window.slotbydup || []).push({
id: '4740881',
container: s,
size: '200,200',
display: 'inlay-fix'}

我要回帖

更多关于 linux sigpipe 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信