从零开始学习Linux(12)---进程间通信(信号量与信号)

目录

1.信号量    

2.信号

1.core功能

2.信号集

3.内核态和用户态

用户态(User Mode)

内核态(Kernel Mode)

4.volatile关键字


1.信号量    

        信号量是计算机科学中用于同步和互斥的一种抽象数据类型。在并发编程中,当多个进程或线程需要访问共享资源时,信号量用来确保资源在同一时刻只被一个进程或线程访问,从而避免竞争条件。

信号量通常具有以下特性:

  1. 整数值:信号量是一个非负整数,用来表示可用的资源的数量。

  2. 两个原子操作

    • P操作(Proberen测试):如果信号量的值大于零,则将其减一;否则,进程或线程会被阻塞,直到信号量值变为正。

    • V操作(Verhogen增加):增加信号量的值,并唤醒等待的进程或线程。

根据信号量的取值,可以分为以下两种:

  • 二进制信号量:其值只能是0或1,通常用于互斥。

  • 计数信号量:其值可以是任何非负整数,用于表示资源的可用数量。

可用ipcs -s查看信号量:

2.信号

        信号是操作系统提供的让用户(进程)给其他进程发送异步信息的一种方式,Linux定义了一系列的信号,每个信号都有一个唯一的编号和一个默认的行为。输入kill -l可以列出所有的信号,以下是一些常见的信号类型:

进程可以针对信号执行以下操作:

  • 忽略:进程可以选择忽略某些信号。
  • 捕获:进程可以提供一个信号处理函数(信号处理程序),当信号发生时,该函数将被调用。
  • 默认行为:如果进程没有指定信号的处理方式,那么将执行信号的默认行为。

下面是简单示例代码:

#include<iostream>           // 引入标准输入输出流库,用于打印信息到标准输出
#include<unistd.h>           // 引入unistd.h头文件,提供对POSIX操作系统API的访问,如sleep()函数
#include<signal.h>           // 引入signal.h头文件,提供信号处理的函数和宏定义
#include<sys/types.h>        // 引入sys/types.h头文件,提供系统调用所需的类型定义// 定义信号处理函数,参数signo为接收到的信号编号
void handler(int signo) {std::cout<<"get a sig,number is:"<<signo<<std::endl; // 打印接收到的信号编号exit(100); // 使用exit函数退出程序,返回状态码100
}int main() {signal(SIGINT,handler); // 将SIGINT信号(通常由Ctrl+C产生)的处理函数设置为handlerwhile(true) {           // 创建一个无限循环std::cout<<"I am activing...,pid:"<<getpid()<<std::endl; // 打印活动信息,包括进程IDsleep(1);           // 调用sleep函数,使程序暂停执行1秒}return 0;               // 程序正常结束,返回0
}

使用kill函数发送信号给指定进程,返回0表示成功,-1表示失败:

#include<iostream>
#include<cerrno>
#include<cstring>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>using namespace std;int main(int argc, char *argv[]) {// 检查命令行参数的数量是否为3(包括程序名称本身)if (argc != 3) {std::cout << "Usage:" << argv[0] << " -signumber pid" << std::endl; // 打印使用说明return 1; // 参数数量不正确,返回错误码1}// 从第一个命令行参数中提取信号编号,跳过前导的'-'字符int signumber = std::stoi(argv[1] + 1);// 从第二个命令行参数中提取进程IDint pid = std::stoi(argv[2]);// 使用kill函数发送信号给指定进程,返回0表示成功,-1表示失败int n = kill(pid, signumber);// 检查kill函数的返回值if (n < 0) {// 如果返回值小于0,说明发送信号失败,打印错误信息std::cerr << "kill error," << strerror(errno) << std::endl;}// 如果没有错误,程序正常结束,返回0return 0;
}

使用raise函数发送信号给自己:

#include<iostream>
#include<cerrno>
#include<cstring>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>using namespace std;void handler(int signumber)
{std::cout<<"get a sinal,number is:"<<signumber<<std::endl;
}int main()
{signal(2,handler);int cnt=0;while (true){cout<<"cnt:"<<cnt++<<endl;sleep(1);if(cnt%5==0){cout<<"send 2 to caller"<<endl;raise(2);}} 
}

