管道(pipe)

普通的Linux shell都允许重定向,而重定向使用的就是管道。

例如:ps | grep vsftpd .管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的头端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。管道主要用于不同进程间通信。

可以通过打开两个管道来创建一个双向的管道。但需要在子进程中正确地设置文件描述符。必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。

相关函数:

//打开一个管道,2个int的数组fildes分别存储读端和写端的FD

Int pipe(int fildes[2]);

//管道读

ssize_t read(int fd, void* buf, size_t count);

//管道写

ssize_t write(int fd, const void* buf, size_t count);

代码说明:子进程写,父进程读

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include <string.h> #define READFD 0
#define WRITEFD 1 int main(int argc, char *argv[])
{
int pipe_fd[];
pid_t pid; if (pipe(pipe_fd) < )
{
printf("pipe create error\n");
exit();
} if ((pid = fork()) < )
{
printf("fork error\n");
exit();
} // parent
if (pid > )
{
char buf_r[];
memset(buf_r, , sizeof(buf_r)); close(pipe_fd[WRITEFD]);
sleep();
while (read(pipe_fd[], buf_r, ))
{
printf("print from parent ==> %s\n", buf_r);
}
close(pipe_fd[READFD]);
exit();
}
else if (pid == )
{
close(pipe_fd[READFD]);
write(pipe_fd[WRITEFD], "Hello", );
write(pipe_fd[WRITEFD], " Pipe", );
close(pipe_fd[WRITEFD]);
exit();
} exit();
}

结果说明:

[root@rocket ipc]# g++ -g -o ipc_pipe ipc_pipe.cpp

[root@rocket ipc]# ./ipc_pipe

print from parent ==> Hello Pipe

命名管道(FIFO)

命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的匿名管道(pipe)类似。

由于Linux中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常的统一,也使它的使用非常方便,同时我们也可以像平常的文件名一样在命令中使用。

相关函数:

//创建命名管道

int mkfifo(const char *filename, mode_t mode);

观察命名管道:

[root@rocket tmp]# file my_fifo

my_fifo: fifo (named pipe)

可以看出,命名管道是一种特殊的文件,可以按照文件的读写方式去操作。

访问命名管道

打开FIFO文件

与打开其他文件一样,FIFO文件也可以使用open调用来打开。注意,mkfifo函数只是创建一个FIFO文件,要使用命名管道还是要调用open将其打开。

有两点要注意:

1、就是程序不能以O_RDWR模式打开FIFO文件进行读写操作,而其行为也未明确定义,因为如一个管道以读/写方式打开,进程就会读回自己的输出,我们通常使用FIFO只是为了单向的数据传递。

2、就是传递给open调用的是FIFO的路径名,而不是正常的文件。

打开FIFO文件通常有四种方式,

open(const char *path, O_RDONLY);

open(const char *path, O_RDONLY | O_NONBLOCK);

open(const char *path, O_WRONLY);

open(const char *path, O_WRONLY | O_NONBLOCK);

在open函数的调用的第二个参数中,你看到选项O_NONBLOCK,选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。

open调用的阻塞是什么一回事呢?很简单,对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。

对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。

代码说明:fifo读写进程

写进程

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h> int main(int argc, char** argv)
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -;
int res = ;
const int open_mode = O_WRONLY; if(access(fifo_name, F_OK) == -)
{
//管道文件不存在
//创建命名管道
res = mkfifo(fifo_name, );
if(res != )
{
fprintf(stderr, "Could not create fifo %s\n", fifo_name);
exit(EXIT_FAILURE);
}
} printf("Process %d opening FIFO O_WRONLY\n", getpid());
//以只写阻塞方式打开FIFO文件,以只读方式打开数据文件
pipe_fd = open(fifo_name, open_mode);
printf("Process %d result %d\n", getpid(), pipe_fd); if(pipe_fd != -)
{
write(pipe_fd, "hello", );
write(pipe_fd, " fifo", );
printf("Process write finished\n", getpid());
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}

读进程

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h> int main(int argc, char** argv)
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -;
int res = ;
int open_mode = O_RDONLY;
char buffer[PIPE_BUF + ];
int bytes_read = ; //清空缓冲数组
memset(buffer, '\0', sizeof(buffer)); if(access(fifo_name, F_OK) == -)
{
//管道文件不存在
//创建命名管道
res = mkfifo(fifo_name, );
if(res != )
{
fprintf(stderr, "Could not create fifo %s\n", fifo_name);
exit(EXIT_FAILURE);
}
} printf("Process %d opening FIFO O_RDONLY\n", getpid());
//以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名
pipe_fd = open(fifo_name, open_mode);
printf("Process %d result %d\n",getpid(), pipe_fd);
sleep(); //这里sleep一下,先等写进程写数据
if (pipe_fd != -)
{
//读取FIFO中的数据
res = read(pipe_fd, buffer, PIPE_BUF);
bytes_read += res;
printf("get data %s \n", buffer); close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
} printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}

结果说明:

[root@rocket ipc]# g++ -g -o ipc_fifo_reader ipc_fifo_reader.cpp

[root@rocket ipc]# g++ -g -o ipc_fifo_writer ipc_fifo_writer.cpp

[root@rocket ipc]# ./ipc_fifo_writer

Process 74640 opening FIFO O_WRONLY

// 这里会阻塞住,直到ipc_fifo_reader调用open打开同一个fifo

Process 74640 result 3

Process write finished

Process 74640 finished

[root@rocket ipc]# ./ipc_fifo_reader

Process 74639 opening FIFO O_RDONLY

Process 74639 result 3

get data hello fifo

Process 74639 finished, 10 bytes read

