news 2026/2/1 5:49:21

【Linux 进程间通信】信号通信与共享内存核心解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Linux 进程间通信】信号通信与共享内存核心解析

一、概述

在 Linux 系统中,进程间通信(IPC)是实现多进程协作的核心能力,其中信号通信共享内存是两种高频使用的通信方式:

  • 信号通信:主打 “异步通知”,适用于进程间的事件触发、状态唤醒等场景;
  • 共享内存:是最快的 IPC 方式,通过共享物理内存实现数据互通,需搭配信号 / 信号量实现同步。

二、信号通信:异步通知的核心机制

2.1 信号的核心定位

信号是 Linux 内核提供的异步通信机制,本质是 “通知机制”,用于处理系统中的 “随机事件”(如进程暂停、唤醒、终止、自定义事件等),核心特点:

  • 异步性:信号的产生和处理与进程主流程无固定时序;
  • 中断性:信号到达时,进程会暂停当前流程,优先执行信号处理函数,执行完毕后恢复原流程。

2.2 信号发送与接收的完整流程

  1. 信号产生:由随机事件触发(如kill命令、系统调用、硬件异常等);
  2. 内核查找目标进程:Linux 内核接收到信号发送请求后,在进程控制块(PCB)的信号链表中,查找目标 PID 对应的进程;
  3. 中断并执行处理函数:找到目标进程后,暂停其原有工作流程,执行 PCB 中信号编号对应下标的处理函数(如信号 2 对应handle2);
  4. 恢复原流程:信号处理函数执行完毕后,进程回到原有代码继续运行。

2.3 信号相关核心函数

(1)发送信号:kill ()

c

运行

#include <signal.h> int kill(pid_t pid, int sig);
  • 功能:向指定 PID 的进程发送指定编号的信号;
  • 参数
    • pid:接收信号的进程 PID;
    • sig:信号编号(可通过kill -l查看所有信号编号);
  • 返回值:成功返回 0,失败返回 - 1。

示例:向 PID 为 1000 的进程发送 SIGCONT(唤醒)信号

c

运行

kill(1000, 18); // 18是SIGCONT的默认编号,等价于kill -CONT 1000
(2)捕获并自定义信号处理:signal ()

c

运行

#include <signal.h> // 函数原型(简化版) sighandler_t signal(int signum, sighandler_t handler);
  • 功能:注册信号处理函数,自定义信号的处理行为;
  • 参数
    • signum:要捕获的信号编号;
    • handler:信号处理方式,支持 3 种:
      • SIG_DFL:使用系统默认处理行为;
      • SIG_IGN:忽略该信号;
      • 自定义函数:如void myhandle(int num),接收信号编号作为参数;
  • 返回值:成功返回原信号处理函数地址,失败返回SIG_ERR

示例:自定义 SIGCONT 信号的处理函数

c

运行

void myhandle(int num) { printf("捕获到信号%d,进程被唤醒\n", num); } // 注册信号处理函数 signal(SIGCONT, myhandle);

2.4 信号相关辅助命令

  • 查看所有信号的编号和名称:kill -l
  • 查看信号的详细说明和默认处理行为:man 7 signal

三、共享内存:最快的进程间通信方式

3.1 共享内存的核心定位

共享内存是 System V 标准提供的 IPC 方式,核心是让多个进程映射同一块物理内存到自己的地址空间,实现数据直接互通。

  • 优势:无需数据拷贝,是所有 IPC 中速度最快的;
  • 注意:共享内存本身无同步 / 互斥机制,需搭配信号、信号量等实现 “读写同步”。

3.2 共享内存的使用流程(核心 6 步)

graph LR A[生成唯一Key值:ftok()] --> B[申请共享内存:shmget()] B --> C[映射到进程地址空间:shmat()] C --> D[读写共享内存:memcpy/strcpy] D --> E[撤销映射:shmdt()] E --> F[删除共享内存:shmctl()]

3.3 共享内存核心函数

(1)生成唯一 Key 值:ftok ()

c

运行

#include <sys/ipc.h> key_t ftok(const char *pathname, int proj_id);
  • 功能:通过文件路径和自定义标识生成唯一键值,用于标识共享内存;
  • 参数
    • pathname:任意文件路径(需保证文件不被删除 / 重建,否则 Key 值会变化);
    • proj_id:整型标识(通常用 ASCII 单字符,如'!');
  • 返回值:成功返回唯一 Key 值,失败返回 - 1。

示例

c

运行

key_t key = ftok("./", '!'); // 基于当前目录生成Key值
(2)申请共享内存:shmget ()

c

运行

