一、学习任务
自学教材第6章,提交学习笔记(10分),评分标准如下
1. 知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容 (4分)
“我在学***X知识点,请你以苏格拉底的方式对我进行提问,一次一个问题”
核心是要求GPT:“请你以苏格拉底的方式对我进行提问” 然后GPT就会给你提问,如果不知道问题的答案,可以反问AI:“你的理解(回答)是什么?”
如果你觉得差不多了,可以先问问GPT:“针对我XXX知识点,我理解了吗?” GPT会给出它的判断,如果你也觉得自己想清楚了,可以最后问GPT:“我的回答结束了,请对我的回答进行评价总结”,让它帮你总结一下。
2. 问题与解决思路,遇到问题最先使用chatgpt等AI工具解决,并提供过程截图(3分)
3. 实践过程截图,代码链接(2分)
4. 其他(知识的结构化,知识的完整性等,提交markdown文档,使用openeuler系统等)(1分)
二、知识总结
6.1 信号和中断
中断:是从I/O设备或协处理器发送到CPU的外部请求,它将CPU从正常执行转移到中断处理。
“中断”是发送给“进程”的事件,它将“进程”从正常活动转移到其他活动,称 为“中断处理”。“进程”可在完成“中断”处理后恢复正常活动。
中断有以下几种情况:
1.人员中断
2.进程中断
这类中断是发送给进程的中断。当某进程正在执行时,可能会收到来自3个不同来源的 中断:
●来自硬件的中断:终端、间隔定时器的“Ctrl+C”组合键等。
●来自其他进程的中断: kill(pid,SIG#)、death_of_child等。
●自己造成的中断:除以0、无效地址等。
3.硬件中断
这类中断是发送给处理器或CPU的信号。它们也有三个可能的来源:
●来自硬件的中断:定时器、I/O 设备等。
●来自其他处理器的中断; FFP 、DMA、多处理器系统中的其他CPU。
●自己造成的中断:除以0、保护错误、INT 指令。
每个中断都有唯一的中断向量号。
4.进程的陷阱错误
进程可能会自己造成中断。这些中断是由被CPU 识别为异常的错误引起的。
6.2 Unix/Linux 信号示例
(1)按 “Ctrl+C” 组合键通常会导致当前运行的进程终止。
(2)用户可使用nohup a.out &命令在后台运行一个程序。
(3)用户可以使用sh 命令kill pid (or kill -s 9 pia)杀死该进程。
6.3 Unix/Linux中的信号处理
6.3.1信号的来源
来自硬件中断的信号:在进程执行过程中, 一些硬件中断被转换为信号发送给进程。
来自异常的信号: 当用户模式下的进程遇到异常时,会陷入内核模式,生成一个信号,并发送给自己。
来自其他进程的信号:进程可使用kill(pid,sig)系统调用向 pid 标识的目标进程发送信号。
6.3.2信号处理函数
每个进程 PROC都有一个信号处理数组 int sig[32] 。sig[32]数组的每个条目都指定了如 何处理相应的信号,其中0表示 DEFault(默认),1表示IGNore (忽略),其他非零值表示 用户模式下预先安装的信号捕捉(处理)函数。

6.3.3安装信号捕捉函数
进程可使用系统调用:int r = signal(int signal_numberr void *handler);来修改选定信号编号的处理函数,SIGK1LL (9)和SIGSTOP (19)除外,它们不能修改。signal()系统调用在所有类Unix系统中均可用,但它有一些缺点:
- 在执行已安装的信号捕捉函数之前,通常将信号处理函数重置为DEFault。为捕捉下次出现的相同信号,必须重新安装捕捉函数。
- signal()不能阻塞其他信号。
- signal()可能不适用于多线程程序中的线程。
- 不同Unix版本的signal。可能会有所不同。
所以现在signal()已经被sigaction()函数所代替,它的原型是int sigaction (int signum, const struct sigaction *act, struct sigaction *oldact);,sigaction结构体的定义为

6.4信号处理步骤
-
当某进程处于内核模式时,会检查信号并处理未完成的信号。如果某信号有用户安装的捕捉函数,该进程会先清除信号,获取捕捉函数地址,对于大多数陷阱信号,则将已安装的捕捉函数重置为 DEFault。然后,它会在用户模式下返回,以执行捕捉函数,以这种方式篡改返回路径。当捕捉函数结束时,它会返回到最初的中断点,即它最后进入内核模式的地方。
-
重置用户安装的信号捕捉函数:用户安装的陷阱相关信号捕捉函数用于处理用户代码中的陷阱错误。由于捕捉函数也在用户模式下执行,因此可能会再次出现同样的错误。如果是这样,该进程最终会陷入无限循环,一直在用户模式和内核模式之间跳跃。为了防止这种情况,Unix 内核通常会在允许进程执行捕捉函数之前先将处理函数重置为 DEFault。这意味着用户安装的捕捉函数只对首次出现的信号有效。
-
信号和唤醒:在Unix/Linux,内核中有两种 SLEEP进程;深度休眠进程和浅度休眠进程。前一种进程不可中断,而后一种进程可由信号中断。如果某进程处于不可中断的SLEEP 状态,到达的信号(必须来自硬件中断或其他进程)不会唤醒进程。如果它处于可中断的SLEEP状态,到达的信号将会唤醒它。
6.5信号与异常
Unix信号最初设计用于以下用途。
- 作为进程异常的统一处理方法。
- 让进城通过预先安装的信号捕捉函数用户模式下的程序错误。
- 在特殊情况下,它会让某一个进程通过信号杀死另一个进程。
6.6Linux中的IPC
6.6.1管道和 FIFO
一个管道有一个读取端和一个写入端。管道的主要用途是连接一对管道写进程和读进程。管道写进程可将数据写入管道,读进程可从 管道中读取数据。管道控制机制要对管道读写操作进行同步控制。未命名管道供相关进程 使用。命名管道是 FIFO的,可供不相关进程使用。在Linux中的管道读取操作为同步和阻 塞。如果管道仍有写进程但没有数据,读进程会进行等待。
必要时,可通过对管道描述符的 fcntl 系统调用将管道操作更改为非阻塞。
6.6.2信号
进程可使用kill 系统调用向其他进程发送信号,其他进程使用信号捕捉函数处理信号。 将信号用作IPC 的一个主要缺点是信号只是用作通知,不含任何信息内容。
6.6.3System VIPC
Linux 支持SystemV IPC, 包括共享内存、信号量和消息队列。在Linux中,多种 SystemV IPC函数,例如用于添加/移除共享内存的 shmat/shmdt、用于获取/操作信号量 的 semget/semop 和用于发送/接收消息的 msgsnd/msgrev,都是库包装函数,它们都会向 Linux 内核发出一个ipcO 系统调用。ipc() 的实现是Linux所特有的,不可移植。
6.6.4POSIX 消息队列
POSIX 标准(IEEE 1003.1-2001)以消息队列为基础定义了IPC 机制。它们类似于 System V IPC 的消息队列,但更通用且可移植。
6.6.5线程同步机制
Linux 不区分进程和线程。在Linux中,进程是共享某些公共资源的线程。如果是使用 有共享地址空间的 clone()系统调用创建的进程,它们可使用互斥量和条件变量通过共享内 存进行同步通信。另外,常规进程可添加到共享内存,使它们可作为线程进行同步。
6.6.6套接字
套接字是用于跨网络进程通信的IPC 机制。
三、chatgpt问答





四、课本代码实现
#include <stdio.h> #include <unistd.h> #include <signal.h> #include <string.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); } }
以上为sigaction() 的使用示例,我尝试在虚拟机中进行运行。

可以看到其中有%d的使用错误和参数过多的问题。
于是在chatgpt的帮助下进行了修改。
#include <stdio.h> #include <unistd.h> #include <signal.h> #include <string.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", getpid()); // Use %d to print the process ID fprintf(stderr, "enter 'kill %d' to send SIGTERM signal to it\n", getpid()); // Use fprintf for printing to stderr while (1) { sleep(10); } return 0; // Added return statement }
运行结果如下:

浙公网安备 33010602011771号