Linux —— 进程间通信之管道

1. 匿名管道

  这里说的管道是指匿名管道(无名管道),是 UNIX 系统IPC最古老的形式。

1. 管道的特点:

  1. 其本质是一个伪文件(实为内核缓冲区)。

  2. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

  3. 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

  4. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

  5. 数据一旦被读走,便不在管道中存在,不可反复读取。

  6. 管道的大小有限制,当要写⼊的数据量⼤于PIPE_BUF时,linux将不再保证写⼊入的原⼦子性。

  7. 一般而言,内核会对管道操作进行同步与互斥。

  8. 一般而言,进程退出,管道释放,所以管道的生命周期随进程。

2. 创建管道

int pipe(int fd[2]);   
// 头文件:#include <unistd.h>
// 参数fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
// 返回值:若成功返回0,失败返回-1

3. 实现父子进程通信

  把子进程 ls -l 命令的结果放入管道中,父进程从管道中读取这些信息,用wc -l命令计算文件个数。
  
这里写图片描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int fds[2];
    pipe(fds);

    if(fork() == 0)
    {
        close(1);      //关闭标准输出
        close(fds[0]); //关闭子进程的读
        dup(fds[1]);   //让子进程管道的写与标准输出建立联系
        close(fds[1]);
        execlp("ls", "ls", "-l", NULL);
        exit(0);
    }else{
        close(0);      //关闭标准输入
        close(fds[1]); //关闭父进程的写
        dup(fds[0]);   //让父进程管道的读与标准输入建立联系,即把从管道里面读入的 wc -l 的结果写入到文件中
        close(fds[0]);
        execlp("wc", "wc", "-l", NULL);
        exit(0);
    }
    return 0;
}

4. 管道读写规则

  1. 当没有数据可读时
    1. O_NONBLOCK disableread 调用阻塞,即进程暂停执行,一直等到有数据来到为止。
    2. O_NONBLOCK enableread 调用返回-1,errno值为EAGAIN
  2. 当管道满的时候
    1. O_NONBLOCK disable: write调用阻塞,直到有进程读走数据。
    2. O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
  3. 如果所有管道写端对应的文件描述符被关闭,则read返回0。

  4. 如果所有管道读端对应的文件描述符被关闭,则write操作会产⽣生信SIGPIPE,进而可能导致write进程退出。

  5. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原⼦子性。

  6. 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。


2. 命名管道

1.定义

  管道的应用有一个限制是只能在具有共同祖先(具有亲缘关系)的进程间通信。如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  命名管道有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

2. 匿名管道与命名管道的区别:

  1. 匿名管道由pipe函数创建并打开。命名管道由mkfifo函数创建,打开用open。

  2. FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

3. 创建命名管道

int mkfifo(const char *pathname, mode_t mode);
// 头文件:#include <sys/stat.h>
// 返回值:成功返回0,出错返回-1
// 参数1:mkfifo() 会依参数pathname建立特殊的FIFO文件,该文件必须不存
// 参数2:mode 表示创建出来的FIFO文件的权限。

4. 使用示例

  FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。下面的例子演示了使用 FIFO 进行 IPC 的过程:
  
< code mkfifo_w.c>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>

int main()
{
    int n, i;
    int fd;
    char buff[1024];
    time_t tp;

    printf("I am %d process\n", getpid());
    if((fd = open("fifo", O_CREAT|0666)) < 0) perror("open error"), exit(1);

    for(i = 0; i < 10; ++i){
        time(&tp); //获取系统当前时间
        n = sprintf(buff, "Process %d's time is %s", getpid(), ctime(&tp));
        printf("Send message: %s", buff); // 打印

        if(write(fd, buff, n+1) < 0)  // 写入到FIFO中
        {
                perror("Write FIFO Failed");
                close(fd);
                exit(1);
        }
        sleep(1);  // 休眠1秒
    }

    close(fd);  // 关闭FIFO文件
    return 0;
}

< code mkfifo_r.c >

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>

int main()
{
    int fd;
    int len;
    char buff[1024];

    if(mkfifo("fifo", 0666) < 0 && errno!=EEXIST) // 创建FIFO管道
        perror("Create FIFO Failed");

    if((fd = open("fifo", O_RDONLY)) < 0)  // 以读打开FIFO
    {
        perror("Open FIFO Failed");
        exit(1);
    }

    while((len = read(fd, buff, 1024)) > 0) // 读取FIFO管道
    {
        printf("Read message: %s", buff);
    }

    close(fd);  // 关闭FIFO文件
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值