【Linux】进程信号{初识信号/常见的信号/中断信号/信号的产生}

news2024/10/6 14:39:02

文章目录

  • 0.浅谈中断信号
  • 1.初识信号
  • 2.中断信号
  • 3.信号的产生
    • 测试:SIGINT
  • 4.core dump核心转储
  • 5.系统接口产生信号
    • 5.1kill给指定发
    • 5.2raise向自己发
    • 5.3abort自己给自己发6
  • 6.由于软件条件不满足产生信号
    • 6.1SIGPIPE
    • 6.2SIGALRM
  • 7. 硬件异常产生信号
    • 7.1除零错误
    • 7.2野指针/越界访问

0.浅谈中断信号

在Linux系统下,中断信号和中断设备是两个核心概念,它们在系统稳定性、响应速度和进程管理中起着至关重要的作用。

中断信号是一种软件机制,用于通知进程发生了某种特定事件。这些事件可能包括用户操作(如按下Ctrl+C)、硬件故障或系统状态的改变等。当中断信号被触发时,系统会向相关的进程发送一个信号,进程可以选择忽略该信号、执行默认操作或捕获信号并执行自定义的操作。常见的中断信号包括SIGINT(用户按下Ctrl+C时发送)、SIGKILL(强制终止进程)和SIGSTOP(暂停进程的执行)等。通过使用信号处理函数,进程可以在收到信号时执行特定的操作,如保存数据、清理资源等,从而确保系统的稳定性和数据的完整性。

另一方面,中断设备是硬件与操作系统交互的一种方式。当硬件设备需要CPU的注意或处理时,它会发送一个中断信号给CPU。这个信号告诉CPU暂时停止当前正在执行的程序,转而处理该中断请求。每个硬件设备都有一个唯一的中断号,用于在系统内标识和定位相应的中断处理程序。当中断发生时,CPU会根据中断号找到对应的中断处理程序,并执行相应的操作。这样,操作系统就能够有效地管理和调度各种硬件设备,确保它们得到及时的处理和响应。

总的来说,中断信号和中断设备在Linux系统中协同工作,共同实现了事件驱动的处理机制。它们确保了系统在面临各种事件和请求时能够做出及时、准确的响应,从而保证了系统的稳定性和性能。

shell命令:od + 文件:以二进制方式打开一个文件

1.初识信号

信号和信号量是两个东西,没有关系

Linux信号和信号量在操作系统中各自扮演着重要的角色,但它们的目的、功能和使用方式有着明显的差异。

首先,从定义上来看,Linux信号是一种软件中断,用于通知进程某个异步事件的发生。当进程接收到信号时,它会根据信号的类型执行相应的操作。这种通信机制使得进程可以响应各种事件,例如终止、挂起、恢复等。而信号量则是一个特殊的变量,用于控制多个进程对共享资源的访问。信号量的值表示可用资源的数量,进程在访问共享资源前需要先获取信号量,访问完成后释放信号量。

其次,从功能上来看,信号主要用于进程间的异步通信,通知进程某个事件的发生。这种通信方式无需进程之间的显式协调,可以在任何时候由系统或其他进程发送。而信号量则主要用于同步进程对共享资源的访问,防止多个进程同时访问同一资源导致的数据不一致或冲突。

最后,从使用方式上来看,信号的发送和接收通常通过系统调用实现,例如kill函数用于发送信号,而进程可以通过设置信号处理函数来响应接收到的信号。而信号量的操作则包括初始化、P操作(等待)和V操作(信号),这些操作需要原子性地执行以确保数据的一致性。

总的来说,Linux信号和信号量都是进程间通信和同步的重要机制,但它们在定义、功能和使用方式上有所不同。信号主要用于异步通信,而信号量则主要用于同步对共享资源的访问。

生活中,有哪些信号相关的场景呢?

红绿灯 闹钟 王者荣耀 信号转向灯 狼烟
1.为什么认识这些信号呢? 记住了对应场景下的信号 +如何应对这个信号做出相应动作
2.接受过教育,经历过事情能够识别这个信号
3.如果特定信号没有产生,通过以往的经验依旧知道应该做出什么动作
4.收到这个信号的时候,可能不会立即处理这个信号(当下有更重要的事)
5.信号在无法立即被处理的时候,要被临时的记住

什么是Linux信号

本质是一种通知机制,用户 or 操作系统通过发送一定的信号通知进程,某些事件已经发生,可以在后续进行处理

进程如何处理?

a. 进程要处理信号,必须具备信号“识别”的能力(能够接收到信号 + 知道做出什么反应)
b. 进程通过程序员的代码设置能够“识别”信号呢
c. 信号产生是随机的,进程可能正在忙自己的事情,信号可能不是立即处理的
d. 信号会临时记录,方便后续在合适的时候进行处理
e. 一般而言,信号的产生相对于进程而言是异步的

信号如何产生

举例: ctrl+c:本质是通过键盘组合键向目标进程发送2号信号使其退出

信号处理的常见方式

