IPC:管道

本文详细介绍了Linux中的管道原理,包括其内存存储、工作方式(单工、半双工、全双工)、操作限制和特点。重点讲解了无名管道和有名管道的区别,以及它们在进程间通信中的应用场景和使用方法,还提供了创建和使用管道的示例代码。
摘要由CSDN通过智能技术生成

一、管道的概念

1.原理

在进程3G~4G的内核空间中,创建一个特殊的文件(管道),管道的数据直接保存在内存中。

2.特性

1)管道可以看成是一个特殊的文件,一般的文件存储在外存中,而管道内容是存储在内存中。

2)管道遵循先进先出的原则。(队列形式实现)

3)对于管道的读操作是一次性的,被读取的数据会从管道中删除

4)管道是一种半双工的通信方式

a、单工:只能A发消息给B,B不能发消息给A

b、半双工:同一时间,只能A发消息给B,或者B发消息给A

c、全双工:同一时间,AB能够互相发消息

5)对于管道的操作,只能使用文件IO函数,因为需要直接操作内核空间。例如open,close,read,write。但是不能使用 lseek 函数。

6)当管道的读写端均关闭时,管道内存空间会被释放

7)管道的大小:65536bytes=64K

8)从管道中读取数据

  • 当管道的读写端均存在,
    1. 当管道中没有数据的时候,read函数阻塞。
    2. 当管道中拥有的数据为10个,读取5个,实际读取5个
    3. 当管道中拥有的数据为5个,读取10个,实际读取5个
  • 当管道的写端不存在的时候后,(父子进程中均有写端,均关闭)
    1. 当管道中有数据的时候,会先将管道中的所有数据读取完毕
    2. 当管道中没有数据的时候,read函数不会阻塞,立即返回0;

9)向管道中写入数据

  • 当管道的读写端均存在,
    1. 将管道写满后,write函数会阻塞。
  • 当管道的读端不存在的时候,(父子进程中均有读端,均关闭)
    1. 调用write函数,尝试向管道中写入数据会导致调用write函数的进程退出。
    2. 调用write函数的进程会收到管道破裂信号(SIGPIPE),管破裂信号的处理函数会让进程退出。

二、无名管道(pipe)

  1. 无名管道即没有名字的管道文件,即在文件系统中不可见的管道文件。
  2. 无名管道使用的时候无法使用open函数打开文件,因为不知道路径以及名字。
  3. 无名管道只能用于具有 亲缘关系 的进程间通信。

为什么无名管道只能用于具有 亲缘关系 的进程间通信。

  1. 无名管道在文件系统中不可见,所以两个无关的进程,无法拿到同一根管道的读写端。
  2. 子进程会继承父进程的文件描述符表,所以在父进程中创建一根管道拿到读写端后,调用fork函数,创建出来的子进程也会有该管道的读写端。

pipe:创建一个无名管道,同时打开无名管道的读写端

int pipe(int pipefd[2]);

参数:

int pipefd[2]:需要传入一个整型数组的首地址,且数组的容量为2。

                        用于存储两个打开的文件描述符;

                        pipefd[0]:读端

                        pipefd[1]: 写端

返回值: 成功,返回0; 失败,返回-1,更新errno;

三、有名管道

有名字的管道文件,但是管道的数据依然保存在内存中

有名管道的特点:

  1. 有名管道是有名字的管道,即在文件系统中可以看到路径以及名字的管道文件。
  2. 有名管道由于在文件系统中可见,所有无亲缘关系的进程,可以通过open函数打开同一根有名管道。
  3. 有名管道可以使用在 无亲缘关系 的进程间通信。

创建有名管道

1.shell指令:终端输入mkfifo

mkfifo ./fifo

2.用mkfifo函数创建:创建一个有名管道

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

参数:

char *pathname:指定要创建的有名管道路径以及名字;

mode_t mode:有名管道的权限,真实的权限的mode & ~umask:0664 0777

the permissions of the created file are (mode & ~umask).

返回值:

成功,返回0;

失败,返回-1,更新errno;

文件已经存在的错误是一个合法的错误,需要排除,代码允许正常运行