#include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg);
  • 功能:向内核申请指定大小的共享内存;
  • 参数
    • key:ftok 生成的唯一 Key 值;
    • size:共享内存大小(字节,建议为 4096 的整数倍);
    • shmflg:权限 + 创建标识,常用IPC_CREAT | 0666(不存在则创建,权限为 666);
  • 返回值:成功返回共享内存 ID(shmid),失败返回 - 1。

示例

c

运行

int shmid = shmget(key, 4096, IPC_CREAT | 0666);
(3)映射共享内存:shmat ()

c

运行

#include <sys/shm.h> void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 功能:将共享内存映射到进程的地址空间;
  • 参数
    • shmid:shmget 返回的共享内存 ID;
    • shmaddr:指定映射地址,NULL 表示由系统自动分配;
    • shmflg:访问权限,0 表示可读写,SHM_RDONLY表示只读;
  • 返回值:成功返回映射地址,失败返回(void*)-1

示例

c

运行

void *p = shmat(shmid, NULL, 0); // 映射为可读写
(4)读写共享内存

共享内存映射后可直接当作普通内存使用,支持字符串 / 二进制数据操作:

c

运行

// 写入字符串 strcpy((char*)p, "共享内存测试数据"); // 读取字符串 printf("共享内存内容:%s\n", (char*)p); // 二进制数据读写(如结构体) memcpy(p, &data, sizeof(data));
(5)撤销映射:shmdt ()

c

运行

#include <sys/shm.h> int shmdt(const void *shmaddr);
  • 功能:断开进程与共享内存的映射关系(仅解绑,不删除共享内存);
  • 参数:shmat 返回的映射地址;
  • 返回值:成功返回 0,失败返回 - 1。

示例

c

运行

shmdt(p); // 撤销映射
(6)删除共享内存:shmctl ()

c

运行

#include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 功能:修改共享内存属性或删除共享内存;
  • 参数
    • shmid:共享内存 ID;
    • cmd:操作指令,IPC_RMID表示删除;
    • buf:NULL 表示仅删除,无需获取属性;
  • 返回值:成功返回 0,失败返回 - 1。

示例

c

运行

shmctl(shmid, IPC_RMID, NULL); // 彻底删除共享内存

3.4 共享内存相关命令

  • 查看系统中所有共享内存、信号量、消息队列:ipcs -a
  • 删除指定 ID 的共享内存:ipcrm -m 共享内存ID

四、共享内存与管道(无名 / 有名)的核心区别

管道(无名pipe/ 有名mkfifo)也是常用 IPC 方式,但与共享内存差异显著:

特性共享内存管道(无名 / 有名)
读写权限双方均可读写无名管道:固定读端 / 写端;有名管道:双向但需同步
阻塞特性无读 / 写阻塞读阻塞(无数据)、写阻塞(缓冲区满)
同步机制无,需搭配信号 / 信号量自带同步(阻塞机制)
数据存储内存中,不删除则一直存在内核缓冲区,读取后数据消失
数据拷贝无拷贝(直接操作内存)需内核态 / 用户态拷贝
易用性需手动管理映射 / 删除可当作文件操作,更简单

管道核心函数补充

(1)创建无名管道:pipe ()

c

运行

#include <unistd.h> int pipe(int pipefd[2]);
  • 功能:创建并打开无名管道;
  • 参数pipefd[0]为读端,pipefd[1]为写端;
  • 返回值:成功返回 0,失败返回 - 1。
(2)创建有名管道:mkfifo ()

c

运行

#include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
  • 功能:创建有名管道文件;
  • 参数
    • pathname:管道文件的路径 + 名称;
    • mode:文件权限(8 进制,如 0666);
  • 返回值:成功返回 0,失败返回 - 1。

五、完整示例:共享内存 + 信号实现进程通信

5.1 进程 A:创建共享内存,写入数据,等待信号唤醒

c

运行

#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> void myhandle(int num) { printf("进程A捕获到信号%d,被唤醒\n", num); } int main() { // 1. 生成Key值 key_t key = ftok("./", '!'); if (key == -1) { perror("ftok"); return 1; } // 2. 申请共享内存 int shmid = shmget(key, 4096, IPC_CREAT | 0666); if (shmid == -1) { perror("shmget"); return 1; } // 3. 映射共享内存 void *p = shmat(shmid, NULL, 0); if (p == (void*)-1) { perror("shmat"); return 1; } // 4. 写入数据 strcpy((char*)p, "Hello, 共享内存+信号通信"); printf("进程A PID:%d,已写入数据到共享内存\n", getpid()); // 5. 注册SIGCONT信号处理函数 signal(SIGCONT, myhandle); // 6. 等待信号唤醒 printf("进程A进入阻塞,等待信号...\n"); pause(); // 7. 撤销映射 shmdt(p); // 8. 删除共享内存(可选) shmctl(shmid, IPC_RMID, NULL); return 0; }

5.2 进程 B:读取共享内存,发送信号唤醒进程 A

