当前位置: 首页 > news >正文

嵌入式解谜日志之Linux操作系统—共享内存

信号通信

概述
信号默认行为
行为说明
Term默认终止进程(如 SIGINT
Ign默认忽略信号(如 SIGCHLD
Core终止进程并生成core文件(可通过 gdb a.out -c core 调试,如 SIGSEGV
Stop默认停止进程(如 SIGSTOP
Cont默认继续进程(如 SIGCONT

关键限制:9号(SIGKILL)和19号(SIGSTOP)信号不可捕获或忽略,只能执行默认操作

信号发送函数
kill(pid, sig)
int kill(pid_t pid, int sig);
raise(sig)
int raise(int sig);
alarm(seconds)
unsigned int alarm(unsigned int seconds);
pause()
int pause(void);

功能:挂起进程执行,直到收到信号

signal(signum, handler)
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

典型用法
signal(SIGINT, SIG_IGN); // 忽略Ctrl+C
signal(SIGALRM, handler); // 注册自定义闹钟处理

共享内存(System V IPC)

System V IPC对象

对象类型用途核心函数
共享内存(shm)高效进程间数据共享shmgetshmatshmdt
信号量集(sem)进程同步控制semgetsemopsemctl
消息队列(msg)结构化数据传递msggetmsgsndmsgrcv
IPC对象操作通用流程
  1. 生成唯一键值

key_t ftok(const char *pathname, int proj_id);

常用管理命令
命令说明
ipcs -a查询所有IPC对象状态
ipcrm -m shmid删除指定ID的共享内存
ipcrm -s semid删除指定ID的信号量集
ipcrm -q msgid删除指定ID的消息队列
共享内存特性
写端程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>   // IPC常量定义
#include <sys/shm.h>   // 共享内存操作函数
#include <sys/types.h> // 系统数据类型
#include <unistd.h>    // POSIX标准函数int main(int argc, char **argv)
{// 生成唯一键值:当前目录路径 + '!'标识符key_t key = ftok("./", '!');if (-1 == key){perror("ftok");return 1;}printf("key is 0x%x\n", key); // 打印生成的键值// 创建共享内存:指定key/4096字节/创建权限int shmid = shmget(key, 4096, IPC_CREAT | 0666);if (-1 == shmid){perror("shmid");return 1;}// 映射共享内存到进程地址空间(可读写)void *p = shmat(shmid, NULL, !SHM_RDONLY);if ((void *)-1 == p){perror("shmat");return 1;}// 准备测试数据并写入共享内存char buf[] = "hello,this is shm test";memcpy(p, buf, strlen(buf)); // 复制有效字符串内容// 撤销地址映射shmdt(p);return 0;
}

理想运行结果

key is 0x210217b3  // 键值因系统环境而异

执行说明:成功写入数据后立即退出,无输出错误信息

读端:

    #include <stdio.h>      // 标准输入输出函数(printf等)
    #include <stdlib.h>     // 标准库函数(exit等)
    #include <string.h>     // 字符串操作函数(memcpy等)
    #include <sys/ipc.h>    // IPC(进程间通信)相关定义(key_t等)
    #include <sys/shm.h>    // 共享内存相关函数(shmget、shmat等)
    #include <sys/types.h>  // 基本系统数据类型(pid_t等)
    #include <unistd.h>     // 系统调用函数(fork、sleep等)int main(int argc, char **argv)
    {// 1. 生成共享内存的唯一标识键值(key)// ftok函数:根据路径和项目ID生成key,相同参数生成相同key// 参数1:存在的文件路径(用于确保唯一性)// 参数2:项目ID(0-255之间的字符,用于区分同一目录下的不同IPC对象)key_t key = ftok("./", '!');if (-1 == key)  // 生成key失败{perror("ftok");  // 打印错误信息(如文件不存在)return 1;        // 非0退出表示程序异常}printf("key is 0x%x\n", key);  // 打印生成的16进制key值// 2. 创建或获取共享内存段// shmget函数:创建新的共享内存或获取已存在的共享内存// 参数1:key值(标识共享内存)// 参数2:共享内存大小(字节),4096是常见的页大小倍数// 参数3:标志位,IPC_CREAT表示不存在则创建;0666是权限(rwxrwxrwx)int shmid = shmget(key, 4096, IPC_CREAT | 0666);if (-1 == shmid)  // 创建/获取共享内存失败{perror("shmget");  // 打印错误信息(如权限不足、内存已满)return 1;}// 3. 将共享内存附加到当前进程的地址空间// shmat函数:将共享内存映射到进程的虚拟地址空间,返回映射后的指针// 参数1:共享内存标识符(shmid)// 参数2:指定映射地址(NULL表示由系统自动分配)// 参数3:访问权限,!SHM_RDONLY表示可读可写(SHM_RDONLY为只读)void *p = shmat(shmid, NULL, !SHM_RDONLY);if ((void *)-1 == p)  // 附加共享内存失败{perror("shmat");  // 打印错误信息(如权限不足)return 1;}// 4. 读取共享内存中的数据char buf[4096] = {0};  // 本地缓冲区,用于存储从共享内存读取的数据// memcpy:内存复制函数,将共享内存的数据复制到本地缓冲区// 参数1:目标地址(本地buf)// 参数2:源地址(共享内存指针p)// 参数3:复制的字节数(buf的大小,确保不越界)memcpy(buf, p, sizeof(buf));printf("从共享内存读取到的数据:%s\n", buf);  // 打印读取到的数据// 5. 解除共享内存与当前进程的关联// shmdt函数:分离共享内存,仅断开映射关系,不删除共享内存本身shmdt(p);// 6. (可选)删除共享内存段// 注释说明:取消注释后,程序退出时会彻底删除共享内存// 注意:若有其他进程正在使用该共享内存,删除后可能导致错误// shmctl(shmid, IPC_RMID, NULL);return 0;  // 0退出表示程序正常结束
    }
    

    理想运行结果(需先运行写端):

    key is 0x210217b3
    buf hello,this is shm test

    读阻塞模拟方案

    核心机制
    读端代码(带阻塞控制)
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ipc.h>   // IPC常量
    #include <sys/shm.h>   // 共享内存操作
    #include <sys/types.h> // 系统类型
    #include <unistd.h>    // POSIX函数
    #include <signal.h>    // 信号处理// SIGCONT信号处理函数(空实现)
    void myhandle(int num)
    {// 仅用于覆盖默认行为,确保pause()可被唤醒
    }int main(int argc, char **argv)
    {// 注册SIGCONT信号处理signal(SIGCONT, myhandle);// 生成唯一键值(与写端一致)key_t key = ftok("./", '!');if (-1 == key){perror("ftok");return 1;}printf("key is 0x%x\n", key);// 创建/获取共享内存段int shmid = shmget(key, 4096, IPC_CREAT | 0666);if (-1 == shmid){perror("shmid");return 1;}// 附加共享内存(可读写)void *p = shmat(shmid, NULL, !SHM_RDONLY);if ((void *)-1 == p){perror("shmat");return 1;}// 存储自身PID到共享内存首部pid_t pid = getpid();*(int*)p = pid;printf("recv, pid:%d\n", pid); // 显示读端PID// 核心阻塞点:等待SIGCONT信号pause();// 信号唤醒后读取数据char buf[4096] = {0};memcpy(buf, p, sizeof(buf));printf("buf %s\n", buf); // 显示接收内容// 撤销映射shmdt(p);return 0;
    }
    
    写端代码(带唤醒功能)
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <signal.h>int main(int argc, char **argv)
    {// 生成相同键值key_t key = ftok("./", '!');if (-1 == key){perror("ftok");return 1;}printf("key is 0x%x\n", key);// 获取已有共享内存int shmid = shmget(key, 4096, IPC_CREAT | 0666);if (-1 == shmid){perror("shmid");return 1;}// 附加共享内存void *p = shmat(shmid, NULL, !SHM_RDONLY);if ((void *)-1 == p){perror("shmat");return 1;}// 读取读端PID(共享内存首部)pid_t pid = 0;memcpy(&pid, p, sizeof(pid));printf("kill, recv pid:%d\n", pid);// 准备测试消息并写入char buf[] = "hello,this is shm test";memcpy(p, buf, strlen(buf));// 撤销映射shmdt(p);// 发送SIGCONT唤醒读端kill(pid, SIGCONT);return 0;
    }
    
    执行流程与理想结果
    执行顺序
    1. 先启动读端

      • 生成共享内存键值
      • 存储自身PID
      • 主动挂起pause() 阻塞)
    2. 再启动写端

      • 获取共享内存
      • 读取读端PID
      • 写入测试消息
      • 发送唤醒信号
    3. 读端被唤醒

      • 继续执行
      • 读取共享数据
      • 正常退出
    终端输出

    读端终端(先运行):

    key is 0x210217b3
    recv, pid:12345
    buf hello,this is shm test

    关键行为:输出 recv, pid:12345 后进程挂起,收到信号后继续执行

    写端终端(后运行):

    key is 0x210217b3
    kill, recv pid:12345
    

    关键行为:成功获取PID并发送唤醒信号后立即退出

    验证要点
    ✅ 阻塞效果:读端在 pause() 处明确阻塞
    ✅ 信号同步:SIGCONT 确保读端仅在数据就绪后执行
    ✅ 数据一致性:消息完整传递(写端未写入 \0 但缓冲区初始化为零)
    ⚠️ 典型错误:
    先运行写端 → 读取无效PID(0)导致 kill(0, SIGCONT) 失败
    缺少信号处理函数 → pause() 永久挂起

    http://www.dtcms.com/a/361794.html

    相关文章:

  • Python备份实战专栏第5/6篇:Docker + Nginx 生产环境一键部署方案
  • 基于多种分词算法的词频统计的中文分词系统的设计与实现
  • 信创之-麒麟v10服务器安装tengine(已完成)
  • 推荐系统中Redis 数据存储:二进制序列化协议选型与优化
  • linux连接服务器sftp无法输入中文
  • 基于SpringBoot的教务管理系统(源码+文档)
  • C/C++ Linux系统编程:进程通讯完全指南,管道通讯、共享内存以及消息队列
  • 零基础从头教学Linux(Day 25)
  • vue3使用Eslint
  • B样条曲线在节点u处添加节点的操作方法
  • 心率监测系统优化方案全解析
  • 火语言 RPA:轻松生成界面应用,让开发触手可及​
  • 求欧拉回路:Hierholzer算法图解模拟
  • 计算机网络技术(四)完结
  • 算法题-02
  • 大型语言模型监督微调(SFT)
  • GitLab 18.3 正式发布,更新多项 DevOps、CI/CD 功能【二】
  • MiniCPM-V-4.5:重新定义边缘设备多模态AI的下一代视觉语言模型
  • 前端测试深度实践:从单元测试到E2E测试的完整测试解决方案
  • Axios与Ajax:现代Web请求大比拼
  • 新手向:前端开发中的常见问题
  • Laser Lorentzian Lineshape
  • 进程控制之进程创建与终止
  • Vue3+TS 流星夜景
  • TensorFlow 2.10 是最后一个支持在原生Windows上使用GPU的TensorFlow版本
  • Redisson和Redis实现分布式锁的对比
  • 【免费数据】2019年我国36个主要城市的高分辨率城市空地分布矢量数据
  • 【2025ICCV】
  • FOUPK3云服务平台旗下产品
  • Python 实战:内网渗透中的信息收集自动化脚本(7)