第六章学习笔记
第六章 信号和信号处理
1.信号和中断
- 信号:发给进程的请求,将进程从正常执行转移到中断处理。
- 中断:是从I/O设备或协处理器发送到CPU的外部请求,它将CPU从正常执行转移到中断处理。
- “中断”是发送给“进程”的事件,它将“进程”从正常活动转移到其他活动,称为“中断处理”。“进程”可在完成“中断”处理后恢复正常活动。
- 中断主要有以下几种类型:
- 人员中断
- 进程中断
- 硬件中断
- 进程的陷阱错误
2.Unix/Linux信号示例
- 按“Ctrl+C”组合键通常会导致当前运行的进程终止。原因如下:Ctrl+C组合键会生成一个键盘硬件中断。键盘中断处理程序将Ctrl+C组合键转换为SIGINT(2)信号,发送给终端上的所有进程,并唤醒等待键盘输入的进程。在内核模式下,每个进程都要检查和处理未完成的信号。进程对大多数信号的默认操作是调用内核的kexit(exitValue)函数来终止。在Linux中,exitValue的低位字节是导致进程终止的信号编号。
- 用户可使用
nohup a.out &
命令在后台运行一个程序。即使在用户退出后,进程仍将继续运行。 - 用户再次登录时也许会发现(通过ps-u LTD)后台进程仍在运行。用户可以使用sh命令
kill pid (or kill -s 9 pid)
杀死该进程。之所以是9个信号是因为在最初的Unix中,只有9个信号。9号信号被保留为终止进程的终极手段。虽然后来的Unix/Linux系统将信号编号扩展到了31,但是信号编号9的含义仍然保留了下来。
3.Unix/Linux中的信号处理
- Unix/Linux支持的31种信号,在signal.h文件中均有定义,每种信号都有一个符号名。
- 来自硬件中断的信号:在进程执行过程中,一些硬件中断被转换为信号发送给进程。硬件信号示例是中断键(CtrI+C),它产生一个SIGINT(2)信号。
- 间隔定时器,当它的时间到期时,会生成一个SIGALRM( 14),SIGVTALRM(26)或SIGPROF (27)信号。
- 其他硬件错误,如总线错误、IO陷阱等。
- 来自异常的信号:当用户模式下的进程遇到异常时,会陷入内核模式,生成一个信号,并发送给自己。常见的陷阱信号有SIGFPE(8),表示浮点异常(除以0),最常见也是最可怕的是SIGSEGV(11),表示段错误,等等。
- 来自其他进程的信号:进程可使用kill(pid, sig)系统调用向pid标识的目标进程发送信号。读者可以尝试以下实验。在 Linux 下,运行简单的C程序
main(){ while(1); }
使进程无限循环。从另一个(X-window)终端,使用ps -u查找循环进程pid。然后输入sh命令kill -s 11 pid循环进程会因为段错误而死亡。当某进程被某个信号终止时,它的exitValue就包含这个信号编号。父进程sh只是将死亡子进程的信号编号转换为一个错误字符串。
4.信号处理步骤
- 当某进程处于内核模式时,会检查信号并处理未完成的信号。如果某信号有用户安装的捕捉函数,该进程会先清除信号,获取捕捉函数地址,对于大多数陷阱信号,则将已安装的捕捉函数重置为DEFault。然后,它会在用户模式下返回,以执行捕捉函数,以这种方式篡改返回路径。当捕捉函数结束时,它会返回到最初的中断点,即它最后进入内核模式的地方。因此,该进程会先迁回执行捕捉函数,然后再恢复正常执行。
- 重置用户安装的信号捕捉函数:用户安装的陷阱相关信号捕捉函数用于处理用户代码中的陷阱错误。由于捕捉函数也在用户模式下执行,因此可能会再次出现同样的错误。如果是这样,该进程最终会陷入无限循环,一直在用户模式和内核模式之间跳跃。为了防止这种情况,Unix内核通常会在允许进程执行捕捉函数之前先将处理函数重置为 DEFault。这意味着用户安装的捕捉函数只对首次出现的信号有效。若要捕捉再次出现的同一信号,则必须重新安装捕捉函数。但是,用户安装的信号捕捉函数的处理方法并不都一样,在不同Unix版本中会有所不同。例如,在 BSD Unix中,信号处理函数不会被重置,但是该信号在执行信号捕捉函数时会被阻塞。
- 信号和唤醒:在Unix/Lintx内核中有两种SLEEP进程;深度休眠进程和浅度休眠进程。前一种进程不可中断,而后一种进程可由信号中断。如果某进程处于不可中断的SLEEP状态,到达的信号(必须来自硬件中断或其他进程)不会唤醒进程。如果它处于可中断的SLEEP状态,到达的信号将会唤醒它。例如,当某进程等待终端输入时,它会以低优先级休眠,这种休眠是可中断的,SIGINT这类信号即可唤醒它。
5.信号与异常
Unix信号最初设计用于以下用途。
- 作为进程异常的统一处理方法:当进程遇到异常时,它会陷人内核模式,将陷阱原因转换为信号编号,并将信号发送给自己。如果在内核模式下发生异常,内核只打印一条PANIC错误消息,然后就停止了。如果在用户模式下发生异常,则进程通常会终止,并以内存转储进行调试。
- 让进程通过预先安装的信号捕捉函数处理用户模式下的程序错误。这类似于MVS[IBM MVS]中的 ESPIE宏。
- 在特殊情况下,它会让某个进程通过信号杀死另一个进程。注意,这里所说的杀死并不是直接杀死某个进程,而只是向目标进程发出“死亡”请求。
6.Linux中的IPC
IPC是指用于进程间通信的机制。在Linux中,IPC包含以下组成部分:
-
管道和FIFO
一个管道有一个读取端和一个写入端。管道的主要用途是连接一对管道写进程和读进程。管道写进程可将数据写入管道,读进程可从管道中读取数据。管道控制机制要对管道读写操作进行同步控制。未命名管道供相关进程 使用,命名管道是FIFO的,可供不相关进程使用。在Linux中的管道读取操作为同步和阻塞。如果管道仍有写进程但没有数据,读进程会进行等待。 -
信号
进程可使用kill系统调用向其他进程发送信号.其他进程使用信号捕捉函数处理信号,将信号用作IPC的一个主要缺点是信号只是用作通知,不含任何信息内容。 -
System V IPC
包括共享内存、信号址和消息队列。在Linux中,多种 System V 1PC函数,例如用于添加/移除共享内存的shmat/shmdt、用于获取/操作信号反的semget/semop和用于发送/接收消息的msgsnd/msgrcv,都是库包装函数,它们都会向 Linux内核发出一个ipc()系统调用。ipc()的实现是Linux所特有的,不可移植。 -
POSIX消息队列
-
线程同步机制
进程是共享某些公共资源的线程。如果是使用有共享地址空间的clone()系统调用创建的进程,它们可使用互斥量和条件变量通过共享内存进行同步通信。另外,常规进程可添加到共享内存,使它们可作为线程进行同步。 -
套接字
用于跨网络进程通信的IPC机制。
二、知识点挑战
1.Unix/Linux中的信号处理
2.Linux中的IPC