进程间通信 - 管道

进程间通信 - 管道

进程间通信的方式:

  • 管道:匿名管道和有名管道
  • 信号 - 系统开销小
  • 共享映射区 - 有无血缘关系的进程间通信都可以
  • 本地套接字 - 稳定

IPC(InterProcess Communication):进程间通信

1. 管道

1.1管道的概念:管道是内核缓冲区,可以叫做伪文件(不占用磁盘文件)
(1)管道的特点:
①管道有读端和写端。数据从写端流入,读端流出。读端和写端是两个文件描述符。
在这里插入图片描述
②含有管道的进程结束后,管道被释放
③管道默认是阻塞的。数据必须读端流入,写端流出

2.管道的原理:

(1)内部实现方式:队列(环形队列),先进先出。
(2)缓冲区大小:默认为4k,大小会根据实际情况做适当的调整

3.管道的局限性

(1)数据只能读取一次,不能重复使用,数据传输方向是单向的(半双工)

单工:遥控器
半双工:数据传输的方向是单向的,对讲机
双工:数据时双向流动的,电话

(2)匿名管道:只适用于由血缘关系的进程间通信

4.创建匿名管道

函数:int  pipe(int  pipefd[2]);

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

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if(ret == -1){
        perror("pipe");
        exit(1);
    }

    printf("pipe[0] = %d\n", fd[0]);
    printf("pipe[1] = %d\n", fd[1]);

    close(fd[0]);
    close(fd[1]);

    return 0;
}

5.父子进程使用匿名管道通信

单个进程也能使用管道完成通信,平时没有这个必要

例:父进程将数据写入管道,子进程从管道中读取数据

/* 实现 ps aux | gerp "bash" */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if(ret == -1){
        perror("pipe");
        exit(1);
    }

    pid_t pid = fork();
    if(pid == -1){
        perror("fork");
        exit(1);
    }

    //父进程 ps aux
    //子进程 grep "bush"
    if(pid > 0){
        //写管道操作,关闭读端
        close(fd[0]);
        //重定向,STDOUT -> 管道的写端
        dup2(fd[1], STDOUT_FILENO);
        //执行ps aux
        execlp("ps", "ps", "aux", NULL);
        perror("execlp");
        exit(1);
    }
    else if(pid == 0){
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        execlp("grep", "grep", "bash", "--color=auto", NULL);
        perror("execlp");
        exit(1);
    }

    close(fd[0]);
    close(fd[1]);

    return 0;
}

注意事项:
父进程写数据 – 关闭写端
子进程读数据 – 关闭读端
因为管道是阻塞的

程序中的重定向
父进程中 – dup2(fd[1], STDOUT_FILENO); //STDOUT_FILENO跟随 fd[1]
子进程中 – dup2(fd[0], STDIN_FILENO); //STDIN_FILENO跟随 fd[0]
重定向图示

5.1 管道的读写行为

1.读操作

(1)管道中有数据:

  • read(fd) - 正常读数据,返回读出的字符数

(2)管道中无数据:

  • 写端全部关闭:read解除阻塞,返回0,相当于读文件时读到了尾部
  • 没有全部关闭:read阻塞
2.写操作

(1)读端全部关闭

  • 管道破裂,进程被终止:内核给当前进程发信号SIGPIPE(13号信号)

(2)读端没有被全部关闭

  • 缓冲区写满:write阻塞
  • 缓冲区没满:write继续写

(3)设置管道为非阻塞

  • 获取原来的flags:int flag = fcntl(fd[0].F_GETFL);
  • 设置新的flags:flag |= O_NONBLOCK;
    //flags  =  flags  |  O_NONBLOCK;
  • fcntl(fd[0],  F_SETFL,  flags);

5.2 查看管道缓冲区大小

1.命令查看:ulimit -a
2.函数:

#include <unistd.h>

       long fpathconf(int fd, int name);
       long pathconf(const char *path, int name);

6. 有名管道(fifo)

6.1 fifo的特点

  • 在磁盘上有文件
  • 磁盘上的文件为伪文件,占用的大小为0
  • 内核中由对应的缓冲区

6.2 fifo的使用

1.使用场景:没有血缘关系的进程间通信
2.创建方式:

  • 命令:mkfifo 管道名
  • 函数:mkfifo
#include <sys/types.h>
#include <sys/stat.h>

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

参数:
①pathname:fifo文件名
②mode:fifo文件的打开方式
例:该进程以只读的方式打开 myfifo

int fd = open("myfifo", O_RDONLY);

6.3 fifo文件的IO函数操作

  • open() / close()
  • read() / write()
  • 不能使用lseek()

6.4 进程间通信:使用 myfifo 进程通信

现有两个进程a,b属于不同的进程组
a进程进行读操作,b进程进行写操作
a.c —> read

int fd = open("myfifo", O_RDONLY);
read(fd, buf, sizeof(buf);
close(fd);

b.c —>read

int fd1 = open("myfifo", O_WRONLY);
write(fd1, "hello,world", 11);
close(fd1);
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值