0
点赞
收藏
分享

微信扫一扫

服务器的配置复杂,租用时该如何选择参数?

  🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343
🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12625432.html

9efbcbc3d25747719da38c01b3fa9b4f.gif

目录

信号和信号量

 信号

 信号的处理

信号捕捉

 信号的产生

系统调用 

 signal

raise 

abort 

由软件条件产生信号 

alarm

 硬件异常产生信号

Core、Term

 阻塞信号

信号其他相关常见概念 

在内核中的表示

sigset_t 

 信号集操作函数

 sigprocmask

 sigpending

 捕捉信号

 内核如何实现信号的捕捉

sigaction

可重入函数

 volatile

SIGCHLD信号 


前言

信号和信号量

二者之间没有任何关系。

 信号

84b89208dc5b4040a763da034c0e3d23.png

 信号的处理

信号处理有三种情况:

  1. 默认动作
  2. 忽略动作
  3. 自定义处理--信号的捕捉

信号捕捉

signal

ab430f9750ca44f6b99ec76d69b0c484.png

1ab8d85084ff482f912dc609af3c2172.png

98dc1b1c5c454b6aa9da411253daa20f.png1e7d58c6fd3e43be987f3469f6b9c034.pngbfaf5de76a424f7cadbed20fbddf0564.png

4dcbeff36b0b448f8e4678f8b59dc7dd.png

de8a264bf56646509f21a42b98e2f22d.png

 信号的产生

信号的产生方式:

  1. 通过kill命令,向指定进程发送指定信号
  2. 键盘可以产生信号。ctrl+c(SIGINT)、ctrl+\(SIGQUIT)
  3. 系统调用
  4. 软件条件
  5. 异常

系统调用 

kill

dc45431a547841d6aa8a0600f557d03e.png

dc39a8140ddb4cacb8bdef47db787c7f.png7c973cbda3964e6a8867789532365fed.png

raise 

6ef2d179ed5f4ce4ae95734118b2c296.png

 11062809930340c098f82b8dee60171b.pnga74a7941975d4d0b9f2fcc110230af8f.png

abort 

c2bcb50c73334baa9a8f6a02c2c739a5.png

8a50aad970c44f12bc34be5808677f74.png

d387145f773d429e8282d26768dfecf2.png

19a6d3bbe78b4229b50259b6c6e8924b.png

由软件条件产生信号 

SIGPIPE是一种由软件条件产生的信号。下面介绍alarm函数和SIGALRM信号。

alarm

86f383812d9a42d5b97a8db1d1977199.png 2be97c82cd824ec1b6f6ba882b00931e.png

d5a9dde03ec7423285af96e699e6245a.png

 我们修改代码成如下:23dd00433dc7413280a33e2b3d97c8d6.png876d93db928c4c4d80f5cc364688a995.png

64380d8c8c5b4699a6b0f1fe848f45ba.png 44156547be874dacb1b62cd9f59b97a1.png

6688cf2f0e034d26a6bc1a669558a249.png 358f1abd6d1b4969a973217d645407a1.png

 硬件异常产生信号

70fc8b80791f431d9423dd5a6875c24c.png98619f953bcc4271bca07464013bdc40.pngb6d2281f402d4cc7917d449ab02e83b9.pngde11e42574be427697613206dbae1cbe.png

67705bd063794f72a664cf1ff9189294.png

Core、Term

c8ec41c35b5a41f7aabb6fd6fab1ceef.png

7ab712812eb749289fb3fb1ccad91f16.png dc3197591f0d42fbafeee6681e6ce6ce.png

1bf92466d88741649211db694af39585.png

16145ee31a5c46579b99d4745528219c.png

 2fd1eb3bdb004ff4bbea726e6f37583a.png

969b5964b5d145a9bee106392394096c.png

c23b355ae87b42adb7bb04f7f11dd1a7.pngc2ff7476993441a2af3a6ae75cec901e.png

 阻塞信号

信号其他相关常见概念 

  • 实际执行信号的处理动作称为信号递达 (Delivery)
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (Block )某个信号。阻塞和有没有未决,二者没有关系
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
  • 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

在内核中的表示

信号在内核中的表示示意图

12bb14ff09a14f369f1b77c2d3a9f15f.png

4c35d1a21ec249e4b5dc7de7dad559f9.png

8c5ad0065f7e4294a97ded51fcb9a0e3.png

6f4d8c1a5edf4f0ebe3e0464be3355a5.png

fe2149407abe4e50aa7108cce1f42c17.png