两个程序都使用阻塞模式的FIFO,为了让大家更清楚地看清楚阻塞究竟是怎么一回事,首先我们运行./ipc_fifo_writer,发现其阻塞在open调用,直到./ipc_fifo_reader调用open,而./ipc_fifo_reader的open调用虽然也是阻塞模式,但是./ipc_fifo_writer早已运行,即早有另一个进程以写方式打开同一个FIFO,所以open调用立即返回。

Linux进程间通信(一) - 管道的更多相关文章

  1. Linux 进程间通信(二) 管道

    Linux 进程间通信-管道 进程是一个独立的资源分配单位,不同进程之间的资源是相互独立的,没有关联,不能在一个进程中直接访问另一个进程中的资源.但是,进程不是孤立的,不同的进程之间需要信息的交换以及 ...

  2. Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

    整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...

  3. Linux进程间通信之管道

    1,进程间通信 (IPC ) Inter-Process Communication 比较好理解概念的就是进程间通信就是在不同进程之间传播或交换信息. 2,linux下IPC机制的分类:管道.信号.共 ...

  4. Linux -- 进程间通信之管道

    管道是 Linux 里的一种文件类型,同时也是 Linux 系统下进程间通信的一种方式   创建一个管道文件有两种方式:  Shell 下命令 mkfifo + filename,即创建一个有名管道 ...

  5. linux进程间通信-有名管道(FIFO)

    有名管道(FIFO) 命名管道也被称为FIFO文件,是一种特殊的文件.由于linux所有的事物都可以被视为文件,所以对命名管道的使用也就变得与文件操作非常统一. (1)创建命名管道 用如下两个函数中的 ...

  6. Linux进程间通信-命名管道

    前面我们讲了进程间通信的一种方式,匿名管道.我们知道,匿名管道只能用于父子关系的进程之间.那么没有这种关系的进程之间该如何进行数据传递呢? 1.什么是命名管道 匿名管道是在缓存中开辟的输出和输入文件流 ...

  7. Linux进程间通信-匿名管道

    前面我们讲了进程间通信的一种方式,共享内存.下面看一看另一种机制,匿名管道.1.什么是管道管道是一个进程的数据流到另一个进程的通道,即一个进程的数据输出作为另一个进程的数据输入,管道起到了桥梁的作用. ...

  8. linux进程间通信--有名管道

    有名管道 只有当一个库函数失败时,errno才会被设置.当函数成功运行时,errno的值不会被修改.这意味着我们不能通过测试errno的值来判断是否有错误存在.反之,只有当被调用的函数提示有错误发生时 ...

  9. linux进程间通信--无名管道

    管道 只能用于具有亲缘关系的进程之间通信是一个半双工的通信模式, 具有固定的写读端和写端,管道可以看成一种特殊的文件,对它可以使用普通的read.write等操作 管道的创建: #include &l ...

随机推荐

  1. 发布Android开源库,看这个文章就够了!

    最近在Flipboard实习期间写了一个轮播工具,技术上没什么难点,不过动画效果还是不错的,决定改改代码写个库开源出去.项目地址:http://github.com/chengdazhi/Decent ...

  2. 最新IP地址数据库Dat格式-高性能高并发版(2019年3月)

    最新IP地址数据库->Dat  二进制文件 高性能高并发-qqzeng-ip.dat 格式 全球IP数据库-20190301-Dat 版                国内IP数据库-20190 ...

  3. 论坛中不同类型的贴的排序问题(涉及数据库的:CASE... END)

    在论坛中,会有不同类型的帖子,比如说:普通贴.精华帖.置顶帖: 论坛的这些贴子会根据不同的类型进行排序,当然不仅仅只是看帖子类型,还有贴子的动态情况来进行排序. 在这里演示一下简单的帖子排序,我们只关 ...

  4. [转载]SecureCRT 绝佳配色方案, 保护你的眼睛

    FROM:http://blog.csdn.net/zklth/article/details/8937905 SecureCRT 绝佳配色方案, 保护你的眼睛   关键词:SecureCRT配色,  ...

  5. ASP.NET MVC学习---(四)MVC初窥

    前面三篇大幅度的介绍了EF框架 这并不是没有道理的 现在使用mvc开发一般都离不开ef 因为它们相结合可以为你带来完美的体验 当然 前面所描述的仅仅是ef框架的冰山一角 它是一门学问很深的功课 如果你 ...

  6. 软件测试技术---Web应用软件测试

    从测试的角度看,Web应用软件的以下特点会导致Web应用软件的测试有别于其他软件的测试 1.基于无连接协议 2.内容驱动 3.开发周期短 4.演化频繁 5.安全性要求较高 6.美观性要求较高 Web应 ...

  7. How is javascript asynchronous AND single threaded?

    原文: https://www.sohamkamani.com/blog/2016/03/14/wrapping-your-head-around-async-programming/ ------- ...

  8. 转:myeclipse和eclipse的区别和联系,以及版本间的对应关系

    myeclipse和eclipse的区别和联系,以及版本间的对应关系 Eclipse:IBM花了4千万美金来开发这个IDE(Integrated Development Environment).第一 ...

  9. Java模式的秘密--java常用的几种模式

    要学习设计模式,首先要明白设计模式,就是为实现某一种或某一组功能提供的代码编码方式.它没有固定的套路,只有约定俗成的风格.所有编码者可以根据已有的设计模式开放思维,设计出自己的设计模式,也会在无意中使 ...

  10. java检测http请求的ip地址 Java问题通用解决代码

    以后再用到的话,至少能起个参考作用 java实现,struts2的Action中,依赖HttpServletRequest     package net.dookoo.web.action;   i ...