我的编程空间,编程开发者的网络收藏夹
学习永远不晚

ECF机制:信号 (Signal)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

ECF机制:信号 (Signal)

   

💭 写在前面:ECF (异常控制流) 机制是存在于系统的所有层级中的,所以这一块的知识我们需要系统地去学习。前几章我们探讨过了异常 (Exceptions),由硬件触发,在内核代码中处理。讲解了进程的上下文切换 (Process Context Switch),"异常 + 内核代码"。本章我们将探讨信号 (signal),将 "异常 + 内核代码 + 用户代码" 相结合!

📜本文目录:

0x00 什么是内核(Shell)

0x01 简单的 Shell 示例

0x02 不用担心!ECF 救你来了!(ECF Comes to the Rescue!)

0x03 信号(Signals)

0x04 接收信号(Receiving a Signal)

0x05 未决信号和阻塞信号(Pending and Blocking Signals)

0x06 未决/阻断的比特(Pending/Blocked Bits)

0x07 发出信号:进程组(Sending Signals: Process Groups)

0x08 用 /bin/kill 程序发送信号

0x09 从键盘发出信号

0x0A 以程序化的方式发送信号


0x00 什么是内核(Shell)

A shell is an application program that runs other programs on behalf of the user

Shell 是一种应用程序,它代表用户运行其他程序。

sh          // 原始的 Unix shell (Stephen Bourne,AT&T贝尔实验室,1977)csh/tcsh    // BSD Unix C shellbash        // "Bourne-Again" Shell(默认的 Linux shell)

对于 Shell,其中最熟悉的莫过于 bash 了: 

Linux 进程层次结构(Linux Process Hierarchy):

我们可以使用 pstree 指令去查看进程层次结构:

$ pstree

0x01 简单的 Shell 示例

"Shell execution is a repeated sequence of read & evaluate"

Shell 的本质就是一个循环:从命令行读取一行,执行所请求的操作:

  • 内置命令(例如,退出)
  • 从文件加载和执行程序
