UNIX信号
2024-08-22 15:53:12 阿炯

信号(Signals)是一种软中断,是一种处理异步事件的方法。一般来说,操作系统都支持许多信号。尤其是UNIX,比较重要应用程序一般都会处理信号。UNIX也定义了许多信号,比如SIGINT表示中断字符信号,也就是Ctrl+C的信号,SIGBUS表示硬件故障的信号;SIGCHLD表示子进程状态改变信号;SIGKILL表示终止程序运行的信号等等。

信号量(semaphore)系7种多进程通信(IPC)中的一种。

信号是Unix及类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。

信号类似于中断,不同之处在于中断由处理器调解并由内核处理,而信号由内核调解(可能通过系统调用)并由进程处理,下文还会对其进行简单的对比说明。内核可以将中断作为信号传递给导致中断的进程(典型的例子有SIGSEGV、SIGBUS、SIGILL和SIGFPE)。

信号起源于20世纪70年代的贝尔实验室Unix,后在POSIX标准中有所规定;嵌入式进程可能会发现信号对于进程间通信很有用,因为信号的计算和内存占用很小。

信号量编程是UNIX下非常重要的一种技术,这些信号量也可以在文件/usr/include/sys/signal.h中查看。

#define SIGHUP  进程由於控制终端死去或者控制终端发出起命令
#define SIGINT  键盘中断所产生的信号
#define SIGQUIT  键盘终止
#define SIGILL  非法的指令
#define SIGTRAP   进程遇到一个追踪(trace)或者是一个中断嵌套
#define SIGABRT  由abort系统调用所产生的中断信号
#define SIGIOT  类似於SIGABRT
#define SIGBUS   进程试图使用不合理的记忆体
#define SIGFPE  浮点异常
#define SIGKILL  KILL
#define SIGUSR1  用户自定义
#define SIGSEGV  段错误
#define SIGUSR2  用户自定义
#define SIGPIPE  管道操作时没有读只写
#define SIGALRM 由alarm系统调用产生的timer时钟信号
#define SIGTERM 收到终端信号的进程
#define SIGSTKFLT 堆叠错误
#define SIGCHLD  子进程向父进程发出的子进程已经stop或者终止的信号
#define SIGCONT  继续运行的信号
#define SIGSTOP  stop
#define SIGTSTP  键盘所产生的stop信号
#define SIGTTIN   当运行在後状态时却需要读取stdin的资料
#define SIGTTOU   当运行在後状态时却需要写向stdout
#define SIGURG   socket的紧急情况
#define SIGXCPU  进程超额使用CPU分配的时间
#define SIGXFSZ  进程使用了超出系统规定文件长度的文件
#define SIGVTALRM  内部的alarm时钟过期
#define SIGPROF  在一个程式段中描绘时钟集过期
#define SIGWINCH 终端视窗的改变
#define SIGIO 非同步IO
#define SIGPOLL  SIGIO pollable事件发生

通过结合trap命令使用:trap <command-list>  <signal-list>


指令'kill -l'会列出所有信号名称(Print a list of signal names. These are found in /usr/include/linux/signal.h)。只有第9种信号(SIGKILL)才可以无条件终止进程,其他信号进程都有权利忽略。

> kill -l
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  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX


No    Name         Default Action       Description

1     SIGHUP       terminate process    terminal line hangup

2     SIGINT       terminate process    interrupt program

3     SIGQUIT      create core image    quit program

4     SIGILL       create core image    illegal instruction

5     SIGTRAP      create core image    trace trap

6     SIGABRT      create core image    abort program (formerly SIGIOT)

7     SIGEMT       create core image    emulate instruction executed

8     SIGFPE       create core image    floating-point exception

9     SIGKILL      terminate process    kill program

10    SIGBUS       create core image    bus error

11    SIGSEGV      create core image    segmentation violation

12    SIGSYS       create core image    non-existent system call invoked

13    SIGPIPE      terminate process    write on a pipe with no reader

14    SIGALRM      terminate process    real-time timer expired

15    SIGTERM      terminate process    software termination signal

16    SIGURG       discard signal       urgent condition present on socket

17    SIGSTOP      stop process         stop (cannot be caught or ignored)

18    SIGTSTP      stop process         stop signal generated from keyboard

19    SIGCONT      discard signal       continue after stop

20    SIGCHLD      discard signal       child status has changed

21    SIGTTIN      stop process         background read attempted from control terminal

22    SIGTTOU      stop process         background write attempted to control terminal

