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

news2024/9/19 1:58:12

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产生)的处理函数设置为handler
    while(true) {           // 创建一个无限循环
        std::cout<<"I am activing...,pid:"<<getpid()<<std::endl; // 打印活动信息,包括进程ID
        sleep(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);
    // 从第二个命令行参数中提取进程ID
    int 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;
    }
    // 如果没有错误,程序正常结束,返回0
    return 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(最大的信号编号)到1
    for (int signo = 31; signo > 0; signo--)
    {
        // 使用sigismember函数检查信号是否在挂起信号集中
        if (sigismember(&pending, signo))
        {
            // 如果信号在挂起信号集中,打印1
            cout << "1";
        }
        else
        {
            // 如果信号不在挂起信号集中,打印0
            cout << "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(最大的信号编号)到1
    for (int sig = 31; sig >= 1; sig--)
    {
        // 使用sigismember函数检查信号是否在挂起信号集中
        if (sigismember(&pending, sig))
        {
            // 如果信号在挂起信号集中,打印1
            cout << "1";
        }
        else
        {
            // 如果信号不在挂起信号集中,打印0
            cout << "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;
    // 设置信号处理函数为handler
    act.sa_handler = handler;
    // 设置信号行为的标志为0
    act.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.coloradmin.cn/o/2141528.html

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

相关文章

Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约

Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约 文章目录 Fisco Bcos 2.11.0配置console控制台2.10.0及部署调用智能合约前言版本适配一、启动FIsco Bcos区块链网络二、获取控制台文件三、配置控制台3.1 执行download_console.sh脚本3.2 拷贝控制台配置文件3.3 修…

读构建可扩展分布式系统:方法与实践06异步消息传递

1. 异步消息传递 1.1. 通信是分布式系统的基础&#xff0c;也是架构师需要纳入其系统设计的主要问题 1.2. 客户端发送请求并等待服务器响应 1.2.1. 这就是大多数分布式通信的设计方式&#xff0c;因为客户端需要得到即时响应后才能继续 1.2.2. 并非所有系统都有这个要求 1…

数据时代,职场离不开的远程控制工具

中秋了大概率是在正常放假了吧&#xff0c;如果突发遇到需要你处理的文件怎么办呢&#xff1f;其实有远程操作工具你就不用到办公室了。向日葵远程控制软件这些工具就可以帮我们远程实现控制电脑操作。如果你也有这方面需求就继续看吧&#xff0c;这次我将介绍几款我用过效果比…

Redis常见应用场景

目录 一、实现博客点赞功能 二、实现博客点赞用户列表功能 三、好友关注和取关以及求共同关注 四、实现关注推送 1、拉模式 2、推模式 3、推拉结合 四、三种模式对比 这里简单记录一下&#xff0c;没有实现方法&#xff0c;只是帮助记忆 一、实现博客点赞功能 可以通…

[NSSRound#4 SWPU]hide_and_seek-用gdb调试

看反汇编 ; __unwind { .text:0000000000001514 F3 0F 1E FA endbr64 .text:0000000000001518 55 push rbp .text:0000000000001519 48 89 E5 mov rbp, rsp .text:000000000000151C 53 …

python tkinter

基本使用 基于tkinter创建 GUI基本四步&#xff1a;窗口->组件->布局->事件 1.创建窗口对象 from tkinter import *root Tk() # 创建窗口root.mainloop() # 进入事件循环 2.创建组件 按钮文本等组件 btn Button(root) # 创建Button组件&#xff0c;使组件在…

re题(25)BUUFCTF-[GUET-CTF2019]re

BUUCTF在线评测 (buuoj.cn) 查下壳&#xff0c;是upx壳 脱一下 查看字符串&#xff0c;定位到主函数&#xff0c;也可以用ctrlE的方式找到主函数 明显&#xff0c;sub_4009AE是对flag加密的关键函数 进入sub_4009AE看一下 看到这儿有一堆大数和方程&#xff0c;我们知道要用z…

Transformer模型详细步骤

Transformer模型是nlp任务中不能绕开的学习任务&#xff0c;我将从数据开始&#xff0c;每一步骤都列举出来&#xff0c;然后对应重点的代码进行讲解 ------------------------------------------------------------------------------------------------------------- Trans…

Skytower

一、安装配置靶机 下载地址: SkyTower: 1 ~ VulnHub 下载之后解压发现是VirtualBox格式的 我们下载一个VirtualBox&#xff0c;这是官网 Downloads – Oracle VirtualBox 安装到默认路径就 打开后点击注册 选择解压后的vbox文件 然后点击左上角管理 点击导出虚拟电脑&…

PCIe进阶之TL:Request Handling Rules

1 Handling of Received TLPs 本节介绍接收到的 TLP 在数据链路层经过完整性验证之后,这些 TLP 在事务处理层时的处理方式。这些规则如下图所示: 接收侧会忽略保留字段。如果 Fmt 字段显示存在至少一个 TLP Prefix : (1)通过检查后续 DWORD 的第一个字节中的 Fmt 字段,…

两个人群填充参考(CHN100K和NARD)

分别是中国人群和东北亚人群的填充参考&#xff0c;测试了下&#xff0c;中国人群的参考注册还是相对友好的&#xff0c;没有像有些网站一样严格限制。东北亚的没有测试&#xff0c;两个数据库的特点都是包含了少数民族&#xff0c;研究朝鲜或蒙古族或其他民族的同学&#xff0…

Java 枚举 新特性

Java 枚举&#xff08;enum&#xff09;自JDK 1.5引入以来&#xff0c;随着版本的升级不断增强。本文将回顾枚举的演进&#xff0c;尤其是结合switch语句的应用&#xff0c;展示枚举如何在现代Java中变得更加灵活。 1. JDK 1.5&#xff1a;Java 枚举的诞生 在JDK 1.5之前&…

Dbt基本概念与快速入门

在过去的几年里&#xff0c;数据科学界已经慢慢地接受了以数据为中心的范式。我们不仅关注日益复杂的机器学习模型&#xff0c;还要更多地关注数据质量。这使得数据工程、分析工程领域技术和工具成为热点。dbt(数据构建工具)是一个显著改善数据工程师生活的工具。它的目的是向数…

【漏洞复现】金某云星空ERP GetImportOutData .net反序列化漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

Chinese Spelling Correction as Rephrasing Language Model(AAAI2024)

Chinese Spelling Correction as Rephrasing Language Model(AAAI2024) 一&#xff0e;概述 目前最先进的方法将CSC(Chinese Spelling Correction)作为序列标注任务&#xff0c;并在句子对上微调基于bert的方法。然而&#xff0c;我们注意到在将一个字符标注为另一个字符的过…

springboot+mybatis+mysql仿百度网盘系统2.0

springbootmybatismysql仿百度网盘系统2.0 一、系统介绍二、功能展示1.用户登陆2.主页3.全部文件4.文件上传5.文件分享6.文件分类 三、其它1.其他系统实现 一、系统介绍 系统主要功能&#xff1a; 普通用户&#xff1a;用户登陆、主页、全部文件、上传文件、文件分类、文件分部…

初始爬虫6

数据提取 数据提取总结 响应分类 结构化 json数据&#xff08;高频出现&#xff09; json模块 jsonpath模块 xml数据&#xff08;低频出现&#xff09; re模块 …

AJAX 入门 day3

目录 1.XMLHttpRequest 1.1 XMLHttpRequest认识 1.2 用ajax发送请求 1.3 案例 1.4 XMLHttpRequest - 查询参数 1.5 XMLHttpRequest - 数据提交 2.Promise 2.1 Promise认识 2.2 Promise - 三种状态 2.3 案例 3.封装简易版 axios 3.1 封装_简易axios_获取省份列表 3…

TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错

目录 1&#xff0c;前言2&#xff0c;二者关系2.1&#xff0c;使用 3&#xff0c;遇到的问题3.1&#xff0c;TS 中使用 JS 1&#xff0c;前言 通过 Vite 创建的 Vue3 TS 项目&#xff0c;根目录下会有 tsconfig.json 和 tsconfig.node.json 文件&#xff0c;并且存在引用关系…

【云原生监控】Prometheus之PushGateway

Prometheus之PushGateway 文章目录 Prometheus之PushGateway介绍作用资源列表基础环境一、部署PushGateway1.1、下载软件包1.2、解压软件包1.3、编辑配置systemctl启动文件1.4、创建日志目录1.5、加载并启动1.6、监控端口1.7、访问PushGateway 二、 配置Prometheus抓取PushGate…