a. 默认(进程自带的,程序员写好的逻辑)
b.忽略 (信号处理的一种方式,不做处理)
c. 自定义动作(捕捉信号)

如何理解组合键变成信号

键盘的工作方式是通过中断方式进行 能够识别组合键如ctrl+c
OS解释组合键得出组合键要传达的信号 -> 查找进程列表 -> 找到前台运行的进程 -> OS写入信号到进程内部的位图结构

如何理解信号被进程保存

进程PCB内部必须具有保存信号的相关数据结构(位图,unisgned int)
信号用位图存储 信号位图存放在进程PCB中 比特位表示哪一个信号 1表示产生 向进程发送信号实际上就是修改对应进程得到信号位图结构

信号发送的本质

信号位图是在task struct->task struct内核数据结构->OS
OS 向 目标进程 信号,OS直接修改pcb中的指定的位图结构,完成“发送”信号的过程

同步/异步/互斥

在计算机层面,同步、异步、互斥是三个核心概念,它们各自描述了不同的并发或并行操作中的行为特性。以下是对这些概念的简要解释:

同步(Synchronization)
同步指的是多个进程或线程在执行过程中,需要按照某种特定的顺序或条件来协调它们的执行。换句话说,同步确保某些操作或事件按照预期的顺序发生。当多个线程或进程需要共享或访问相同的资源时,同步就变得尤为重要,因为它可以防止数据竞争和不一致。

同步机制通常包括互斥锁(mutexes)、信号量(semaphores)、条件变量(condition variables)等,这些机制用于控制对共享资源的访问,确保在某一时刻只有一个线程或进程可以访问。

异步(Asynchrony)
异步指的是进程或线程的执行不需要按照严格的顺序,或者某些操作可以在没有等待其他操作完成的情况下开始。异步操作不会阻塞调用线程或进程,允许它们继续执行其他任务,直到异步操作完成后再进行处理。

异步编程常用于I/O密集型任务,如文件读写、网络请求等,因为这些操作通常涉及到等待外部系统响应,使用异步可以显著提高程序的响应性和吞吐量。

互斥(Mutual Exclusion)
互斥是同步的一种形式,用于确保在任意时刻只有一个线程或进程可以访问特定的共享资源或代码段。这是通过互斥锁来实现的,当一个线程或进程获得锁时,其他尝试访问该资源的线程或进程将被阻塞,直到锁被释放。

互斥对于保护共享资源免受数据竞争和不一致的影响至关重要。它确保了在任何给定时间,只有一个执行线程可以修改数据,从而维护了数据的一致性和完整性。

简而言之,同步和异步关注的是任务执行的顺序和方式,而互斥则是同步的一种具体实现方式,用于保护共享资源不被同时访问。这些概念在并发编程、多线程、分布式系统等领域中非常重要。

理解信号

信号是在操作系统中用于通知进程发生某个事件的一种机制。它是一种异步的通信方式,可以用于进程间的通信和进程与操作系统之间的通信。

信号的产生:信号可以由多种事件触发,例如用户按下某个特定的键、通过系统调用向进程发信号、软件错误、硬件异常等。当事件发生时,操作系统会向相应的进程发送一个信号。
信号的发送和接收:信号的发送是由操作系统负责的,而信号的接收是由进程负责的。进程具有识别信号并执行相应动作的能力,进程可以通过注册信号处理函数signal来指定对某个特定信号的处理方式。
信号的处理:进程接收到信号后,可以采取不同的处理方式。常见的处理方式包括忽略信号、执行默认的信号处理动作、执行自定义的信号处理函数等。需要注意的是,信号是一种异步的通信方式,进程无法预测信号何时到达。进程可能正在处理优先级更高,更重要的任务,所以信号的处理工作可能不是立即执行的。进程会临时记录下对应的信号,方便后续进行处理。
操作系统提供了一些函数和系统调用来处理信号,例如signal函数用于注册信号处理程序,kill函数用于向进程发送信号。

进程如何记录接收到的信号

在进程PCB内部保存了信号位图字段,用于记录该进程是否收到了对应信号。
信号位图在进程PCB中,而PCB属于内核数据结构,由操作系统进行信号位图的写入。

发送信号的本质

操作系统直接修改目标进程PCB中的信号位图字段,将对应信号的比特位由0置1,完成信号的发送。

常见的信号

SIGHUP:挂起信号(1)
SIGINT:中断信号,通常由Ctrl+C触发(2)
SIGQUIT:退出信号,通常由Ctrl+\触发(3)
SIGILL:非法指令信号(4)
SIGABRT:异常终止信号(6)
SIGFPE:浮点异常信号(8)
SIGKILL:强制终止信号,无法被忽略或捕获(9)
SIGSEGV:段错误信号(11)
SIGPIPE:管道破裂信号(13)
SIGALRM:定时器到期信号(14)
SIGTERM:终止信号,用于正常终止进程(15)
SIGUSR1:用户自定义信号1(10)
SIGUSR2:用户自定义信号2(12)

