Linux信号实践(3) --信号内核表示

简介: 信号在内核中的表示   执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。

信号在内核中的表示

   执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

   注意,阻塞和忽略是不同,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。信号在内核中的表示可以看作是这样的:

 

图-信号的发送过程

 

解释说明:

  1)PCB进程控制块(task_struct)中函数有信号屏蔽状态字(block)信号未决状态字(pending)还有是否忽略标志

  2) 信号屏蔽状态字(block), 1代表阻塞、0代表不阻塞;

      信号未决状态字(pending)的1代表未决,0代表信号可以抵达了;

  3)向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,若阻塞,信号未决状态字(pending)相应位制成1;若阻塞解除,信号未决状态字(pending)相应位制成0;表示信号可以抵达了。

  4)block状态字、pending状态字均64位(bit);

  5)block状态字用户可以读写,pending状态字用户只能读;这是信号设计机制

 

  思考1:状态字都64bit,编程时,如何表示状态字那?

  思考2:block状态字信息如何获取或者操作那?哪些api?

  思考3:pending状态字信息如何获取或者操作那?哪些api?

    答案见下;

 

信号集操作函数(状态字表示) 

#include <signal.h>
int sigemptyset(sigset_t *set);	//把信号集清零;(64bit/8=8字节)
int sigfillset(sigset_t *set); 	//把信号集64bit全部置为1
int sigaddset(sigset_t *set, int signo); 	//根据signo,把信号集中的对应位置成1
int sigdelset(sigset_t *set, int signo); 	//根据signo,把信号集中的对应位置成0
int sigismember(const sigset_t *set, int signo);	//判断signo是否在信号集中

sigprocmask:读取/更改信号屏蔽状态字(Block)

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

返回值:若成功则为0,若出错则为-1

  读取:如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。

  更改:如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

How:

 

 

sigpending获取信号未决状态字(pending)信息 

#include <signal.h>
int sigpending(sigset_t *set);

DESCRIPTION

 sigpending()  returns the set of signals that are pending for delivery to the calling thread 

(i.e., the signals which have been raised while blocked).  

The mask of pending signals is returned in set.

/**	示例1:添加信号SIGINT到信号屏蔽字
此时再按下Ctrl+C键, 进程也接收不到SIGINT信号了
**/
inline void err_exit(std::string message);
void sigHandler(int signo);
void printSigSet(const sigset_t &sigset);

int main()
{
//虽然此处安装了信号处理函数, 但是该进程还是接收不到SIGINT信号
    if (signal(SIGINT, sigHandler) == SIG_ERR)
        err_exit("signal error");

    //添加信号屏蔽字: 屏蔽SIGINT信号
    sigset_t addset;
    sigemptyset(&addset);
    sigaddset(&addset, SIGINT);
    if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)
        err_exit("sigprocmask error");

    // 不断打印当前的信号屏蔽字
    sigset_t sigset;
    while (true)
    {
        sigpending(&sigset);
        printSigSet(sigset);
        sleep(1);
    }
}

inline void err_exit(std::string message)
{
    perror(message.c_str());
    exit(EXIT_FAILURE);
}
void sigHandler(int signo)
{
    cout << "catch a signal, number = " << signo << endl;
}
void printSigSet(const sigset_t &sigset)
{
    for (unsigned i = 1; i < NSIG; ++i)
    {
        if (sigismember(&sigset, i))
            putchar('1');
        else
            putchar('0');
    }
    putchar('\n');
}
/** 示例2:在示例1的基础上继续屏蔽SIGINT信号, 但是如果该进程接收到了SIGQUIT信号, 则将对SIGINT信号的屏蔽解除, 需要
1.在main函数中再安装一个SIGQUIT捕捉函数
2.对sigHandler函数进行改造
**/
int main()
{
    if (signal(SIGINT, sigHandler) == SIG_ERR)
        err_exit("signal SIGINT error");
    if (signal(SIGQUIT, sigHandler) == SIG_ERR)
        err_exit("signal SIGQUIT error");

    //添加信号屏蔽字: 屏蔽SIGINT信号
    sigset_t addset;
    sigemptyset(&addset);
    sigaddset(&addset, SIGINT);
    if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)
        err_exit("sigprocmask error");

    // 不断打印当前的信号屏蔽字
    sigset_t sigset;
    while (true)
    {
        sigpending(&sigset);
        printSigSet(sigset);
        sleep(1);
    }
}

