漫话linux:信号

1.信号与进程:

        1.进程必须拥有处理信号的能力

        2.进程在收到信号的时候不一定会立即处理,会选择在合适的时间进行处理

        3.信号的产生和处理中间一定会存在时间窗口,所以进程具有保存信号状态的(是否结束或者被处理)的能力

2.前台进程:./后进程不再接收信号,变成前台进程,当./后,bash进程或者正在使用的shell会把控制权交给此程序,所以在程序结束运行前,不能在终端中再输入其他命令

在./命令后面加一个&,接收指令,表示不受ctrl c进行控制,只会受kill命令控制

./myxinhao & 

后台进程允许在同一个终端上输入其他命令,通常不会接收键盘输入,但它的标准输出和标准错误输出仍会显示到屏幕上,除非已经重定向

linux在一次登录中一般会创建一个bash,所以在一次登录中只允许一个进程是前台运行状态(无论何时,接收用户键盘输入的只能有一个进程),但运行多个进程处于后台运行状态(可以同时进行多个进程的运行,但不会接收用户输入,且不会影响前台进程),当没有进行其他前台进程的运行时,默认前台进程是bash或shell,bash作为命令行解释器,会自动接收和执行用户命令,在启动后台进程时,bash命令仍然是前台进程可以获取其他指令

以下为关闭后台进程的过程(我的程序当前没有执行myxinhao进程,只是做个演示)

ctrl c为何可以杀死我们的前台进程?

键盘输入首先会被前台进程收到,ctrl c的本质就是被进程解释成为收到了信号(2号信号)

kill-l(是英文小写L)

接下来就是信号的分类,1-30是普通信号,剩下为需要立即处理的实时信号 

2.信号的处理(三选一):默认动作,忽略,自定义动作(信号的捕捉),进程收到信号2的默认动作,就是终止自己

signal 修改特定进程对于信号的处理动作,自定义捕捉

void myhandler(int signo)
{
    cout << "process get a signal: " << signo <<endl;
    // exit(1);
}
signal(2, myhandler);

        1.signal只需要设置一次,往后都有效

        2.signal是在后序执行中触发,信号的产生和代码运行是不同步的,叫软中断

#include<iostream>
#include<unistd.h>
#include<cstdio>
#include<signal.h>
 
void handler(int signo)
{
    cout<<"捕捉到信号:"<<signo<<endl;
}
 
int main()
{
    signal(2,handler);
    int cnt=0;
    while(true)
    {
        printf("我是一个进程,我正在运行%d\n",cnt++);
        sleep(1);
    }
    return 0;
}

3.硬件层面

来聊聊以上信号的流程:首先键盘被摁下,数据被存在os的缓冲区,外设有数据了,cpu硬件中断进行识别,记录中断号,并将数据存放在寄存器中(在寄存器中,高低压电被解释为01数据,同时cpu中的中断信息被操作系统读取,把外设数据拷贝到缓冲区中,有了中断os就不用轮询检查外设了) 

我们学的信号,就是用软件方式,对进程模拟的硬件中断

数据拷贝到缓冲区前,os 会判断数据,控制(ctrl c 转化成为 2 号信号发送给进程)

os 就是不断接收外部中断来接受外设

不同的文件传的信号涉及到的是不同的缓冲区

4.信号的产生

        1.键盘组合键:ctrl c(信号2),ctrl \(信号3),不是所有的信号都可以被signal捕捉,如9,19(为了安全起见杀进程和暂停进程不能被捕捉)

        2.kill命令(kill -命令 pid)

        3.系统调用:man raise给自己发送信号==kill(getpid(),2),man abort 给自己发 6 号信号,终止

以上三种都是信号的产生方式,都是操作系统发送给进程的

5.硬件异常,自动退出

        1.常见例子:\0,野指针

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

