Linux —— 信号初识

Linux —— 信号初识

  • 什么是信号
  • 测试几个信号
  • signal函数
      • 函数原型
      • 参数说明
      • 返回值
      • 注意事项
      • 示例
  • 后台程序
  • 前台转后台
  • 检测输入
    • 中断向量表

我们今天来继续学习Linux的内容,今天我们要了解的是Linux操作系统中的信号:

什么是信号

信号是操作系统内核与进程之间进行异步通信的一种机制,它允许系统或进程向另一个进程发送简短的控制信息,以通知进程有特定的事件发生或要求进程采取某种行动信号是软中断,意味着它们是由软件生成的,并非直接由硬件触发。以下是Linux信号的一些关键概念:

  1. 信号的来源
  • 内核: 内核可以因各种事件自动发送信号给进程,如进程试图执行非法指令、访问无效内存地址、用户按下Ctrl+C终止进程等。
  • 进程: 进程可以通过系统调用kill()向自己或其他进程发送信号。
  • 终端: 用户在终端上执行操作,如按下Ctrl+C或Ctrl+Z,也会导致内核向前台进程发送信号SIGINT(中断)或SIGTSTP(停止)。
  • 硬件: 尽管信号主要由软件生成,某些硬件异常(如断电)也可以间接触发信号。
  1. 信号的目的

    • 通知事件: 信号用来告知进程某些状态变化或事件的发生,如子进程结束、定时器到期等。
    • 控制进程行为: 信号可以请求进程采取特定动作,比如终止、暂停、继续执行或调整优先级等。
  2. 信号的处理方式

    • 默认动作: 每个信号都有一个默认的行为,如SIGINT通常会导致进程终止。
    • 忽略: 进程可以选择忽略某些信号,即不对信号做出反应。
    • 自定义处理: 进程可以定义自己的信号处理函数,通过signal()sigaction()系统调用来指定信号的处理方式。
  3. 信号掩码和阻塞

    • 进程可以设置信号掩码来暂时阻止(阻塞)某些信号的传递,直到进程解除阻塞。
  4. 常见信号:

    • SIGINT (2): 当用户按下Ctrl+C时发送,通常用于中断进程。
    • SIGTERM (15): 用来请求进程正常终止。
    • SIGHUP (1): 挂起信号,通常在终端挂断时发送给与之相连的进程。
    • SIGKILL (9): 不能被忽略或阻塞,用于强制结束进程。
    • SIGSTOP (19): 停止进程,不能被捕获或忽略。

我们可以用kill -l来查看所有的信号:
在这里插入图片描述这里我们来看几个比较重要的:

  1. SIGHUP (1) - 挂起信号
    当终端线路挂断时发送给控制终端所属的进程组。通常用于通知进程配置文件可能已更改,需要重新加载。守护进程经常捕获此信号以实现优雅重启。
  2. SIGINT (2) - 中断信号
    当用户按下Ctrl+C时产生,请求进程中断当前操作并退出。默认情况下会导致进程终止。
  3. SIGQUIT (3) - 退出信号
    类似于SIGINT,但通常伴随着生成核心转储(core dump),用于调试。在终端下,通常是Ctrl+\ 发送此信号。
  4. SIGKILL (9) - 强制终止信号
    不能被捕获、忽略或阻塞,用于立即结束进程。当其他手段无法终止进程时使用。
  5. SIGTERM (15) - 终止信号
    一种温和的请求进程终止的信号,进程可以注册处理函数来自定义清理操作。是结束进程的首选方式。
  6. SIGSEGV (11) - 段错误信号
    当进程尝试访问不允许其访问的内存段时发送,通常指示程序中的内存访问错误。
  7. SIGALRM (14) - 闹钟信号
    与定时器相关联,当设定的定时器超时时发送。常用于实现定时任务或超时检测。
  8. SIGCHLD (17) - 子进程状态改变信号
    父进程接收到此信号,表明其子进程已经终止或停止。用于监控子进程状态并回收资源。
  9. SIGSTOP (19) - 停止信号
    强制进程停止执行。不能被忽略或被捕获,类似于暂停键,常用于调试。
  10. SIGCONT (18) - 继续执行信号
    使被SIGSTOP停止的进程恢复执行。通常配合SIGSTOP使用,用于控制进程的暂停与继续。
  11. SIGUSR1 和 SIGUSR2 (10, 31) - 用户自定义信号
    这两个信号留给用户自定义用途,可以用于进程间通信或触发特定的处理逻辑。

