进程间通信—管道通信

一.进程间通信(IPC)分类

1.古老的通信方式

  1. 无名管道(Pipes)

    • 只能用于有亲缘关系的进程间通信。
    • 数据流是单向的,通常用于父子进程间。
  2. 有名管道(Named Pipes,FIFO)

    • 可以用于任意两个进程间通信,即使它们没有亲缘关系。
    • 允许不相关的进程以先进先出(FIFO)的方式发送和接收数据。
  3. 信号(Signals)

    • 一种异步通信机制,用于通知进程有某些事件发生。
    • 信号可以在任何时候发送给进程,接收进程可以捕获并处理信号。

2.IPC对象通信

  1. 消息队列(Message Queues)

    • 用于进程间的消息传递。
    • 消息队列允许进程发送和接收格式化的消息。
  2. 共享内存(Shared Memory)

    • 允许多个进程共享一个给定的存储区。
    • 这是最快的IPC形式,因为避免了数据的复制。
  3. 信号量集(Semaphores)

    • 用于控制对共享资源的访问。
    • 信号量可以用来实现同步和互斥。

3.Socket通信

  1. Sockets
    • 用于网络通信,也可用于本机进程间通信。
    • 提供了一种双向通信的机制,支持面向连接(如TCP)和无连接(如UDP)的通信。

4.特殊说明

  • 信号是古老通信方式中唯一的异步通信方式。
  • 共享内存是所有通信方式中最有效的方式。

二.管道通信

  • 无名管道:只能用于有亲缘关系的进程间通信。
  • 有名管道(FIFO):可以用于任意单机进程间通信。

1.管道特性

  1. 管道是半双工工作模式。
  2. 所有的管道都是特殊文件,不支持定位操作。(×)lseek->> fd  fseek ->>FILE*
  3. 管道读写使用文件IO。//fgets,fread,fgetc,open,read,write,close;

2.阻塞破裂

  1. 写入阻塞
    当管道的读端存在时,如果向管道写入的数据量超过了管道的容量限制(通常是64KB),写操作将被阻塞,直到管道中的数据被读取,为新写入的数据腾出空间。
  2. 读取阻塞
    如果管道的写端存在,而读端尝试读取管道中的数据,但管道为空,读操作将被阻塞,直到有数据可读。
  3. 管道破裂
    当管道的读端关闭后,写端如果尝试写入数据,将触发SIGPIPE信号,导致写入进程异常终止或退出。这被称为“管道破裂”,因为管道的一端不再存在,无法完成数据传输。
  4. 读取返回0
    当管道的写端关闭,且管道内的数据已被完全读取,此时如果继续读取管道,read 函数将返回0,表示没有更多的数据可读。这并不意味着管道已经“破裂”,而是表明管道已被清空。

3.管道使用框架

  1. 创建管道
  2. 读写管道
  3. 关闭管道

三.无名管道

  • 使用 pipe 函数创建。
  • 亲缘关系进程使用,有固定的读写端。

1.创建并打开管道

  • 使用 pipe() 函数创建一个无名管道。

  • 包含头文件 <unistd.h>

#include <unistd.h>

int pipe(int pipefd[2]);
  • pipefd[0]:无名管道的固定读端。
  • pipefd[1]:无名管道的固定写端。
  • 返回值:成功返回 0,失败返回 -1。

2.读写管道

  • 使用文件 IO 的读写方式。
  • 读端使用 read() 函数读取数据。
  • 写端使用 write() 函数写入数据。

3.关闭管道

  • 使用 close() 函数关闭管道的读端或写端。
  • fd:要关闭的文件描述符(可以是读端或写端)。

  • 注意:
    • 无名管道的创建应在 fork() 调用之前进行,以确保父子进程能够正确地使用管道两端。

四.有名管道

  • 使用 mkfifo 创建。
  • 可以在文件系统中可见。

1.有名管道操作框架

   1.创建有名管道
  • 使用 mkfifo 函数创建有名管道。
  • 函数原型:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
  • 参数:
    • pathname: 有名管道的路径和名称。
    • mode: 有名管道的八进制文件权限。
  • 返回值:
    • 成功: 返回 0。
    • 失败: 返回 -1,并设置 errno

   2.打开有名管道
  • 使用 open 函数打开有名管道。
  • 注意只能以只读(O_RDONLY)或只写(O_WRONLY)模式打开,不能使用 O_RDWR

   3.读写管道
  • 使用文件 IO 函数进行读写操作。
   4.关闭管道
  • close(fd);
   5.卸载管道
  • remove();

          int unlink(const char *pathname);
          功能:将指定的pathname管道文件卸载,同时从文件系统中删除。
          参数: ptahtname 要卸载的有名管道 。
          返回值:成功为0,失败为-1。