23    SIGIO        discard signal       I/O is possible on a descriptor (see fcntl(2))

24    SIGXCPU      terminate process    cpu time limit exceeded (see setrlimit(2))

25    SIGXFSZ      terminate process    file size limit exceeded (see setrlimit(2))

26    SIGVTALRM    terminate process    virtual time alarm (see setitimer(2))

27    SIGPROF      terminate process    profiling timer alarm (see setitimer(2))

28    SIGWINCH     discard signal       Window size change

29    SIGINFO      discard signal       status request from keyboard

30    SIGUSR1      terminate process    User defined signal 1

31    SIGUSR2      terminate process    User defined signal 2


1) SIGHUP
本信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一session内的各个作业,这时它们与控制终端不再关联。

登录Linux时系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个 Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进程组和后台有终端输出的进程就会中止。不过可以捕获这个信号,比如wget能捕获SIGHUP信号并忽略它,这样就算用户退出了Linux登录它也能继续下载。

此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。

2) SIGINT
程序终止(interrupt)信号,在用户键入INTR字符(通常是Ctrl-C)时发出,用于通知前台进程组终止进程。

3) SIGQUIT
和SIGINT类似,但由QUIT字符(通常是Ctrl-/)来控制。进程在因收到SIGQUIT退出时会产生core文件,在这个意义上类似于一个程序错误信号。

4) SIGILL
执行了非法指令,通常是因为可执行文件本身出现错误或者试图执行数据段。堆栈溢出时也有可能产生这个信号。

5) SIGTRAP
由断点指令或其它trap指令产生,由debugger使用。

6) SIGABRT
调用abort函数生成的信号。

7) SIGBUS
非法地址,包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数,但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。

8) SIGFPE
在发生致命的算术运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。

9) SIGKILL
用来立即结束程序的运行。本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。

10) SIGUSR1
留给用户使用。

11) SIGSEGV
试图访问未分配给自己的内存,或试图往没有写权限的内存地址写数据。

12) SIGUSR2
留给用户使用。

13) SIGPIPE
管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。

14) SIGALRM
时钟定时信号,计算的是实际的时间或时钟时间。alarm函数使用该信号。

15) SIGTERM
程序结束(terminate)信号,与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了才会尝试SIGKILL。

17) SIGCHLD
子进程结束时,父进程会收到这个信号。

如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情况应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)。

18) SIGCONT
让一个停止(stopped)的进程继续执行,本信号不能被阻塞;可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作。例如重新显示提示符...

19) SIGSTOP
停止(stopped)进程的执行。注意它和terminate以及interrupt的区别:该进程还未结束,只是暂停执行;本信号不能被阻塞,处理或忽略。

20) SIGTSTP
停止进程的运行,但该信号可以被处理和忽略。用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号。

21) SIGTTIN
当后台作业要从用户终端读数据时,该作业中的所有进程会收到SIGTTIN信号。缺省时这些进程会停止执行。

22) SIGTTOU
类似于SIGTTIN,但在写终端(或修改终端模式)时收到。

23) SIGURG
有"紧急"数据或out-of-band数据到达socket时产生。

24) SIGXCPU
超过CPU时间资源限制,这个限制可以由getrlimit/setrlimit来读取/改变。

25) SIGXFSZ
当进程企图扩大文件以至于超过文件大小资源限制。

26) SIGVTALRM
虚拟时钟信号。类似于SIGALRM,但是计算的是该进程占用的CPU时间。

27) SIGPROF
类似于SIGALRM/SIGVTALRM,但包括该进程用的CPU时间以及系统调用的时间。

28) SIGWINCH
窗口大小改变时发出。

29) SIGIO
文件描述符准备就绪,可以开始进行输入/输出操作。

30) SIGPWR
电源失效故障。

31) SIGSYS
非法的系统调用。

在以上列出的信号中,程序不可捕获、阻塞或忽略的信号有:SIGKILL,SIGSTOP
不能恢复至默认动作的信号有:SIGILL,SIGTRAP
默认会导致进程流产的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默认会导致进程退出的信号有:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
默认会导致进程停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH

此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在进程挂起时是继续,否则是忽略,不能被阻塞。


一个进程可以自定义如何处理传入的POSIX信号。如果一个进程没有定义一个信号处理进程,那么这个信号的默认处理进程将被使用。下表列出了一些与POSIX兼容的UNIX系统的默认操作。