这些信号在系统编程中扮演着关键角色,理解它们有助于编写更稳定、可维护的代码,尤其是在需要处理进程间通信、异常情况或实现特定行为的场景中。

测试几个信号

我们创建一个cc文件:

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

int main()
{
    while(true)
    {
        cout << "running process ..." << endl;
        sleep(1);
    }
}

我们用g++编译,运行一下:
在这里插入图片描述
我们**Ctrl+C**可以终止进程:
在这里插入图片描述
同时,我们重新运行,另开一个窗口:
在这里插入图片描述
我们也可以使用信号3:
在这里插入图片描述或者Ctrl + \
在这里插入图片描述

signal函数

为了验证Ctrl + C信号2是否是同一件事情,我们可以利用signal来验证:
在这里插入图片描述
在Linux中,signal()函数是一个用于处理信号的关键函数,它允许进程对操作系统发送的各种信号做出响应。信号是Linux和其他类UNIX系统中一种进程间通信(IPC)的方式,用于通知进程发生了某种事件,如用户请求终止进程、硬件故障、定时器到期等。下面是对signal()函数的基本介绍和使用方法:

函数原型

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

参数说明

  • signum:要处理的信号编号,比如SIGINTSIGTERM等。
  • handler:当指定的信号发生时,系统调用的处理函数。它可以是以下几种:
  • SIG_DFL(默认处理):恢复信号的默认行为,如终止进程。
  • SIG_IGN(忽略信号):忽略此信号。
  • 自定义函数:一个用户自定义的函数指针,该函数原型通常为void function(int signum),其中signum是接收到的信号编号。

返回值

  • 如果成功,signal()返回之前为该信号设置的处理函数的地址。如果之前没有设置处理函数(即使用默认处理),则返回SIG_DFL;如果之前忽略了该信号,则返回SIG_IGN
  • 在某些系统上,如果提供了无效的signumhandler不是SIG_DFLSIG_IGN或有效的函数指针,signal()可能会失败并返回SIG_ERR

注意事项

  • signal()的行为在不同版本的POSIX标准和不同的Unix系统之间有所不同,特别是关于信号处理函数的重新安装性。某些系统遵循传统BSD语义,而另一些则遵循POSIX.1-1990或POSIX.1-2001语义。
  • 对于实时信号(如SIGRTMINSIGRTMAX之间的信号),推荐使用sigaction()函数来代替signal(),因为它提供了更精细的控制和更一致的跨平台行为。

示例

下面是一个简单的示例,展示了如何使用signal()来捕获SIGINT(通常是Ctrl+C)信号,并忽略它,使得进程在接收到此信号时终止:

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

void signal_hander(int signum)
{
    printf("Caught SIGINT, but ignoring...\n");
    exit(0);
}

int main()
{
    signal(2,signal_hander);

    while(true)
    {
        cout << "process running ..." << endl;
        sleep(1);
    }
}

我们重新编译一下,运行一下:
在这里插入图片描述
我们又用信号2来试验一下:
在这里插入图片描述

后台程序

大家发现没有,如果我们运行process,此时我们输入命令行是没有用的:
在这里插入图片描述
如果我们不想这样,我们可以把它放在后台,只要后面带一个&就行:
在这里插入图片描述
此时再用Ctrl + C是无法结束后台进程的:
在这里插入图片描述此时,只用两种办法,第一种用信号,或者pkill + 进程名
在这里插入图片描述第二种,让后台程序回到前台:
在这里插入图片描述后台程序一运行时,会有一个编号,此时fg + 编号可以让后台程序回到前台:
在这里插入图片描述回到前台,就可以使用Ctrl + C。

前台转后台

