Ctrl-C我们再熟悉不过了,可如果一开始,我们有Ctrl-C这样一个具体的需求,操作系统应该怎样设计/实现?

  • UNIX信号
  • job control 怎样在shell里管理多个进程

为什么Ctrl-C能退出程序?
1.Ctrl-C并不能杀掉所有的程序,比如没法用Ctrl-C来退出vim
2.两个进程同时使用了read(STDIN_FILENO,buf,size);,按下Ctrl-C,谁收到了?

Ctrl-C的背后涉及若干操作系统中的对象
按下Ctrl-C的终端

  • tty(1)
  • echo hello > $(tty) 我们甚至可以向其它tty打印

事实上,它俩都不是最先收到Ctrl-C的,终端最先收到Ctrl-C,向前台的进程组(process group)发送了SIGINT信号,这是一种进程间通信的机制,和(管道)pipe和(共享内存)mmap一样,只不过信号是异步的,而管道和共享内存是同步的,在按下Ctrl-C时,同时给进程发送了信号signal,进程就会跳转到处理器预设的signal处理程序,等于说在软件上模拟硬件上的中断机制,使得可以在进程间做异步通信。

下面介绍一下signal函数

//signal函数原型
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int sinum,sighandler_t handler);

对于sighandler_t signal(int signum, sighandler_t handler)函数来说,signum显然是信号的编号,handler是中断函数的指针。typedef void (*sighandler_t)(int)中断函数的原型中,有一个参数是int类型,显然也是信号产生的类型,方便使用一个函数来处理多个信号。

signal()会按照参数signum指定的编号来设置该信号的处理函数,当指定的信号到达时就会跳转到参数handler指定的函数执行,如果参数handler不是指针,则必须是下列两个常数之一:

  • SIG_IGN 忽略参数signum指定的信号
  • SIG_DFL 将参数signum指定的信号重设为预设的信号处理方式
    返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)

当使用vim编辑时,之所以Ctrl-C无效,因为它的进程不响应这个机制;在手册里,有这样说,signal()可以设置一个编号为signum信号的handler,可以有SIG_IGN(忽略这个信号)、SIG_DFL(默认的信号处理程序),对于Ctrl-C来说,SIG_DFL的处理就是退出程序;而vim设定了一个自定义的signal handler

job control
何为PID?进程标识符,为每个进程分配唯一标号,进程创建时分配PIDShell支持前台进程组和后台进程组,进程组指的是已从shell命令行启动的命令;通常,前台进程组可以控制终端;一个程序也可以附上&被后台运行,比如testrun是一个程序,testrun &[2] 4365成为一个后台进程组;如果后台启动了多个进程组,shell将按照这些进程组启动的顺序进行编号,比如

$ testrun &[2] 4365
$ backup &[3] 4374
$ account &[4] 4377

当后台进程组完成了会报[1] + done testrun,jobs命令将会列出所有的后台进程组,jobs -l选项还可以列出PID,比如

[2]  Running testrun & 
[3]- Running backup &
[4]+ Running account &

另外,fg(foreground)命令可以在前台恢复执行被挂起的进程,默认情况下,fg和下面的bg针对于最近的进程,如果有编号,比如fg %2,表示使第2个任务在前台运行;或者可以使用fg %testrun命令,%+针对于最近的后台进程组,而%-针对于次新的后台进程组。Ctrl-Z可以用来挂起一个进程放入后台,bg命令可以在后台恢复执行被挂起的进程,而此时将无法再使用Ctrl-Z再次挂起该进程。比如,正在使用vi编辑一个文件,需要执行shell命令查询一些需要的信息,可以使用Ctrl-Z挂起vi,等执行完shell命令之后再使用fg恢复vi继续编辑你的文件。