sigset_t 

 信号集操作函数

 sigset_t类型内部如何存储这些bit依赖于系统实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量

 sigprocmask

59fdd174e762484fbfd6496c6c60a3f9.png

 sigpending

d6a25c1eceee4a75b04a69c7b3dbb144.png

91d7df5addca41eda0dfe1da011e4800.png107199e254834b1ea4fa4b3e5d55ae99.pngd12b32cc046e4601b103e179e834eb2e.png

完整代码

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>

void PrintPending(sigset_t &pending)
{
    std::cout << "curr process[" << getpid() << "]pending: ";
    for (int signo = 31; signo >= 1; signo--)
    {
        if (sigismember(&pending, signo))
        {
            std::cout << 1;
        }
        else
        {
            std::cout << 0;
        }
    }
    std::cout << "\n";
}

void handler(int signo)
{
    std::cout << signo << " 号信号被递达!!!" << std::endl;

    std::cout << "-------------------------------" << std::endl;
    sigset_t pending;
    sigpending(&pending);
    PrintPending(pending);
    std::cout << "-------------------------------" << std::endl;
}

int main()
{
    // 0. 捕捉2号信号
    signal(2, handler); // 自定义捕捉
    signal(2, SIG_IGN); // 忽略一个信号
    signal(2, SIG_DFL); // 信号的默认处理动作

    // 1. 屏蔽2号信号
    sigset_t block_set, old_set;
    sigemptyset(&block_set);
    sigemptyset(&old_set);
    sigaddset(&block_set, SIGINT); 
    // 1.1 设置进入进程的Block表中
    sigprocmask(SIG_BLOCK, &block_set, &old_set); // 真正的修改当前进行的内核block表,完成了对2号信号的屏蔽!

    int cnt = 15;
    while (true)
    {
        // 2. 获取当前进程的pending信号集
        sigset_t pending;
        sigpending(&pending);

        // 3. 打印pending信号集
        PrintPending(pending);
        cnt--;

        // 4. 解除对2号信号的屏蔽
        if (cnt == 0)
        {
            std::cout << "解除对2号信号的屏蔽!!!" << std::endl;
            sigprocmask(SIG_SETMASK, &old_set, &block_set);
        }

        sleep(1);
    }
}

 捕捉信号

d92118c110b84bb890a1ced90e10476c.png

 内核如何实现信号的捕捉

6bb9791f14024fcf90ef93969ebccd72.png

 再谈地址空间

cdcaf260452043098e78b1c1d57e6222.png

sigaction

c39469be3bf7420f88f60898ec2dc5e1.png88e82c504a29410a821b806504efaf86.png

ca6a5190273849319100ef47cd6a643b.png875b18d2ce3d4a4db4fae1fd8a434435.png

 97af8f7a994a4465b7a792f0c2213fb9.png

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

void Print(sigset_t &pending)
{
    for(int sig = 31; sig > 0; sig--)
    {
        if(sigismember(&pending, sig))
        {
            std::cout << 1;
        }
        else
        {
            std::cout << 0;
        }
    }
    std::cout << std::endl;
}

 void handler(int signum)
{
    std::cout << "get a sig: " << signum << std::endl;
    while(true)
    {
        sigset_t pending;
        sigpending(&pending);

        Print(pending);

        sleep(1);
        // sleep(30);
        // break;
    }
    // exit(1);
}

int main()
{
    struct sigaction act, oact;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);  // 如果你想在处理2号时(OS对2号自动屏蔽),同时对其他信号也进行屏蔽
    sigaddset(&act.sa_mask,3);
    act.sa_flags = 0;
    sigaction(2, &act, &oact);

    while(true)
    {
        std::cout << "I am a process, pid: " << getpid() << std::endl;
        sleep(1);
    }
    return 0;
}

38c822748a904d629108ed0d7c086b78.png

可重入函数

bf20cf4c73fb4b81989e09d83a30bc75.png

 volatile

47a9a0dd48454c6ab15522e09f924971.png5e04b08c4cac46a5bf670d85eaa918f8.png

47f6618b95e94a37a1aaa36c9ae1a778.png

ad2803ace13e48a19ed34198a0840e57.png

71c987f6de7c4fb189fa8250a9fbc3bf.png5a3e9513455f48dd8276e09e7565f3a1.png

SIGCHLD信号 

子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。 

2cb66197b08348a893f6755f0493605e.png


 wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进 程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一下,程序实现复杂。 

要想不产生僵尸进程还有另外一种办法:父进程调用signal将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。

cc131fe31b364c30b09a24994c024faa.png

举报

相关推荐

0 条评论