下面是alarm函数的原型和基本用法:

  • seconds:指定定时器超时的时间,以秒为单位。
  • 返回值:返回之前设置的定时器剩余的秒数,如果没有设置定时器,则返回0。
int g_cnt=0;void handler(int sig)
{cout<<"get a sig:"<<sig<<"g_cnt:"<<g_cnt<<endl;exit(0);
}int main()
{signal(SIGALRM,handler);alarm(1);while (true){g_cnt++;}// int cnt=0;// while (true)// {//     cout<<"cnt:"<<cnt++<<endl;// }
}

异常信号:

void handler(int sig)
{cout<<"get a sig:"<<sig<<endl;exit(1);
}int main()
{signal(SIGFPE,handler);int a=10;a/=0;while(true) sleep(1);return 0;
}

1.core功能

通过ulimit -a [大小]打开Linux的core功能

        在Linux系统中,core功能通常指的是核心转储(core dump)功能,这是操作系统在进程崩溃或接收到特定信号时生成的一种文件,其中包含了进程内存和寄存器状态的快照。核心转储文件对于调试和故障分析非常有用,因为它可以帮助开发者或系统管理员了解导致进程崩溃的原因。

2.信号集

        信号集(signal set)是Linux操作系统中用于管理信号的一种数据结构,它用于指定一组信号的集合,以便进行信号的阻塞、解除阻塞和查询操作。信号集是信号处理机制的一部分,用于处理信号的发送和接收。

        信号集在内核中由sigset_t类型表示,这是一个无符号整数类型的数组,其中每个元素代表一个信号编号。

   sigprocmask 函数是 Linux 操作系统中用于信号屏蔽操作的系统调用。这个函数允许进程设置或查询当前进程的信号屏蔽字(signal mask),以决定哪些信号可以被进程接收到,哪些信号将被阻塞。

#include <signal.h>int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
  • how: 指定如何操作信号屏蔽字。它可以是以下值之一:

    • SIG_BLOCK: 添加set中指定的信号到当前进程的信号屏蔽字中。
    • SIG_UNBLOCK: 从当前进程的信号屏蔽字中移除set中指定的信号。
    • SIG_SETMASK: 设置当前进程的信号屏蔽字为set中指定的信号集合。
  • set: 指向一个sigset_t类型的指针,其中包含了要添加到或从信号屏蔽字中移除的信号集合。

  • oset: 指向一个sigset_t类型的指针,如果how参数是SIG_BLOCKSIG_UNBLOCK,则这个指针指向当前进程的信号屏蔽字,sigprocmask函数会根据how参数的操作更新这个屏蔽字。如果how参数是SIG_SETMASK,则这个指针可以设置为NULL,因为新的信号屏蔽字将直接设置到当前进程的屏蔽字中。

sigprocmask 函数返回0表示成功,返回-1表示失败,并设置errno以指示错误类型。

void PrintSig(sigset_t &pending)
{// 打印当前进程的挂起信号位图cout << "Pending bitmap:";// 遍历信号编号,从31(最大的信号编号)到1for (int signo = 31; signo > 0; signo--){// 使用sigismember函数检查信号是否在挂起信号集中if (sigismember(&pending, signo)){// 如果信号在挂起信号集中,打印1cout << "1";}else{// 如果信号不在挂起信号集中,打印0cout << "0";}}// 换行cout << endl;
}int main()
{// 定义两个信号集变量,用于屏蔽和挂起信号sigset_t block, oblock;// 初始化block信号集为空集sigemptyset(&block);// 将信号2添加到block信号集中sigaddset(&block, 2);// 遍历从1到31的所有信号,并将它们添加到block信号集中for (int signo = 1; signo <= 31; signo++){sigaddset(&block, signo);}// 使用sigprocmask函数设置block信号集为当前进程的信号屏蔽字int n = sigprocmask(SIG_SETMASK, &block, &oblock);// 断言n的值为0,如果不是0,则程序会退出assert(n == 0);// 打印设置信号屏蔽字成功的消息cout << "block 2 signal success" << endl;// 创建一个无限循环,用于持续检查挂起信号while (true){// 初始化pending信号集为空集sigset_t pending;sigemptyset(&pending);// 使用sigpending函数获取当前进程的挂起信号集n = sigpending(&pending);// 断言n的值为0,如果不是0,则程序会退出assert(n == 0);// 调用PrintSig函数打印挂起信号位图PrintSig(pending);// 暂停进程执行1秒sleep(1);}
}

