20191330雷清逸 学习笔记9

20191330 雷清逸 学习笔记9(第六章)

一、知识点归纳以及自己最有收获的内容

知识点归纳

摘要

本章讲述了信号和信号处理;介绍了信号和中断的统一处理,有助于从正确的角度看待信号;将信号视为进程中断,将进程从正常执行转移到信号处理;解释了信号的来源,包括来自硬件、异常和其他进程的信号;然后举例说明了信号在Unix/Linux中的常见用法;详细解释了Unix/Linux中的信号处理,包括信号类型、信号向量位、信号掩码位、进程PROC结构体中的信号处理程序以及信号处理步骤;用示例展示了如何安装信号捕捉器来处理程序异常,如用户模式下的段错误;还讨论了将信号用作进程间通信(IPC)机制的适用性。

最有收获的部分

  • 有关信号和中断的相关知识
  • 有关信号示例的相关知识
  • 有关信号处理的相关知识
  • 有关信号处理步骤的相关知识
  • 有关信号与异常的相关知识
  • 有关IPC的相关知识

信号和中断

  • “中断”是从I/O设备或协处理设备发送到CPU的外部请求,它将CPU从正常执行转移到中断处理。

(1)首先,我们将进程的概念概括为:一个”进程“(引号中)就是一系列活动。广义的”进程“包括:

  • 从事日常事物的人。
  • 在用户模式或内核模式下运行的Unix/Linux进程。
  • 执行机器指令的CPU。

(2)“中断”是发送给“进程”的事件,它将“进程”从正常活动转移到其他活动,称为”中断处理“。”进程“可在完成”中断“处理后恢复正常活动。

(3)“中断”一词可应用于任何“进程”,并不仅限于计算机中的CPU。

1.人员中断

当某人在办公室读书、评分时,可能发生一些事件,例如:

  • 大楼着火
  • 电话响了
  • 有人敲门
  • ……

所有这些事件都叫做人员中断,因为他们把人从正常活动转向“应对或处理中断”。处理完中断后,此人可以继续此前的活动(如果这个人还活着而且仍然急的自己之前的活动)。根据来源,我们可以将中断分为以下几种:

  • 来自硬件的中断:大楼着火等等。
  • 来自其他人的中断:电话响了等等。
  • 自己造成的中断:切到手指等等。

还可以根据紧急程度将中断分为:

  • 不可屏蔽(NMI):大楼着火等等。
  • 可屏蔽:有人敲门等。

2.进程中断

这类中断是发送给进程的中断。

这类中断可能会有三个来源:

  • 来自硬件的中断:终端、间隔定时器的“Ctrl+C”组合键等。
  • 来自其他进程的中断:kill(pid,SIG#)、death_of_child等。
  • 自己造成的中断:除以0、无线地址等。

与多种类的人员中断不同,我们始终可限制在一个进程中的中断的数量。Unix/Linux中的进程中断称为信号,编号为1到31。

与人员类似,进程也可屏蔽某些类型的信号,以推迟处理。必要时,进程还可能会修改信号动作函数。

3.硬件中断

这类中断是发送给处理器或CPU的信号。它们也有三个可能的来源:

  • 来自硬件的中断:定时器等等。
  • 来自其他处理器的中断:FFP、DMA、多处理器系统中的其他CPU。
  • 自己造成的中断:除以0、保护错误等等。

每个中断都有唯一的中断向量号。动作函数是中断向量表中的中断处理程序。

4.进程的陷阱错误

进程可能会自己造成中断。这些中断是由被CPU识别为异常的错误引起的。

当进程遇到异常时,它会陷入操作系统内核,将陷阱原因转换为信号编号,并将信号发送给自己。

当进程在用户模式下发生异常,进程会默认终止,并使用一个可选的内存转储进行调试。

当进程在内核模式下发生陷阱,原因一定是硬件错误,或者很可能是内核代码中的漏洞,在这种情况下,内核无法处理。

Unix/Linux信号示例

(1)“Ctrl+C”组合键通常会导致当前运行的进程终止。原因如下:

“Ctrl+C”组合键会生成一个键盘硬件中断。键盘中断处理程序将“Ctrl+C”组合键转换为SIGINT(2)信号,发送给终端上的所有进程,并唤醒等待键盘输入的进程。

(2)用户可使用nohup.a.out&命令在后台运行一个程序。即使在用户退出后,进程仍将继续运行。nohup命令会使sh像往常一样复刻子进程来执行程序,但是子进程会忽略SIGHUP(1)信号。当用户退出时,sh会向与终端有关的所有进程发送一个SIGHUO信号。后台进程在接收到这一信号后,会忽略它并继续运行。

(3)也许用户几天后再次登录,发现后台进程扔在运行,可以通过命令:

kill pid (or kill -s 9 pid)

杀死该进程。

执行杀死的进程向pid标识的目标进程发送一个SIGTERM(15)信号,请求它死亡。目标进程将会遵从请求并终止。如果进程选择忽略SIGTERM信号,它可能拒绝死亡,在这种情况下,我们可以使用:

kill -s 9 pid

在最初的Unix中,只有9个信号。9号信号被保留为终止进程的终极手段。

Unix/Linux中的信号处理

信号类型

Unix/Linux支持31种不同的信号,每种信号在signal.h文件中都有定义:

1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP

6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1

11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM

16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP

21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ

26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR

31) SIGSYS

