Linux进程间的通信(三)IPC-信号通信和system-V消息队列

news2024/9/29 20:53:00

目录

信号通信

信号动作的改写

测试

信号的发送

消息队列

消息队列创建要用到的函数

send.c:

recv.c

控制消息队列


信号通信

        信号通信是一种在 Unix 和类 Unix 系统(如 Linux)中用于进程间异步通知的机制。信号是一种软件中断,用于通知进程某个特定事件的发生。

        软件中断是一种由软件指令触发的中断机制。与硬件中断(由硬件设备如定时器、外部设备触发)不同,软件中断是通过执行特定的指令来请求 CPU 暂停当前的执行流程,转而执行特定的中断处理程序。(关于软件中断在系列文章操作系统内核中会详细讲解)

两个

killall  -信号值   进程名  
kill     -信号值   进程pid 

信号值如下图所示

 

信号动作的改写

信号处理函数

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

signum:需要注册的信号值 
handler:信号处理函数  
失败:返回 SIG_ERR 

void (*sighandler_t)(int)      回调函数名

测试

注册一个 ctrl c 信号的处理函数,让用户 ctrl c 无法结束当前进程

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

// 自定义的SIGINT信号处理函数
void sigint_handler(int sig) {
    printf("你按下了Ctrl + C,但这个操作不会结束进程\n");
}

int main() {
    // 注册SIGINT信号的处理函数
    signal(SIGINT, sigint_handler);
    while (1) {
        printf("进程正在运行...\n");
        sleep(1);
    }
    return 0;
}

signal(SIGINT, sigint_handler)语句将sigint_handler函数注册为SIGINT信号的处理函数。

当用户按下Ctrl + C时,会调用sigint_handler函数,而不是默认的终止进程的操作。

信号的发送

发送一个信号到一个进程

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

pid:pid进程号
sig:想要发送的信号

返回值: 0 发送成功 
           -1  发送错误
 

设置 seconds 秒后发送一个 SIGALRM 信号给当前进程

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

seconds:秒数

systemv通信

        消息队列、共享内存和信号量被统称为system-V IPC,V是罗马数字5,是 Unix的AT&T分支的其中一个版本,一般习惯称呼他们为IPC对象,这些对象的操作接口都比较类似,在系统中他们都使用一种叫做key的键值来唯一标识,而且他们都是“持续性”资源--即他们被创建之后,不会因为进程的退出而消失,而会持续地存在,除非调用特殊的函数或者命令删除他们。

消息队列

        对比消息队列,管道这种通信机制的一个弊端是:你无法在管道中读取一个指定的数据,因为这些数据没有做任何标记,读者进程只能按次序地挨个读取,因此多对进程之间的相互通信,除非使用多条管道分别处理,否则无法使用一条管道来完成。

消息队列的基础命令:

查看:

查看消息队列:ipcs -q

查看共享内存:ipcs -m

查看信号量:ipcs -s

查看所有的 IPC对象:ipcs -a

删除:

删除指定的消息队列:ipcrm -q MSG_ID 或者 ipcrm -Q msg_key

删除指定的共享内存:ipcrm-m SHM_ID 或者 ipcrm -M shm_key

删除指定的信号量:ipcrm -s SEM_ID 或者 ipcrm -S sem_key

消息队列创建要用到的函数

通过路径名和进程id生成一个systemv密钥

#include <sys/types.h>
#include <sys/ipc.h>

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

pathname:当前系统存在的一个路径名 
proj_id:一个大于 0 的8位数据  
返回值:
成功 返回一个密钥 
             失败 -1

ftok的典型实现是调用stat函数,然后组合以下三个值:
①    pathname所在的文件系统的信息(stat结构的st_dev成员)。
②    该文件在本文件系统内的索引节点号(stat结构的st_ino成员)。
③    proj_id的低序8位(不能为0)。
上述三个值的组合产生一个32位键。

 
        ftok返回的是根据文件(pathname)信息和计划编号(proj_id)合成的IPC key键值,返回值的后四位是文件在stat结构体中的成员——文件索引号的的后四位,键值的正数第三第四位是stat结构体中普通文件所在存储器的设备号的后两位,第一第二位是ftok函数自带参数proj_id(计划参数)转换成16进制数的后两位,经测试发现,proj_id转换成二进制时第八位不能为1,如果为1则会创建密钥失败,比如128-255、384-511全都会创建失败,应该是由于二进制第八位表示的是符号位导致的

