IPC之管道详解


基本概念:

linux管道(也叫无名管道)由pipe函数创建,提供一个单路(单向)数据流。(注:linux不支持全双工的管道,某些系统支持

PIPE(2)                                                  Linux Programmer's Manual                                                  PIPE(2)

NAME
       pipe, pipe2 - create pipe

SYNOPSIS
       #include <unistd.h>

       int pipe(int pipefd[2]);
返回:若成功则为0,若出错则为-1

该函数返回两个文件描述符:fd[0]和fd[1]。前者打开来读,后者打开来写。


管道的典型用途是以下述方式为两个不同进程(一个是父进程,一个是子进程)提供进程间通信的手段。首先,由一个进程(它将成为父进程)创建一个管道后调用fork派生一个自身的副本,如图4-3所示:

接着,父进程关闭这个管道的读出端,子进程关闭同一管道的写入端。这就在父子进程间提供了一个单向数据流,如图4-4所示:

我们在某个shell中输入一个像下面这样的命令时:

who | sort | lp

该shell将执行上述步骤创建三个进程和其间的两个管道。它还把每个管道的读出端复制到相对应进程的标准输入,把每个管道的写入端复制到相应进程的标准输出。图4-5展示了这样的管道线:



到此为止所示的所有管道都是半双工的即单向的,只提供一个方向的数据流。当需要一个双向数据流时,我们必须创建两个管道,每个方向一个。实际步骤如下:

(1)创建管道1(fd1[0]和fd1[0])和管道2(fd2[0]和fd2[0])

(2)fork

(3)父进程关闭管道1的读出端(fd1[0])

(4)父进程关闭管道2的写入端(fd2[1])

(5)子进程关闭管道1的写入端(fd1[1])

(6)子进程关闭管道2的读出端(fd2[0])

它产生如图4-6所示的管道布局


例子1,提供一个双向数据流的两个管道,这里的pipe返回的文件描述符默认是阻塞模式。

gcc pipe.c

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

#define MAXLINE 1024

void Perror(const char *s)
{
    perror(s);
    exit(EXIT_FAILURE);
}

void client(int readfd, int writefd)
{
    /* read msg  */
    int i = 0;
    for (i; i<3; i++) {
        char buff[MAXLINE] = {0};
        int n = read(readfd, buff, MAXLINE);
        if (n > 0) {
            printf("read from server:%s\n", buff);
        }
    }
    char *buff = "goodby server";
    write(writefd, buff, strlen(buff));
}

void server(int readfd, int writefd)
{
    /* send msg  */
    int i = 0;
    for (i; i<3; i++) {
        char buff[MAXLINE] = {0};
        sprintf(buff, "hello world %d", i);
        int n = write(writefd, buff, strlen(buff));
        //printf("send to client:%d bytes\n", n);
        sleep(1);
    }
    char buff[MAXLINE] = {0};
    int n = read(readfd, buff, MAXLINE);
    if (n > 0) {
        printf("read from client:%s\n", buff);
    }
}

int main()
{
    int pipe1[2], pipe2[2];
    pid_t childpid;

    if (pipe(pipe1) == -1) {
        Perror("create pipe1 failed");
    }
    if (pipe(pipe2) == -1) {
        Perror("create pipe2 failed");
    }

    childpid = fork();
    if (childpid == 0) { /* child */
        close(pipe1[1]);
        close(pipe2[0]);

        server(pipe1[0], pipe2[1]);
        exit(0);
    }
    close(pipe1[0]);
    close(pipe2[1]);

    client(pipe2[0], pipe1[1]);

    /* parent */
    waitpid(childpid, NULL, 0);

    return 0;
}

例子1运行结果:



关于管道的大小,可以通过ulimit -a或者ulimit -p命令查看,显示结果为512*8=4kb,实际值并不是这个,后面通过实验证明。



例子2,管道大小的测试

gcc pipe_size.c

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

#define MAXLINE 1024

void Perror(const char *s)
{
    perror(s);
    exit(EXIT_FAILURE);
}

void client(int readfd, int writefd)
{
    /* read msg  */
    int i = 0;
    while (1) {
        sleep(1);
    }
}

void server(int readfd, int writefd)
{
    /* read msg  */
    int i = 0;
    int sum = 0;
    for (i; i<=1000000; i++) {
        char buff[MAXLINE] = "0123456789";
        int n = write(writefd, buff, strlen(buff));
        sum += n;
        printf("send to pipe:%d bytes, i:%d\n", sum, i);
    }
}

int main()
{
    int pipe1[2], pipe2[2];
    pid_t childpid;

    if (pipe(pipe1) == -1) {
        Perror("create pipe1 failed");
    }
    if (pipe(pipe2) == -1) {
        Perror("create pipe2 failed");
    }

    childpid = fork();
    if (childpid == 0) { /* child */
        close(pipe1[1]);
        close(pipe2[0]);

        server(pipe1[0], pipe2[1]);
        exit(0);
    }
    close(pipe1[0]);
    close(pipe2[1]);

    client(pipe2[0], pipe1[1]);

    /* parent */
    waitpid(childpid, NULL, 0);

    return 0;
}

例子2运行结果:


分析运行结果发现,管道的size与ulimit返回的结果并不相符合。实际上管道的大小是64kb,这也与测试结果吻合。

管道大小的分析文章,可以参考:http://blog.csdn.net/judwenwen2009/article/details/44134415


关于数据持久性:当一个管道或FIFO的最后一次关闭发生时,仍在该管道或FIFO上的数据将被丢弃。


关于更多IPC之管道和FIFO的额外属性


参考:《unix网络编程》·卷2

End;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值