学习笔记9
- 信号和中断
- Unix/Linux 中的信号处理
- 信号处理步骤与异常
- Linux 中的 IPC
- 实践过程
信号和中断
“中断”是从I/O设备或协处理器发送到CPU的外部请求,它将CPU从正常执行转移到中断处理。“信号”是发送给进程的请求,将进程从正常执行转移到中断处理。
中断的概念和机制
1.硬件中断:
-
概念:硬件中断是由计算机系统的硬件设备生成的事件所触发的中断。这些硬件设备可以包括键盘、鼠标、磁盘驱动器、网卡、定时器等。硬件中断通常用于告知CPU发生了某种事件,需要处理或响应。
-
机制:当硬件设备生成一个中断信号时,它会向中断控制器发送请求(通常使用IRQ线),中断控制器会将该中断请求传递给CPU。CPU会根据中断请求的优先级,暂停当前执行的程序,保存程序状态,跳转到与该中断相关的中断处理程序。中断处理程序负责处理中断事件,然后返回到原始程序的执行点。
2.进程中断:
-
概念:进程中断是由操作系统内部的事件或条件触发的中断。这些事件包括操作系统调度、进程间通信、异常(如除零错误、无效操作码等)等。进程中断通常用于管理操作系统的任务和进程之间的切换。
-
机制:当发生进程中断时,操作系统会根据中断的类型,挂起当前运行的进程,保存其状态,然后选择一个新的进程来执行。这实现了多任务处理,允许操作系统在多个进程之间共享CPU时间。
3.人员中断:
-
概念:人员中断是由计算机用户或外部人员触发的中断。这些中断通常来自外部输入设备,如键盘、鼠标、触摸屏,或者来自网络通信等。
-
机制:人员中断是用户直接触发的,例如,当用户按下键盘上的键时,键盘会生成一个中断信号,操作系统会响应并传递给相应的应用程序处理。用户的输入将被用于控制程序的执行。
Unix/Linux 中的信号处理
Unix/Linux中的信号处理是一种重要的机制,用于进程间通信和处理异步事件。
1.信号类型:
Unix/Linux系统中有许多不同类型的信号,每个信号都有一个唯一的编号,通常是正整数。一些常见的信号类型包括:
SIGINT
:终止进程,通常由用户在终端上按下Ctrl+C触发。SIGTERM
:终止进程,通常是通过kill命令触发。SIGHUP
:终止或重新启动进程,通常在终端断开连接时触发。SIGKILL
:强制终止进程,不能被捕获或忽略。SIGSTOP
:暂停进程的执行。SIGCONT
:继续进程的执行,从暂停状态恢复。- someting else
2.信号的来源:
信号可以来自多个来源,包括:
- 用户:通过终端或命令行工具,用户可以发送信号来控制进程。
- 操作系统:操作系统可以向进程发送信号,以通知进程发生了某些事件,如子进程终止、硬件错误等。
- 其他进程:一个进程可以向另一个进程发送信号,以实现进程间通信。
3.进程proc结构体的信号:
Unix/Linux中,每个进程都有一个proc结构体,用于保存有关进程状态的信息,其中包括信号的处理状态。proc结构体中包括:
sighand
:一个指向信号处理函数的指针,用于定义进程接收信号时的操作。sigpending
:一个用于存储未决信号的集合,这些信号在进程处理之前排队等待。signal
:一个用于存储已安装的信号处理函数的数组。blocked
:一个用于指定被阻止的信号的集合,这些信号不会被处理,而是排队等待。
4.信号处理函数:
每个信号都可以关联一个信号处理函数,用于定义接收到该信号时应采取的操作。信号处理函数可以是用户自定义的函数,也可以是操作系统提供的默认处理函数。用户可以使用signal函数或sigaction函数来为进程安装信号处理函数。
5.安装信号捕捉函数:
在Unix/Linux中,可以使用以下两个主要函数来安装信号处理函数:
-
signal(int signum, void (*handler)(int))
:这是一种较早的信号处理函数安装方法,用于指定信号编号和信号处理函数的指针。不推荐使用,因为它在不同系统上的行为不一致。 -
sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
:这是一种较新且更强大的信号处理函数安装方法。它允许更精细的控制信号处理,包括指定信号处理函数、标志位和信号掩码等。 -
C语言示例:在C语言中,使用 signal() 函数可以安装信号处理函数。
#include <stdio.h> #include <signal.h> void signal_handler(int signum) { printf("Received signal %d\n", signum); } int main() { signal(SIGINT, signal_handler); // 安装SIGINT的信号处理函数 while (1) {} return 0; }
信号处理步骤
1.进程处于内核模式: 当信号被发送到进程时,进程必须从用户模式切换到内核模式,以便内核能够处理信号。在内核模式下,操作系统可以执行特权操作,例如更改进程的状态和执行信号处理函数。
2.重置用户安装的信号捕捉函数: 如果进程已经为特定信号安装了自定义的信号处理函数,首先,操作系统将保存用户安装的信号处理函数的地址。然后,操作系统将重置信号处理函数为默认行为,这可能包括终止进程或忽略信号,具体取决于信号的默认行为。
3.信号和唤醒: 接下来,操作系统会执行与接收到的信号相关的操作,这包括:
-
a. 终止进程:如果信号的默认行为是终止进程,那么进程将被终止,其状态将变为"终止",并且进程将被从进程表中移除。
-
b. 执行信号处理函数:如果信号处理函数已被安装,操作系统会执行该函数。信号处理函数可以是用户自定义的,用于处理信号时采取特定操作,比如记录信息、清理资源、或采取其他适当的措施。
-
c. 唤醒进程:如果信号没有终止进程,操作系统会将进程从信号等待状态唤醒,以便它能够继续执行。这通常涉及到将进程状态从"等待信号"状态更改为"就绪"状态,以确保进程被调度并执行。
Linux 中的 IPC
1.管道和FIFO(命名管道):
- 管道:一种单向通信机制,可用于具有父子关系的进程间通信。使用
pipe()
系统调用创建,数据单向流动,通常在父子进程之间使用。 - FIFO:也称为命名管道,与管道类似,但允许无关进程间的通信。使用
mkfifo()
创建,进程通过打开FIFO文件进行通信。
2. 信号:
- 信号:用于通知进程发生了某种事件的软件通知机制。可以由操作系统、其他进程或用户发送。使用
kill()
系统调用发送信号,signal()
或sigaction()
用于注册信号处理函数。
3.System V IPC:
- 消息队列:允许进程通过消息进行通信,支持多种数据类型。使用
msgget()
、msgsnd()
和msgrcv()
等系统调用创建和操作消息队列。 - 共享内存:允许多个进程访问同一块物理内存。使用
shmget()
、shmat()
等系统调用来创建和附加共享内存段。 - 信号量:用于进程同步和互斥,控制进程对共享资源的访问。使用
semget()
、semop()
等系统调用来操作信号量。
4.POSIX消息队列:
-
POSIX消息队列:类似System V消息队列,但与POSIX标准兼容。使用
mq_open()
、mq_send()
和mq_receive()
等函数进行操作。
5.线程同步机制: -
互斥锁:用于防止多个线程同时访问共享资源。使用
pthread_mutex_init()
、pthread_mutex_lock()
和pthread_mutex_unlock()
等函数操作互斥锁。 -
条件变量:用于线程间的条件等待和通知。使用
pthread_cond_init()
、* pthread_cond_wait()
和pthread_cond_signal()
等函数。
6.套接字(Socket):
- 套接字:用于不同主机间进程间通信。包括TCP和UDP套接字,可用于网络通信。使用
socket()
、bind()
、connect()
和send()
、recv()
等系统调用进行套接字编程。