c

运行

#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char *argv[]) { if (argc < 2) { printf("用法:%s <进程A的PID>\n", argv[0]); return 1; } pid_t pid_a = atoi(argv[1]); // 1. 生成相同的Key值 key_t key = ftok("./", '!'); if (key == -1) { perror("ftok"); return 1; } // 2. 获取共享内存 int shmid = shmget(key, 4096, 0666); if (shmid == -1) { perror("shmget"); return 1; } // 3. 映射共享内存 void *p = shmat(shmid, NULL, 0); if (p == (void*)-1) { perror("shmat"); return 1; } // 4. 读取共享内存数据 printf("进程B读取到共享内存数据:%s\n", (char*)p); // 5. 发送SIGCONT信号唤醒进程A kill(pid_a, 18); printf("进程B已向进程A发送唤醒信号\n"); // 6. 撤销映射 shmdt(p); return 0; }

5.3 运行步骤

  1. 编译进程 A:gcc shm_signal_a.c -o a.out,运行:./a.out(记录进程 A 的 PID);
  2. 编译进程 B:gcc shm_signal_b.c -o b.out,运行:./b.out <进程A的PID>
  3. 观察进程 A 输出:捕获到信号 18,被唤醒,完成通信。

六、总结

  1. 信号通信:核心是 “异步通知”,通过kill发送信号、signal捕获信号,适用于事件触发、进程唤醒等场景;
  2. 共享内存:最快的 IPC 方式,核心流程是 “Key→申请→映射→读写→解绑→删除”,需搭配信号 / 信号量实现同步;
  3. 与管道对比:共享内存无阻塞、无数据拷贝,但需手动管理;管道易用性高,自带同步但速度慢;
  4. 实际开发中,共享内存 + 信号是高性能进程通信的常用组合,既保证数据传输效率,又能实现事件同步。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/30 14:11:01

Linly-Talker背后的技术栈:Transformer+Diffusion组合应用

Linly-Talker背后的技术栈&#xff1a;Transformer与Diffusion的协同艺术 在虚拟主播深夜仍在带货、AI教师全天候讲解知识点、数字客服精准回应用户提问的今天&#xff0c;我们正悄然步入一个“非人类却拟人”的交互新时代。驱动这一变革的核心&#xff0c;并非昂贵的动作捕捉设…

作者头像 李华
网站建设 2026/1/30 6:29:40

Langchain-Chatchat OpenTelemetry统一观测知识平台

Langchain-Chatchat OpenTelemetry统一观测知识平台 在企业级AI应用逐渐从“能用”走向“可靠”的今天&#xff0c;一个看似简单的本地知识库问答系统&#xff0c;背后却可能隐藏着复杂的调用链路与性能瓶颈。当用户提问“年假是如何规定的&#xff1f;”时&#xff0c;系统不仅…

作者头像 李华
网站建设 2026/1/31 17:51:00

Linly-Talker支持多语言吗?中文语音合成表现实测

Linly-Talker支持多语言吗&#xff1f;中文语音合成表现实测 在虚拟主播、AI客服和在线教育日益普及的今天&#xff0c;一个能“听懂”用户提问、“说出”自然回应&#xff0c;并配上逼真口型动作的数字人&#xff0c;已经不再是科幻电影里的设定。越来越多企业开始尝试用AI数字…

作者头像 李华
网站建设 2026/1/30 12:23:05

25、Windows 容器与服务器维护全解析

Windows 容器与服务器维护全解析 1. Windows 容器基础 Windows 容器是 Windows Server 2016 或部分 Windows 10 版本中的全新技术,它与虚拟机有相似之处,但在构建时无需像虚拟机那样配置所有运行所需的服务。Windows 容器是快速的操作系统构建方式,能让应用在独立环境中运…

作者头像 李华
网站建设 2026/1/31 23:12:27

Langchain-Chatchat新人培训知识问答系统

Langchain-Chatchat 新人培训知识问答系统 在企业数字化转型的浪潮中&#xff0c;新员工培训、制度查询和内部技术支持等场景正面临一个共性难题&#xff1a;信息分散、响应滞后、人力成本高。尽管大语言模型&#xff08;LLM&#xff09;已经展现出强大的自然语言处理能力&…

作者头像 李华
网站建设 2026/2/1 4:50:47

Langchain-Chatchat Consul服务发现知识库

Langchain-Chatchat 与 Consul 构建企业级智能知识中枢 在当今企业数字化转型的浪潮中&#xff0c;如何让沉睡在 PDF、Word 和内部文档中的知识“活”起来&#xff0c;正成为 AI 落地的关键突破口。尤其是金融、制造、医疗等对数据隐私高度敏感的行业&#xff0c;既渴望引入大模…

作者头像 李华