Linux软件编程学习笔记(八):进程间通信详解(1)

前言

进程间通信(Inter-Process Communication,IPC)是指在操作系统中,不同进程之间交换数据和信息的技术。由于进程空间是独立的,每个进程独享自己的0-4G虚拟内存空间,因此需要借助特定的机制来实现进程间的数据交换。本文将详细介绍Linux下常用的进程间通信方式。


一、进程间通信概述

1. 进程间通信的必要性

  • 进程空间是独立的,无法直接访问其他进程的内存空间
  • 多个进程协同工作需要进行数据交换
  • 进程间通信是构建复杂应用程序的基础

2. 进程间通信的方法

  1. 管道(无名管道、有名管道)
  2. 信号
  3. 消息队列
  4. 共享内存
  5. 信号灯

二、管道通信

1. 无名管道

1.1 概念

无名管道只能用于具有亲缘关系的进程间通信(如父子进程之间)。

1.2 函数接口

pipe函数

c 复制代码
int pipe(int pipefd[2]);
  • 功能:创建一段用于进程间通信的内核缓存区
  • 参数
    • pipefd[0]:读文件描述符
    • pipefd[1]:写文件描述符
  • 返回值:成功返回0,失败返回-1
1.3 管道特性

关于写端:

  • 管道中有写端存在时:
    • 管道中有数据则阻塞等待直到数据被读出
  • 管道中没有写端时:
    • 管道中有数据则直接读出
    • 管道中没有数据则直接结束,不需要阻塞等待

关于读端:

  • 管道中有读端存在时:
    • 如果管道没有写满(64K),直接写入数据
    • 如果管道已写满,则需要有数据读出才能继续写入
  • 管道中没有读端时:
    • 写入数据会产生管道破裂的错误,使进程直接退出

2. 有名管道(FIFO)

2.1 概念

有名管道可以用于任意两个进程间的通信,不局限于具有亲缘关系的进程。

2.2 函数接口

mkfifo函数

c 复制代码
int mkfifo(const char *pathname, mode_t mode);
  • 功能:创建一个有名管道文件
  • 参数
    • pathname:有名管道的路径
    • mode:权限(如0664)
  • 返回值:成功返回0,失败返回-1
2.3 特性说明
  • 读写两端必须同时加入才能继续向下执行
  • 如果只有读端或写端,则会阻塞等待直到另一端加入
2.4 聊天程序示例

write.c(负责发送数据)

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(void)
{
    int fd = open("/tmp/fifo", O_WRONLY);
    char buf[1024] = {0};
    
    while (1) {
        gets(buf);
        write(fd, buf, strlen(buf));
    }
    
    close(fd);
    return 0;
}

read.c(负责接收数据)

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(void)
{
    mkfifo("/tmp/fifo", 0664);
    int fd = open("/tmp/fifo", O_RDONLY);
    char buf[1024] = {0};
    
    while (1) {
        read(fd, buf, sizeof(buf));
        printf("收到消息:%s\n", buf);
    }
    
    close(fd);
    return 0;
}

三、信号通信

1. 信号概述

信号是内核层与应用层通信的一种机制,用于通知进程发生了某种事件。

2. 信号处理方式

  1. 缺省:按照默认方式处理信号
  2. 忽略:不处理信号
  3. 捕捉:按照指定的方式处理信号

3. 常见信号类型

信号编号 信号名称 说明
2 SIGINT 从键盘输入Ctrl+C产生
3 SIGQUIT 从键盘输入Ctrl+\产生
9 SIGKILL 不能被忽略和捕捉
11 SIGSEGV 段错误信号
13 SIGPIPE 管道破裂信号
14 SIGALRM 定时时间到达信号
17 SIGCHLD 子进程结束信号
19 SIGSTOP 暂停信号
20 SIGTSTP 挂起信号(Ctrl+Z)

4. 函数接口

4.1 signal函数
c 复制代码
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  • 功能:改变信号的处理方式
  • 参数
    • signum:信号的编号
    • handler
      • SIG_IGN:忽略信号
      • SIG_DFL:缺省处理信号
      • 自定义函数指针:捕捉信号
  • 返回值:成功返回上一次注册的信号处理方式,失败返回SIG_ERR

使用示例:

c 复制代码
#include <stdio.h>
#include <signal.h>

void handler(int signum)
{
    printf("收到信号:%d\n", signum);
}

int main(void)
{
    // 捕捉SIGINT信号
    signal(SIGINT, handler);
    
    // 忽略SIGQUIT信号
    signal(SIGQUIT, SIG_IGN);
    
    while (1) {
        sleep(1);
    }
    
    return 0;
}
4.2 pause函数
c 复制代码
int pause(void);
  • 功能:让进程睡眠,直到收到信号
4.3 alarm函数
c 复制代码
unsigned int alarm(unsigned int seconds);
  • 功能:隔seconds秒后给调用进程发送SIGALRM信号

使用示例:

c 复制代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handler(int signum)
{
    printf("定时时间到!\n");
    alarm(2);  // 重新定时
}

int main(void)
{
    signal(SIGALRM, handler);
    alarm(2);  // 2秒后发送SIGALRM信号
    
    while (1) {
        pause();  // 等待信号
    }
    
    return 0;
}

四、其他进程间通信方式

1. 消息队列

消息队列是内核中存放消息的链表,允许多个进程向同一个队列读写消息。

2. 共享内存

共享内存是最快的IPC方式,多个进程共享同一块物理内存区域,无需数据复制。

3. 信号灯

信号灯(信号量)主要用于进程间同步,控制多个进程对共享资源的访问。


总结

本文详细介绍了Linux下进程间通信的几种主要方式:

  1. 无名管道:适用于有亲缘关系的进程间通信
  2. 有名管道:适用于任意进程间的通信
  3. 信号:内核与应用层通信的异步通知机制

在实际开发中,需要根据具体场景选择合适的IPC方式:

  • 简单数据流通信:使用管道
  • 异步事件通知:使用信号
  • 大量数据交换:使用共享内存
  • 多个进程协同:使用消息队列或信号灯
相关推荐
啄缘之间几秒前
1.【SVA】什么是SVA?
经验分享·学习·测试用例·verilog·sva
山岚的运维笔记5 分钟前
mysql 专业笔记 -- 第 36 章:MySQL 管理
运维·数据库·笔记·后端·mysql·oracle·dba
我是章汕呐21 分钟前
地级市能源消耗量及消耗强度数据【2006-2023年】平衡面板
人工智能·经验分享·算法·回归
小小的木头人33 分钟前
Linux systemd Service 与 Timer 定时任务原理及实战
linux
charliejohn35 分钟前
计算机考研 408 数据结构 堆的插入与删除 堆排序
算法
sunoo-22940 分钟前
【嵌入式裸机开发Day04】i.MX6ULL 蜂鸣器驱动详解:从引脚地址查找到代码实现全流程
arm开发·单片机·嵌入式硬件·学习·串口·裸机
Purple Coder1 小时前
stm32
学习
老鱼说AI1 小时前
从点积到希尔伯特空间:向量内积的几何本质与大模型相似度度量
人工智能·深度学习·线性代数·算法·机器学习·数学建模
边境悍匪1 小时前
蜗牛学苑 Java 智能体学习 Day45|Knife4j+SpringBoot3、书城项目整合 MyBatis 思维导图复盘
java·开发语言·vue.js·学习·spring
爱吃苹果的日记本1 小时前
离散数学第五课
学习·离散数学