进程间的通信方式之【管道】

10 篇文章 0 订阅
本文详细介绍了进程间通信的必要性,以及管道(包括匿名管道和命名管道)、共享内存、消息队列、信号量、信号等六种通信方式的原理和使用方法,特别强调了匿名管道在父子进程间的应用和命名管道在不同进程间的数据交换作用。
摘要由CSDN通过智能技术生成

进程间的通信方式主要分为三类:管道、共享内存、消息队列;但实际上进程的通信方式可以分为六类:管道、共享内存、消息队列、信号量、信号、socket套接字编程。

进程间通信的必要性:

1.进程在操作系统内核中是独立的进程控制块(PCB),即一个一个struct task_struct{....}结构体对象。

2.每一个进程有自己独立的进程地址空间,即进程间的数据是独立的,也就造就了进程间的独立性。

3.进程和进程间交换数据较困难,所以有了进程间通信来完成数据交换过程。

一、管道

匿名管道:

只能在单个进程内部进程通信,即父子进程。

pipe函数的认识:

NAME

        pipe, pipe2 - create pipe

SYNOPSIS

        #include <unistd.h>

        int pipe(int pipefd[2]);

DESCRIPTION

        pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe.
RETURN VALUE

        On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

A. 管道函数成功调用会创建出来一个匿名管道,对应在内核中会有一个内核缓冲区

B. Pipefd是一个数组,数组有两个元素,分别对应管道的读写两端.

  1. fd[0]:是管道的读端
  2. fd[1]:是管道的写端
  3. 都是文件描述符

C. Pipefd这个参数是输出性参数,不需要程序员赋值.由Pipe函数在调用时赋值.

D.创建成功返回0,创建失败返回-1.

pipe函数使用:

#include <iostream>
#include <unistd.h>

int main(){
    int fd[2];
    int ret = pipe(fd);

    if(ret < 0){
        perror("pipe error");
        exit(-1);
    }

    // success
    printf("read fd[0]: %d\n", fd[0]);
    printf("write fd[1]: %d\n", fd[1]);

    return 0;
}

结果显示读端fd[0] 是3,写端fd[1]是4,对应也是的文件描述符,因为0,1,2分别是标准输入、输出、错误所占用了。

验证:

在原代码的基础上,加一个while死循环,查看该进程的pid,然后进行在查看该进程下的文件描述符,可以看到存在3、4号fd:

$ ll /proc/[pid]/fd

读写验证:

int main(){
    int fd[2];
    int ret = pipe(fd);

    if(ret < 0){
        perror("pipe error");
        exit(-1);
    }

    // success
    printf("read fd[0]: %d\n", fd[0]);
    printf("write fd[1]: %d\n", fd[1]);
    
    write(fd[1], "hello", 5);

    char buf[1024] = {0};
    read(fd[0], buf, sizeof(buf) - 1);
    printf("buf: %s\n", buf);

    return 0;
}

通过write往fd[1]写入了“hello”,然后使用read从fd[0]读出内容。

父子进程匿名管道

父子进程间是共享的同一个文件描述符,所以使用匿名管道,可以实现父子进程间的通信。

#include <iostream>
#include <unistd.h>

int main(){
    int fd[2];
    int ret = pipe(fd);

    if(ret < 0){
        perror("pipe error");
        exit(-1);
    }

    // success
    printf("read fd[0]: %d\n", fd[0]);
    printf("write fd[1]: %d\n", fd[1]);
    
    ret = fork();
    if(ret < 0){
      perror("fork fail");
      exit(-1);
    }
    else if(ret == 0){
        // 父进程写
      write(fd[1], "hello", 5);
    }
    else{
        // 子进程读
      char buf[1024] = {0};
      read(fd[0], buf, sizeof(buf) - 1);
      printf("buf : %s\n", buf);
    }
    

    return 0;
}

命名管道:

  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  • 命名管道是一种特殊类型的文件

使用指令创建:
$ mkfifo filename

使用程序代码创建:

函数: int mkfifo(const char *filename,mode_t mode);
例如:

int main(int argc, char *argv[])
{
mkfifo("p2", 0644);
return 0;
}

命名管道打开方式:

使用open函数

命名管道的打开规则
如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
O_NONBLOCK enable:立刻返回成功
如果当前打开操作是为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

验证:

使用mkfifo指令创建一个管道mypipo

使用cat < mypipo读取管道中的数据

使用如下代码往管道内写如数据:

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(){
  // ./mypipo表示打开这个管道文件
  int outfd = open("./mypipo", O_WRONLY);
  write(outfd, "hello\n", 6);
  close(outfd);
  return 0;
}

一个进程往mkfifo创建的管道写入数据,另一个进程可用从这个管道读出数据。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值