例:

                1.当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程

                2.当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程

        3.由此看出,在C/C++中,除零,内存越界等异常,在系统层面上,是被当成信号处理的

        4.在捕捉到错误后,系统报错会陷入死循环,因为系统是为了让用户可以查看到错误信息的捕捉,但捕捉后 CPU 状态寄存器一直在获取异常信息,因为异常只影响进程,CPU把错误信息一直给os传,一直在被调动,上层用来宽容度保存资源和日志,所以交给了上层对异常进行处理和退出

补充:右边监视窗口的打开 ps ajx | grep myprocess

        5.\0和野指针会让进程崩溃,因为os会给进程发送信号,而cpu使用eip与pc扫描函数,以及状态寄存器,存在溢出标志位

原理:寄存器知道进程上下文,虽然我们修改的是 CPU 内部的状态寄存器,但是进程只影响你自己

操作系统检测到了硬件错误之后,一直发送

很多异常都是硬件引起的

CPU 也是硬件!操作系统是硬件的管理者

MMU 内存管理单元,集成在了 CPU 内部,CPU 读到的是虚拟地址

都在 CPU 中三位一体:页表 虚拟地址 MMU->物理地址

地址转化失败:虚拟到物理转化失败(野指针),转化失败 CPU 会进行物理报错--信号

溢出与越界:CPU通过不同的异常处理机制来区分溢出和越界。溢出通常通过算术指令的状态标志来指示,而越界通过内存访问异常来指示

6.软件条件:闹钟

异常不止有硬件,比如管道只读不写报错13

alarm命令会返回上一次闹钟的剩余时间

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
#include<signal.h>
using namespace std;
 
void catchSig(int signo) {
    cout << "进程捕捉到了一个信号,信号编号是: " << signo << endl;
    alarm(1); // 重新设置闹钟信号,延迟时间为 1 秒
}
 
int main(int argc, char* argv[]) {
    signal(SIGALRM, catchSig); // 设置信号处理函数
    alarm(5); // 设置初始的闹钟信号,延迟时间为 5 秒
 
    int cnt = 0; // 初始化计数器
 
    while (true) {
        cnt++;
        if (cnt == 3) {
            int n = alarm(0); // 取消现有的闹钟信号,并返回剩余时间
            cout << "取消闹钟信号,剩余时间: " << n << " 秒" << endl;
        }
        sleep(1); // 让程序暂停 1 秒
    }
 
    return 0;
}

操作系统中存在大量的闹钟,对闹钟的管理转化为对堆的增删查改,struct alarm* head,还是先组织在描述,进行抽象

时间戳--记录当前时间,超时时间,os周期性的检查这些闹钟,循环PCB进行比较

Term,Core两个都是终止进程,有什么区别?
其实这有关于,进程退出时,核心转储问题。

默认云服务器上面的 core 功能是被关闭的,防止 core dump 冲击内存,core dump 会占内存,可以通过下面命令进行打开

int main()
{
    //核心转储
    while(true)
    {
        int a[10];
        a[10000]=10;
    }
    return 0;
}

操作:打开 core 功能后,gdb 加载,先运行,在 core-file,事后调试

core ==term +core dump,Core退出的可以被核心转储的以便于后序快递定位问题

7.发送

对于普通信号而言,对于进程而言,自己有还是没有,收到哪一个信号,是给进程的 PCB 发来实现管理

task_struct{
    int signal;//0000 0000... //普通信号,位图管理信号
}

1.比特位的内容是 0 还是 1,表面是否收到

2.比特位的位置(第几个),表示信号的编号

3.所谓的“发信号”,本质就是 os 去修改 task_struct 的信号位图对应的比特位,“写信号”!

意味 OS 是进程的管理者,只有他有资格才能修改 task_struct 内部的属性!!!

(操作系统进行操作还要考虑上层软件哦

信号保存为什么?

进程收到信号之后,可能不会立即处理这个信号,信号不会被处理,就要有一个时间窗口

34-64 实时信号,有一个实时队列

连续发了好几个 2 号信号,位图怎么处理?都是一样的信号,没必要保存

注意:kill -9还是可以杀死进程,无论你怎么修改,无法对9号信号设定捕捉,即使你做了,OS也不会给你设置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值