对应于SIGKILLSIGSTOP信号,这两个信号是保留的,不能被阻塞或忽略

3.内核态和用户态

        在计算机操作系统中,用户态(User Mode)和内核态(Kernel Mode)是两种不同的运行模式,它们在操作系统中扮演着不同的角色,并且具有不同的权限和功能。

用户态(User Mode)

        用户态是操作系统中为普通应用程序提供的一种运行模式。在用户态中,进程只能访问自己的地址空间和有限的系统资源。用户态的主要特点是:

  • 进程只能访问自己分配的内存空间,不能直接访问其他进程或内核的内存空间。
  • 进程的执行受到一定的限制,不能直接执行某些操作,如访问硬件设备或修改系统配置。
  • 用户态的进程通常通过系统调用来请求内核服务,如文件操作、网络通信等。

内核态(Kernel Mode)

        内核态是操作系统为内核提供的一种运行模式。在内核态中,内核可以访问所有的系统资源,包括硬件设备、内存空间和文件系统。内核态的主要特点是:

  • 内核可以访问所有的系统资源,包括硬件设备、内存空间和文件系统。
  • 内核可以执行所有类型的操作,包括直接访问硬件设备、修改系统配置等。
  • 内核态的进程通常是通过中断或系统调用从用户态切换到内核态。

信号捕捉的过程如下:

  sigaction 是一个系统调用,用于设置信号的行为,包括信号处理函数、信号掩码和信号动作。这个系统调用允许用户程序对信号进行更精细的控制,包括设置信号的处理方式、屏蔽信号、设置信号动作等。

#include <signal.h>int sigaction(int signum, const struct sigaction *restrict new_action, struct sigaction *restrict old_action);
  • signum: 要设置行为的信号编号。
  • new_action: 指向struct sigaction的指针,其中包含了新的信号行为设置。这个结构体定义了信号处理函数、信号掩码和信号动作。
  • old_action: 指向struct sigaction的指针,用于保存旧的信号行为设置,如果需要的话。
struct sigaction {void (*sa_handler)(int);           /* 信号处理函数 */void (*sa_sigaction)(int, siginfo_t *, void *); /* 信号处理函数,可以获取信号的附加信息 */sigset_t sa_mask;                 /* 信号掩码,用于阻塞信号 */int sa_flags;                     /* 信号行为的标志 */void (*sa_restorer)(void);        /* 恢复函数,通常设置为NULL */
};

使用代码如下:

void Print(sigset_t &pending)
{// 打印当前进程的挂起信号位图cout << "curr process pending:";// 遍历信号编号,从31(最大的信号编号)到1for (int sig = 31; sig >= 1; sig--){// 使用sigismember函数检查信号是否在挂起信号集中if (sigismember(&pending, sig)){// 如果信号在挂起信号集中,打印1cout << "1";}else{// 如果信号不在挂起信号集中,打印0cout << "0";}}// 换行cout << endl;
}void handler(int signo)
{// 信号处理函数cout << "signal:" << signo << endl;// 创建一个信号集,用于存储挂起的信号sigset_t pending;// 初始化信号集为空集sigemptyset(&pending);// 创建一个无限循环,不断检查是否有信号被挂起while (true){// 使用sigpending函数获取当前进程的挂起信号集sigpending(&pending);// 调用Print函数打印挂起信号位图Print(pending);// 暂停进程执行1秒sleep(1);}
}int main()
{// 定义信号处理结构体struct sigaction act, oact;// 设置信号处理函数为handleract.sa_handler = handler;// 设置信号行为的标志为0act.sa_flags = 0;// 初始化信号掩码为空集sigemptyset(&act.sa_mask);// 使用sigaction函数设置SIGINT信号的处理方式sigaction(2, &act, &oact);// 创建一个无限循环,不断执行while (true){// 暂停进程执行1秒sleep(1);}// 程序正常结束return 0;
}

