Linux应用开发-进程间通信-管道

本文详细介绍了Linux系统中的管道机制,包括匿名管道的创建步骤、特点以及C语言实现,以及有名管道的概念和mkfifo命令的使用。着重讨论了管道的单向性和阻塞特性,以及它们在父子进程间通信中的应用。
摘要由CSDN通过智能技术生成

管道

管道中的数据只能单向流动,也就是半双工通信,如果想实现相互通信(全双工通信),我们需要创建两个管道。

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

注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到 内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。

特点

管道分为有名管道fifo和无名管道pipe

  1. 管道创建后是单向的,一端是写,一端是读

  2. 管道有大小,linux内核对每个管道的大小限制在4096字节

  3. 无名管道一般用在父子进程间通讯

  4. 有名管道一般用在进程与进程之间通讯

  5. 管道是阻塞的,读取管道过程中一直处于阻塞的状态

  6. 管道在被读取前,只能写入一次,多次写入是无效的

匿名管道

特点:

1、创建之后在文件系统不可见

2、半双工通信

3、固定的读端(0),写端(1)

4、只能用于具有亲缘关系的进程间的通信

匿名管道用完了就会被自动销毁。

例如linux系统的命令:

command1 | command2

以上这行代码就组成了一个管道,它的功能是将前一个命令(command1)的输出,作为后一个命令(command2)的输入。

匿名管道的创建

通过pipe 函数来创建匿名管道。

创建成功则返回 0,创建失败就返回 -1。

int pipe (int fd[2]);

该函数拥有一个存储空间为 2 的文件描述符数组:

  • fd[0] 指向管道的读端,fd[1] 指向管道的写端

  • fd[0] 的输出数据,是 fd[1] 的输入数据。

实现步骤

  1. 父进程创建两个匿名管道,管道 1(fd1[0]和 fd1[1])和管道 2(fd2[0] 和 fd2[1];因为管道的数据是单向流动的,所以要想实现数据双向通信,就需要两个管道,每个方向一个。

  2. 父进程 fork 出子进程,于是对于这两个匿名管道,子进程也分别有两个文件描述符指向匿名管道的读写两端;

  3. 父进程关闭管道 1 的读端 fd1[0] 和 管道 2 的写端 fd2[1],子进程关闭管道 1 的写端 fd1[1] 和 管道 2 的读端 fd2[0],这样,管道 1 只能用于父进程写、子进程读;管道 2 只能用于父进程读、子进程写。

管道是用环形队列实现的,数据从写端流入从读端流出,这就实现了父子进程之间的双向通信。

有名管道

匿名管道由于没有名字,只能用于父子进程间的通信。为了克服这个缺点,提出了有名管道,也称做 FIFO,因为数据是先进先出的传输方式。

所谓有名管道也就是提供一个路径名与之关联,这样,即使与创建有名管道的进程不存在亲缘关系的进程,只要可以访问该路径,就能够通过这个有名管道进行相互通信。

Linux系统命令实现

使用 Linux 命令 mkfifo 来创建有名管道

 mkfifo myPipe

往 myPipe 这个有名管道中写入数据:(数据未被读走时将阻塞)

echo "hello" > myPipe

执行另外一个命令来读取这个有名管道里的数据:

cat myPipe

C语言实现

步骤进程A进程B步骤
1.创建管道mkfifo
2.打开管道openopen1.打开管道
3.读写管道write/readwrite/read2.读写管道
4.关闭管道closeclose3.关闭管道
5.删除管道unlink
写入
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#define FIFO_FILE "/tmp/fifo"

int main(void){
    printf("创建管道...\n");
    if(mkfifo(FIFO_FILE,0666) == -1){
        perror("mkfifo");
        return -1;    
    }
    
    printf("打开管道...\n");
    int fd = open(FIFO_FILE,O_WRONLY);
    if(fd == -1){
        perror("open");
        return -1;
    }
    
    printf("发送数据...\n");
    for(;;){
        printf(">");
        char buf[1024];
        gets(buf);
        if(!strcmp(buf,"!"))
            break;
        if(write(fd,buf,(strlen(buf)+1)))  * sizeof(buf[0]) == -1){
            perror("write");
            return -1;        
        }                     
    }
    
    printf("关闭管道...\n");
    if(close(fd) == -1){
        perror("close");
        return -1;    
    }
    
    printf("删除管道...\n");
    if(unlink(FIFO_FILE) == -1){
        perror("unlink");
        return -1;    
    }
    
    printf("大功告成!\n");
    return 0;
}

读取
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#define FIFO_FILE "/tmp/fifo"

int main(void){
    printf("打开管道...\n");
    int fd = open (FIFO_FILE,O_RDONLY);
    if(fd == -1){
        perror("open");
        return -1;
    }
    
    printf("接收数据...\n");
    for(;;){
        char buf[1024];
        size_t rb = read(fd,buf,sizeof(buf));
        if(rb == -1){
            perror("read");
            return -1;
        }    
        
        if(!rb)
            break;
        printf("< %s\n",buf);
    }
    
    printf("关闭管道...\n");
    if(close(fd) == -1){
        perror("close");
        return -1;
    }
    
    printf("大功告成!\n");
    return 0;

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌新程序猿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值