信号可移植代号默认行为描述
SIGABRT6终止 (核心转储)进程终止信号
SIGALRM14终止计时器告警
SIGBUS不适用终止 (核心转储)访问内存对象未定义区域
SIGCHLD不适用忽略子进程终止、暂停、继续
SIGCONT不适用继续如果被暂停,重新继续执行
SIGFPE8终止 (核心转储)错误的算术运算
SIGHUP1终止挂起
SIGILL4终止 (核心转储)非法的指令
SIGINT2终止终端中断信号
SIGKILL9终止杀死 (无法被捕获或忽略的信号)
SIGPIPE13终止写入一个没有连接另一端的管道
SIGPOLL不适用终止可轮询事件
SIGPROF不适用终止性能调优定时器超时
SIGQUIT3终止 (核心转储)终端退出信号
SIGSEGV11终止 (核心转储)非法的内存引用
SIGSTOP不适用暂停暂停执行(无法被捕获或忽略的信号)
SIGSYS  不适用终止 (核心转储)错误的系统调用
SIGTERM  15终止终止信号
SIGTRAP5终止 (核心转储)追踪/断点陷阱
SIGTSTP不适用暂停终端中止信号
SIGTTIN不适用暂停后台进程尝试读
SIGTTOU不适用暂停后台进程尝试写
SIGUSR1不适用终止用户自定义信号1
SIGUSR2不适用终止用户自定义信号2
SIGURG不适用忽略Out-of-band data is available at a socket
SIGVTALRM不适用终止虚拟定时器超时
SIGXCPU不适用终止 (核心转储)超出CPU时间限制
SIGXFSZ不适用终止 (核心转储)超出文件大小限制
SIGWINCH不适用忽略终端窗口大小已变化


编号34以上的是实时信号,这些信号各自在什么条件下产生,默认的处理动作是什么(Term表示终止当前进程,Core表示终止当前进程并且Core Dump,Ign表示忽略该信号,Stop表示停止当前进程,Cont表示继续执行先前停止的进程),在signal(7)中都有详细说明。

[1,31]:不可靠信号,多个信号不会排队只保留一个,即信号可能丢失。

[34,64]:SIGRTMIN及之后的信息是可靠(实时信号)的,支持排队信号不会丢失,可使用sigqueue发送信号,不像0~31有缺省的定义。

编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。两者的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。


产生信号的条件主要有:

1. 用户在终端按下某些键时,终端驱动程序会发送信号给前台进程,例如Ctrl-C产生SIGINT信号,Ctrl-\产生SIGQUIT信号,Ctrl-Z产生SIGTSTP信号。

2. 硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。

3. 再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

4. 一个进程调用kill(2)函数可以发送信号给另一个进程。

5. 可以用kill(1)命令发送信号给某个进程,kill(1)命令也是调用kill(2)函数实现的,如果不明确指定信号则发送SIGTERM信号,该信号的默认处理动作是终止进程。

6. raise:给自己发送信号。raise(sig)等价于kill(getpid(), sig)。

7. killpg:给进程组发送信号。killpg(pgrp, sig)等价于kill(-pgrp, sig)。

8. sigqueue:给进程发送信号,支持排队,可以附带信息。

9. 当内核检测到某种软件条件发生时也可以通过信号通知进程,例如闹钟超时产生SIGALRM信号,向读端已关闭的管道写数据时产生SIGPIPE信号。


用户程序可以调用signal(2)/sigaction(2)函数告诉内核如何处理某种信号(若未注册则按缺省处理),可选的处理动作有三种:
1. 忽略此信号(SIG_IGN),但有两个信号不能被忽略:SIGKILL和SIGSTOP;

2. 执行该信号的默认处理动作(SIG_DFL);

3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(catch)一个信号。


信号与中断的区别

信号与中断的相似点:
(1)采用了相同的异步通信方式;
(2)当检测出有信号或中断请求时,都暂停正在执行的程序而转去执行相应的处理程序;
(3)都在处理完毕后返回到原来的断点;
(4)对信号或中断都可进行屏蔽。

信号与中断的区别:
(1)中断有优先级,而信号没有优先级,所有的信号都是平等的;
(2)信号处理程序是在用户态下运行的,而中断处理程序是在核心态下运行;
(3)中断响应是及时的,而信号响应通常都有较大的时间延迟。


考虑到程序的可移植性,应该尽量采用POSIX信号函数,POSIX信号函数主要分为两类:
1、POSIX 1003.1信号函数:kill()、sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、sigpending()、sigprocmask()、sigsuspend()。