如何查看信号

  1. kill -l命令可以查看系统定义的信号列表。[1,31]是普通信号,没有32,33信号,[34,64]是实时信号,共有62个信号

在这里插入图片描述
2. man 7 signal命令查看信号的详细描述
在这里插入图片描述

为什么没有32,33号信号?

在Linux中,信号是一种在进程间通信的机制,用于通知进程某个事件的发生。在Linux的信号集中,编号为1~31的信号是传统UNIX支持的信号,被称为非实时信号或不可靠信号。这些信号不支持排队,如果多个这样的信号同时发送给进程,它们可能会被合并成一个处理,这可能导致信号丢失。

编号为32~63的信号是后来扩充的,被称为实时信号或可靠信号。这些信号支持排队,即使多个相同的实时信号同时发送给进程,它们也会保持各自的状态,不会被合并,因此不会出现信号丢失的情况。

至于32号和33号信号,它们原本应该是用于实时信号的一部分,但这两个信号(SIGCANCEL和SIGSETXID)被glibc(GNU C Library)内部征用了,用于实现线程的取消。因此,从内核层面来看,32号信号本应是最小的实时信号(SIGRTMIN),但由于被glibc征用,glibc将SIGRTMIN设置成了34号信号。

所以,32号和33号信号并不是标准的实时信号,而是被用于特定的内部目的。因此,在查看或处理Linux信号时,需要注意这两个信号的特殊处理情况。

2.中断信号

  1. 中断设备比如说8259这样的硬件单元 说CPU的针脚 中断向量表啊 上下文线程保存和线程恢复
  2. 每一个设备都有自己对应的中断编号 简称中断号 中断号可以理解就是个数组用函数指针的函数指针数组的方式和一张表来对应
  3. 当一个设备一旦触发了中断 直接通过硬件的方式向计算机比如CPU某些针脚上发送对应的中断信息 中断信息对应的中断号以硬件的方式被写入到特定的寄存器 操作系统识别对应的寄存器内部的中断号 根据中断编号查找自己内置相关的中断处理方法 调用中断的方法完成硬件到软件的行为
  4. 中断分很多类比如键盘中断

中断里面最重要的有两类中断

一类是网卡中断 网卡发送消息接收消息 硬件上先收到消息 软件上操作系统通过中断去知道数据
第二类中断时钟中断 OS不断收到中断信号去轮询获取诸多信息:某个进程时间片用完没有,哪个进程要调度等等。
信号是被保存下来的 — 保存下来就有他的意义 — 无法立即处理先记录下来有时间再处理

3.信号的产生

捕捉特定信号的函数

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
当调用signal(signum, handler)时,进程会将其内部的信号处理函数表(或称为信号映射表)中与信号signum相对应的位置设置为handler。**当该进程接收到信号signum时,**操作系统会查找这个表,找到对应的处理函数并执行。如果没有接收到该信号则不做处理。对于handler,如果显示设置了,则调用显示设置的函数,否则调用默认配置的函数。

测试:SIGINT

在这里插入图片描述

在这里插入图片描述

发送信号的方式

  1. ctrl+c
  2. ctrl+/
  3. kill -SIGINT pid
  4. kill -2 pid

ctrl+c 已经无法终止,那怎么终止?

在这里插入图片描述
开发环境 – 发布环境 – 测试环境 – 生产环境
即:敲代码 – 合并编译 – 测试 – 项目上线(用户可以获取)
现在我们用的云服务器实际上是开发/发布/测试/生产集一体的环境

4.core dump核心转储

在这里插入图片描述
0. 一般是为了调试,由OS将当前进程在内存中的相关核心数据,转存到磁盘中

  1. 一般而言,云服务器(生产环境)的核心转储功能是被关闭的!
  2. 打开与关闭:当进程出现某种异常的时候,是否由OS将当前进程在内存中的相关核心数据,转存到磁盘中在这里插入图片描述

只在当前的会话中打开云服务器core dump

ulimit -c 10240

打开后 kill -8 pid后的不同(形成了一个以pid为后缀的文件)

在这里插入图片描述

这个文件有什么用?【试错调试】定位错误代码 显示错误原因

gdb + exe调试模式下:
core-file + core文件
在这里插入图片描述

验证进程等待的core dump标记位

int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        sleep(1);
        int a = 100;
        a /= 0;
        exit(0);
    }

    int status = 0;
    waitpid(id, &status, 0);
    cout << "父进程:" << getpid() << " 子进程:" << id
         << " exit sig: " << (status & 0x7F) //0111 1111
         << " is core: " << ((status >> 7) & 1) << endl;

    return 0;
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

为什么生产环境一般都是要关闭core dump?

在生产环境中关闭核心转储(core dump)的主要原因有以下几点:

磁盘空间:核心转储文件通常很大,因为它们包含了进程在崩溃时的完整内存映像。如果频繁发生核心转储,这些大文件会迅速占用大量的磁盘空间,可能导致磁盘空间不足,影响系统的正常运行。