void sigHandler(int signo)
{
    switch (signo)
    {
    case SIGINT:
        cout << "catch a signal SIGINT" << endl;
        break;
    case SIGQUIT:
        //如果是SIGQUIT信号, 则将SIGINT信号的屏蔽进行解除
        sigset_t unblockSet;
        sigemptyset(&unblockSet);
        sigaddset(&unblockSet, SIGINT);
        if (sigprocmask(SIG_UNBLOCK, &unblockSet, NULL) == -1)
            err_exit("sigprocmask unblock error");
        else
            cout << "sigprocmask success" << endl;
        break;
    default:
        cerr << "unknown signal" << endl;
        break;
    }
}
/**
    连续的按Ctrl+c键盘,虽然发送了多个SIGINT信号,
    但是因为信号是不稳定的(不支持排队),所以只保留了一个,如下图
*/

//示例: 换成实时信号SIGRTMAX
int main()
{
    if (signal(SIGRTMAX, sigHandler) == SIG_ERR)
        err_exit("signal SIGRTMAX error");
    if (signal(SIGQUIT, sigHandler) == SIG_ERR)
        err_exit("signal SIGQUIT error");

    //添加信号屏蔽字: 屏蔽SIGRTMAX信号
    sigset_t addset;
    sigemptyset(&addset);
    sigaddset(&addset, SIGRTMAX);
    if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)
        err_exit("sigprocmask error");

    // 不断打印当前的信号屏蔽字
    sigset_t sigset;
    while (true)
    {
        sigpending(&sigset);
        printSigSet(sigset);
        sleep(1);
    }
}
void sigHandler(int signo)
{
    if (signo == SIGRTMAX)
        cout << "catch a signal SIGRTMAX" << endl;
    else if (signo == SIGQUIT)
    {
        sigset_t unblockSet;
        sigemptyset(&unblockSet);
        sigaddset(&unblockSet, SIGRTMAX);
        if (sigprocmask(SIG_UNBLOCK, &unblockSet, NULL) == -1)
            err_exit("sigprocmask unblock error");
        else
            cout << "sigprocmask success" << endl;
    }
    else
        cerr << "unknown signal" << endl;
}

/** 向该进程连续发送四个SIGRTMAX, 则进程都能够收到**/

 

 

综合案例

   1) 创建子进程与父进程;

   2) 注册SIGINT非实时信号与SIGRTMIN实时信号,并将这两种信号添加到进程屏蔽信号组中;

   3) 注册用户自定义信号;

   4) 子进程发送5次非实时信号,发5次实时信号;

   5) 然后子进程发送SIGUSR1解除进程对SIGINT,SIGTRMIN信号的阻塞

   6) 观察实时信号与非实时信号的区别

//程序示例
void onSigAction(int signalNumber, siginfo_t *sigInfoStruct, void *)
{
    //获取接收到的数据
    int receiveNumber = sigInfoStruct->si_int;

    //如果收到的是SIGUSR1信号,则解除对SIGINT,SIGRTMIN的屏蔽
    if (signalNumber == SIGUSR1)
    {
        sigset_t unblockSet;
        sigemptyset(&unblockSet);
        sigaddset(&unblockSet,SIGINT);
        sigaddset(&unblockSet,SIGRTMIN);;
        sigprocmask(SIG_UNBLOCK,&unblockSet,NULL);

        //Value值会是乱码!
        //cout << "Receive SIGUSR1, Value = " << receiveNumber << endl;
        cout << "Unblock, Receive SIGUSR1" << endl;
    }
    else if (signalNumber == SIGINT)
    {
        cout << "Receive SIGINT, Value = " << receiveNumber << endl;
    }
    else if (signalNumber == SIGRTMIN)
    {
        cout << "Receive SIGRTMIN, Value = " << receiveNumber << endl;
    }
}

