兜兜零元 发表于 2024-8-6 04:35:10

Linux利用系统之进程信号

一、信号

1、概念



[*]在Linux中用于进程间通信和控制的一种机制,是进程之间事件异步通知的一种方式,属于软中断。
[*]全部信号的产生,最终都要由OS来进行实行,由于OS是进程的管理者。
[*]信号的处理可以不立刻处理,而在合适的时候再处理。它有一个时间窗口,在这个时间窗口内,必须记着信号的到来。
[*]只管进程没有收到信号,它也要知道对合法信号的处理利用,这属于进程内置功能的一部分。
2、系统界说的信号列表

https://img-blog.csdnimg.cn/direct/d3f5dae7814b4ef791d59130be4923b5.png


[*]每个信号都有一个编号和一个宏界说名称,这些宏界说可以在signal.h中找到。
[*]编号34以下的信号为普通信号,编号34及以上的信号为实时信号。
[*]信号 SIGKILL 和 SIGSTOP 无法被捕获或者忽略。
3、常见的信号处理方式



[*]SIG_IGN:忽略信号。
[*]SIG_DFL:实行信号的默认处理动作。
[*]捕捉信号:提供一个信号处理函数,要求内核在处理该信号时切换到用户态实行这个处理函数。
二、产生信号的方式

1、终端按键

(1)组合键



[*]Ctrl + c:信号2(SIGINT)。
[*]Ctrl + \:信号3(SIGQUIT)。
(2)示例代码

void handler(int signo)
{
    cout << "get a signo: " << signo << endl;
}

int main()
{
    for(int i = 1; i <= 31; ++i)
    {
      signal(i, handler);
    }

    while(true)
    {
      cout << "I am a crazy process" << " pid = " << getpid() << endl;
      sleep(1);
    }
    return 0;
}
(3)运行效果

https://img-blog.csdnimg.cn/direct/2d0e122fd1804fd09620f5de21390c02.png
https://img-blog.csdnimg.cn/direct/f559b7a906d341559e23a681b870d207.png
2、调用系统函数

(1)kill下令



[*]下令行输入:kill -signo pid
(2)kill函数

【1】函数

https://img-blog.csdnimg.cn/direct/c535a49d64ff4bea9ffa7cfbc05090a2.png
【2】描述



[*]kill函数,即系统调用,可用于向任何进程或进程组发送任何信号。
[*]如果 pid 为正数,则将 sig 信号发送到 pid 指定的进程。
[*]如果 pid 等于 0,则 sig 信号将发送到调用进程的进程组中的每个进程。
[*]如果 pid 等于 -1,则 sig 信号将发送到调用进程有权发送信号的每个进程,但进程1(init)除外。
[*]如果 pid 小于 -1,则 sig 将发送到进程组中 ID 为 -pid 的每个进程。
[*]如果 sig 为 0,则不发送信号,但仍实行错误检查。这可用于检查进程 ID 或进程组 ID 是否存在。
(3)raise函数

【1】函数

https://img-blog.csdnimg.cn/direct/a2dde6551b5346c49b3e381bb60c847f.png
【2】描述



[*]raise函数向调用进程或线程发送 sig 信号。
[*]在单线程程序中,它等价于 kill(getpid(), sig);
[*]在多线程程序中,它等价于 pthread_kill(pthread_self(), sig);
[*]如果信号有自界说的处理函数,则raise函数只有在该函数返回后才会返回。
(4)abort函数

【1】函数

https://img-blog.csdnimg.cn/direct/465d0d975cb84990869ccfc769ae9455.png
【2】描述



[*]abort函数首先取消 SIGABRT 信号的阻塞状态,然后为调用它的进程引发该信号。 而这会导致进程异常终止,除非 SIGABRT 信号被捕获而且信号处理程序(自界说的信号处理方法)没有返回。
[*]如果abort函数导致进程终止,则关闭并刷新全部打开的流。
[*]如果 SIGABRT 信号被忽略,或被返回的处理程序(自界说的信号处理方法)捕获,则 abort函数仍将终止进程。 它通过恢复 SIGABRT 的默认配置,然后再次引发该信号来实现此目的。
3、软件条件

(1)alarm函数

https://img-blog.csdnimg.cn/direct/ef22b20fe1de4efebd07b45fd84f69ff.png
(2)描述



[*]alarm函数安排在seconds秒后将 SIGALRM 信号传送到调用进程。
[*]如果秒数为零,则取消任何挂起的警报。
[*]在任何环境下,任何先前用alarm函数设置的“闹钟”都会被取消。
[*]alarm函数的返回值为,如果没有先前安排的“闹钟”,则返回零;反之,返回距离任何先前安排的“闹钟”发出 SIGALRM 信号的剩余秒数。
(3)示例代码

void handler(int signo)
{
    cout << "get signo: " << signo << endl;
    unsigned int n = alarm(5);
    cout << "剩余时间:" << n << endl;
}

int main()
{
    signal(SIGALRM, handler);
    alarm(50);
    while(true)
    {
      cout << "this process pid: " << getpid() << endl;
      sleep(1);
    }

    return 0;
}
(4)运行效果