4.volatile关键字

        在C和C++编程语言中,volatile是一个关键字,用于修饰变量或类型,以指示编译器在优化代码时不要假设该变量是常量。当一个变量被声明为volatile时,编译器不会对它的读写操作进行优化,这意味着每次访问该变量时,编译器都会直接从内存中读取其值,而不是从寄存器或缓存中读取。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

集群聊天服务器项目【C++】项目介绍和环境搭建

前言&#xff1a;学习一个基于C集群聊天服务器的项目&#xff0c;记录学习的内容和学习的过程。 1.项目介绍 在 Linux 环境下基于 muduo 开发的集群聊天服务器。实现新用户注册、用户登录、添加好友、添加群组、好友通信、群组聊天、保持离线消息等功能。 2.技术栈 Json序列…

感谢老美苦苦相逼,逼出华为鸿蒙PC

文&#xff5c;琥珀食酒社 作者 | 随风 哎&#xff0c;告诉大家一个不好的消息 刚刚余总说 Windows PC是最后一批了 因为美国新一轮制裁又来了 但大家别急 再告诉大家一个好消息 那就是我们的鸿蒙PC要来了 今天不是华为三折叠手机和iPhone 16首发吗 估计老美是前端时间…

vue项目加载cdn失败解决方法

注释index.html文件中 找到vue.config.js文件注释、

mybatisplus乐观锁

使用方法&#xff1a; 1.添加version锁标记字段 2.实体类添加对应字段&#xff0c;并加上Version注解 3.添加配置类 Configuration public class MpComfig {Beanpublic MybatisPlusInterceptor mpInterceptor(){MybatisPlusInterceptor mpInterceptor new MybatisPlusIntercep…

人工免疫算法(AIS算法)求解实例---旅行商问题 (TSP)

目录 一、采用AIS求解 TSP二、 旅行商问题2.1 实际例子&#xff1a;求解 6 个城市的 TSP2.2 **求解该问题的代码**2.3 代码运行过程截屏2.4 代码运行结果截屏&#xff08;后续和其他算法进行对比&#xff09; 三、 如何修改代码&#xff1f;3.1 减少城市坐标&#xff0c;如下&a…

Windows环境本地部署Oracle 19c及卸载实操手册

前言: 一直在做其他测试,貌似都忘了Windows环境oracle 19c的部署,这是一个很早很早的安装记录了,放上来做个备录给到大家参考。 Oracle 19c‌:进一步增强了自动化功能,并提供了更好的性能和安全性。这个版本在自动化、性能和安全性方面进行了重大改进,以满足现代企业对数…

Serverless 安全新杀器:云安全中心护航容器安全

作者&#xff1a;胡志广(独鳌) 云安全中心对于 Serverless 容器用户的价值 从云计算发展之初&#xff0c;各大云厂商及传统安全厂商就开始围绕云计算的形态来做安全解决方案。传统安全与云计算安全的形态与做法开始发生变化&#xff0c;同时随着这 10 多年的发展&#xff0c;…

12种常见的华为杯数学建模竞赛matlab代码(建议收藏)