隐私和安全性:核心转储文件可能包含敏感信息,如密码、密钥或其他机密数据。如果核心转储文件没有被妥善管理和保护,这些信息可能会被未授权的人员访问,造成安全风险。

性能影响:生成核心转储文件是一个相对耗时的操作,因为它需要复制进程的内存内容到磁盘。在生产环境中,这种性能开销可能是不希望的,特别是在需要快速响应和高吞吐量的场景下。

故障排查:虽然核心转储文件对于调试和故障排查非常有用,但在生产环境中,通常更倾向于使用日志和监控工具来追踪和定位问题。核心转储文件可能不是首选的故障排查手段,因为它们通常包含大量的数据,需要专业的知识和工具来分析。

自动化管理:在生产环境中,系统的稳定性和可靠性是至关重要的。关闭核心转储可以避免因手动管理这些文件而引入的潜在错误和复杂性。

因此,为了保持生产环境的稳定性、安全性和性能,通常会关闭核心转储功能。然而,在开发或测试环境中,开启核心转储可能是一个好的做法,以便在程序崩溃时能够获取详细的调试信息。

5.系统接口产生信号

5.1kill给指定发

在这里插入图片描述

在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>

using namespace std;

//传参错误 提示用户传参格式
static void Usage(string proc)
{
    cout << "Usage:\r\n\t" << proc << " signumber processid" << endl;
}
// ./mykill 2 pid
int main(int argc, char *argv[])
{

    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    int signumber = atoi(argv[1]);
    int procid = atoi(argv[2]);

    kill(procid, signumber);
    return 0;
}

在这里插入图片描述

5.2raise向自己发

在这里插入图片描述

5.3abort自己给自己发6

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>

using namespace std;

int main(int argc, char *argv[])
{
    cout << "Start running..." << endl;
    sleep(1);
    abort();// ==> raise(6) ==> kill(getpid(), 6)
    //通常用来终止进程
    return 0;
}

在这里插入图片描述

代码退出进程的方式

return/exit/_exit/abort.

怎么理解通过系统调用接口产生信号?

用户调用系统接口 ->OS提取参数(pid,signum)-> OS向目标进程写信号 -> 修改对应进程的信号位图结构 -> 进程后续会处理信号 -> 执行对应的处理动作

6.由于软件条件不满足产生信号

6.1SIGPIPE

在这里插入图片描述

管道:读端不进行读的操作并且还关闭了读端,写端一直在写 ⇒ 占用内存/浪费资源/没有意义
0S会通过发送信号自动终止对应的写端进程 ⇒ 13)SIGPIPE

如何验证?

1.创建匿名管道
2.让父进程进行读取,子进程进行写入
3.父子通信一段时间
4.父进程关闭读端 并且调用wait/waitpid等待子进程 子进程一直写入就行
5.OS终止子进程,子进程退出,父进程waitpid拿到子进程的status
6.提取退出信号 ⇒ SIGPIPE

测试代码

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;

int main()
{
    int pipefd[2] = {0};
    int n = pipe(pipefd);

    assert(n != -1);
    (void)n;

    pid_t id = fork();
    assert(id != -1);

    if (id == 0) // 子进程一直写
    {
        close(pipefd[0]);
        string msg = "I am child";
        char send_buffer[128];
        while (true)
        {
            snprintf(send_buffer, sizeof(send_buffer), "%s[%d]", msg.c_str(), getpid());
            write(pipefd[1], send_buffer, strlen(send_buffer));
            sleep(1);
        }
    }

    close(pipefd[1]);
    char receive_buffer[128];
    int count = 5;
    while (count--)
    {
        ssize_t s = read(pipefd[0], receive_buffer, sizeof(receive_buffer) - 1);
        if (s > 0)
        {
            receive_buffer[s] = 0; // 字符串自定义约定
            cout << "father[" << getpid() << "] get a msg: " << receive_buffer << endl;
        }
    }
    close(pipefd[0]); // 关闭读端

    int status = 0;
    waitpid(id, &status, 0);
    if (WIFEXITED(status))
    {
        printf("child_pid: %d exit_code: %d\n", id, WEXITSTATUS(status));
    }
    else
    {
        printf("child_pid: %d exit_signal: %d core_dump: %d\n", id, status & (0x7f), (status >> 7) & 1);
    }

    return 0;
}

在这里插入图片描述

6.2SIGALRM

在这里插入图片描述
在这里插入图片描述

调用alarm函数可以设定一个闹钟 告诉内核在seconds秒之后给当前进程发SIGALRM信号 该信号的默认处理动作是终止当前进程。

alarm的返回值

在Linux中,alarm() 函数定义在 <unistd.h> 头文件中。当你调用 alarm() 函数并为其提供一个参数(以秒为单位的时间),它将设置一个定时器,当定时器到期时,会向进程发送一个 SIGALRM 信号。

alarm() 函数的返回值是:

