Linux系统编程系列之进程间通信(IPC)-管道

一、什么是管道

        在Linux系统下,一切皆文件,所以管道就是一个文件,用来实现进程间通信的一种方式。分析小技巧:对于一些陌生的概念,都把它当成是文件,然后操作的时候,就是三部曲。文件打开,文件读写,文件关闭。

二、有哪几种管道

        匿名管道具名管道。有些地方又称为无名管道和有名管道。

三、管道的特性

        1、匿名管道(PIPE)

               (1) 匿名管道没有名称,因此无法使用open创建或者打开,事实上匿名管道有自己独特的创建接口。

                (2)匿名管道只能用于父子进程之间的通信,不能用于别的进程。因为匿名管道没有名称,别的进程无法创建和打开,二而父子进程遵循读时共享,写时复制的原则,自然就能够拿到匿名管道的文件描述符,然后就可以通信。

                (3)匿名管道描述符只能通过继承的方式传递给后代进程,只能用于亲缘进程间(通常用于父子进程间)的通信。

                (4)匿名管道拥有两个文件描述符,一个专用于读fd[0],一个专用于写fd[1],因此在创建管道时,必须传递一个至少包含两个整型元素的数组。

                (5)不能有多个进程同时对匿名管道进行写操作,否则数据有可能被覆盖。

匿名管道适用于一对一的、具有亲缘关系的进程间的通信。

        2、具名管道(FIFO)

                (1)具名管道通常被称为FIFO(First In First Out),存放的数据都是按顺序被读出的

                (2)具名管道更接近普通文件,有文件名,可以使用open()函数打开,支持read()/write()等读写操作。

                (3)有专门的接口创建:mkfifo()。创建新的具名管道文件时,必须保证创建路径位于Linux系统内,尤其是虚拟机种操作的时候,不可将管道文件创建在共享文件夹中,因为共享文件夹是windows系统的,不支持管道文件。

                (4)支持多路同时写入。

         3、管道读写特性

           管道文件默认是阻塞的,可以修改文件描述的阻塞特性来达到目的。

Linux系统编程系列之进程间通信(IPC)-管道_第1张图片

四、管道的接口API有哪些

// 匿名管道
#include 

int pipe(int fd[2]);

返回值:成功返回0, 失败返回-1



// 具名管道
#include 
#include 

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

返回值:成功返回0, 失败返回-1

五、案例

        1、匿名管道

#include 
#include 
#include 
#include 


int main(int argc, char *argv[])
{
    int fd[2];

    int ret = pipe(fd); // 创建匿名管道
    if(ret == -1)
    {
        perror("pipe fail\n");
        return -1;
    }

    char msg[128] = {0};
    pid_t pid = fork(); // 创建子进程
    if(pid > 0)     // 父进程
    {
        while(1)
        {
            printf("please input data:");
            scanf("%s", msg);
            // 匿名管道fd[1]是写入端
            write(fd[1], msg, strlen(msg));
            bzero(msg, sizeof(msg));
        }
    }
    else if(pid == 0)   // 子进程
    {
        int i = 1;
        int ret = 0;
        
        while(1)
        {
            bzero(msg, sizeof(msg));
            while(1)
            {
                // 匿名管道fd[0]是接收端
                ret = read(fd[0], msg, 128);
                
                if(i == 1)
                {
                    printf("\nrecv data from parent process:\n");
                    i--;
                }
                
                printf("%s", msg);
                if(ret < 128)
                {
                    i = 1;
                    printf("\n");
                    break;
                } 
            }
        }
    }
    else
    {
        perror("fork fail\n");
        return -1;
    }


    return 0;
}

Linux系统编程系列之进程间通信(IPC)-管道_第2张图片

        2、具名管道

// 具名管道操作示例

#include 
#include 
#include 
#include 
#include 
#include 

#define FIFO_PATH "/tmp/fifo.txt"   // 具名管道文件路径,不能在共享文件夹路径下创建

int main(int argc, char *argv[])
{
    int ret = access(FIFO_PATH, F_OK);  // 判断管道文件是否存在
    if(ret == -1)
    {
        ret = mkfifo(FIFO_PATH, 0777);   // 不存在就创建具名管道
        if(ret == -1)
        {
            perror("mkfifo fail\n");
            return -1;
        }
    }

    int fd = open(FIFO_PATH, O_RDWR);   // 打开管道文件
    if(fd == -1)
    {
        perror("open fail\n");
        return -1;
    }
    
    char msg[128] = {0};
    pid_t pid = fork();
    if(pid > 0) // 父进程
    {
        while(1)
        {
            printf("please input data: ");
            scanf("%s", msg);
            write(fd, msg, strlen(msg));    // 像普通文件操作一样写入数据
            bzero(msg, sizeof(msg));
        }
    }
    else if(pid == 0)   // 子进程
    {
        int i = 1;
        int ret1 = 0;
        while(1)
        {
            while(1)
            {
                bzero(msg, 128);
                ret1 = read(fd, msg, 128);  // 像普通文件一样读取数据

                if(i == 1)
                {
                    printf("recv data from parent process:");
                    i = 0;
                }
                printf("%s", msg);
                if(ret1 < 128)
                {
                    i = 1;
                    printf("\n");
                    break;
                }
            }            
        }
    }
    else
    {
        perror("fork fail\n");
        close(fd);  // 关闭管道文件
        return -1;
    }

    close(fd);  // 关闭管道文件

    return 0;
}

Linux系统编程系列之进程间通信(IPC)-管道_第3张图片

        3、设置非阻塞特性

// 管道非阻塞操作示例

#include 
#include 
#include 
#include 
#include 
#include 

#define FIFO_PATH   "/tmp/fifo.txt"


int main(int argc, char *argv[])
{
    int ret = access(FIFO_PATH, F_OK);
    if(ret == -1)
    {
        ret = mkfifo(FIFO_PATH, 0777);
        if(ret == -1)
        {
            perror("mkfifo fail\n");
            return -1;
        }
    }

    int fd = open(FIFO_PATH, O_RDWR);
    if(fd == -1)
    {
        perror("open fail\n");
        return -1;
    }

    // 设置非阻塞特性
    int status = fcntl(fd, F_GETFL); // 获取文件特性
    status |= O_NONBLOCK;   // 加上非阻塞特性
    fcntl(fd, F_SETFL, status);     // 重新设置文件特性


    char msg[128] = {0};

    while(1)
    {

        printf("read data:\n");
        ret = read(fd, msg, 128);   // 非阻塞状态下会一直循环
        if(ret <= 0 )
        {
            printf("no data\n");
        }
        else
        {
            printf("%s\n", msg);
        }
        sleep(1); // 加上睡眠可以减少程序对CPU的占用
    }

    close(fd);

    return 0;
}

Linux系统编程系列之进程间通信(IPC)-管道_第4张图片

六、总结

        匿名管道用于通常用于父子进程间的通信,而具名管道用于任何多个进程间的通信,管道文件默认是阻塞的,管道的数据读取都是按顺序的,不能使用lseek()函数跳过某个字节。

你可能感兴趣的:(Linux,C语言程序设计,c语言,linux)