快捷键Ctrl + Z可以让前台程序停止,转向后台:
在这里插入图片描述
命令行jobs可以看到所有的后台程序:
在这里插入图片描述如果我们想开启,我们使用bg + 序号
在这里插入图片描述就可以让程序在后台运行。

检测输入

我们输入Ctrl + Z等这些组合键,操作系统识别键盘输入的过程大致如下:

  1. 硬件层面:现代键盘通常通过USB或无线连接与计算机通信,以前的老式键盘可能使用PS/2接口。当用户按下键盘上的一个键时,键盘硬件会生成一个电信号,这个信号代表了特定按键的扫描码。
  2. 中断请求:键盘控制器将这个信号转换成键盘中断请求,并发送给计算机的中断控制器。中断是CPU对外部事件的一种快速响应机制,它能够暂停当前正在执行的任务,转而处理紧急的外部事件。
  3. 中断处理:CPU接收到中断请求后,会保存当前任务的状态(如程序计数器等),然后跳转到中断处理程序的入口地址执行。对于键盘中断,这个处理程序通常是操作系统的一部分。
  4. 读取扫描码:在中断处理程序中,操作系统会读取键盘控制器中的数据寄存器,获取按键的扫描码。扫描码是一个独一无二的标识,对应于键盘上的每一个键。
  5. 转换为ASCII码或虚拟键码:操作系统接着会将扫描码转换为操作系统内部可以理解的形式,如ASCII码(用于文本字符)或虚拟键码(用于功能键和特殊键)。这个过程可能涉及查表或其他映射机制。
  6. 事件队列与应用程序:转换后的字符或按键信息会被封装成一个事件,并放入系统的消息队列中。等待处理的事件包括按键按下和释放等。当应用程序(如文本编辑器)调用相应的API(如Windows的 GetMessage 或 Linux 的 select/poll)检查消息队列时,操作系统会将这些事件传递给应用程序。
  7. 应用程序响应:应用程序根据接收到的键盘事件执行相应的操作,比如在文本框中显示字符或响应快捷键命令。
  8. 释放中断:一旦中断处理完成,操作系统会恢复之前被中断的任务状态,继续执行。

整个过程确保了用户在键盘上的输入能够迅速、准确地被操作系统捕捉并传递给正在运行的应用程序。

中断向量表

这里面还有一个中间向量表:

中断向量表是计算机系统中一个非常关键的数据结构,它存储了所有中断服务程序(ISR,Interrupt Service Routines)的入口地址。这些中断服务程序负责处理各种硬件或软件触发的中断事件。以下是中断向量表的一些关键特性与作用:

  1. 内存中的固定位置:中断向量表通常位于内存中的一个固定位置,使得CPU在任何时候都能迅速访问到它。在某些体系结构中,比如x86,中断向量表可能位于低地址区域,便于快速响应中断。
  2. 条目结构:表中的每个条目对应一个中断类型或中断源,条目内包含的是相应中断服务程序的起始地址(或者是一个跳转指令,间接指向实际的中断处理程序)。每个条目可能占用2个、4个或更多字节,具体取决于处理器架构。
  3. 中断类型号与向量地址:中断类型号是一个标识特定中断的数字。在一些系统中,中断类型号乘以某个固定值(如4)可以得到该中断向量在中断向量表中的地址,这样CPU就能根据中断类型快速定位到正确的中断处理程序。
  4. 中断响应过程:当CPU检测到一个中断请求时,它会立即停止当前的任务执行,保存现场(即当前的处理器状态),然后根据中断类型号查询中断向量表,获取中断服务程序的入口地址,并跳转到该地址开始执行中断处理程序。
  5. 复位与初始化:在系统启动或复位时,中断向量表的基地址会被初始化到一个预定义的位置。某些处理器(如ARM Cortex-M系列)允许通过VTOR(向量表偏移寄存器)来重新定位中断向量表的位置,以适应不同的系统配置需求。
  6. 固定与动态分配:在一些简单的系统中,中断向量表可能是静态定义的,而在更复杂的系统中,部分中断向量可能支持动态分配,允许操作系统或固件在运行时安装或更改中断服务例程的地址。

