浅谈Linux管道 - 极悦
专注Java教育14年 全国咨询/投诉热线:444-1124-454
极悦LOGO图
始于2009,口口相传的Java黄埔军校
首页 hot资讯 浅谈Linux管道

浅谈Linux管道

更新时间:2020-12-16 17:42:17 来源:极悦 浏览1275次

管道(pipe)是一个我们在学习Linux命令行的时候就会引入的一个很重要的概念。管道是UNIX环境中历史最悠久的进程间通信方式,从本质上说,管道也是一种文件,也是遵循UNIX的“一切皆文件”的原则设计的。虽然实现形态上是文件,但是管道本身并不占用磁盘或者其他外部存储的空间。在Linux的实现上,它占用的是内存空间。所以,Linux管道实际上就是一个操作方式为文件的内存缓冲区。

 

一、Linux管道分两种类型:匿名管道和命名管道也叫做有名或无名管道

匿名管道最常见的形态就是我们在shell操作中最常用的”|”。它的特点是只能在父子进程中使用,父进程在产生子进程前必须打开一个管道文件,然后fork产生子进程,这样子进程通过拷贝父进程的进程地址空间获得同一个管道文件的描述符,以达到使用同一个管道通信的目的。此时除了父子进程外,没人知道这个管道文件的描述符,所以通过这个管道中的信息无法传递给其他进程。这保证了传输数据的安全性,当然也降低了管道了通用性,于是系统还提供了命名管道。

 

二、Linux管道的实现机制    

在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题:

1.限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不象文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。

 

2.读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。注意:从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。

 

三、linux管道的结构

在 Linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的。

 

四、linux管道的代码示例

管道由pipe函数创建:

#include <unistd.h>

int pipe(int filedes[2]);

 

调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端。向这个文件读写数据其实是在读写内核缓冲区。pipe函数调用成功返回0,调用失败返回-1。

 

子进程通过管道向父进程发送数据。限制在父子进程间通信。

#include<stdlib.h>

#include<stdio.h>

#include<sys/types.h>

#include<sys/wait.h>

#include<unistd.h>

#include<string.h>

 

int main () {

    char* msg;

    char buf[20];

    int pipe_filed[2];

    pipe(pipe_filed);

    pid_t pid = fork();

    if(pid < 0) {

        perror("fork errir.");

        exit(1);

    } else if (0 == pid) {

        msg = "child";

        write(pipe_filed[1], msg, sizeof(msg));

        printf("child process send: %s\n", msg);

    } else {

        read(pipe_filed[0], buf, sizeof(buf));

        printf("parent process recv: %s\n", buf);

 

        int status;

        wait(&status);

        if (WIFEXITED(status))

            printf("Child exited with code %d\n", WEXITSTATUS(status));

        else if (WIFSIGNALED(status))

            printf("Child terminated abnormally, signal %d\n", WTERMSIG(status));

    }

 

    return 0;

}

两个进程通过一个管道只能实现单向通信,比如上面的例子,子进程写父进程读,如果有时候也需要父进程写子进程读,就必须另开一个管道。

 

管道其实是一个在内核内存中维护的缓冲器,这个缓冲器的存储能力是有限的。管道被填满之后,后续向管道写入操作都会被堵塞直到有读取进程读取管道中的数据。至于如何从Linux管道中读取数据,可以在本站的Linux教程中找到详细的解答。


提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>