#define EEXIST 17

可用access()函数来判断用户是否具有访问某个文件的权限

需要包含#include<unistd.h>

 int access(const char *pathname,int mode)
 参数:
         pathname:表示要测试的文件的路径
         mode:表示测试的模式可能的值有:
         R_OK:是否具有读权限
         W_OK:是否具有可写权限
         X_OK:是否具有可执行权限
         F_OK:文件是否存在
返回值:若测试成功则返回0,否则返回-1

可用access函数来判断管道文件是否已经存在,

if(access("./myfifo",F_OK)==-1){
    //如果管道文件myfifo不存在的话,则创建管道文件
    mkfifo("myfifo",0666);
}    

有名管道的使用

  1. 通过open函数打开有名管道,close关闭有名管道
  2. 通过read write函数读写有名管道,与读写普通文件一致。

O_RDONLY: 只读

O_WRONLY: 只写

O_RDWR : 读写

----以上三种必须,且只能包含一种-----

O_NONBLOCK 非阻塞形式

  1. flags == O_WRONLY
    1. open函数会阻塞,当有另外一个进程 以读 的方式打开同一根FIFO时候,解除阻塞。
  2. flags == O_RDONLY
    1. open函数会阻塞,当有另外一个进程 以写 的方式打开同一根FIFO时候,解除阻塞。
  3. flags == O_RDWR
    1. open函数不会阻塞,函数运行成功,管道的读写端均被打开
  4. flags == O_WRONLY | O_NONBLOCK
    1. open函数不阻塞,open函数会运行失败,此时写端打开失败
  5. flags == O_RDONLY | O_NONBLOCK
    1. open函数不阻塞,open函数会运行成功,此时读端打开成功

默认情况下,当管道只有读端,或者只有写端的方式打开,open函数会阻塞。

直到读写端均被打开了,open函数解除阻塞。

四、代码

1.写端写入

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

int main(int argc, const char *argv[])
{
    umask(0);

    //创建有名管道
    if(mkfifo("./myfifo", 0664) < 0)                                      
    {   
        //文件已经存在的错误是一个合法的错误,需要排除,代码允许正常运行
        if(17 != errno)     //EEXIST != errno
        {
            perror("mkfifo");
            return -1; 
        }
    }   
    printf("myfifo create success\n");

    //以只写的方式打开有名管道, 阻塞
    int fd = open("./myfifo", O_WRONLY);
    if(fd < 0)
    {   
        perror("open");
        return -1; 
    }   
    printf("open fifo success wronly  fd=%d\n", fd);

    char buf[128] = ""; 
    while(1)
    {   
        printf("请输入>>>");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf)-1] = 0;

        if(write(fd, buf, sizeof(buf)) < 0)
        {
            perror("write");
            return -1; 
        }
        if(strcasecmp(buf, "quit") == 0)    //忽略大小写的比较
            break;

        printf("写入成功\n");
    }   

    close(fd);
    return 0;
}

2.读端读取

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

int main(int argc, const char *argv[])
{                                                                                            
    umask(0);

    //创建有名管道
    if(mkfifo("./myfifo", 0664) < 0)
    {
        //文件已经存在的错误是一个合法的错误,需要排除,代码允许正常运行
        if(17 != errno)     //EEXIST != errno
        {
            perror("mkfifo");
            return -1;
        }
    }
    printf("myfifo create success\n");

    //以只读的方式打开有名管道, 阻塞
    int fd = open("./myfifo", O_RDONLY);
    if(fd < 0)
    {
        perror("open");
        return -1;
    }
    printf("open fifo success rdonly  fd=%d\n", fd);

    char buf[128] = "";
    ssize_t res = 0;
    while(1)
    {
        bzero(buf, sizeof(buf));
        res = read(fd, buf, sizeof(buf));
        if(res < 0)
        {
            perror("read");
            return -1;
        }
        else if(0 == res)
        {
            printf("写端退出\n");
            break;
        }
        printf("res=%ld : buf=%s\n", res, buf);
        if(strcasecmp(buf, "quit") == 0)
            break;
    }

    close(fd);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值