如果之前没有设置任何定时器,那么它返回0。
如果之前已经设置了一个定时器,那么它返回之前定时器的剩余时间(以秒为单位),也就是说,返回的是从当前时间到之前设置的定时器到期时间的剩余秒数。
这个函数通常用于确保某个操作在一定时间内完成,如果超时,则通过 SIGALRM 信号来进行处理。需要注意的是,如果再次调用 alarm(),它会取消之前的定时器并设置新的定时器。

此外,需要注意的是,alarm() 设置的定时器只会在进程接收到其他信号之前被发送,这意味着如果进程长时间阻塞在某个系统调用上(例如 read() 或 sleep()),那么定时器可能会延迟发送,直到系统调用返回。

如果你需要更精确或更复杂的定时器控制,可能需要考虑使用其他机制,如 setitimer() 或 timer_create() 等函数。

alarm返回值的作用

alarm() 函数的返回值在编程中有几个重要的用途:

了解之前的定时器状态:如果之前已经设置了一个定时器,alarm() 的返回值表示那个定时器的剩余时间。这允许你了解之前定时器的状态,以及是否还有足够的时间等待它到期。

避免定时器重叠:如果你打算设置一个新的定时器,但希望避免与之前的定时器重叠,你可以使用 alarm() 的返回值来决定是否需要取消或调整之前的定时器。例如,如果返回值大于你希望设置的新定时器的时长,那么你可以决定不设置新的定时器,因为之前的定时器仍然有足够的时间才会到期。

处理定时器重置:当你再次调用 alarm() 时,之前的定时器会被取消,新的定时器会被设置。返回的剩余时间可以帮助你了解之前的定时器被取消时还有多长时间才会到期,这对于某些需要精确控制时间间隔的应用程序来说可能是重要的。

调试和日志记录:在开发和调试过程中,alarm() 的返回值可以用来记录定时器的状态和变化,这有助于跟踪和理解程序的行为。

决定是否需要处理 SIGALRM 信号:如果 alarm() 返回0,说明之前没有设置定时器,因此你可能不需要为 SIGALRM 信号设置处理函数。如果返回了一个非零值,那么你可能需要准备处理这个信号,尤其是当你的程序逻辑依赖于定时器的到期时。

总的来说,alarm() 的返回值为你提供了关于定时器状态的重要信息,这可以帮助你更好地管理和控制你的程序中的定时器行为。然而,需要注意的是,alarm() 函数相对简单,并且其使用受到一些限制(例如,它只能设置一个定时器,且定时器的精度和可靠性可能不如其他更现代的定时器机制)。在复杂的程序中,可能需要考虑使用更高级的定时器API。

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>
using namespace std;

//long int or long long int
uint64_t count = 0;

void catchSig(int signum)
{
    cout << "进程捕捉到了一个信号" << signum << " Pid: " << getpid()
         << " final count: " << count << endl;
}

int main(int argc, char *argv[])
{
    // 验证1s之内 一共会进行多少次count++
    // 1. count最大只到10w:cout + 网络远距离发送 = IO
    alarm(1); // 这个闹钟一旦触发就自动移除 一次性闹钟
    /*
    int count = 0;
    while (true)
    {
        cout << "count: " << count << endl;
        count++;
    }
    */

    // 2. 如果单纯向计算算力呢?
    signal(SIGALRM, catchSig);
    while (true)
        count++;
    return 0;
}

在这里插入图片描述

  1. sleep()是自己写的代码 要考虑放在哪里以实现轮询操作
    闹钟是只要把闹钟先写好 之后的代码块无需增加轮询访问 只用编写测试代码即可
  2. IO的效率其实非常低 尤其是带上网络(差别在10^3 ) cpu 内存 外设 (纳秒 微秒 毫秒)

定时器的使用与成绩

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>
#include <cstring>

using namespace std;

typedef function<void(int)> func;
vector<func> funSet;
uint64_t count = 0;

void showCount(int signum = 0)
{
    cout << "进程捕捉到了一个信号" << signum << " Pid: " << getpid()
         << " final count: " << count << endl;
}
void showLog(int test = 0)
{
    cout << "日志功能" << endl;
}
void showUser(int test = 0)
{
    pid_t id = fork();
    if (id == 0)
    {
        // cout << "current user: "; 线程不安全
        char msg[32] = "current user: ";
        write(1, msg, strlen(msg));
        execl("/usr/bin/whoami", "whoami", (char *)NULL);
        exit(1);
    }
    wait(nullptr);
}
void Timed_Tasks(int test = 0)
{
    // flush_data
    // 断开连接 用户长时间不登录 再次登录要输入密码
}

// 定时器功能
void catchSig(int signum)
{
    for (auto &fun : funSet)
    {
        fun(signum);
    }
    alarm(1);
}
void addFun()
{
    funSet.push_back(showCount);
    funSet.push_back(showLog);
    funSet.push_back(showUser);
}
int main()
{
    addFun();

    signal(SIGALRM, catchSig);
    alarm(1); // 这个闹钟一旦触发就自动移除

    while (true)
        count++;
    return 0;
}