2、POSIX 1003.1b信号函数:在信号的实时性方面对POSIX 1003.1做了扩展,包括以下三个函数:sigqueue()、sigtimedwait()、sigwaitinfo()。其中sigqueue主要针对信号发送,而sigtimedwait及sigwaitinfo()主要用于取代sigsuspend()函数

为了增强程序的稳定性,在信号处理函数中应使用可重入函数。信号处理程序中应当使用可再入(可重入)函数(注:所谓可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错);因为进程在收到信号后,就将跳转到信号处理函数去接着执行。如果信号处理函数中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,可能会出现不可预料的后果。不可再入函数在信号处理函数中被视为不安全函数。

在早期的UNIX中信号是不可靠的,不可靠在这里指的是:信号可能丢失,一个信号发生了,但进程却可能一直不知道这一点。

现在Linux在SIGRTMIN实时信号之前的都叫不可靠信号,这里的不可靠主要是不支持信号队列,就是当多个信号发生在进程中的时候(收到信号的速度超过进程处理的速度的时候),这些没来的及处理的信号就会被丢掉,仅仅留下一个信号。可靠信号是多个信号发送到进程的时候(收到信号的速度超过进程处理信号的速度的时候),这些没来的及处理的信号就会排入进程的队列。等进程有机会来处理的时候,依次再处理,信号不丢失。


发送信号

在一个运行的进程的控制终端键入特定的组合键可以向它发送某些信号:
1.Ctrl-C发送INT信号(SIGINT);默认情况下,这会导致进程终止。
2.Ctrl-Z发送TSTP信号(SIGTSTP);默认情况下,这会导致进程挂起。
3.Ctrl-\发送QUIT信号(SIGQUIT);默认情况下,这会导致进程终止并且将内存中的信息转储到硬盘(核心转储)。
4.(这些组合键可以通过stty命令来修改)。

kill()系统调用会在权限允许的情况下向进程发送特定的信号,类似地,kill命令允许用户向进程发送信号。raise(3)库函数可以将特定信号发送给当前进程。像除数为零、段错误这些异常也会产生信号(这里分别是SIGFPE和SIGSEGV,默认都会导致进程终止和核心转储)。内核可以向进程发送信号以告知它一个事件发生了。例如当进程将数据写入一个已经被关闭的管道时将会收到SIGPIPE信号,默认情况下会使进程关闭。
    
处理信号

信号处理函数可以通过signal()系统调用来设置。如果没有为一个信号设置对应的处理函数,就会使用默认的处理函数,否则信号就被进程截获并调用相应的处理函数。在没有处理函数的情况下,进程可以指定两种行为:忽略这个信号(SIG_IGN)或者用默认的处理函数(SIG_DFL)。但是有两个信号是无法被截获并处理的:SIGKILL和SIGSTOP。

可移植编号

对于大多数信号,相应的信号编号由实现定义。此列列出了POSIX标准中指定的数字。

行为释义
终止  – 进程异常终止。进程终止的结果和调用 _exit() 是一样的,除了终止可以向 wait() 和 waitpid() 返回导致进程终止的信号。
终止(核心转储) – 进程异常终止。这种进程中止的过程根据实现有所不同,一般会创建一个核心文件。
忽略 – 进程忽略该信号。
暂停 – 进程被暂停(不是终止)。
继续 – 进程恢复执行。


信号量之System V 与 POSIX

信号量是一种计数器,用来控制对多个进程/线程共享的资源进行访问;常和锁一同使用。在某个进程/线程正在对某个资源进行访问时,信号量可以阻止另一个进程/线程去打扰。生产者和消费者模型是信号量的典型使用。

为什么信号量分两套

POSIX是“可移植操作系统接口(Portable Operating System Interface )”的首字母简写,但它并不是一个单一的标准,而是一个电气与电子工程学会即IEEE开发的一系列标准,它还是由ISO(国际标准化组织)和IEC(国际电工委员会)采纳的国际标准;而System V是Unix操作系统众多版本的一个分支,它最初是由AT&T在1983年第一次发布,其一共有四个版本,而最成功的是System V Release 4,或者称为SVR4。这样看来,一个是Unix 的标准之一(另一个标准是Open Group),一个是Unix众多版本的分支之一(其他的分支还有Linux跟BSD),应该来说,Posix标准正变得越来越流行,很多厂家开始采用这一标准。

那么两者有什么区别,或者说应用场景:
1、POSIX信号量常用于线程;system v信号量常用于进程的同步。

2、从使用的角度,System V 信号量的使用比较复杂,而 POSIX 信号量使用起来相对简单。

