进程间通信 --管道

一、进程间通信常用的方式

(1) 管道

(2) System V

(3) POSIX

二、目的

(1) 数据传输

一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。

(2) 资源共享

多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。

(3) 通知事件

一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

(4) 进程控制

有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

三、 本质

让两个不同的进程看到一份公共的资源

四、匿名管道

1. 局限性

半双工,数据只能在一个方向上移动

只能在具有公共祖先的进程之间使用

2. 简单用法: 

3.  创建管道

(1)pipe 函数

#include <unistd.h>
int pipe(int pipefd[2]);
// 成功:0 失败:-1

注意:pipefd 是输出型参数,它会返回两个参数,pipefd[0] 是读,pipefd[1] 是写

(2)代码说明

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

int main()
{
    int fd[2];
    int ret = pipe(fd);
    if (ret < 0)
    {
        perror("pipe");
        return 1;
    }
    pid_t id = fork();
    // fd[0] 读,fd[1] 写
    printf("fd[0], fd[1] -> %d, %d\n", fd[0], fd[1]);
    if (id < 0)
    {
        printf("fork error!\n");
    }
    else if (id == 0)
    {
        // child
        close(fd[0]);
        const char* msg = "I am child !";
        for (int i = 0; i < 5; i++)
        {
            write(fd[1], msg, strlen(msg));
            sleep(1);
        }
    }
    else 
    {
        // parent
        close(fd[1]);
        char buf[1024];
        while (1)
        {
            ssize_t s = read(fd[0], buf, sizeof(buf) - 1);
            if (s > 0)
            {
                buf[s] = 0;
                printf("father get a message: %s\n", buf);
            }
        }
    }
    return 0;
}

父进程创建管道,父进程 fork 子进程,父进程关闭 fd[1](写),子进程关闭 fd[0](读)

(3)结果

4. 特性

(1) 单向传递

(2) 具有血缘关系(父子,兄弟,爷孙)的进程之间,常用于父子之间

(3) 自带同步互斥4

(4) 生命周期随进程(进程退出,管道释放)

(5) 管道在进行通信的时候,对外层提供面向字节流的服务,以字节来读取和写入,字节数的大小完全取决于自己

5. 几个概念

(1) 数据不一致:读写双方因为访问共同资源二导致的数据不一致的问题

例如: hello word    和    shagua

读第一个读到空格时,读到第二个,就出现了错误

(2) 临界资源:把两个进现在程看到的看到的公共资源称为临界资源

(3) 临界区:每个进程中访问临界资源的那段代码称为临界区

(4) 互斥:在任何一个时间点,临界区在访问临界资源时,有且仅有一个人在访问,即你用的时候别人都不能用,别人用的时候,你也不能去用

     同步:在现有的互斥基础上,让多个人访问临界资源具有了顺序性,即我们大家利用一些共同的资源区,大家一起合作,完成某些事情,但是我在干某些小事的时候,可能要等到你做完另一些小事

(5) 原子性:在进行某些资源的操作时时,不会有中间状态

6. 读写规则

(1) 写方一直在写,读端不读,但读端不关闭文件描述符,写方因为互斥与同步在把管道写满之后就会停下来

(2) 如果读写双方不挂不关闭文件描述符,一个不写,一个不读,双方都要等待,这个过程就叫同步

(3) 如果写端一直在写,有朝一日写端不写,并把写端关闭,读端会一直读,直到把管道中文件描述符读完,最后读返回值 0,这个 0 代表着读到了文件的结尾

(4)  写端一直在写,读端不读且关闭文件描述符,写端会因为操作异常被操作系统向目标发送13号信号(SIGPIPE)终止

7. 管道大小

管道大小为:512*8 = 4096

7.  popen 与 pclose+

#include <stdio.h>
FILE *popen(const char *command, const char *type);
// 成功:文件指针 失败:NULL
int pclose(FILE *stream);
// 成功:command 的终止状态 失败:-1

 popen:先执行 fork,然后 exec command,返回一个标准 I/O 的文件指针

pclose:关闭标准 I/O 流,返回 shell 终止状态

五、命名管道

1. 创建命名管道

(1)函数创建

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int mkfifo(const char *pathname, mode_t mode);
int mkfifoat(int dirfd, const char *pathname, mode_t mode);
// 成功:0 失败:-1

(1)如果 pathname 是绝对路径,fd 被忽略,与 mkffio 类似

(2)如果 pathname 是相对路径,fd 是一个打开目录的有效文件描述符,路径名和目录有关

(3)如果 pathname 是相对路径,fd 有一个特殊值 AT_FDCWD,路径名以当前目录开始,与 mkffio 类似

(2)命令行创建

mkfifo filename

2. 读写规则

如果当前打开操作是为读而打开FIFO时

O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO

O_NONBLOCK enable:立刻返回成功

如果当前打开操作是为写而打开FIFO时

O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO

O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

3. 用途

(1)shell 命令使用 FIFO 将数据从一条管道传送到另一条时,无需创建临时文件

(2)客户进程 - 服务进程,FIFO用作汇聚点,在客户进程服务进程之间传递数据

代码说明:

reader.c

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

int main()
{
    int fd = open("./myfifo", O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("open ok!\n");
    while (1)
    {
        char buf[1024] = {0};
        ssize_t read_size = read(fd, buf, sizeof(buf) - 1);
        if (read_size < 0)
        {
            perror("read");
            return 1;
        }
        if (read_size == 0)
        {
            perror("read done");
            return 0;
        }
        buf[read_size] = '\0';
        printf("%s\n", buf);
    }
    close(fd);
    return 0;
}

writer.c

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

int main()
{
    int fd = open("./myfifo", O_WRONLY);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    while (1)
    {
        printf("> ");
        fflush(stdout);
        char buf[1024] = {0};
        ssize_t read_size = read(0, buf, sizeof(buf) - 1);
        if (read_size < 0)
        {
            perror("read");
            return 1;
        }
        if (read_size == 0)
        {
            printf("read done!\n");
            return 0;
        }
        buf[read_size] = '\0';
        write(fd, buf, strlen(buf));
    }
    close(fd);
    return 0;
}

运行截图:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值