int main()
{
    struct sigaction act;
    //如果需要使得信号处理程序接收额外数据,
    //则必须将sa_flags位置为SA_SIGINFO
    act.sa_flags = SA_SIGINFO;
    sigemptyset(&act.sa_mask);
    act.sa_sigaction = onSigAction;

    //注册信号处理函数
    if (sigaction(SIGINT,&act,NULL) < 0)
        err_exit("sigaction SIGINT");
    if (sigaction(SIGRTMIN,&act,NULL) < 0)
        err_exit("sigaction SIGRTMIN");
    if (sigaction(SIGUSR1,&act,NULL) < 0)
        err_exit("sigaction SIGUSR1");

    //将信号SIGINT,SIGRTMIN信号阻塞
    sigset_t blockSet;
    sigemptyset(&blockSet);
    sigaddset(&blockSet,SIGINT);
    sigaddset(&blockSet,SIGRTMIN);
    if (sigprocmask(SIG_BLOCK,&blockSet,NULL) < 0)
        err_exit("sigprocmask error");

    pid_t pid = fork();
    if (pid == -1)
        err_exit("fork error");
    else if (pid == 0)
    {
        union sigval Value;
        Value.sival_int = 200;

        //给父进程发送五次带额外数据的非可靠信号(其实最终只接受到了一次)
        for (int i = 0; i < 5; ++i)
        {
            ++ Value.sival_int;
            if (sigqueue(getppid(),SIGINT,Value) != 0)
                err_exit("sigqueue error");
        }

        //给父进程发送五次带额外数据的可靠信号(最终接收到了五次!!!)
        Value.sival_int = 0;
        for (int i = 0; i < 5; ++i)
        {
            ++ Value.sival_int;
            if (sigqueue(getppid(),SIGRTMIN,Value) != 0)
                err_exit("sigqueue error");
        }

        //给父进程发送SIGUSR1信号,解除对SIGINT,SIGRTMIN信号的阻塞
        kill(getppid(),SIGUSR1);
    }

    while (true)
    {
        pause();
    }
}

运行结果:

 

 其实只是收到了一份非可靠信号SIGINT!

目录
相关文章
|
14天前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
14天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
15天前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
15天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
18天前
|
负载均衡 算法 Linux
深入探索Linux内核调度机制:公平与效率的平衡####
本文旨在剖析Linux操作系统内核中的进程调度机制,特别是其如何通过CFS(完全公平调度器)算法实现多任务环境下资源分配的公平性与系统响应速度之间的微妙平衡。不同于传统摘要的概览性质,本文摘要将直接聚焦于CFS的核心原理、设计目标及面临的挑战,为读者揭开Linux高效调度的秘密。 ####
32 3
|
20天前
|
负载均衡 算法 Linux
深入探索Linux内核调度器:公平与效率的平衡####
本文通过剖析Linux内核调度器的工作机制,揭示了其在多任务处理环境中如何实现时间片轮转、优先级调整及完全公平调度算法(CFS),以达到既公平又高效地分配CPU资源的目标。通过对比FIFO和RR等传统调度策略,本文展示了Linux调度器如何在复杂的计算场景下优化性能,为系统设计师和开发者提供了宝贵的设计思路。 ####
33 6
|
20天前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
21天前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
44 1
|
21天前
|
算法 前端开发 Linux
深入理解Linux内核调度器:CFS与实时性的平衡####
本文旨在探讨Linux操作系统的核心组件之一——完全公平调度器(CFS)的工作原理,分析其在多任务处理环境中如何实现进程间的公平调度,并进一步讨论Linux对于实时性需求的支持策略。不同于传统摘要仅概述内容要点,本部分将简要预览CFS的设计哲学、核心算法以及它是如何通过红黑树数据结构来维护进程执行顺序,同时触及Linux内核为满足不同应用场景下的实时性要求而做出的权衡与优化。 ####
|
1月前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
68 4
下一篇
DataWorks