3、对 POSIX 来说,信号量是个非负整数。而 System V 信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为 System V IPC 服务的,信号量只不过是它的一部分。

4、Posix信号量是基于内存的,即信号量值是放在共享内存中的,它是由可能与文件系统中的路径名对应的名字来标识的。而System v信号量则是基于内核的,它放在内核里面。

5、POSIX 信号量的头文件是 <semaphore.h>,而 System V 信号量的头文件是 <sys/sem.h>。

6、POSIX 还有有名信号量,一般用于进程同步, 有名信号量是内核持续的。


因为竞态条件的存在,信号的处理是有弱点的。因为信号是异步的,所以在处理一个信号的过程中,进程可能收到另一个信号(甚至是相同的信号)。sigprocmask()系统调用可以用来阻塞和恢复信号的传递。信号可以造成进程中系统调用的中断,并在信号处理完后重新开始未完成的系统调用。信号处理函数应该没有任何不想要的副作用,比如,errno的改变、信号掩码的改变、信号处理方法的改变,以及其他全局进程性质的改变。在信号处理函数内使用不可重入函数,如malloc和printf,也是不安全的。

与硬件异常的关系

进程的运行也可能导致硬件异常,如将一个数除以零,或者出现TLB不命中。在类Unix系统中,这会自动运行内核的异常处理进程。对于某些异常如页缺失,内核有足够的信息来处理完并恢复进程的运行。但是对于另外一些异常,内核不能处理而只能通过发送信号把异常交给进程自己处理。例如在x86架构的CPU上,如果一个进程尝试将一个数除以零,将会产生divide error异常,并使内核向出错的进程发送SIGFPE信号。相似地,如果一个进程尝试访问虚拟地址空间以外的内存,内核将向进程发送SIGSEGV信号。异常与信号的具体对应关系在不同的CPU架构上是有所不同的。

类Unix中可通过kill与fuser指令向进程发送信号,另有raise、killpg、alarm、pause、abort、sleep、usleep、nanosleep和setitimer函数。下面将分三娄介绍此类函数,不过先说一下最常用的kill的用法。

kill 命令

用于终止指定的进程(terminate a process),是 Unix/Linux 下进程管理的常用命令。

用途
1.通常在需要终止某个或某些进程时,先使用 ps/pidof/pstree/top 等工具获取进程 pid,然后用 kill 杀掉进程。

2.向指定的进程或进程组发送信号(The command kill sends the specified signal to the specified process or process group),或者确定进程号为 pid 的进程是否还在。例如,许多程序都把 HUP 信号作为重新读取配置文件的触发条件。

命令格式

kill <pid>
kill -TERM <pid>

发送 TERM 信号到指定进程,如果进程没有捕获该信号,则进程终止(If no signal is specified, the TERM signal is sent. The TERM signal will kill processes which do not catch this signal.)

killall

killall httpd
杀死同一进程组内的所有进程,允许指定要终止的进程的名称,而非pid

常用信号

HUP

kill -s HUP pid
HUP 1 终端断线

让 Linux 缓和的执行进程关闭,然后重启。在对配置文件修改后需要重启进程时可发送此信号。

两种解释
1.被许多守护进程理解为一个重置请求.如果一个进程不用重新启动就能重新读取它的配置文件并调整自己以适应变化的话,那么HUP通常来触发这种行为.

2.由终端驱动程序生成,试图来"清除"(终止)跟某个特定终端相连的进程.

例如:某个终端会话结束时,或者当调制解调器被挂断时,shell后台不接受HUP的信号的影响.有的用户可使用nohup来模仿这种行为.

重启命令
ExecReload=/bin/kill -s HUP $PID

INT
INT 2 中断(同 Ctrl + C)
当用户键入时由终端驱动程序发送的信号.这是一个终止当前操作的请求.如果捕获了这个信号,一些简单的程序应该退出,或者允许自给被终止,这也是程序没有捕获到这个信号时的默认处理方法.拥有命令行或者输入模式的那些程序应该停止它们在做的事情,清除状态,并等待用户的再次输入.
QUIT
QUIT 3 退出(同 Ctrl + \)

停止命令
ExecStop=/bin/kill -s QUIT $PID

QUIT和TERM类似--不同的是:它会生成内存转储。

KILL
kill -s KILL pid
KILL 9 强制终止

kill -9 pid

这个强大和危险的命令迫使进程在运行时突然终止,进程在结束后不能自我清理。

危害:导致系统资源无法正常释放,一般不推荐使用,除非其他办法都无效。

