无名管道系统调用

分享到:
           

    本文关键字: linux 管道通信,linux 进程通信方式 ,无名管道

    1.管道创建与关闭说明

    管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1],其中fd[0]固定用于读管道,而fd[1]固定用于写管道,如图1所示,这样就构成了一个半双工的通道。


图1 无名管道的读写机制

    管道关闭时只需将这两个文件描述符关闭即可,可使用普通的close()函数逐个关闭各个文件描述符。

    2.管道创建函数

    创建管道可以通过调用pipe()来实现。表1列出了pipe()函数的语法要点。

表1 pipe()函数语法要点

所需头文件 #include <unistd.h>
函数原型 int pipe(int fd[2])
函数传入值 fd[2]:管道的两个文件描述符,之后就可以直接操作这两个文件描述符
函数返回值 成功:0
出错:-1

    3.管道读写说明

    用pipe()函数创建的管道两端处于一个进程中,由于管道是主要用于在不同进程间通信的,因此在实际应用中没有太大意义。实际上,通常先是创建一个管道,再调用fork()函数创建一个子进程,该子进程会继承父进程所创建的管道,这时,父子进程管道的文件描述符对应关系如图2所示。


图2 父子进程管道的文件描述符对应关系

    此时的关系看似非常复杂,实际上却已经给不同进程之间的读写创造了很好的条件。父子进程分别拥有自己的读写通道,为了实现父子进程之间的读写,只需把无关的读端或写端的文件描述符关闭即可。例如,在图3中将父进程的写端fd[1]和子进程的读端fd[0]关闭。此时,父子进程之间就建立起了一条“子进程写入父进程读取”的通道。


图3 关闭父进程fd[1]和子进程fd[0]

    同样,也可以关闭父进程的fd[0]和子进程的fd[1],这样就可以建立一条“父进程写入子进程读取”的通道。另外,父进程还可以创建多个子进程,各个子进程都继承了相应的fd[0]和fd[1]。这时,只需关闭相应端口就可以建立其各子进程间的通道。

    4.管道读写注意点

    管道读写需注意以下几点:

    ● 只有在管道的读端存在时,向管道写入数据才有意义。否则,向管道写入数据的进程将收到内核传来的SIGPIPE信号(通常为Broken pipe错误)。

    ● 向管道写入数据时,Linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读取管道缓冲区中的数据,那么写操作将会一直阻塞。

    ● 父子进程在运行时,它们的先后次序并不能保证。因此,为了保证父子进程已经关闭了相应的文件描述符,可在两个进程中调用sleep()函数。当然这种调用不是很好的解决方法,在后面学到进程之间的同步机制与互斥机制后,请读者自行修改本小节的实例程序。

    5.使用实例

    在本例中,首先创建管道,之后父进程使用fork()函数创建子进程,后通过关闭父进程的读描述符和子进程的写描述符,建立起它们之间的管道通信。

    /* pipe.c */
    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #define MAX_DATA_LEN 256
    #define DELAY_TIME 1

    int main()
    {
        pid_t pid;
        int pipe_fd[2];
        char buf[MAX_DATA_LEN];
        const char data[] = "Pipe Test Program";
        int real_read, real_write;

        memset((void*)buf, 0, sizeof(buf));
        if (pipe(pipe_fd) < 0) /* 创建管道 */
        {
            printf("pipe create error\n");
            exit(1);
        }
        if ((pid = fork()) == 0) /* 创建一个子进程 */
        {
            /* 子进程关闭写描述符,并通过使子进程暂停1s等待父进程已关闭相应的读描述符 */
            close(pipe_fd[1]);
            sleep(DELAY_TIME * 3);
            /* 子进程读取管道内容 */
            if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
            {
                printf("%d bytes read from the pipe is '%s'\n", real_read, buf);
            }
            close(pipe_fd[0]); /* 关闭子进程读描述符 */
            exit(0);
        }
        else if (pid > 0)
        {
            /* 父进程关闭读描述符,并通过使父进程暂停1s等待子进程已关闭相应的写描述符 */
            close(pipe_fd[0]);
            sleep(DELAY_TIME);
            if((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
            {
                printf("Parent wrote %d bytes : '%s'\n", real_write, data);
            }
            close(pipe_fd[1]); /* 关闭父进程写描述符 */
            waitpid(pid, NULL, 0); /* 收集子进程退出信息 */
            exit(0);
        }
    }

    将该程序交叉编译,下载到开发板上的运行结果如下:

    $ ./pipe
    Parent wrote 17 bytes : 'Pipe Test Program'
    17 bytes read from the pipe is 'Pipe Test Program

    本文选自华清远见嵌入式培训教材《从实践中学嵌入式Linux应用程序开发》

   热点链接:

   1、Linux下进程间通信方式-管道
   2、Linux下进程间通信
   3、实验:编写守护进程
   4、实验:编写多进程程序
   5、Linux守护进程

更多新闻>>