1.使用神经网络模型(向量量子化方法LVQ)解决分类/预测问题 clc;clear;​% 第一类蝗虫的触角和翅膀p1 [1.24, 1.27; 1.36, 1.74; 1.38, 1.64; 1.38, 1.82; 1.38, 1.90; 1.40, 1.70; 1.48, 1.82; 1.54, 1.82; 1.56, 2.08];​% 第二类蝗虫的触角和翅膀p2 [1.14, 1.82;…

小众语言ruby在苹果中的初步应用

前言 感觉Ruby在苹果系统中充当一种脚本语言来使用。 1、直接输入ruby没有反应 2、可显示结果的命令 ruby -e "puts Goodbye, cruel world!" 效果如下图&#xff1a; 说明苹果系统中ruby已经安装完毕&#xff0c;或者就是自带的。 3、编辑运行第一个ruby程序 输入…

阿里云盘bug,个人照片泄露 安当TDE透明加密完美保障数据安全

近期&#xff0c;阿里云盘出现了一个严重的隐私安全事件。用户在创建新相册时&#xff0c;系统意外地加载出了其他用户的照片&#xff0c;这些照片包括自拍、风景照、家人旅游照片等&#xff0c;引发了用户对隐私安全的担忧。阿里云盘官方对此事件迅速作出回应&#xff0c;确认…

ADB 安装教程:如何在 Windows、macOS 和 Linux 上安装 Android Debug Bridge

目录 一、ADB 介绍 二、Windows 系统安装 ADB 1. 下载 ADB 2. 解压文件 3. 验证 ADB 安装 4. 配置环境变量 5. 验证全局 ADB 使用 三、macOS 系统安装 ADB 1. 下载 ADB 2. 解压文件 3. 配置环境变量 4. 验证 ADB 安装 四、Linux 系统安装 ADB 1. 使用包管理器安装…

MySQL高阶1890-2020年最后一次登录

目录 题目 准备数据 分析数据 题目 编写解决方案以获取在 2020 年登录过的所有用户的本年度 最后一次 登录时间。结果集 不 包含 2020 年没有登录过的用户。 返回的结果集可以按 任意顺序 排列。 准备数据 Create table If Not Exists Logins (user_id int, time_stamp …

钉钉 钉钉打卡 钉钉定位 2024 免费试用 保用

打卡助手定位 如图&#xff0c;表示开启成功&#xff0c;软件已定位到钉钉打卡位置。 测试显示&#xff0c;高德地图位置已成功修改。 开启助手定位后&#xff0c;观察效果&#xff0c;打卡按钮由无法打卡变为可打卡状态&#xff0c;照片还显示打卡地点。 伙伴们担心作弊行为会…

《程序猿之设计模式实战 · 观察者模式》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

摩尔-彭罗斯伪逆(pinv)

摩尔-彭罗斯伪逆是一种矩阵&#xff0c;可在不存在逆矩阵的情况下作为逆矩阵的部分替代。此矩阵常被用于求解没有唯一解或有许多解的线性方程组。 对于任何矩阵 A 来说&#xff0c;伪逆 B 都存在&#xff0c;是唯一的&#xff0c;并且具有与 A’ 相同的维度。如果 A 是方阵且非…

[Linux]自定义shell详解

自定义shell 前言1.命令行提示符&#xff0c;字符串的打印1.1命令行提示符2.命令行字符串 2.0对命令行字符串进行切割2.执行命令3.有趣的小问题完整代码 前言 写之前我们先看看一个完整的shell都包括了什么 $符号前面&#xff08;包括这个符号&#xff09;就是命令行提示符&a…

Mac 上哪个剪切板增强工具比较好用? 好用剪切板工具推荐

在日常文字编辑中&#xff0c;我们经常需要重复使用复制的内容。然而&#xff0c;新内容一旦复制&#xff0c;旧内容就会被覆盖。因此&#xff0c;选择一款易用高效的剪贴板工具成为了许多人的需求。本文整理了一些适用于 macOS 系统的优秀剪贴板增强工具&#xff0c;欢迎大家下…

OJ 旋转图像

题目&#xff1a; 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例&#xff1a; 解题规律: 我们以题目中的示例二作为例子&a…

2024年全新deepfacelive如何对应使用直播伴侣-腾讯会议等第三方软件

# 2024年全新deepfacelive如何对应使用直播伴侣-腾讯会议等第三方软件 前提按照之前的步骤打开deepfacelive正确配置并且在窗口已经输出了换脸后的视频&#xff0c;不懂步骤可以移步 https://doc.youyacao.com/88/2225 ## 首先下载obs并配置 https://obsproject.com/ 通过…

Vue: 创建vue项目

目录 一.创建项目 二.项目添加 三.添加成功 一.创建项目 打开本机终端输入npm create vuelatest 二.项目添加 1. 项目名称&#xff1a; Project name: one_vue 2.是否添加TypeScript支持&#xff1a;Add TypeScript? Yes 3.是否添加JSX支持&#xff1a;Add JSX Suppor…