https://img-blog.csdnimg.cn/direct/9bf9fe0d54f14b9bb90d4bd5c89c78bb.png
https://img-blog.csdnimg.cn/direct/a76e43a9c36048039dcc4907aae599be.png
4、硬件异常

硬件异常被硬件以某种方式检测到并通知内核,然后内核向当前进程发送适当的信号。
三、阻塞信号

1、概念



[*]信号递达(Delivery):实行信号的处理动作。
[*]信号未决(Pending):信号从产生到递达之间的状态。
[*]信号阻塞(Block):被阻塞的信号产生时将保持在未决状态,直到进程排除对该信号的阻塞才能实行递达的动作,即只要信号被阻塞就不会递达。
2、信号在内核中的示意图

https://img-blog.csdnimg.cn/direct/0fbeef964b5d46788887062c7703f88c.png
3、解释



[*]标志位block是信号阻塞,标志位pending是信号未决,1表示设置,0表示未设置。函数指针表handler是信号的处理方法。
[*]信号产生时,内核在进程控制块中设置该信号的未决标志(置1),直到信号递达才扫除该标志(置0)。
[*]在上方的图中,SIGHUP信号产生过,但是被阻塞了,以是临时不能递达,当它递达时实行默认处理动作;SIGINT信号未产生过且被阻塞了,当产生SIGINT信号时,该信号将会被阻塞,即pending只将该信号的位置置为1而不会递达。它的处理动作是忽略,但在没有排除阻塞之前不能忽略这个信号。由于进程仍偶然机改变处理动作之后再排除阻塞。SIGQUIT信号未阻塞也未产生过,它的处理动作是用户自界说函数sighandler。
[*]在Linux中,常规信号在进程排除阻塞之前如果产生过多次,则在递达之前只计一次。而实时信号如果在递达之前产生过多次可以依次放在一个队列里,即实时信号的每一次产生都有效。
四、信号集

1、sigset_t

(1)界说

https://img-blog.csdnimg.cn/direct/b6372ef9eecd484d99b0ae4d483d262b.png
https://img-blog.csdnimg.cn/direct/4f18808d227545ec910cb929fdea7c8e.png
(2)应用



[*]在标志位block和pending中,每个信号只有一个bit的标志,即非0即1,不会记录对应信号产生的次数。
[*]sigset_t:信号集,阻塞(block)和未决(pending)标志可以用它来存储,这个范例可以表示每个信号的“有效”或“无效”状态。
[*]在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。
[*]阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask)。
2、信号集利用函数

(1)函数

https://img-blog.csdnimg.cn/direct/c5d9d478712b49249d5812b9986879db.png
(2)描述



[*]sigemptyset:将 set 给出的信号集初始化为空,即将全部的信号都从聚集中排除。
[*]sigfillset:将 set 初始化为 full,即将全部信号都设置在内。
[*]sigaddset:添加 set 中signum对应的信号。
[*]sigdelset:删除 set 中signum对应的信号。
[*]sigismember:测试 signum 是否是 set 的成员,即测试 signum 是否在set 中。
[*]sigset_t 范例的对象必须通过调用 sigemptyset 或 sigfillset 进行初始化,然后才能转达给函数 sigaddset、sigdelset和 sigismember;或者下面描述的其他 glibc 函数(sigisemptyset()、sigandset() 和 sigorset())。 如果不这样做,效果是未界说的。
(3)返回值



[*]sigemptyset、sigfillset、sigaddset 和 sigdelset 成功时返回 0,错误时返回 -1。
[*]对于sigismember来说,如果 signum 是 set 的成员,则返回 1,如果 signum 不是 set 的成员,则返回 0,错误时返回 -1。
[*]堕落时,这些函数会设置 errno 以指示原因。
3、sigprocmask函数

(1)函数

https://img-blog.csdnimg.cn/direct/7b9330822b6845d4817af306117ab8ff.png
(2)描述



[*]sigprocmask函数用于获取和/或更改调用线程的信号掩码。
[*]sigprocmask函数调用的举动取决于 how 的值。
[*]如果 oldset 为非 NULL,则信号掩码的上一个值将存储在 oldset 中。
[*]如果 set 为 NULL,则信号掩码保持不变(即忽略 how),但信号掩码的当前值仍通过 oldset 返回(如果它不是 NULL)。
(3)参数how的有效值



[*]SIG_BLOCK:阻塞信号集将被设置为当前信号集和参数 set 的并集,即将 set 中包罗的信号都阻塞。
[*]SIG_UNBLOCK:参数 set 聚集中的信号将从当前受阻信号集中移除,即 set 中包罗的信号将被设置为不阻塞。 答应实验解锁未被阻塞的信号。
[*]SIG_SETMASK:将阻塞信号集的内容设置为参数 set 的内容。
4、sigpending函数

(1)函数

https://img-blog.csdnimg.cn/direct/1d6a05f2413042e3869108fa885ded8b.png
(2)描述



[*]sigpending函数返回等待传送到调用线程的信号集(即在被阻塞时引发的信号)。
[*]挂起信号的掩码在参数set中返回。
5、示例代码

