ECF机制:信号 (Signal)

这篇具有很好参考价值的文章主要介绍了ECF机制:信号 (Signal)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

   ECF机制:信号 (Signal)

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 shell
bash        // "Bourne-Again" Shell(默认的 Linux shell)

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

ECF机制:信号 (Signal)

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

ECF机制:信号 (Signal)

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

$ pstree

ECF机制:信号 (Signal)

0x01 简单的 Shell 示例

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

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

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

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

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

简单的 Shell eval 函数:

void eval(char* cmdline)
{
	char* argv[MAXARGS]; /* Argument list execve() */
	char buf[MAXLINE]; /* Holds modified command line */
	int bg; /* Should the job run in bg or fg? */
	pid_t pid; /* Process id */
	strcpy(buf, cmdline);
	bg = parseline(buf, argv);
	if (argv[0] == NULL)
		return; /* Ignore empty lines */
	if (!builtin_command(argv)) {
		if ((pid = fork()) == 0) { /* Child runs user job */
			execve(argv[0], argv, environ);
			// If we get here, execve failed.
			printf("%s: %s\n", argv[0], strerror(errno));
			exit(127);
		}
		/* Parent waits for foreground job to terminate */
		if (!bg) {
			int status;
			if (waitpid(pid, &status, 0) < 0)
				unix_error("waitfg: waitpid error");
		}
		else
			printf("%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 和已到达的事实信息。

ECF机制:信号 (Signal)

发送信号 (Sending a Signal):

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

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

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

ECF机制:信号 (Signal)

 

0x04 接收信号(Receiving a Signal)

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

一些可能的反应方式:

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

ECF机制:信号 (Signal)

 

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

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

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

注意!信号不在队列中:

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

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

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

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

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

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

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

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

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

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

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

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

ECF机制:信号 (Signal)

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

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

ECF机制:信号 (Signal)

 

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

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

ECF机制:信号 (Signal)

举个例子:

kill -9 24818
向进程24818发送SIGKILL
相当于以下内容:
kill -SIGKILL 24818

kill -9 -24817
向进程组中的每个进程发送SIGKILL
进程组中的每个进程24817

 

0x09 从键盘发出信号

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

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

ECF机制:信号 (Signal)

Example of ctrl-c and ctrl-z:

bluefish> ./fork17
Child: pid=28108 pgrp=28107
Parent: pid=28107 pgrp=28107
<types ctrl-z>
[1]+ Stopped ./fork17
bluefish> ps u
PID TTY ... STAT TIME COMMAND
…
28107 pts/8 ... T 0:01 ./fork17
28108 pts/8 ... T 0:01 ./fork17
28109 pts/8 ... R+ 0:00 ps w
bluefish> fg
./fork17
<types ctrl-c>
bluefish> ps u
PID TTY ... STAT TIME COMMAND
...
28110 pts/8 ... R+ 0:00 ps w

STAT(过程状态)图例:

  • 第一个字母::终止态、:运行态
  • 第二个字母:+:前台进程组、更多细节见 "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); /* Child: Infinite Loop */
	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));
		else
			printf("Child %d terminated abnormally\n", wpid);
	}
}

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

ECF机制:信号 (Signal)

 

ECF机制:信号 (Signal)

📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   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://www.toymoban.com/news/detail-402041.html

