Linux:信号

1.信号的本质:是一种通知机制,用户或者操作系统通过发信号通知进程事件已经发生,进程在合适的时间处理。

2.结合进程:

a.进程要处理信号,必须有识别的能理(看到信号,处理信号)

b.凭什么进程要处理信号?程序员编写代码

c.信号产生的时间是随机的,进程可能正在做自己的事情所以信号的处理不一定是立即的

d.信号会被处理,后序处理

e.在什么时候?

g.一般而言,信号的产生相对进程而言是异步的

3.信号是如何产生的

a.组合键(ctrl+...)想前台进程发信号。

b.使用kill命令向目标进程发送命令

4.信号处理的常见方式

a.默认处理操作(进程自带,程序员写好的)

b.忽略(本质也是处理了信号)

c.自定义动作(信号捕捉)

5.常见的信号

可以通过kill -l查看信号列表 注意:没有0信号和32,33信号,1-31普通信号 34-64实时信号.

6.如何理解组合键发送信号?

键盘的工作方式是中断方式进行的 驱动可以识别组合键 通过OS解释组合键->查找进程列表->前台进程->OS向该进程PCB写入

7.理解信号的保存

发送信号

a.什么信号?

b.是否产生?

c.进程需要具有保存信号的数据结构(位图)

本质:该位图在进程的PCB结构体中,OS可以修改PCB中的字段,进而可以向PCB位图中写信号。

8.信号的处理方法

1.默认 2.忽略 3.自定义捕捉

这里先讲一下自定义捕捉:

signal 

 signal函数会把传入的信号编号传给handler,特定的信号处理动作一般只有一个。

signal函数,仅仅修改的处理动作,只有收到信号才会处理,相当于注册了一个方法,只有收到了该信号才会去调用handler方法。

写一个demo代码

 这里自定义了2号信号的动作,把默认的退出改为了打印信号名。

9.核心转储

我们使用 man 7 signal

 

 我们注意观察 “Action” 列其中“Term”是退出 “Ign”是忽略,“Core”就是核心转储

什么是核心转储呢? 当进程出现异常时,OS是否把当前进程的核心数据转存到磁盘中,以便于调试。

我们可以用- ulimit -a查看是否开启了核心转储 再通过上边提示的选项选择需要功能,可以-c选择文件数目, -l选择文件大小

 当发送了具有核心转储的信号会产生core文件

 这里写了一个除零错误,触发了核心转储 产生的core文件

我们使用 gdb core-file core.23842可以直接定位错误,但是,在生产中一般关闭core_dump 因为生产是服务器挂了重复启动重复挂会产生大量core文件,会挤满磁盘。

10.接口

int kill (pid_t , int sig) // 向目标进程发信号sig

raise(int sig) // 向自己发信号

abort() 向自身发送 SIGABRT 信号用来终止进程

unsigned int alarm (unsigned int second)  // 设定一个闹钟 second时间到了会给自己发送alarm信号 函数返回值:如果在seconds秒内再次调用了alarm函数设置了新的闹钟,则后面定时器的设置将覆盖前面的设置,即之前设置的秒数被新的闹钟时间取代;当参数seconds为0时,之前设置的定时器闹钟将被取消,并将剩下的时间返回.下面写一个demo。

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

typedef function<void(void)> func;
vector<func> callback;
void show()
{
    cout << "我正在使用Show\n";
}

void play()
{
    cout << "我正在使用Play\n";
}

void log()
{
    cout << "我正在使用Log\n";
}
void catching(int sig)
{
    for (auto it : callback)
    {
        it();

    }
    alarm(1); //当收到了alarm信号后 在这里设置会循环式的执行catching 

}

int main()
{

    callback.push_back(show);
    callback.push_back(play);
    callback.push_back(log);

    signal(SIGALRM,catching);

    alarm(1);

    while(1)
    {}
    return 0;
}

 

如何理解软件发送信号呢?

以闹钟信号为例

a.OS先识别软件触发条件是否满足

b.OS构造信号发送到指定进程 

c.OS管理闹钟结构体,被某种数据结构维护,OS会轮询检测超时,然后向对应进程发送信号

如何理解硬件信号异常呢?