在这里插入图片描述

如何理解软件条件给进程发送信号

诸多进程会使用诸多闹钟,内核中存在大量闹钟,先描述再组织,闹钟节点链表链接,定时轮询,闹钟到时触发信号。
a. OS先识别到某种软件条件触发或者不满足
b. OS 构建信号,发送给指定的进程

7. 硬件异常产生信号

7.1除零错误

在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>

using namespace std;

void handler(int signum)
{
    sleep(1);
   cout << "进程捕捉到了一个信号:" << signum << " Pid: " << getpid() << endl;
    //exit(1);
}

int main()
{
    signal(SIGFPE, handler);
    int a = 100;
    a /= 0;

    return 0;
}

在这里插入图片描述

如何理解除零?

  1. 执行运算这个操作的是硬件 – CPU
  2. 程序运行成为进程,执行到“除”这个动作,硬件无法完成除法运算,并产生异常信号。将CPU内部的状态寄存器的溢出标记位置为1,OS识别到计算时有溢出信号,从task_struct* current获取当前运行的进程pid,发送SIGFPE给pid,进程做出相应处理。

在联合软硬件的角度来看,除零是一个涉及硬件运算和软件逻辑处理的问题。

首先,从硬件层面来看,当处理器遇到除零指令时,它会尝试执行除法运算。然而,由于除数的值为零,这个运算在硬件层面是无法完成的。因此,硬件会返回一个错误状态或者特定的异常信号,以指示这个操作是无效的。

接下来,从软件层面来看,当操作系统或应用程序捕获到这个由硬件产生的异常信号时,它会根据预设的错误处理机制来响应。这可能包括记录错误信息、终止程序的执行、或者触发一个特定的错误处理函数。具体如何处理取决于软件的设计和实现。

联合软硬件来看,除零操作会导致以下情况发生:

硬件无法完成除法运算,并产生异常信号。
软件捕获到这个异常信号,并根据预设的机制进行错误处理。
这个过程展示了软件和硬件之间的紧密合作。硬件负责执行指令并返回结果或异常状态,而软件则负责处理这些结果或异常,确保系统的稳定性和可靠性。

需要注意的是,除零在数学上是无意义的,因此在实际编程中应该避免这种情况的发生。通过合理的代码设计和错误检查机制,可以确保程序在遇到除零情况时能够做出适当的处理,避免程序崩溃或产生不可预测的结果。

一旦出现硬件异常,进程一定会退出吗

不一定!一般默认是退出,如果不退出,我们也做不了什么

当发生除零错误,进程收到了“float point exception”后的默认处理动作是让进程退出,如果我们在代码里捕获信号,修改自定义行为,不让进程退出,由于溢出标记位仍然为1,此时OS只要识别到“1”,就会向进程发出信号,进程捕获到信号又不退出,如此陷入了死循环。
这个错误是由于OS将计算的结果返回前检测到溢出标记位为1,我们能做的就是在自定义捕获信号里让进程退出并输出错误信息(这实际上就是OS默认的行为,只不过我们为了了解中间过程,故意这么干)

7.2野指针/越界访问

在这里插入图片描述

#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>

using namespace std;

void handler(int signum)
{
    sleep(1);
    cout << "进程捕捉到了一个信号:" << signum << " Pid: " << getpid() << endl;
    //exit(1);
}

int main()
{
    /*
    signal(SIGFPE, handler);
    int a = 100;
    a /= 0;
    */

    signal(SIGSEGV, handler);
    int *p = nullptr;
    *p = 100;

    return 0;
}

在这里插入图片描述
0. 野指针/越界访问 不一定报错!

  1. 二者必须通过地址,找到目标位置
  2. 语言上面的地址是虚拟地址
  3. 将虚拟地址转成物理地址需要页表 + MMU(Memory Manager Unit硬件)

硬件常识

  1. 几乎任何的外设,常见的硬件,都有可能有寄存器。
  2. 访问磁盘数据:进程将目标地址放入磁盘寄存器,磁盘读取后去查找。
  3. 基于外设式的编程—串口式编程,外设有数据/指令寄存器,同样也是驱动程序驱动外设的原理。

野指针和越界访问是编程中常见的错误情况,它们涉及到硬件、软件、页表、内存管理单元(MMU)以及信号等多个层面的交互。下面我将结合这些概念,从硬件和软件的角度解释野指针或越界访问时发生了什么。

野指针和越界访问的基本概念
野指针是指向一个不可知地址的指针,它可能是由于指针未初始化、指针越界访问或指针指向的空间被释放等原因造成的。而越界访问则是指针访问了超出其分配空间范围的内存。

从硬件角度看
在硬件层面,内存是由一系列的地址空间组成的,每个地址对应一个固定的内存单元。当CPU通过指针访问内存时,它会生成一个逻辑地址,这个地址需要经过MMU(内存管理单元)的转换,变成物理地址,才能访问实际的内存单元。

