Linux系统编程|信号进阶 + SystemV IPC(消息队列、共享内存)

一、信号进阶:发送信号、pause、alarm定时器

1.1 kill() ------ 向指定进程发送信号

kill不只是杀死进程,本质功能:给指定PID的进程发送信号,可以发送任意合法信号。

头文件

#include <sys/types.h>

#include <signal.h>

int kill(pid_t pid, int sig);

参数说明

  • pid:接收信号的进程ID

  • pid > 0:信号发送给pid对应的进程

  • pid = 0:发送给当前进程组所有进程

  • pid = -1:发送给系统内有权限的全部进程

  • sig:信号编号(如`SIGINT`、`SIGUSR1`)

返回值

  • 成功返回0;失败返回1,设置errno。

示例:kill(pid, SIGKILL);强制杀死目标进程。

1.2 pause() ------ 进程休眠等待信号唤醒

#include <unistd.h>

int pause(void);

功能:将当前进程挂起休眠,直到捕获到一个信号,执行完信号处理函数之后pause才返回。

返回值:永远返回1,errno设置为EINTR,表示被信号中断。

使用场景:进程休眠等待外部信号触发业务逻辑。

注意:必须提前注册信号捕获处理函数,如果收到的信号是默认终止行为,进程直接退出,pause不会返回。

1.3 alarm() 定时信号

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

功能:seconds秒之后,向自己进程发送SIGALRM信号。

  • SIGALRM默认动作:终止进程。

  • 如果多次调用alarm,新调用会覆盖上一次定时器。

返回值:返回上一次剩余倒计时秒数;之前没有设置返回0。

简单示例:

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

void alarm_handler(int sig)

{

printf("收到定时SIGALRM信号!\n");

}

int main(void)

{

signal(SIGALRM, alarm_handler);

alarm(3); //3秒后发出SIGALRM

pause();

printf("程序继续往下执行\n");

return 0;

}

```

二、SystemV IPC通用知识点

2.1 ftok() 生成IPC对象key键值

SystemV IPC(消息队列、共享内存、信号量集)依靠`key_t`键值,多个进程使用同一个key,就可以访问同一个内核IPC对象。

#include <sys/types.h>

#include <sys/ipc.h>

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

  • pathname:必须是真实存在的文件路径;

  • proj_id:项目ID,0255的整数;

返回值:成功返回key;失败返回1。

原理:利用文件inode + proj_id计算出唯一key;同一个文件+同一个proj_id得到的key完全相同。

2.2 IPC命令行调试工具

ipcs -a # 查看系统所有SystemV IPC对象

删除消息队列

ipcrm -q 消息队列ID

ipcrm -Q key值

删除共享内存

ipcrm -m shmid

ipcrm -M key

删除信号量集

ipcrm -s semid

ipcrm -S key

三、消息队列 Message Queue

消息队列是**内核维护的消息链表**,每条消息带有消息类型`mtype`;进程可以按照消息类型选择性读取消息,不再像管道只能读取字节流。

特点:

  1. 消息有类型字段,可以按类型筛选接收;

  2. 收发进程不需要同时运行,消息缓存在内核;

  3. 进程结束消息队列不会自动销毁,需要手动删除。

3.1 核心结构体

struct msgbuf {

long mtype; /* 消息类型,必须大于0 */

char mtext1; /* 消息正文,长度自定义 */

};

3.2 全套API

① msgget() 创建/获取消息队列ID

#include <sys/msg.h>

int msgget(key_t key, int msgflg);

  • key:ftok生成的key;

  • msgflg:IPC_CREAT | 0664,不存在则创建,存在直接获取;

返回值:成功返回消息队列ID(msqid);失败1。

② msgsnd() 发送消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

  • msqid:msgget返回ID;

  • msgp:指向msgbuf消息结构体;

  • msgsz:消息正文mtext字节大小,不包含mtype;

  • msgflg:填0代表阻塞发送。

返回值:成功0,失败1。

③ msgrcv() 接收消息

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

  • msgtyp:消息类型

  • msgtyp = 0:读取队列第一条消息;

  • msgtyp >0:读取mtype等于msgtyp的消息;

  • msgflg:0阻塞读取;

返回值:成功返回读到正文字节数,失败1。

④ msgctl() 控制消息队列(删除/获取属性)

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

  • cmd:IPC_RMID删除消息队列,此时buf传NULL;

  • IPC_STAT获取队列属性。

四、共享内存 Shared Memory

SystemV共享内存:Linux速度最高效的IPC通信方式。

原理:内核开辟一块物理内存,多个进程把同一块内存映射到各自用户虚拟地址;进程直接读写这块内存,**减少用户态和内核态之间的数据拷贝**。

注意:共享内存本身不带同步互斥!多进程并发读写会出现竞争,一般搭配信号量做保护。

API接口

① shmget 创建/获取共享内存ID

#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

  • size:共享内存字节大小,建议4096(一页)整数倍;

  • shmflg:IPC_CREAT | 0664;

返回:成功shmid,失败1。

② shmat 映射共享内存到进程用户空间

void *shmat(int shmid, const void *shmaddr, int shmflg);

  • shmaddr:填NULL,由内核自动选择映射地址;

  • shmflg:0读写;SHM_RDONLY只读;

返回值:成功返回映射后的虚拟内存首地址;失败返回(void *)-1。

拿到返回指针,进程就可以直接读写这块内存,就像普通数组一样。

③ shmdt 解除映射

int shmdt(const void *shmaddr);

功能:解除当前进程和共享内存的映射关系。

shmdt仅仅解除映射,不会删除内核里面的共享内存对象!

④ shmctl 删除共享内存对象

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

cmd传IPC_RMID,buf填NULL,删除内核共享内存对象。

相关推荐
_F_y16 分钟前
Linux网络常用指令
linux·运维·网络
小王C语言28 分钟前
QT 基础:QT SDK 下载、配置、新建项目、项目代码解释
开发语言·qt
葡萄成熟时 !44 分钟前
泛型知识笔记
开发语言·笔记·python
新时代牛马1 小时前
SPL-TPL 链式启动:spl.c 与CONFIG_TPL 三阶段加载
c语言·开发语言
布莱克6051 小时前
链表详解:定义、作用、应用场景及与数组的区别(C/C++ 实战)
c语言·开发语言·c++·链表
2601_966949651 小时前
实时行情中的成交量 Volume 到底是什么?包含盘中撤单量吗?从交易数据语义到 QuantDash 实战解析
开发语言·人工智能·python·量化·quantdash·量化数据源
鹿角片ljp1 小时前
LeetCode 141. 环形链表|从 HashSet 到快慢指针 O (1) 空间最优解
算法·leetcode·链表
薛定e的猫咪2 小时前
(NeurIPS 2022)GraphGPS:MPNN 与全局注意力的融合之道
人工智能·深度学习·学习·算法
老当益壮梁奶奶2 小时前
Linux软件编程学习笔记(九):消息队列、共享内存与信号灯详解
linux·c语言·笔记·学习