int main(int argc, char** argv){char cmdline[MAXLINE]; while (1) {printf("> ");fgets(cmdline, MAXLINE, stdin);if (feof(stdin))exit(0);eval(cmdline);}    ...

在我的 《看表情包学Linux》专栏中有一插叙章节,就是实现一个简单的 Shell 的,感兴趣可以跟着自己实现一个简单的 Shell!

🔗 链接:【Linux】简易Shell的实现

简单的 Shell eval 函数:

void eval(char* cmdline){char* argv[MAXARGS]; char buf[MAXLINE]; int bg; pid_t pid; strcpy(buf, cmdline);bg = parseline(buf, argv);if (argv[0] == NULL)return; if (!builtin_command(argv)) {if ((pid = fork()) == 0) { execve(argv[0], argv, environ);// If we get here, execve failed.printf("%s: %s\n", argv[0], strerror(errno));exit(127);}if (!bg) {int status;if (waitpid(pid, &status, 0) < 0)unix_error("waitfg: waitpid error");}elseprintf("%d %s\n", pid, cmdline);}return;}

该示例中存在的问题:

Shell 必须设计为持续运行,不应该积累不需要的资源(内存、子进程等)。我们的示例 Shell 正确地等待并回收前台作业,但是后台作业呢?当它们终止时将会变成僵尸进程,除非 Shell 终止,否则将永远不会被回收,这将造成内存泄漏,可能会使内核耗尽内存!

0x02 不用担心!ECF 救你来了!(ECF Comes to the Rescue!)

💡 解决方案:异常控制流 (ECF)

当后台进程完成时,内核会中断常规处理以向我们发出 Warning。

我们的 Shell 程序可以收到这些事件的通知,在 Unix 中,这种 Warning 机制称为 信号 (signal)。

ECF(Event-driven Control Flow)是一种 Shell 设计模式,它通过事件驱动方式管理子进程和资源。使用 ECF 设计的 Shell 可以在不积累不必要资源的情况下持续运行,并正确地回收前台和后台作业。ECF 设计模式的关键思想是让 Shell 在执行命令之前将其设置为后台模式或前台模式。当命令运行完成后,Shell 会接收到一个事件,通知它该如何处理该作业。如果作业在前台模式下运行,Shell 会等待作业完成,然后回收资源。如果作业在后台模式下运行,Shell 会立即回收所有相关资源,不会创建僵尸进程,也不会占用不必要的内存资源。

ECF 是一种优秀的 Shell 设计模式,已经被广泛应用于各种操作系统中,例如 Linux 和 macOS 等。使用 ECF 设计的 Shell 具有稳定性高、性能优良等优点,是一种非常实用的 Shell 设计模式。

0x03 信号(Signals)

"A signal is a small message that notifies a process that an event of some type has occurred in the system"

信号是一条小消息,通知进程系统中发生了某种类型的事件。

  • 类似于异常和中断。
  • 从内核(有时是由另一个进程的请求)发送到进程,
  • 信号类型由小整数 ID(1-30)标识,
  • 信号中仅包含 ID 和已到达的事实信息。

发送信号 (Sending a Signal):

内核通过更新目标进程的上下文状态向 目标进程 (destination proces) 传递 (send) 信号。

内核发送信号有以下原因之一:

  • 内核检测到系统事件,例如除以零(SIGFPE)或子进程的终止(SIGCHLD)。
  • 另一个进程调用 kill 系统调用来显式请求内核向目标进程发送信号。

 

0x04 接收信号(Receiving a Signal)

当一个目标进程被内核强迫以某种方式对信号做出反应时,它就会收到一个信号。

一些可能的反应方式:

  • Ignore:忽略该信号(什么都不做)
  • Terminate:终止进程(可选择核心转储 core dump)
  • Catch:通过执行被称为信号处理程序的用户级函数来捕捉信号,这类似于响应异步中断时调用硬件异常处理程序:

 

0x05 未决信号和阻塞信号(Pending and Blocking Signals)

如果一个信号已发送但尚未收到,那么该信号是 未决 (pending) 的。

每种类型最多只能有一个未接信号 (pending signal),

注意!信号不在队列中:

  • 如果一个进程已经有一个类型为 k 的待定信号,那么
  • 如果一个进程已经有一个 k 类型的待处理信号,那么随后发送给该进程的 k 类型的信号将被丢弃

一个进程可以 阻塞 (Blocking) 某些信号的接收。

被阻塞的信号可以被发送,但不会被接收,直到该信号被解除阻塞为止。

有些信号不能被阻塞,比如 SIGKILL、SIGSTOP。

或只能在其他进程发送时被阻塞,比如 SIGSEGV、SIGILL等 。

当然,这并不意味着你可以向任何进程发送 SIGKILL、SIGSTOP...(权限检查是另一回事)

0x06 未决/阻断的比特(Pending/Blocked Bits)

内核在每个进程的上下文中维护未决和阻断的位向量。

未决信号集 (pending):代表未决信号的集合

  • 内核在发送 k 类型的信号时设置挂起中的位 k
  • 内核在收到 k 类型的信号时清除挂起中的位 k 

阻塞信号集 (blocking):代表阻塞信号的集合

  • 可以通过使用 sigprocmask 函数(内部调用系统)来设置和清除
  • 这个位向量也被称为 信号掩码 (signal mask)

0x07 发出信号:进程组(Sending Signals: Process Groups)

每个进程组有一个领头进程,进程组是一个或多个进程的集合,通常它们与一组作业相关联,可以接受来自同一终端的各种信号。

 

0x08 用 /bin/kill 程序发送信号

/bin /kill 程序 发送任意信号给一个 进程或进程组。

举个例子:

kill -9 24818向进程24818发送SIGKILL相当于以下内容:kill -SIGKILL 24818kill -9 -24817向进程组中的每个进程发送SIGKILL进程组中的每个进程24817

 

0x09 从键盘发出信号

输入 ctrl-c(ctrl-z)会导致内核向前台进程组中的每个工作发送 SIGINT(SIGTSTP)

  • SIGINT:默认动作是终止每个进程
  • SIGTSTP:默认动作是停止(暂停)每个进程。

Example of ctrl-c and ctrl-z:

bluefish> ./fork17Child: pid=28108 pgrp=28107Parent: pid=28107 pgrp=28107[1]+ Stopped ./fork17bluefish> ps uPID TTY ... STAT TIME COMMAND…28107 pts/8 ... T 0:01 ./fork1728108 pts/8 ... T 0:01 ./fork1728109 pts/8 ... R+ 0:00 ps wbluefish> fg./fork17bluefish> ps uPID TTY ... STAT TIME COMMAND...28110 pts/8 ... R+ 0:00 ps w

STAT(过程状态)图例:

  • 第一个字母:T:终止态、R:运行态
  • 第二个字母:+:前台进程组、更多细节见 "man ps"。

0x0A 以程序化的方式发送信号

输入 man 2 kill 查看 kill() 函数的手册:

void fork12(){pid_t pid[N];int i, child_status;for (i = 0; i < N; i++)if ((pid[i] = fork()) == 0)while (1); for (i = 0; i < N; i++) {printf("Killing process %d\n", pid[i]);kill(pid[i], SIGINT);}for (i = 0; i < N; i++) {pid_t wpid = wait(&child_status);if (WIFEXITED(child_status))printf("Child %d terminated with exit status %d\n",wpid, WEXITSTATUS(child_status));elseprintf("Child %d terminated abnormally\n", wpid);}}

🚩 运行结果如下:(当 N = 5)

 

📌 [ 笔者 ]   王亦优📃 [ 更新 ]   2022.4.4❌ [ 勘误 ]   📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,              本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料 

C++reference[EB/OL]. []. http://www.cplusplus.com/reference/.

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

来源地址:https://blog.csdn.net/weixin_50502862/article/details/129728653

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

ECF机制:信号 (Signal)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Python Signal(信号) 异步

信号的概念信号(signal)--     进程之间通讯的方式,是一种软件中断。一个进程一旦接收到信号就会打断原来的程序执行流程来处理信号。几个常用信号:SIGINT     终止进程  中断进程  (control+c)SIGTERM  
2023-01-31

iOS Mach异常和signal信号分析

这篇文章主要讲解了“iOS Mach异常和signal信号分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“iOS Mach异常和signal信号分析”吧!1. iOS Mach异常 1.1
2023-06-04

「信号机制」Python信号处理—sig

转载请注明出处:https://blog.csdn.net/jinixin/article/details/80383177 本文是信号机制三篇记录中的第二篇,介绍Python语言中负责信号处理的signal模块,并会给出一些小demo;第
2023-01-31

Linux的Signal机制是什么

这篇文章主要介绍了Linux的Signal机制是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Linux的Signal机制是什么文章都会有所收获,下面我们一起来看看吧。Signal机制在Linux中是一个非
2023-06-27

linux信号解释(3)--信号处理机制

如果需要进程捕获某个信号,并作出相应的处理,就需要注册信号处理函数(其实就是内核里需要识别信号函数,类似C语言里的include某函数库)。    处理信号就类似软中断,内核为每个进程准备了一段信号向量表,记录信号的处理机制。当某个信号发生
2023-01-31

浅谈Linux信号机制

目录一、信号列表1.1、实时信号非实时信号1.2、信号状态1.3、信号生命周期1.4、信号的执行和注销二、信号掩码和信号处理函数的继承2.1、信号处理函数的继承2.2、信号掩码的继承2.3、sigwait 与多线程2.4、多进程下的信号三、
2022-06-03

Django的信号机制详解

Django提供一种信号机制。其实就是观察者模式,又叫发布-订阅(Publish/Subscribe) 。当发生一些动作的时候,发出信号,然后监听了这个信号的函数就会执行。 Django内置了一些信号,比如:django.db.models
2022-06-04

Linux信号机制是什么

Linux信号机制是一种用于进程间通信的机制,用于在进程之间传递异步事件的通知。当某个进程接收到一个信号时,它可以选择忽略、捕获或默认处理该信号。信号可以由操作系统、其他进程或进程自身发送。常见的信号包括SIGINT(键盘中断信号)、SIG
2023-08-12

深入理解Django的信号机制

本文主要介绍了深入理解Django的信号机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-02-08

Linux信号机制的基础知识介绍

本篇内容介绍了“Linux信号机制的基础知识介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Linux进程基础一文中已经提到,Linux以
2023-06-13

qt信号与槽机制的原理是什么

Qt的信号与槽(Signals and Slots)机制是Qt的一个重要特性,用于处理对象之间的事件通信。它的原理基于C++的特性和Qt的元对象系统。在使用信号与槽机制前,首先需要定义一个信号和一个槽。信号是一个声明,它是一个在特定事件发生
2023-09-27

详解Python的Flask框架中的signals信号机制

Flask 提供了信号(Signals)功能,是一种消息分发机制。类似于钩子(Hooks)。使用信号功能可以降低程序的耦合,分解复杂的业务模型。例如在更新了产品数据后,可以发送一个信号。当有需要对产品数据进行处理的功能时,就可以捕获信号进行
2022-06-04

计算机网络中哪些信号属于模拟信号

这篇文章给大家分享的是有关计算机网络中哪些信号属于模拟信号的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。实际生产生活中的各种物理量,如摄相机摄下的图像、录音机录下的声音、车间控制室所记录的压力、流速、转速、湿度等
2023-06-20

详解java中的互斥锁信号量和多线程等待机制

互斥锁和信号量都是操作系统中为并发编程设计基本概念,互斥锁和信号量的概念上的不同在于,对于同一个资源,互斥锁只有0和1 的概念,而信号量不止于此。也就是说,信号量可以使资源同时被多个线程访问,而互斥锁同时只能被一个线程访问互斥锁在java中
2023-05-31

Golang协程的通信机制

go 协程通过通道(发送和接收数据)和同步原语(管理对共享资源的访问)进行通信。通道用于通过发送和接收操作在协程之间传输数据。同步原语包括互斥锁(控制对共享资源的访问)、条件变量(等待条件满足后继续执行)和一次性信号(确保操作只执行一次)。
Golang协程的通信机制
2024-04-16

android IPC之binder通信机制

Binder通信机制说来简单,但是在使用的过程的遇到了一些问题,最后终于解决了,在这总结一下,一并分享给大家: 1、要使用Binder通信,首先要定义接口,然后实现服务端BnInterface***和客户端BpInterface***,说到
2022-06-06

编程热搜

目录