每种信号都有一个符号名。

信号处理步骤

(1)当某进程处于内核模式时,会检查信号并处理未完成的信号。

(2)重置用户安装的信号捕捉函数:用户安装的陷阱相关信号捕捉函数用于处理用户代码中的陷阱错误。

(3)信号和唤醒:在Unix/Linux内核中有两种SLEEP进程:深度休眠进程和浅度休眠进程。

信号与异常

  • 作为进程异常的统一处理办法。
  • 让进程通过预先安装的信号捕捉函数处理用户模式下的程序错误。
  • 在特殊情况下,它会让某个进程通过信号杀死另一个进程。

二、问题与解决思路

问题:信号处理函数运行的过程是怎么样的?

答:(1)注册信号处理函数

信号的处理是由内核来代理的,首先程序通过sigal或sigaction函数为每个信号注册处理函数,而内核中维护一张信号向量表,对应信号处理机制。这样,在信号在进程中注销完毕之后,会调用相应的处理函数进行处理。

(2)信号的检测与响应时机

在系统调用或中断返回用户态的前夕,内核会检查未决信号集,进行相应的信号处理。

(3)处理过程:

程序运行在用户态时->进程由于系统调用或中断进入内核->转向用户态执行信号处理函数->信号处理函数完毕后进入内核->返回用户态继续执行程序

首先程序执行在用户态,在进程陷入内核并从内核返回的前夕,会去检查有没有信号没有被处理,如果有且没有被阻塞就会调用相应的信号处理程序去处理。首先,内核在用户栈上创建一个层,该层中将返回地址设置成信号处理函数的地址,这样,从内核返回用户态时,就会执行这个信号处理函数。当信号处理函数执行完,会再次进入内核,主要是检测有没有信号没有处理,以及恢复原先程序中断执行点,恢复内核栈等工作,这样,当从内核返回后便返回到原先程序执行的地方了。

三、实践内容与截图,代码链接

实践内容为书本P180-191示例6.1

  • 示例6.1(sigaction函数的使用示例)
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handler(int sig, siginfo_t *siginfo, void *context)
{
	printf("handler:sig=%d from PID=%d UID=%d\n",
					sig, siginfo -> si_pid, siginfo -> si_uid);
}

int main(int argc, char *argv[])
{
	struct sigaction act;
	memset(&act, 0, sizeof(act));
	act.sa_sigaction = &handler;
	act.sa_flags = SA_SIGINFO;
	sigaction(SIGTERM, &act, NULL);
	printf("proc PID=%d looping\n");
	printf("enter kill PID to send SIGTERM signal to it\n",getpid());
	while(1){
		sleep(10);
	}
}

在程序运行时,读者可从另一个X终端输入kill PID,向进程发送一个SIGTERM(15)信号。

任何信号都会唤醒进程,即使它处于睡眠状态,也能够处理该信号。

实践截图如下:

posted @ 2021-11-11 17:50  20191330雷清逸  阅读(25)  评论(0编辑  收藏  举报