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

 Linux系统编程系列(16篇管饱,吃货都投降了!)

        1、Linux系统编程系列之进程基础

        2、Linux系统编程系列之进程间通信(IPC)-信号

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

        4、Linux系统编程系列之进程间通信-IPC对象

        5、Linux系统编程系列之进程间通信-消息队列

        6、Linux系统编程系列之进程间通信-共享内存

        7、Linux系统编程系列之进程间通信-信号量组

        8、Linux系统编程系列之守护进程

        9、Linux系统编程系列之线程

        10、Linux系统编程系列之线程属性 

        11、Linux系统编程系列之互斥锁和读写锁

        12、Linux系统编程系列之线程的信号处理

        13、Linux系统编程系列之POSIX信号量

        14、Linux系统编程系列之条件变量

        15、Linux系统编程系列之死锁

        16、 Linux系统编程系列之线程池

一、什么是管道

        在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、管道读写特性

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

四、管道的接口API有哪些

// 匿名管道
#include <unistd.h>

int pipe(int fd[2]);

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



// 具名管道
#include <sys/types.h>
#include <sys/stat.h>

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

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

五、案例

        1、匿名管道

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


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;
}

        2、具名管道

// 具名管道操作示例

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#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;
}

        3、设置非阻塞特性

// 管道非阻塞操作示例

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#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;
}

六、总结

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

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值