备注:   

        在ftok函数的使用中,如果pathname指向的文件或者目录被删除而且又重新创建,那么文件系统会赋予这个同名文件新的节点信息,就会导致相同参数的ftok返回的键值是不一样的,那此时访问到的对象就会不一样,然后系统也不会报错。

参考:linux进程间通信--消息队列相关函数(ftok)详解-CSDN博客

获取一个 system5 的消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);

key: 密钥
msgflg: 权限标记  
             IPC_CREAT                   创建  
             IPC_CREAT|IPC_EXCL  检查是否存在

返回值: 成功   消息队列对象ID    
             失败    -1

发送一个消息到消息队列中 

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

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

从消息队列获取消息

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

msqid:消息队列对象id   
msgp:消息结构体 (由用户自定义)
msgsz:消息的真实大小  
msgtyp:获取的消息的编号
msgflg:消息属性,控制消息发送的方式,有阻塞和非阻塞(IPC_NOWAIT)两种方式,默认为  0
  
返回值:   成功     0     
               失败   -1

用法:

        消息队列是一种先进先出的队列型数据结构,实际上是系统内核中的一个内部链表。消息被顺序插入队列中,其中发送进程将消息添加到队列末尾,接受进程从队列头读取消息。

        多个进程可同时向一个消息队列发送消息,也可以同时从一个消息队列中接收消息。发送进程把消息发送到队列尾部,接受进程从消息队列头部读取消息,消息一旦被读出就从队列中删除。

参考:消息队列(定义、结构、如何创建、消息队列的发送与接收、发送与接收实例)_vc中如何定义使用消息队列-CSDN博客             

//定义一个消息结构体
struct msgbuf
{
    long mtype;       /* 消息的编号, must be > 0 */
    char mtext[1024]; /*消息的内容,长度自定义 */
};

mtype指定了消息类型,为正整数。

引入消息类型之后,消息队列在逻辑上由一个消息链表转化为多个消息链表。发送进程仍然无条件把消息写入队列的尾部,但接收进程却可以有选择地读取某个特定类型的消息中最接近队列头的一个,即使该消息不在队列头。相应消息一旦被读取,就从队列中删除,其它消息维持不变。

成员mtext指定了消息的数据。我们可以定义任意的数据类型甚至包括结构来描述消息数据。

//创建一个消息
struct msgbuf msg = {1, "hello"};

//把消息发送到消息队列中
msgsnd(msgid, &msg, 5, 0);

//创建一个消息缓存区
struct msgbuf buf = {0};

//获取消息
msgrcv(msgid,&buf,sizeof(buf),1,0);

创建消息队列并从消息队列中接收消息

send.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main()
{
   //1.创建密钥
    key_t key = ftok("/home",123);
    if(key < 0)
    {
        perror("创建密钥失败\n");
        return -1;
    }else 
    {
        printf("创建密钥成功\n");
    }


    //2.通过密钥创建消息队列 
    int msgid =  msgget(key,IPC_CREAT|0777);
    if(msgid < 0)
    {
        perror("创建消息队列失败\n");
        return -1;
    }else 
    {
        printf("创建消息队列成功\n");
    }

    //3.定义一个消息结构体
    struct msgbuf
    {
        long mtype;       /* 消息的编号, must be > 0 */
        char mtext[1024]; /*消息的内容,长度自定义 */
    };
    //4.创建一个消息
    //1是消息的编号,消息队列在内核中以链表的形式储存,同时填1就按照先进先出的队列顺序进行排列
    //消息的编号可以改变,改变之后原理就是根据编号在一个消息队列对象中创建多个链表可以根据消息编号直接访问到想要访问的数据
    struct msgbuf msg = {1, "hello"};
    struct msgbuf msg1 = {1, "world"};
    struct msgbuf msg2 = {1, "1234"};
    struct msgbuf msg3 = {1, "5678"};
    struct msgbuf msg4 = {1, "91011"};
    struct msgbuf msg5 = {1, "121314"};
    //5.把消息发送到消息队列中
    msgsnd(msgid, &msg, 5, 0);
    msgsnd(msgid, &msg1, 5, 0);
    msgsnd(msgid, &msg2, 4, 0);
    msgsnd(msgid, &msg3, 4, 0);
    msgsnd(msgid, &msg4, 5, 0);
    msgsnd(msgid, &msg5, 6, 0);
}