#include<iostream>
#include<unistd.h>
#include<signal.h>
using namespace std;

void PrintSignal(const sigset_t& pending)
{
    for(int i = 31; i >= 1; --i)
    {
      if(sigismember(&pending, i))
            cout << "1";
      else
            cout << "0";
    }
    cout << "\n" << endl;
}

int main()
{
    sigset_t bset, oset;
    sigemptyset(&bset);
    sigemptyset(&oset);
    for(int i = 1; i <= 31; ++i)
    {
      sigaddset(&bset, i);
    }
    sigprocmask(SIG_SETMASK, &bset, &oset);
    sigset_t pending;
    sigemptyset(&pending);
    while(true)
    {
      int ret = sigpending(&pending);
      if(ret == -1)
            continue;
      PrintSignal(pending);
      sleep(1);
    }
    return 0;
}
五、信号捕捉

1、捕捉信号

(1)概念



[*]如果信号的处理动作是用户自界说函数,在信号递达时就调用这个函数。
[*]当我们的进程从内核态返回到用户态的时候,进行信号的检测和处理。
(2)过程示意图

https://img-blog.csdnimg.cn/direct/575539abc62247a4ba10cf3ad052ed3a.png
2、signal函数

(1)函数

https://img-blog.csdnimg.cn/direct/6106c660dbbc4b70b4dce92248e911b9.png
(2)描述



[*]signal函数的举动在 UNIX 版本中各不相同,在不同版本的 Linux 中也存在差异。
[*]signal函数将 signum 信号的处置(处理方法)设置为处理程序handler,该处理程序是SIG_IGN、SIG_DFL或自界说函数(handler)的地点。
[*]如果 signum 信号被传送到进程,则会发生以下环境之一。如果处置(处理方法)设置为 SIG_IGN,则忽略该信号;如果处置设置为 SIG_DFL,则发生与该信号关联的默认利用;如果处置设置为自界说函数(handler),则首先将处置重置为 SIG_DFL,或者信号被阻止,然后使用参数 signum 调用处理程序(handler)。 如果调用处理程序导致信号被阻塞,则信号在从句柄(handler)返回时被排除阻塞。
[*]signal函数返回值为,当调用成功时,返回信号处理程序的上一个值,失败则返回SIG_ERR,并设置errno,用来指示错误原因。
3、sigaction函数

(1)函数

https://img-blog.csdnimg.cn/direct/e7b964d8201843b98e099bb5637735f1.png
https://img-blog.csdnimg.cn/direct/e928b98429fe4bbebac6ff475ef35481.png
(2)描述



[*]sigaction系统调用用于更改进程在接收到特定信号时所采取的利用。
[*]signum:指定信号,可以是除 SIGKILL 和 SIGSTOP 之外的任何有效信号。
[*]如果 act 为非 NULL,则signum信号的处理利用设置为 act 中sa_handler设定的利用。 如果 oldact 为非NULL,则signum信号的上一个处理利用生存在 oldact 中。
[*]在某些涉及联合体的体系结构中,不要同时分配(设置)给sa_handler和sa_sigaction。
[*]sa_restorer:元素已过期,不应使用。 POSIX 未指定 sa_restorer 元素。
[*]sa_handler:指定要与 signum 关联的利用,可以设置为SIG_DFL默认利用、SIG_IGN忽略利用或指向信号处理函数的指针,此函数接收信号编号作为其唯一参数。通过用自界说函数就可以用同一个函数处理多种信号,即它是一个回调函数,不外不是被main函数调用,而是被系统所调用。
[*]sa_mask:指定在实行信号处理程序期间应阻止的信号掩码(即添加实行信号到调用信号处理程序的线程的信号掩码中),除了当前信号被自动屏蔽之外,也可以通过设置它来自动屏蔽另外一些信号。 此外,触发处理程序的信号将被阻止,除非使用 SA_NODEFER 标志。
[*]当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字。这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理函数实行竣事为止。
4、可重入函数

(1)概念



[*]如果一个函数,被重复进入的环境下,堕落或者可能堕落,则为不可重入函数。反之,为可重入函数。
六、SIGCHLD信号



[*]子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自界说SIGCHLD信号的处理函数。
[*]父进程自界说SIGCHLD信号的处理函数,这样父进程只需用心处理本身的工作,不必关心子进程。当子进程终止时会通知父进程,然后父进程在信号处理函数中调用wait相干函数清理子进程,这是办理僵尸问题的一种方法,前提是父进程不提前退出。
[*]父进程调用sigaction将SIGCHLD的处理动作设置为SIG_IGN。这样用fork函数创建出来的子进程在终止时会被自动清理掉,而不会产生僵尸进程和通知父进程。系统默认的忽略动作和用户用sigaction函数自界说的忽略通常是没有区别的。但这是一个特例,此方法对于Linux可用,但不保证在其它类UNIX系统上都可用。
   本文到这里就竣事了,如有错误或者不清楚的地方接待批评或者私信
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下
页: [1]
查看完整版本: Linux利用系统之进程信号