立即把进程无条件的杀掉

TERM
kill -s TERM pid
TERM 15 终止

友好告诉进程退出,进程先保存好数据,再正常退出。给父进程发送一个 TERM 信号,试图杀死它和它的子进程。请求彻底终止某项执行操作.它期望接收进程清除自给的状态并退出

CHLD
CHLD 17 中断或停止
当一个进程中断或停止,一个 CHLD 信号被发送给父进程
默认情况下这个信号会被忽略,所以父进程必须捕获这个信号,如果他想被通知,无论什么时候一个子进程的状态发生改变。

通常的做法是:在一个信号捕获函数,调用一个wait 函数,去捕获子进程的ID 和结束终止状态。

CONT
CONT 18 继续(与STOP相反,fg/bg命令)

STOP
STOP 19 暂停(同 Ctrl + Z)

TSTP
TSTP 停止位

fuser 命令

一、kill, raise, killpg 函数

int kill(pid_t pid, int sig);
int raise(int sig);
int killpg(int pgrp, int sig);

kill命令是调用kill函数实现的,kill函数可以给一个指定的进程或进程组发送指定的信号,其中kill函数的pid参数取值不同表示不同含义,具体可参考其手册页。raise函数可以给当前进程发送指定的信号(自己给自己发信号)。killpg 函数可以给进程组发生信号。这三个函数都是成功返回0,错误返回-1。

kill()可以用来送参数sig指定的信号给参数pid指定的进程。参数pid有几种情况:
pid>0 将信号传给进程识别码为pid 的进程;
pid=0 将信号传给和当前进程相同进程组的所有进程;
pid=-1 将信号广播传送给系统内所有的进程;
pid<0 将信号传给进程组识别码为pid绝对值的所有进程。

二、alarm、pause、abort 函数

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号,该信号的默认处理动作是终止当前进程。这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。如某人要小睡一觉,设定闹钟为30分钟之后响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数。

pause函数使调用进程挂起直至捕捉到一个信号:
#include <unistd.h>
int pause(void);

只有执行了一个信号处理程序并从其返回时,pause才返回。

#include <stdlib.h>
void abort(void);

abort函数使当前进程接收到SIGABRT信号而异常终止。就像exit函数一样,abort函数总是会成功的,所以没有返回值。

三、setitimer 和不同精度的睡眠

1、首先来看三种不同的时间结构,如下:
time_t; /* seconds */
struct timeval {
long    tv_sec;    /* seconds */
long    tv_usec;    /* microseconds */
};
struct timespec {
time_t tv_sec;        /* seconds */
long   tv_nsec;    /* nanoseconds */
};

microseconds就是微秒, nanoseconds就是纳秒。

2、三种不同精度的睡眠
unsigned int sleep(unsigned int seconds);
int usleep(useconds_t usec);
int nanosleep(const struct timespec *req, struct timespec *rem);

sleep:单位为秒,1秒
usleep:单位为微秒,1/1000 秒
nanosleep:单位为毫微秒,也就是纳秒,1/1000 000 000 秒

在对精度要求较高的情况下使用select()作为定时器,最大的好处就是不会影响信号处理,线程安全,而且精度能得到保证。

usleep()有有很大的问题:
(1)在一些平台下不是线程安全,如HP-UX以及Linux
(2)usleep()会影响信号
(3)在很多平台,如HP-UX以及某些Linux下,其参数的值必须小于1 * 1000 * 1000也就是1秒,否则该函数会报错,并且立即返回。大部分平台的帮助文档已经明确说了,该函数是已经被舍弃的函数。

Linux下短延时推荐使用select函数,因为准确。

3、setitimer函数
包含头文件<sys/time.h>
功能setitimer()比alarm功能强大,会间歇性产生时钟,支持3种类型的定时器。
原型:int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
参数:第一个参数which指定定时器类型;第二个参数是结构体itimerval的一个实例;第三个参数若不为空则返回先前定时unsleep的时间。
返回值:成功返回0,失败返回-1。

参数 which的取值:

ITIMER_REAL:经过指定的时间后,内核将发送SIGALRM信号给本进程;
ITIMER_VIRTUAL:程序在用户空间执行指定的时间后,内核将发送SIGVTALRM信号给本进程;
ITIMER_PROF:进程在用户空间执行和内核空间执行时,时间计数都会减少,通常与ITIMER_VIRTUAL共用,代表进程在用户空间与内核空间中运行指定时间后,内核将发送SIGPROF信号给本进程。