recv.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main()
{
   //1.创建密钥
    key_t key = ftok("/home",123);
    if(key < 0)
    {
        perror("创建密钥失败\n");
        return -1;
    }else 
    {
        printf("创建密钥成功\n");
    }


    //2.通过密钥创建消息队列 
    int msgid =  msgget(key,IPC_CREAT|0777);
    if(msgid < 0)
    {
        perror("创建消息队列失败\n");
        return -1;
    }else 
    {
        printf("创建消息队列成功\n");
    }

    //3.定义一个消息结构体
    struct msgbuf
    {
        long mtype;       /* 消息的编号, must be > 0 */
        char mtext[1024]; /*消息的内容,长度自定义 */
    };
    //4.创建一个消息缓存区
    struct msgbuf buf = {0};
     
    //5.获取消息 
    msgrcv(msgid,&buf,sizeof(buf),1,0);//1是消息的编号,即使是一个消息队列对象中也可以获得指定的消息
           
    printf("recv:%s\n",buf.mtext);

}

控制消息队列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

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

msqid:消息队列id  
cmd :控制命令  (IPC_RMID) 删除 
buf :消息队列的信息  ,(NULL) 不采集

删除一个消息队列:

msgctl(msgid,IPC_RMID,NULL);

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>

int main()
{
   //1.创建密钥
    key_t key = ftok("/home",123);
    if(key < 0)
    {
        perror("创建密钥失败\n");
        return -1;
    }else 
    {
        printf("创建密钥成功\n");
    }


    //2.通过密钥创建消息队列 
    int msgid =  msgget(key,IPC_CREAT|0777);
    if(msgid < 0)
    {
        perror("创建消息队列失败\n");
        return -1;
    }else 
    {
        printf("创建消息队列成功\n");
    }
    
    //3.删除消息队列  
    msgctl(msgid,IPC_RMID,NULL);

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2177791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库软题3-专门的集合运算

一、投影&#xff08;筛选列&#xff09; 题1 题2 二、选择(筛选行) 三、连接 3.自然连接 题1-自然连接的属性列数&#xff08;几元关系&#xff09;和元组数 解析&#xff1a; 题2-自然连接的属性列数&#xff08;几元关系&#xff09;和元组数 自然连接后的属性个数 A列…

SpringBoot3+Druid YAML配置

背景 Druid连接池是阿里巴巴开源的数据库连接池项目。Druid连接池为监控而生&#xff0c;内置强大的监控功能&#xff0c;监控特性不影响性能。功能强大&#xff0c;能防SQL注入&#xff0c;内置Loging能诊断Hack应用行为。现在已经SpringBoot3&#xff0c;Druid的配置也需要随…

java基础(4)类和对象

目录 1.前言 2.正文 2.1类的定义与使用 2.1.1类的定义 2.1.2类的实例化 2.1.3this引用 2.1.3.1 访问当前对象的成员变量 2.1.3.2调用当前对象的成员方法 2.1.3.3构造函数中的 this 2.1.3.4归纳this 2.2封装 2.2.1封装的定义 2.2.2访问修饰符 2.3static 2.3.1sta…

靠谱的建站公司怎么找?2024高端定制开发建站公司推荐

和所有行业一样&#xff0c;网站建设行业内部现在处于一个鱼龙混杂的状态&#xff0c;大多数的网建企业所做的是与模板网站有关的业务&#xff0c;少部分企业专精于定制高端网站。在低端市场逐渐饱和后&#xff0c;无论什么企业都会有开始进行产品升级的需求&#xff0c;而高端…

刚面试完的前端面试题

今天晚上参加了一场长达40多分钟的技术面。我觉得面试官非常专业&#xff0c;问的问题也都是很棒的&#xff01;自己很多知识都需要学习。所以我决定回想并记录下来。回答不对的地方欢迎大家指正&#xff01; 我自己在小本本上回忆出来的大概就是26道题。后期我会持续更新我学习…

(undone) 阅读 MapReduce 论文笔记

参考&#xff1a;https://pdos.csail.mit.edu/6.824/papers/mapreduce.pdf 摘要&#xff1a;简单介绍了 MapReduce 是在大型分布式系统上工作的 Introduction 的内容总结&#xff1a; 1.介绍背景&#xff1a;为什么我们需要分布式系统&#xff1f;MapReduce 的意义是哪些 2.简…

2024icpc(Ⅱ)网络赛补题E

E. Escape 思路&#xff1a; 可以看成 Sneaker 和杀戮机器人都不能在原地停留&#xff0c;然后杀戮机器人有个活动范围限制。如果 Sneaker 和杀戮机器人可以在原地停留&#xff0c;那么 Sneaker 到达一个点肯定会尽可能早&#xff0c;而且时间必须比杀戮机器人到达这个点短。那…

从传统 RAG 到图 RAG,赋予大型语言模型更强大的知识力量

大型语言模型 (LLMs) 在固定数据集上进行训练&#xff0c;其知识在最后一次训练更新时就已固定。 ChatGPT 的常规用户可能已经注意到其众所周知的局限性&#xff1a;“训练数据截止到 2021 年 9 月”。 这种局限性会导致模型产生不准确或过时的响应&#xff0c;因为它们会“幻…

【计算机网络】初识Socket编程,揭秘Socket编程艺术--UDP篇

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论&#x1f60a; 目录 Socket编程准备知识理解源IP地址和目的IP地址 认识端口号 网络字节序 socket编程socket编程接口socket系统调用bzero函数struct soc…

生信机器学习入门4 - 构建决策树(Decision Tree)和随机森林(Random Forest)分类器

机器学习文章回顾 生信机器学习入门1 - 数据预处理与线性回归&#xff08;Linear regression&#xff09;预测 生信机器学习入门2 - 机器学习基本概念 生信机器学习入门3 - Scikit-Learn训练机器学习分类感知器 生信机器学习入门4 - scikit-learn训练逻辑回归&#xff08;L…

【Android 14源码分析】Activity启动流程-2

忽然有一天&#xff0c;我想要做一件事&#xff1a;去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…

高并发内存池(六):补充内容

目录 有关大于256KB内存的申请和释放处理方法 处理大于256KB的内存申请 补充内容1 补充内容2 补充内容3 处理大于256KB的内存释放 新增内容1 新增内容2 测试函数 使用定长内存池替代new 释放对象时不传对象大小 补充内容1 补充内容2 补充内容3 补充内容4 测试…

Python(五)-函数

目录 函数的定义与调用 特点 语法格式 函数的参数 函数的返回值 函数嵌套调用 变量的作用域 局部变量 全局变量 函数的多种参数 位置参数 关键字参数 默认参数 可变参数 函数的定义与调用 python函数需要使用def关键字来定义,需要先定义,后调用 特点: 先定义…

课堂讨论:评价计算机性能的指标

**课堂讨论&#xff1a;评价计算机性能的指标** --- ### 课堂开始 **王老师**&#xff1a;同学们&#xff0c;今天我们来讨论如何评价计算机性能的指标。小明&#xff0c;你知道有哪些指标吗&#xff1f; **小明**&#xff1a;嗯...有吞吐率和响应时间吧&#xff1f;&#…

双链表的插入删除遍历

双链表的插入操作 双链表的删除操作 双链表的遍历操作

Watchdog Timers(WDT)

文章目录 1. 介绍2. Feature List3. 概述3.1. Safety Watchdog3.2. CPU Watchdog 4. 看门狗定时器功能5. Endinit Functions5.1 Password Access to WDTxCON05.1.1 Static Password5.1.2 Automatic Password Sequencing 5.2 Check Access to WDTxCON05.3 Modify Access to WDTx…

点餐小程序实战教程13餐桌管理

目录 1 创建数据源2 搭建管理后台3 生成餐桌码4 找到自己的appid和secret5 小程序里获取餐桌信息总结 我们上一篇介绍了点餐界面的菜品展示功能。现实中如果你去餐馆用餐&#xff0c;总是给餐桌贴一个二维码&#xff0c;服务员会告诉你扫码点餐。 扫码大家现在都已经非常熟练了…

“从零开始学排序:简单易懂的算法指南“

“一辈人有一辈人要做的事&#xff01;&#xff01;&#xff01;” 这一期的节目呢&#xff0c;是关于排序的内容&#xff0c;相信大家对此一定很熟悉吧&#xff01; 排序&#xff1a; 排序是将一组元素按照一定的规则或标准进行组织和排列的过程。 冒泡排序&#xff1a; 冒…

此连接非私人连接

当你手机浏览器输入网站打开提示“此连接非私人连接&#xff0c;此网站可能在冒充来窃取你的个人或财务信息。你应回到之前的页面”这是因为该网站的SSL数字证书到期导致&#xff0c;需要此网站的管理员重新申请数字证书替换之前的文件才可以实现。 注意&#xff1a;如果你不是…

Token: 数据库、存储系统和API安全的应用

一. Token Token是一种常见的计算机术语&#xff0c;它在不同的上下文中有不同的含义。在身份验证和授权的上下文中&#xff0c;Token通常指的是服务端生成的一串字符串&#xff0c;作为客户端进行请求的一个令牌。当用户登录后&#xff0c;服务器会生成一个Token并返回给客户…