中断向量表的设计和管理是确保系统能够高效、可靠地响应各种内外部事件的基础,对于维持系统的实时性和稳定性至关重要。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hash Function(fft)

链接&#xff1a;H-Hash Function_2024牛客五一集训派对day4 (nowcoder.com) 题意&#xff1a;给定一个序列&#xff0c;求使得任意两数的hash值不同的最小模数&#xff1b; 分析&#xff1a;ab(mod seed) |a-b|%seed0; 也就是说seed不能是任意两数差的因子。 如果暴力求解…

【大麦小米学量化】使用Python读写通达信自选股(含代码转换及完整源代码),想要通过通达信自选股实现量化自动关联交易的有福了

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、通达信自选股文件所在位置二、通达信自选股文件数据结构三、使用Python读写通达信自选股文件&#xff08;附完整源代码&#xff09;1. 切换目录路径2. 将li…

4月30日重庆某厂酸碱管道整改工作汇报-智渍洁

时间:2024.4.30 地点:******老厂酸碱管道整改 施工人员:王成、汪勇、郭建华 事项:老厂酸碱管道更换 完成进度100%酸碱管道支架以添加完成&#xff01;碱管道保温已完成&#xff01; 1吨桶未完成2主水管漏水未处理&#xff0c;3酸 水泵需更换全新4室内少许添加活未完成。 4月30日…

精析React与Vue架构异同及React核心技术——涵盖JSX、组件、Props、State、生命周期与16.8版后Hooks深化解析

React&#xff0c;Facebook开源的JavaScript库&#xff0c;用于构建高性能用户界面。通过组件化开发&#xff0c;它使UI的构建、维护变得简单高效。利用虚拟DOM实现快速渲染更新&#xff0c;适用于单页应用、移动应用&#xff08;React Native&#xff09;。React极大推动了现代…

2-qt之信号与槽-简单实例讲解

前言、因实践课程讲解需求&#xff0c;简单介绍下qt的信号与槽。 一、了解信号与槽 怎样使用信号与槽&#xff1f; 概览 还记得 X-Window 上老旧的回调函数系统吗&#xff1f;通常它不是类型安全的并且很复杂。&#xff08;使用&#xff09;它&#xff08;会&#xff09;有很多…

Redis-分片机制

概述 业务需要&#xff1a;由于单台redis内存容量是有限的&#xff0c;无法实现海量的数据实现缓存存储 概念&#xff1a;由多个redis节点协助工作的机制就是redis的分片机制 作用&#xff1a;为了实现redis扩容 特点&#xff1a;分片机制把该机制中包含的多台redis缓存服务…

RK3568 学习笔记 : u-boot 下通过设置 env ethact 设置当前工作的以太网设备

前言 正点原子 &#xff1a;RK3568 开发板 atompi-ca1 默认有两个网口&#xff0c;通过 u-boot mii 命令&#xff0c;可以查看 网口信息 > mii device MII devices: ethernetfe010000 ethernetfe2a0000 Current device: ethernetfe010000u-boot 下的以太网&#xff0c;不同…

如何为 Nestjs 编写单元测试和 E2E 测试

前言 最近在给一个 nestjs 项目写单元测试&#xff08;Unit Testing&#xff09;和 e2e 测试&#xff08;End-to-End Testing&#xff0c;端到端测试&#xff0c;简称 e2e 测试&#xff09;&#xff0c;这是我第一次给后端项目写测试&#xff0c;发现和之前给前端项目写测试还…

UDP 的报文结构

一.UDP的报文结构 1.UDP的简单介绍 UDP是传输层协议&#xff0c;它是无连接,不可靠传输,面向数据报,全双工 1.无连接&#xff1a;UDP是一种无连接的传输协议&#xff0c;通信双方不需要在发送数据之前建立连接。相比之下&#xff0c;TCP是面向连接的协议&#xff0c;在传输数…

【除了协程还有哪些方式可以实现异步编程】

