Linux系统编程---4(进程间通信IPC,管道)

本文深入解析进程间通信(IPC)的原理与应用,包括数据传输、资源共享、通知事件及进程控制等核心概念。探讨Linux系统下7种文件类型,重点介绍管道、信号、共享内存、消息队列、套接字、命名管道等IPC机制,特别是管道和FIFO(有名管道)的工作原理、特点及限制。

进程间通信目的

  1. 数据传输:一个进程需要将它的数据发送给另一个进程
  2. 资源共享:多个进程之间共享同样的资源。
  3. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。
  4. 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变

IPC

linux系统下7中文件类型

  1. -- 文件
  2. d 目录
  3. l 符号链接
    伪文件:(不占用内存)
  4. s 套接字
  5. b 块设备
  6. c 字符设备
  7. p 管道
    Linux 环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另 一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区, 进程 1 把数据从用户空间拷到内核缓冲区,进程2 再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通 信(IPC,InterProcessCommunication)。
    在这里插入图片描述
    在进程间完成数据传递需要借助操作系统提供特殊的方法,如:文件、管道、信号、共享内存、消息队列、套 接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信 方式有:
  8. 管道 (使用最简单)
    pipe(管道一般读写行为)
    fifo(有名管道,用于非血缘关系)
  9. 信号 (开销最小)
  10. 共享映射区 (无血缘关系)
  11. 本地套接字 (最稳定)

管道

管道的概念:

管道是一种最基本的 IPC 机制,作用于有血缘关系的进程之间,完成数据传递。调用 pipe 系统函数即可创建一 个管道。

管道特质:

  1. 其本质是一个伪文件(实为内核缓冲区)
  2. 由两个文件描述符引用,一个表示读端,一个表示写端。
  3. 规定数据从管道的写端流入管道,从读端流出。

管道的原理:

管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。

管道的局限性:

  1. 数据自己读不能自己写。
  2. 数据一旦被读走,便不在管道中存在,不可反复读取。
  3. 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动
  4. 只能在有公共祖先的进程间使用管道

常见的通信方式

  1. 单工通信,只能收不能发
  2. 半双工通信,数据只能在一个方向上流动
  3. 全双工通信。

管道的特点

  1. 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创 建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  2. 管道提供流式服务
  3. 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  4. 一般而言,内核会对管道操作进行同步与互斥
  5. 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

pipe 函数

int  pipe (int  pipefd[2] ); 成功:0;失败:-1,设置 errno 

函数调用成功返回 r/w 两个文件描述符。无需 open,但需手动 close。规定:fd[0] → r; fd[1] → w,就像 0 对应标准输入,1 对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。
管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢? 通常可以采用如下步骤:
在这里插入图片描述

  1. 父进程调用 pipe 函数创建管道,得到两个文件描述符 fd[0]、fd[1]指向管道的读端和写端。

  2. 父进程调用 fork 创建子进程,那么子进程也有两个文件描述符指向同一管道。

  3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。 由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
    在这里插入图片描述

     #include<stdio.h>
     #include<unistd.h>
     #include<stdlib.h>
     #include<string.h>
     int main(void)
     {
         int fd[2];
         pid_t pid;
         
         int ret=pipe(fd);
         if(ret==-1)
         {   
             perror("pipe error:");
             exit(1);
         }   
        pid = fork();
        if(pid==-1){
            perror("perror error:");
            exit(1);
        }else if(pid==0){    //子进程  读数据 
            close(fd[1]);//关闭管道的写端
             char buf[1024];
             ret=read(fd[0],buf,sizeof(buf));
             if(ret == 0){ 
                 printf("----\n");
             }   
     
             write(STDOUT_FILENO,buf,ret);
        }else{                   //父进程写数据
            sleep(1);                                                                   
             close(fd[0]);//关掉管道的读端
             write(fd[1],"hello pipe\n",strlen("hello pipe\n"));
        }   
     
     }
    

在这里插入图片描述

管道的读写行为

使用管道需要注意以下 4 种特殊情况(假设都是阻塞 I/O 操作,没有设置 O_NONBLOCK 标志) :

  1. 如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为 0),而仍然有进程从管道的读端读数 据,那么管道中剩余的数据都被读取后,再次 read 会返回 0,就像读到文件末尾一样。
  2. 如果有指向管道写端的文件描述符没关闭(管道写端引用计数大于 0),而持有管道写端的进程也没有向管 道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次 read 会阻塞,直到管道中有 数据可读了才读取数据并返回。
  3. 如果所有指向管道读端的文件描述符都关闭了(管道读端引用计数为 0),这时有进程向管道的写端 write, 那么该进程会收到信号 SIGPIPE,通常会导致进程异常终止。当然也可以对 SIGPIPE 信号实施捕捉,不终止进程。具 体方法信号章节详细介绍。
  4. 如果有指向管道读端的文件描述符没关闭(管道读端引用计数大于 0),而持有管道读端的进程也没有从管 道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次 write 会阻塞,直到管道中有空位置了才写 入数据并返回。

总结

读管道
  1. 管道中有数据,read 返回实际读到的字节数
  2. 管道中无数据:
    (1) 管道写端被全部关闭,read 返回 0(好像读到文件结尾)
    (2) 写端没有全部被关闭,read 阻塞等待(不久的将来可能有数据递达,此时会让出 cpu)
写管道
  1. 管道读端全部被关闭, 进程异常终止(也可使用捕捉 SIGPIPE 信号,使进程不终止)
  2. 管道读端没有全部关闭:
    (1) 管道已满,write 阻塞。
    (2) 管道未满,write 将数据写入,并返回实际写入的字节数。

管道缓冲区大小

可以使用 ulimit–a 命令来查看当前系统中创建管道文件所对应的内核缓冲区大小。通常为:

pipe size     (512bytes,-p)8

也可以使用 fpathconf 函数,借助参数 选项来查看。使用该宏应引入头文件<unistd.h>

long  fpathconf(int  fd,int  name); 成功:返回管道的大小 失败:-1,设置 errno

管道的优劣

优点:简单,相比信号,套接字实现进程间通信,简单很多。
缺点:

  1. 只能单向通信,双向通信需建立两个管道。
  2. 只能用于父子、兄弟进程(有共同祖先)间通信。该问题后来使用 fifo 有名管道解决。

FIFO

FIFO 常被称为命名管道,以区分管道(pipe)。管道(pipe)只能用于“有血缘关系”的进程间。但通过 FIFO,不相 关的进程也能交换数据。
FIFO 是 Linux 基础文件类型中的一种。但,FIFO 文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。各 进程可以打开这个文件进行 read/write,实际上是在读写内核通道,这样就实现了进程间通信。
创建方式:

  1. 命令:mkfifo 管道名
  2. 库函数:int mkfifo(const char*pathname, mode_t mode); 成功:0; 失败:-1 一旦使用 mkfifo 创建了一个 FIFO,就可以使用 open 打开它,常见的文件 I/O 函数都可用于 fifo。如: close、 read、 write、unlink 等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值