野指针或越界访问时,CPU会尝试访问一个不正确的逻辑地址。由于这个地址是无效的或越界的,MMU可能无法将其转换为有效的物理地址。此时,硬件可能会触发一个异常或中断,通知操作系统发生了错误。

从软件角度看
在软件层面,操作系统通过页表来管理内存。每个进程都有自己的页表,它记录了逻辑页与物理页帧的对应关系。当进程尝试访问一个内存地址时,操作系统会查找页表,找到对应的物理地址,然后完成访问。

对于野指针或越界访问,操作系统在检查页表时会发现请求的地址不在页表的映射范围内。此时,操作系统会触发一个信号(如SIGSEGV,即段错误信号),通知进程发生了错误。这个信号可以被进程捕获并处理,或者如果进程没有设置相应的信号处理函数,操作系统会默认终止进程的执行。

信号与进程通信
信号是操作系统中用于通知进程发生了某个事件或条件的一种机制。当野指针或越界访问发生时,操作系统发送信号给进程,进程可以根据信号的类型和内容来决定如何处理。这种机制允许进程之间进行通信和错误处理。

进程通信与错误处理
进程可以通过信号机制来处理野指针或越界访问的错误。例如,进程可以设置信号处理函数来捕获SIGSEGV信号,并在函数中进行错误处理,如打印错误信息、清理资源或尝试恢复执行。这样,进程可以在遇到错误时采取适当的行动,而不是直接崩溃。

总结
野指针或越界访问是编程中的严重错误,它们会导致硬件层面的内存访问异常和软件层面的信号处理。通过页表、MMU和信号等机制,操作系统能够识别并处理这些错误,保护系统的稳定性和安全性。因此,在编程过程中,我们应该遵循良好的编程规范,避免野指针和越界访问的发生,确保程序的正确性和可靠性。

上面所说的所有信号产生,最终都要有OS来进行执行,为什么?

OS是进程的管理者

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1523331.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java代码基础算法练习-判断字符串是否为回文-2024.03.16

任务描述&#xff1a; 回文串是指一个正读和反读都一样的字符串&#xff0c;比如“level”或者“noon”等。要求输入 一个字符串&#xff0c;判断此字符串是否为回文。&#xff08;注&#xff1a;设字符串长度小于20&#xff09; 任务要求&#xff1a; package suanfa;import…

python--类和对象+类属性+实例属性+函数在类的调用

python--类和对象类属性实例属性函数在类的调用 类属性--创建、访问&#xff08;类、类实例&#xff09;实例属性--创建、访问&#xff08;类实例&#xff09;初始化实例属性__init__ 私有属性伪私有属性&#xff08;Pseudo-private Attributes&#xff09;私有属性&#xff08…

深入浅出理解 AI 生图模型

目录 引言 一、Stable Diffusion原理 首先 随后 最后 二、DDPM模型 1 资料 2 原理 扩散过程 反向过程 3 公式结论 三、优缺点 优点&#xff1a; 缺点&#xff1a; 四、改进与完事 LDM代表作 原理概括 Latent Space&#xff08;潜空间&#xff09; 五、总结 引…

zookeeper快速入门一:zookeeper安装与启动

本文是zookeeper系列之快速入门中的第一篇&#xff0c;欢迎大家观看与指出不足。 写在前面&#xff1a; 不影响教程&#xff0c;笔者安装zookeeper用的是WSL(windows下的linux子系统&#xff09;&#xff0c;当然你想直接在windows上用zookeeper也是可以的。 如果你也想用ws…

MinGW64 windows gcc编译器安装

下载编译好的文件包 https://sourceforge.net/projects/mingw-w64/ 打开网址 界面左上方 点File 滚轮 滚到下面 点 红框 解压 配置path 环境变量

西门子PLC常用底层逻辑块分享_调节阀

文章目录 前言一、功能概述二、调节阀程序编写1.创建自定义数据类型2.创建FC块“调节阀”3.编写程序 前言 本文分享一个自己编写的调节阀控制逻辑块。 一、功能概述 手动状态、自动状态、检修状态自由切换&#xff1b;手动状态下&#xff0c;手动输入阀门开度值&#xff1b;自…

不可能,看了这篇笔记还不会用例设计!

随着测试技术的提升&#xff0c;我们在不断学习跟进的同时&#xff0c;也不能把基础知识忘却了。 测试基础里面最重要的一个成果输出就是测试用例&#xff0c;也是测试思维的集中体现。现在虽然有些岗位不写用例了&#xff0c;但是要求还要测试执行覆盖全&#xff0c;不能漏测。…

我在技​​术面试中用 ChatGPT 作弊,没人知道

众所周知&#xff0c;ChatGPT 已经彻底改变了人们的工作方式。它既能帮助小型企业自动化管理任务&#xff0c;又能为 Web 开发人员编写整个 React 组件&#xff0c;它的作用可以说怎么夸都不过分。 在 interviewing.io&#xff0c;我们一直在思考 ChatGPT 将给技术面试带来什么…

YOLOV5 改进:增加注意力机制模块(SE)

