信号的产生,保存,处理

信号

1.是什么

2.为什么

OS要求进程具有相应外部信号的能力

3.怎么办

分为 信号的产生 信号的保存 信号的处理

信号的产生

1.kill命令产生+键盘产生信号

1.认识信号

2.处理信号

signal函数会调用里面的handler函数指针,所以它是一个回调函数

调用signal后不会立即执行handler,而只有在未来收到相应信号,才会调用handler。

现在 2号信号(进程自己退出)只会让他打印一句话。

另外,ctrl + c 也是对应的2号信号:

ctrl + z 对应3号信号

忽略信号,第二个参数是一个宏(函数指针类型)

现在会忽略信号

2.系统调用

1.kill

对任意进程发送任意信号

2.raise

自己给自己发任意信号

3.abort

给自己发送指定信号

3.软件条件

1.管道文件关闭信号

13: SIGPIPE 对管道文件这个软件发送信号。

对管道文件写入,读取就是一种软件条件。

2.闹钟

1.一般用法

就是直接到点暂停:

2.自定义法让闹钟一直响应 + 取消闹钟

用signal自定义闹钟的行为,第一次响了以后,换成2秒响一回,并打印上一次闹钟的剩余时间。

而当循环里,count走到10的时候,取消闹钟。

3.闹钟是软件条件的原因

软件:结构体,堆

条件:有过期时间。

4.异常

1.除0错误

发8号信号终止的。

2.野指针

发11号信号终止的。

关于对各种产生方式的解释

1.进程如何接受不同的信号

2.键盘产生信号

来自:硬件中断处理。

最后那里解释为2号信号,发送给进程,进程接收到了信号——>做事情。

3.异常

解释:eax / ebx 的结果,如果发生错误,会存入标志寄存器中,修改寄存器中的标志,CPU再把标志信息返回给OS,OS检测到错误,给进程发送对应的信号。

解释:CR3存页表的起始地址,CR2存出错的地址,当eax处理计算时,发现野指针访问,0号区域(错误)

原因是:

  • 0号区域是只读的
  • 0号区域根本没参与页表映射,所以页表找不到

所以MMU转化时会出错,把错误返回给CR2,CR2告诉OS,OS就会判断,并给相应进程发送对应的信号。

补充前面的知识

从右往左数的第八位是core dump标志,是干啥的?

可以看到下面的信号,大部分都是Term . Core

其实,Term就是常规的终止进程,而Core则是可以定位到哪行代码退出的,并且退出时,将进程在内存中的核心数据(与调试有关的)转储到磁盘上,方便我们事后调试。

因此:core dump 就是核心转储的意思。

注意:core 在云服务器上默认关闭,需要手动开启。 ulimit -a (开启core)

信号的保存

1.要知道的概念

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

注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

2.是什么

三张表:

3.怎么办

1.sigset_t

从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。

因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号

的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有

效”和“无效”的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),

这里的“屏蔽”应该理解为阻塞而不是忽略。

sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统

实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做

任何解释,比如用printf直接打印sigset_t变量是没有意义的。(不同平台有差异)

2.信号集操作函数

#include

int sigemptyset(sigset_t *set); //清空->0

int sigfillset(sigset_t *set); //填满->1

int sigaddset (sigset_t *set, int signo); //加入指定信号

int sigdelset(sigset_t *set, int signo); //删除指定信号

int sigismember(const sigset_t *set, int signo); //判断信号是否在位图中

上面都是对sigset_t类型位图的操作。

3.系统调用

sigprocmask

#include

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

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

sigpending

#include

int sigpending(sigset_t *set);

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

读取当前进程的未决信号集(pending位图),通过set参数传出。

4.用一用上面的调用函数

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

int main()
{
    sigset_t block, oblock;
    sigemptyset(&block);
    sigemptyset(&oblock);
    sigaddset(&block, 2); // 此时,还没有设置进当前的block位图中
    // 屏蔽2号信号,对内核进行操作了
    int n = sigprocmask(SIG_SETMASK, &block, &oblock);
    assert(n == 0);

    while (1)
    {
        // 获取pending位图
        sigset_t pending;
        sigemptyset(&pending);
        n = sigpending(&pending);
        assert(n == 0);

        // 看看pending位图
        Printpending(pending);
        sleep(1);
    }
}

看现象:输入ctrl + c 后2号编号的位置一直是1,处于未决状态。

如果我屏蔽所有的信号呢?

看现象:9号,19号信号无法被屏蔽,18号信号可以解除部分信号的屏蔽。

解除对2号信号的屏蔽:

 int count = 0;
    while (1)
    {
        // 获取pending位图
        sigset_t pending;
        sigemptyset(&pending);
        n = sigpending(&pending);
        assert(n == 0);

        // 看看pending位图
        Printpending(pending);
        count++;
        
        //解除对二号信号的屏蔽  2好信号 1->0
        if(count == 10)
        {
            cout<<"解除对二号信号的屏蔽"<<endl;
            n = sigprocmask(SIG_UNBLOCK,&block,&oblock);
            assert(n==0);
        }
        sleep(1);
    }

现象:解除成功,因为左边进程停掉了。

问题:先递达还是先清0呢?

void handler(int signo)
{
    sigset_t pending;
    sigemptyset(&pending);
    int n = sigpending(&pending); // 我正在处理2号信号哦!!
    assert(n == 0);

    // 3. 打印pending位图中的收到的信号
    std::cout << "递达中...: ";
    Printpending(pending); // 0: 递达之前,pending 2号已经被清0. 1: pending 2号被清0一定是递达之后
    std::cout << signo << " 号信号被递达处理..." << std::endl;
}

int main()
{

    // 对2号信号进行自定义捕捉 --- 不让进程因为2号信号而终止
    signal(2, handler);

答案是:先清0,再递达。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值