2.问题思考

   1. 同步需求和位置
  • 同步的必要性:有名管道的读写操作通常需要同步,以确保数据能够被正确发送和接收。
  • 读端关闭:如果读端关闭,写端将不能继续写入数据。在大多数系统中,写入有名管道而没有打开读端会导致写入操作失败,可能触发SIGPIPE信号,导致写入进程终止。
  • 写端关闭:如果写端关闭,读端可以继续读取管道中已有的数据,直到数据被完全读取。如果管道中没有数据,读端的读取操作将返回0,表示没有更多数据可读。
   2.有名管道在fork之后的亲缘关系进程中的使用
  • 亲缘关系进程间使用:有名管道可以在fork之后创建的亲缘关系进程间使用,例如父子进程或兄弟进程。
  • 启动次序:进程启动的次序可能会影响同步。例如,如果写进程先于读进程启动并尝试写入数据,可能会因为读进程尚未打开管道而阻塞写操作。
   3. 手工操作有名管道实现数据传送
  • 读取有名管道:可以使用cat命令读取有名管道中的数据。
  • 写入有名管道:可以使用echo命令写入有名管道。
cat fifoname


echo "data" > fifoname

五.信号通信

信号通信概述

  • 应用:用于异步通信和中断处理。
  • 信号范围:1~64,其中前32个信号有具体含义。

信号的默认响应

  • Term:默认操作是终止进程。
  • Ign:默认操作是忽略信号。
  • Core:默认操作是终止进程并转储核心(dump core),可用于调试。
  • Stop:默认操作是停止进程。
  • Cont:如果进程当前已停止,则继续执行。

1. 发送端

kill 函数
  • 函数:int kill(pid_t pid, int sig);
  • 功能:通过该函数可以给pid进程发送信号为sig的系统信号。
  • 参数:
    • pid:目标进程的PID(进程标识符)。如果PID为负数,则信号将发送给与PID绝对值相等的进程组中的所有进程。
    • sig:当前程序要发送的信号编号。
  • 返回值
    • 成功:返回0。
    • 失败:返回-1,并设置errno以指示错误。
raise 函数
  • 函数:int raise(int sig)     ==      kill(getpid(),int sig);
  • 功能:等同于kill(getpid(), sig),即发送信号给调用进程本身。
  • 参数:sig,要发送给当前进程的信号编号。
alarm 函数
  • 函数:unsigned int alarm(unsigned int seconds);SIGALAM
  • 功能:设置一个定时器,经过指定的秒数后,向调用进程发送SIGALRM信号。闹钟只有一个,             定时只有一次有效,但是必须根据代码逻辑是否执行判断。
  • 参数seconds,定时器的秒数。
  • 返回值:返回之前设置的定时器剩余秒数,如果之前没有设置,则返回0。
pause 函数
  • 函数:int pause(void);
  • 功能:使调用进程挂起,直到收到信号。

2. 信号

  • 信号是UNIX和类UNIX系统中用于进程间通信的一种机制。
  • 信号可以由操作系统生成(如SIGINTSIGTERM等),也可以由其他进程通过kill函数发送。
  • 信号列表可以通过kill -l命令查看,前32个信号具有特定的默认行为和含义。

3. 接收端

信号处理方式
  • 默认处理:每个信号都有一个默认行为,例如SIGINT默认会终止进程。
  • 忽略处理:进程可以选择忽略某些信号,例如SIGCONTSIGSTOP
  • 自定义处理:进程可以注册自定义的信号处理函数来响应信号。
信号注册函数
  • 原型void ( *signal(int signum, void (*handler)(int)) ) (int);
  • 参数
    • signum:要注册信号处理的信号编号。
    • handler:指向信号处理函数的指针。
  • 返回值:返回指向之前信号处理函数的指针,如果之前没有注册,则返回SIG_ERR
信号处理函数的宏
  • SIG_DFL:指定信号的默认处理。
  • SIG_IGN:指定信号被忽略。
  • handler:指定自定义的信号处理函数。

自定义信号处理
  

 1、必须事先定义自定义函数,必须是如下格式:
        void fun(int sig)  sig 接收到的信息编号
        {

        }

    2、在所有的信号中有如下两个特列:
        10 SIGUSR1
        12 SIGUSR2
        
        专门预留给程序员使用的未定义信号。

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值