到了这里,关于ECF机制:信号 (Signal)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • os.signal golang中的信号处理

    在程序进行重启等操作时,我们需要让程序完成一些重要的任务之后,优雅地退出,Golang为我们提供了signal包,实现信号处理机制,允许Go 程序与传入的信号进行交互。 Go语言标准库中signal包的核心功能主要包含以下几个方面: 1. signal处理的全局状态管理 通过handlers结构体跟

    2024年02月15日
    浏览(52)
  • 仿真与测试:通过Signal Builder模块生成输入信号

    本文研究通过Signal Builder模块生成输入信号的方法。 在汽车的电控软件开发中,经常会在Simulink模型内部进行单元测试。单元测试的本质就是对某一单元(可以是模型级别或者子系统级别)给一组特定的输入信号,通过Simulink仿真得到一组对应的输出信号,然后再与自己期望的

    2023年04月12日
    浏览(34)
  • linux 信号原理 信号处理设置signal, 信号发送kill,信号等待sigsuspend,信号阻塞sigprocmask,一网打尽信号使用

    ​ 专栏内容 : postgresql内核源码分析 手写数据库toadb 并发编程 个人主页 :我的主页 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物. ================================ 信号是一种软中断的方式,让进程陷入中断处理调用中; linux 下信号也是一种进程间通信的手段;进

    2024年02月13日
    浏览(41)
  • Linux系统调用之signal,sigaction函数 (捕捉指定的信号,自定义/默认处理该信号)

    如果,想要深入的学习Linux系统调用中的signal,sigaction函数,还是需要去自己阅读Linux系统中的帮助文档。 具体输入命令: 即可查阅到完整的资料信息。 signal()是一个用于处理Unix和类Unix操作系统(如Linux)中的信号的系统调用。信号是用于在进程间或者进程内部传递通知或者

    2024年02月02日
    浏览(46)
  • 【Linux】进程信号篇Ⅰ:信号的产生(signal、kill、raise、abort、alarm)、信号的保存(core dump)

    kill -l 可以查看所有信号: 其中,前面的数字就是信号,后面的大写英文就是信号名称,实际就是宏。 我们需要关注的是 1~31 号普通信号,关注他们有没有产生(可以用 0 或者 1 表示)。 所以,进程的 pcb 中,需要对产生的信号先用 位图 保存起来,再按照一定的顺序去处理

    2024年01月25日
    浏览(39)
  • Linux进程间通信 - 信号(signal) 与 管道(pipe) 与 消息队列

    什么是进程间通信,就是进程与进程之间进行通信,互相发送消息;可以通过 信号 或者 管道 或者 消息队列 或者 信号量 去通信! 目录 一、信号 1. 信号简介  2. 都有那些信号? 3. 注册信号的函数 1). signal 2). sigaction (项目中强烈推荐使用) 4. 信号发送 1). kill 函数 2). alarm 函

    2024年02月01日
    浏览(40)
  • FPGA_Signal TapII 逻辑分析仪 在线信号波形抓取

    由于一些工程的仿真文件不易产生,所以我们可以利用 quartus 软件自带的 SignalTap 工具对波形进行抓取 对各个信号进行分析处理,让电子器件与FPGA进行正常通讯工作,也验证所绘制的波形图是否一致。 1、 首先确保你的工程已经完成(包括引脚配置,I/O 设置等),然后编译

    2024年02月02日
    浏览(43)
  • PyQt5 垂直滚动条verticalScrollBar操作信号Signal及获取当前值

    1.使用sliderMoved: Tips:使用sliderMoved在鼠标拖动滚动条时可以发出信号激活回调函数,但使用 鼠标滚轮 移动时没有反应 2.使用valueChanged: valueChanged在 拖动和滚轮 移动时都可激活回调函数 获取滚动条的当前值: 注意: self.verticalScrollBar .value() 也可获得当前值,但在滑动时会

    2024年02月11日
    浏览(70)
  • 【TCP/IP】多进程服务器的实现(进阶) - 信号处理及signal、sigaction函数

    目录 信号 signal函数 sigaction函数 用信号来处理僵尸进程          在之前我们学习了如何处理“僵尸进程”,不过可能也会有疑问:调用wait和waitpid函数时我们关注的始终是在子进程上,那么在父进程上如何实现对子进程的管控呢?为此,我们引入一个概念——信号处理。

    2024年02月08日
    浏览(62)
  • Linux中的alarm和setitimer定时器函数以及信号捕捉函数signal和sigaction

    通过 man 2 alarm 查看alarm函数的详细信息。 功能: 设置定时器(闹钟)。从调用这个函数开始进行倒计时,倒计时结束之后(变为0),函数会给当前的进程发送 SIGALARM 信号。 参数: seconds: 倒计时的时长,单位:秒。如果参数为0,定时器无效(不进行倒计时,不发信号)。取

    2024年02月03日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包