例子:除零错误

1.进行计算的是CPU,是硬件

2.CPU中有状态寄存器,保存当前的运算状态,一旦发生了除零错误,寄存器中的某一位标识溢出问题的标记位会被标记为1,OS立即找到当前哪个进程在运行,当前运算的进程的上下文数据保存在寄存器中,所以OS也很容易找到,提取他的PID,OS向该进程发信号,进程在合适的时候处理。

3.一旦硬件异常了一定会退出吗?也不一定,但是不退出也做不了什么,因为该进程的上下文数据会一直保存,进而使得该进程再次被切回时,状态寄存器还是被标记溢出,就会不断的向该进程发送信号,只能选择终止掉该进程,让它被OS回收。

如何理解越界,野指针?

1.通过地址找到目标的位置

2.在语言层面使用的地址都是虚拟的。

3.将虚拟地址转化为物理地址 需要页表+MMU(硬件)

4.野指针-> 非法地址->MMU转化时发现该地址不属于该进程->报错->OS识别到转化为信号->向目标进程发信号。

上面讲了信号的产生,下面讲一讲信号的保存。

信号保存分为三个状态:递达,未决,阻塞

在内核中分别有三张表,被存放在进程的PCB结构体中

sigset_t类型 

sigset_t->不允许用户自行操作->OS给我们提供接口->user可以调用接口让OS帮你操作,因为上述的两张表在PCB结构体中,属于系统权限范围,用户不能越过操作系统直接访问。

sigset_t 成为信号集,可以标识每个信号的状态

接口:

int sigpending(sigset_t *set); 获取当前进程的pending信号集。

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how : 添加屏蔽字 SIG_BLOCK

         去除屏蔽字 SIG_UNBLOCK

         覆盖屏蔽字 SIG_SETMASK

sigset_t * set 添加,删除,覆盖的信号集

sigset_t *oldset 得到老的信号集

显示,设置信号集

int sigemptyset(sigset_t *set); // 初始化信号集为空

int sigfillset(sigset_t *set); // 初始化信号集为全信号

int sigaddset(sigset_t *set, int signum); // 添加信号

int sigdelset(sigset_t *set, int signum); //删除信号

int sigismember(const sigset_t *set, int signum); // 判断信号是否存在在该信号集中

引出问题:

1.如果我们是否可以对所有的信号都自定义捕捉

2.如果将2好信号block,并且不断打印pending ,突然发送一个2信号应该看到pending位图中2好信号比特位0->1 

3.对所有信号都block..是否该进程就不会被杀死了呢?

现在一一验证

1.

 

 这里我们发现9号信号是无法被自定义的,因为它属于管理员信号,不会被用户自定义的.

2.

 3.

 管理员信号是无法被屏蔽的。

在上面我们用了很多的接口但是没有设置pending的,原因是设置所有信号的发送方法和屏蔽字都是对pending的一种设置。

捕捉信号

信号在产生之后无法被立即处理,在合适的时候被处理(什么时候?)

1.

什么时候->信号相关字段保存在PCB中->内核态->在内核态返回到用户态时候,进行信号检测与处理

为什么会进入内核状态? 进行了系统调用,或者出现陷阱或异常

int 80 语句会中断,然后陷入内核,被内置到系统调用中。

 

信号操作:

int signal(int sig);

int sigaction(void(*sa_handler)(int));

当处理信号时,执行自定义动作,如果没处理完,又来了同一个信号,OS如何处理?

本质:为什么要有block,当正在处理某一个信号时,会设置block为1 确保一个时间内只有一个信号被处理。

信号捕捉没有创造线程;

子进程退出会向父进程发送SIG_CHILD信号,但是该信号默认动作是忽略,

如果不关心子进程的返回结果,让子进程自动释放,手动对子进程发的信号忽略即可,OS默认的忽略与手动的忽略不同,手动时OS使用高一级的忽略(仅在Linux有效)

可重入函数:

一个函数,在一个时间内被多个执行流重入,结果没有影响的是可重入,反之是不可重入的。

可重入和不可重入是函数的特征,与函数好坏无关。

不可重入:

调用了malloc或free,因为malloc是全局链表

调用了stl的 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值