1、前言 本章将介绍yolov5的改进项目,为v5增加新的模块---注意力机制、SE模块 大部分更改的代码是重复的,只有少部分需要更改,下面会详细讲解 yolov5的yaml文件介绍:YOLOV5 模型:利用tensorboard查看网络结构和yaml文件介绍-CSDN博客 yolov5的模块更改,C3更改为C2f模块…

用c++实现计数排序、颜色排序问题

3.3.1 计数排序 【问题】 假设待排序记录均为整数且取自区间[0,k],计数排序(count sort)的基本思想是对每一个记录x&#xff0c;确定小于x的记录个数&#xff0c;然后直接将x放在应该的位置。例如&#xff0c;小于x的记录个数是10,则x就位于第11个位置。 【想法】 对于待排序序…

计算机设计大赛 题目:基于机器视觉的图像矫正 (以车牌识别为例) - 图像畸变校正

文章目录 0 简介1 思路简介1.1 车牌定位1.2 畸变校正 2 代码实现2.1 车牌定位2.1.1 通过颜色特征选定可疑区域2.1.2 寻找车牌外围轮廓2.1.3 车牌区域定位 2.2 畸变校正2.2.1 畸变后车牌顶点定位2.2.2 校正 7 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享…

【大模型系列】问答理解定位(Qwen-VL/Llama2/GPT)

文章目录 1 Qwen-VL(2023, Alibaba)1.1 网络结构1.2 模型训练 2 Llama2(2023, Meta)2.1 网络结构2.1.1 MHA/GQA/MQA2.1.2 RoPE(Rotary Position Embedding, 旋转式位置编码)2.1.3 RMSNorm 2.2 推理2.2.1 集束搜索(beam search)2.2.2 RoPE外推 3 GPT系列(OpenAI) 1 Qwen-VL(2023…

免费开源多层级多标签文本分类|文本分类接口|文本自动分类

一、开源项目介绍 一款多模态AI能力引擎&#xff0c;专注于提供自然语言处理&#xff08;NLP&#xff09;、情感分析、实体识别、图像识别与分类、OCR识别和语音识别等接口服务。该平台功能强大&#xff0c;支持本地化部署&#xff0c;并鼓励用户体验和开发者共同完善&#xf…

【集成开发环境】-VS Code:C/C++ 环境配置

简介 VS Code&#xff0c;全称Visual Studio Code&#xff0c;是一款由微软开发的跨平台源代码编辑器。它支持Windows、Linux和macOS等操作系统&#xff0c;并且具有轻量级、高效、可扩展等特点&#xff0c;深受广大开发者的喜爱。 VS Code拥有丰富的功能特性&#xff0c;包括…

6.【Linux】进程间通信(管道命名管道||简易进程池||简易客户端服务端通信)

介绍 进程间通信的方式 1.Linux原生支持的管道----匿名和命名管道 2.System V-----共享内存、消息队列、信号量 3.Posix------多线程、网路通信 进程间通信目的 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享&#xff1a;多个进程之间共享同样的资源。…

科研学习|论文解读——了解在线环境中的多数观点形成过程:Facebook的探索性方法(IPM, 2018)

论文标题 Understanding the majority opinion formation process in online environments: An exploratory approach to Facebook 摘要 在在线社区的社会互动过程中&#xff0c;多数观点经常被观察到&#xff0c;但很少有研究用实证数据来解决这一问题。为了确定一个合适的理论…

解决MySQL密码无法设置问题

引言 当我们第一次在Linux中安装好MySQL服务后&#xff0c;使用如下命令可以查看第一次进入MySQL的临时密码。 cat /var/log/mysqld.log | grep password但是我们如何修改它为自己想要的永久MySQL密码呢&#xff1f;在修改过程中出现的密码太短而无法设置的问题如何解决呢&am…

2024 新版 mysql 和 DBeaver 的安装教程,来啦!

mysql community 官网&#xff1a;https://dev.mysql.com/ DBeaver Community 官网&#xff1a;https://dbeaver.io/download/ 1. mysql 安装教程 进入官网 这里我选择的是社区版&#xff08;对于新手来说&#xff0c;学生是再好不过了&#xff0c;免费又开源&#xff09; 选择…

图解Transformer——注意力计算原理

文章目录 1、输入序列怎样传入注意力模块 2、进入注意力模块的矩阵的每一行&#xff0c;都是源序列中的一个词 3、每一行&#xff0c;都会经过一系列可学习的变换操作 4、如何得到注意力分数 5、Query、Key、Value的作用 6、点积&#xff1a;衡量向量之间的相似度 7、Transform…

如何使用机器学习构建自己的推荐系统?

一、说明 在广阔的电子商务领域&#xff0c;众多产品和服务都在争夺我们的注意力&#xff0c;推荐系统的作用变得至关重要。这些智能系统彻底改变了我们在线发现和接触产品的方式&#xff0c;使其成为现代电子商务平台成功的基石。 推荐系统&#xff0c;通常称为推荐引擎或简称…