在Unity中&#xff0c;除了使用协程实现异步编程外&#xff0c;还有以下几种方法&#xff1a; 异步加载资源&#xff1a; 使用UnityWebRequest类进行异步加载资源&#xff0c;这在加载网络资源或动态加载资源时非常有用。 using UnityEngine; using UnityEngine.Networking;…

【Linux】进程控制 之 进程创建 进程终止 进程等待 进程替换

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…

每日一博 - 闲聊架构设计中的多级缓存设计

文章目录 方法论概述客户端缓存应用层缓存服务层缓存缓存设计的注意事项总结 思维导图戳这里 方法论概述 从客户端到服务层&#xff0c;缓存的应用广泛而重要。通过合理的缓存设计&#xff0c;能够有效地提高系统的性能并降低延迟。 客户端缓存 在客户端层面&#xff0c;浏览…

LLM2Vec介绍和将Llama 3转换为嵌入模型代码示例

嵌入模型是大型语言模型检索增强生成(RAG)的关键组成部分。它们对知识库和用户编写的查询进行编码。 使用与LLM相同领域的训练或微调的嵌入模型可以显著改进RAG系统。然而&#xff0c;寻找或训练这样的嵌入模型往往是一项困难的任务&#xff0c;因为领域内的数据通常是稀缺的。…

基于AT89C51单片机的温度上下限自动控制检报警设计

点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/89247694?spm=1001.2014.3001.5501 C 源码+仿真图+毕业设计+实物制作步骤+06 题 目 基于单片机的温度检测调节系统设计 姓 名 学 号 专业班级 指导教师 年 月 日 任务书 …

Nginx 从入门到实践(2)——Rewrite重写

Nginx Rewrite Rewrite重写 Nginx Rewriteurl组成说明Rewrite基本概述Rewrite使⽤场景rewrite优点 Rewrite配置语法location匹配概述 if指令if 判断指令语法nginx以及if 判断可使用的全局变量 set命令return指令 url组成说明 https://cn.bing.com/search?qNginxRewrite&P…

udp/tcp回显网络编程

udp DatagramSocket 用于接收和发送udp数据报 构造方法&#xff1a; DatagramSocket():创建一个UDP数据报套接字的Socket&#xff0c;绑定到本地上 一个随机可用端口上&#xff0c;一般用于客户端DatagramSocket(int port):创建一个UDP数据报套接字的Socket&#xff0c;绑定到…

Proxmox VE 8 用SDN隔离用户网络

作者&#xff1a;田逸&#xff08;formyz&#xff09; 最新发布的Proxmox VE&#xff08;以下简称PVE&#xff09; 8在Web管理后台集成了易于操作的SDN&#xff08;软件定义网络&#xff09;功能插件&#xff0c;其实质是对不同的PVE用户指定不同的网络&#xff0c;进行逻辑隔离…

将要上市的自动驾驶新书《自动驾驶系统开发》中摘录各章片段 4

第十三章 车联网 数字化设备正变得越来越普遍并且相互联系。这些设备向数字生态系统智能部分的演进创造了迄今为止尚未解决安全问题的新颖应用。一个特定的例子是车辆&#xff0c;随着车辆从简单的交通方式发展到具有新的感知和通讯功能的智能实体&#xff0c;就成为智能城市的…

屏蔽罩材质和厚度对屏蔽效能的影响

​ 一&#xff0e;屏蔽效能的影响因素 屏蔽效能的影响因素主要有两个方面&#xff1a;屏蔽材料的特性和厚度&#xff1b;如下图所示&#xff0c;电磁波经过不同媒介时&#xff0c;会在分界面形成反射&#xff0c;穿过界面的电磁波一部分被反射回去&#xff0c;这部分能量损失…

偶然发现了Python的一个BUG。。。

一般情况下&#xff0c;dict(id1, **{id: 1})这句代码应该报TypeError。但如果在捕获了其他异常的情况下&#xff0c;再来执行这句代码&#xff0c;却是会报KeyError&#xff0c;如下图&#xff1a; Python3.10和Python3.9也能复现该情况&#xff0c;正当我摩拳踩掌&#xff0c…
最新文章