019 UNIX再学习 -- 信号

                                          
                                                                                   
                                                                                
                                           

终于讲到信号部分,很多比较重要的应用程序都需处理信号。第 9 章需要先了解信号机制再看,所以先跳过不讲。现在开始详解信号。

一、信号概念

信号是提供异步事件处理机制的软件中断。

这些异步事件可能来自硬件设备,如用户同时按下 Ctrl+ C 键,也可能来自系统内核,如试图访问尚未映射的虚拟内存,又或者来自用户进程,如尝试计算整数除 0 的表达式。进程之间可以相互发送信号,这使信号称为一种进程间通信的基本手段

信号的异步特性不仅表现为它的产生是异步的,对它的处理同样也是异步的。程序的设计者不可能也不需要精确地预见什么时候触发什么信号,也同样无法预见该信号究竟在什么时候会被处理。一切都在内核的操控下,异步地运行。信号是软件层面对中断机制的一种模拟。


这里简单提一句什么是中断?

中断就是指暂时停止当前程序的执行转而去执行新的程序或者处理出现的意外情况。

中断分为:软件中断 / ctrl+c 和 硬件中断 / 电源拔出。

二、信号处理

信号有一个非常明确的生命周期。

首先,信号被生成,并发送至系统内核。

其次,系统内核存储信号,直到可以处理它。

最后,一旦有空闲,内核即按以下三种方式之一处理信号。

(1)忽略信号:什么也不做。大多数信号都可使用这种方式进行处理,但 SIGKILL 和 SIGTOP 信号决不能被忽略其原因是:它们向内核和超级用户提供了使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(如非法内存引用或除以 0),则进程的运行行为是未定义的。

(2)捕获信号:内核暂停收到信号的进程正在执行的代码,跳转到事先注册的信号处理函数,执行该函数并返回,跳转到捕获信号的地方继续执行。SIGKILL 和 SIGTOP 信号不能捕获

(3)默认操作:不同信号有不同的默认操作,对大多数信号的系统默认动作是终止该进程,但也有一些信号的默认操作是视而不见的,即忽略。

三、信号名称与编号

每个信号都有唯一的名称和编号。信号的名称是以 SIG 开头的文本,如 SIGHUP、SIGINT 等。信号的编号是从 1 开始连续增加的整数,如 1、2、3 等。信号的名称与编号之间的对应关系,依赖于具体实现。

/usr/include/i386-linux-gnu/bits/signum.h 文件中通过宏定义,把每个信号的名称和编号建立了一一映射。


   
   
  1. /* Signals.  */
  2. #define SIGHUP  1 /* Hangup (POSIX).  */
  3. #define SIGINT  2 /* Interrupt (ANSI).  */
  4. #define SIGQUIT  3 /* Quit (POSIX).  */
  5. #define SIGILL  4 /* Illegal instruction (ANSI).  */
  6. #define SIGTRAP  5 /* Trace trap (POSIX).  */
  7. #define SIGABRT  6 /* Abort (ANSI).  */
  8. #define SIGIOT  6 /* IOT trap (4.2 BSD).  */
  9. #define SIGBUS  7 /* BUS error (4.2 BSD).  */
  10. #define SIGFPE  8 /* Floating-point exception (ANSI).  */
  11. #define SIGKILL  9 /* Kill, unblockable (POSIX).  */
  12. #define SIGUSR1  10 /* User-defined signal 1 (POSIX).  */
  13. #define SIGSEGV  11 /* Segmentation violation (ANSI).  */
  14. #define SIGUSR2  12 /* User-defined signal 2 (POSIX).  */
  15. #define SIGPIPE  13 /* Broken pipe (POSIX).  */
  16. #define SIGALRM  14 /* Alarm clock (POSIX).  */
  17. #define SIGTERM  15 /* Termination (ANSI).  */
  18. #define SIGSTKFLT 16 /* Stack fault.  */
  19. #define SIGCLD  SIGCHLD /* Same as SIGCHLD (System V).  */
  20. #define SIGCHLD  17 /* Child status has changed (POSIX).  */
  21. #define SIGCONT  18 /* Continue (POSIX).  */
  22. #define SIGSTOP  19 /* Stop, unblockable (POSIX).  */
  23. #define SIGTSTP  20 /* Keyboard stop (POSIX).  */
  24. #define SIGTTIN  21 /* Background read from tty (POSIX).  */
  25. #define SIGTTOU  22 /* Background write to tty (POSIX).  */
  26. #define SIGURG  23 /* Urgent condition on socket (4.2 BSD).  */
  27. #define SIGXCPU  24 /* CPU limit exceeded (4.2 BSD).  */
  28. #define SIGXFSZ  25 /* File size limit exceeded (4.2 BSD).  */
  29. #define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD).  */
  30. #define SIGPROF  27 /* Profiling alarm clock (4.2 BSD).  */
  31. #define SIGWINCH 28 /* Window size change (4.3 BSD, Sun).  */
  32. #define SIGPOLL  SIGIO /* Pollable event occurred (System V).  */
  33. #define SIGIO  29 /* I/O now possible (4.2 BSD).  */
  34. #define SIGPWR  30 /* Power failure restart (System V).  */
  35. #define SIGSYS  31 /* Bad system call.  */
  36. #define SIGUNUSED 31
当然,我们之前讲 kill 指令时,参看:UNIX再学习 -- ps、top、kill 指令  讲到,执行 kill -l 命令,可以查看系统支持的信号列表。


   
   
  1. # kill -l 
  2. 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP 
  3. 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1 
  4. 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 
  5. 16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 
  6. 21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ 
  7. 26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR 
  8. 31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+ 1  36) SIGRTMIN+ 2  37) SIGRTMIN+ 3 
  9. 38) SIGRTMIN+ 4  39) SIGRTMIN+ 5  40) SIGRTMIN+ 6  41) SIGRTMIN+ 7  42) SIGRTMIN+ 8 
  10. 43) SIGRTMIN+ 9  44) SIGRTMIN+ 10 45) SIGRTMIN+ 11 46) SIGRTMIN+ 12 47) SIGRTMIN+ 13 
  11. 48) SIGRTMIN+ 14 49) SIGRTMIN+ 15 50) SIGRTMAX -14 51) SIGRTMAX -13 52) SIGRTMAX -12 
  12. 53) SIGRTMAX -11 54) SIGRTMAX -10 55) SIGRTMAX -9  56) SIGRTMAX -8  57) SIGRTMAX -7 
  13. 58) SIGRTMAX -6  59) SIGRTMAX -5  60) SIGRTMAX -4  61) SIGRTMAX -3  62) SIGRTMAX -2 
  14. 63) SIGRTMAX -1  64) SIGRTMAX     

列表中,编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。 总共 62 个信号,注意没有 32 和 33 信号。 也不存在编号为 0 的信号,POSIX.1 将此信号编号值称为空信号, 这部分后面会详解讲到的。

四、逐一说明这些信号

上面提到默认操作对大多数信号的系统默认动作是终止该进程,但也有一些信号的默认操作是视而不见的,即忽略。

下面给出对每一种信号的系统默认动作:


上图列出了所有信号的名字,说明了哪些系统支持此信号以及对于这些信号的系统默认动作。在 SUS 列中,“.”表示此种信号定义为基本 POSIX.1 规范部分,“XSI”表示该信号定义在 XSI 扩展部分。

在系统默认动作列,“终止+core”表示在进程当前工作目录的 core 文件中复制了该进程的内存映像(该文件名为 core,由此可以看出这种功能很久之前就是 UNIX 的一部分)。大多数 UNIX 系统调试程序都使用 core 文件检查进程终止时的状态

其中在 Linux 3.2.0 中,core 文件名通过 /proc/sys/kernel/core_pattern 进行配置。


   
   
  1. /proc/sys/kernel # cat core_pattern
  2. |/usr/share/apport/apport %p %s %c


这里出现了 core 文件 是什么东东?

参看:Core文件作用、设置及用法

1、core文件简介

core 文件起始就是内存的映像,当程序崩溃时,存储内存的相应信息,主要用于对程序进行调试。当程序崩溃时便会产生 core 文件,其实准确的应该说 core dump 文件,默认生成位置与可执行程序位于同一目录下,文件名 core.***,其中 *** 是某一数字。

2、开启或关闭 core 文件的生成

(1)关闭或阻止 core 文件生成:

# ulimit -c 0

   
   
(2)打开 core 文件生成:

# ulimit -c unlimited

   
   
(3)检查 core 文件的选项是否打开:


   
   
  1. # ulimit -a
  2. core file size          (blocks, -c) unlimited
  3. data seg size           (kbytes, -d) unlimited
  4. scheduling priority             (-e) 0
  5. file size               (blocks, -f) unlimited
  6. pending signals                 (-i) 7892
  7. max locked memory       (kbytes, -l) 64
  8. max memory size         (kbytes, -m) unlimited
  9. open files                      (-n) 1024
  10. pipe size            (512 bytes, -p) 8
  11. POSIX message queues     (bytes, -q) 819200
  12. real-time priority              (-r) 0
  13. stack size              (kbytes, -s) 8192
  14. cpu time               (seconds, -t) unlimited
  15. max user processes              (-u) 7892
  16. virtual memory          (kbytes, -v) unlimited
  17. file locks                      (-x) unlimited
其中 ulimit 函数我们之前有讲过,参看:UNIX再学习 -- 文件描述符
以上配置只对当前会话起作用,下次重新登录后,还得重新配置。要想配置永久生效,得在 /etc/profile 或者 /etc/security/limits.conf 文件中进行配置,将 0 改为 ulimited。


   
   
  1. #gedit /etc/security/limits.conf
  2. #<domain>      <type>  <item>         <value>
  3. #
  4. #*               soft    core         unlimited
或者在 /etc/profile 中作如下配置:

   
   
  1. #vi /etc/profile
  2. ulimit -S -c unlimited >/dev/null 2>& 1
或者想配置只针对某一用户有效,则修改此用户的 ~/.bashrc 或者~/.bash_profile文件:

ulimit -c unlimited
   
   
ulimit -c 0 是禁止产生core文件,而 ulimit -c 1024 则限制产生的 core 文件的大小不能超过 1024kb。

3. 设置Core Dump的核心转储文件目录和命名规则

/proc/sys/kernel/core_uses_pid 可以控制产生的 core 文件的文件名中是否添加 pid 作为扩展,如果添加则文件内容为1,否则为 0

/proc/sys/kernel/core_pattern 可以设置格式化的 core 文件保存位置或文件名,比如原来文件内容是 core-%e
可以这样修改:
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
将会控制所产生的 core 文件会存放到 /corefile 目录下,产生的文件名为 core-命令名-pid-时间戳

以下是参数列表:


   
   
  1.     %p - insert pid into filename 添加pid
  2.     %u - insert current uid into filename 添加当前uid
  3.     %g - insert current gid into filename 添加当前gid
  4.     %s - insert signal that caused the coredump into the filename 添加导致产生core的信号
  5.     %t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
  6.     %h - insert hostname where the coredump happened into filename 添加主机名
  7.     %e - insert coredumping executable name into filename 添加命令名

4. 使用core文件

在core文件所在目录下键入:

gdb -c core

它会启动 GNU 的调试器,来调试 core 文件,并且会显示生成此 core 文件的程序名,中止此程序的信号等等。
如果你已经知道是由什么程序生成此 core 文件的,比如 MyServer 崩溃了生成 core.12345,那么用此指令调试:
gdb -c core MyServer
gdb使用,参看:C语言再学习 -- GCC编译过程

5. 一个小方法来测试产生core文件

直接输入指令:
kill -s SIGSEGV $$

6. 为何有时程序Down了,却没生成 Core文件。

Linux下,有一些设置,标明了resources available to the shell and to processes。 可以使用
#ulimit -a 来看这些设置。

从这里可以看出,如果 -c 是显示:core file size          (blocks, -c) 

如果这个值为 0,则无法生成core文件。所以可以使用:
#ulimit -c 1024 或者 #ulimit -c unlimited 来使能 core文件。
如果程序出错时生成 core 文件,则会显示 Segmentation fault (core dumped)。

7. Core Dump的核心转储文件目录和命名规则:

/proc/sys/kernel/core_uses_pid 可以控制产生的 core 文件的文件名中是否添加 pid 作为扩展,如果添加则文件内容为1,否则为 0。

看到这里是不是有点蒙,core 文件其实我们之前有接触过的,比上面讲的更清楚。

参看:C语言再学习 -- 段错误(核心已转储)


接着讲信号,在下列条件下不产生 core 文件:

(1)进程是设置用户 ID 的,而且当前用户并非程序文件的所有者。

(2)进程是设置组 ID 的,而且当前用户并非该程序文件的组所有者。

(3)用户没有写当前工作目录的权限。

(4)文件已存在,而且用户对该文件设有写权限。

(5)文件太大。其中 RLIMIT_CORE  表示 core 文件的最大字节数,若其值为 0 则阻止创建 core 文件。


下面较详细地逐一说明这些信号:

参看:linux中的signal机制

1) SIGHUP

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

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

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

这是另一个用户定义的信号,与 SIGUSR1 相似,可用于应用程序。

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

Power failure

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在进程挂起时是继续,否则是忽略,不能被阻塞。


这里顺便提一句:
ctrl-c 是发送 SIGINT 信号,终止一个进程;进程无法再重续。
ctrl-z 是发送 SIGSTOP 信号,挂起一个进程;进程从前台转入后台并暂停,可以用 bg 使其后台继续运行,fg 使其转入前台运行。
ctrl-d 不是发送信号,而是表示一个特殊的二进制值,表示 EOF,通常是表示输入终止,通常进程接收到终止符可以完成运行并退出。

五、产生信号的条件

1、bash按下时,终端会发送信号给前台,Ctrl-C 产生 SIGINT 信号,Ctrl-\产生SIGQUIT信号,Ctrl-Z产生SIGTSTP 信号,上述信号使得进程停止。
2、硬件异常信号,条件由硬件检测到并通知内核,然后内核向当前进程发送信号。例如执行了除以 0 的指令,CPU 的运算单元会产生异常,内核将这个异常解释为 SIGFPE 信号发送给进程。再比如当前进程访问了非法内存地址,MMU 会产生异常,内核将这个异常解释为 SIGSEGV 信号发送给进程。
3、一个进程调用 kill(2) 函数可以发送信号给另一个进程。
4、可以用 kill(1)命令发送信号给某个进程,kill(1)命令也是调用kill(2)函数实现的,如果不明确指定信号则发送 SIGTERM 信号,该信号的默认处理动作是终止进程。
5、当内核检测到某种软件条件发生时也可以通过信号通知进程,例如闹钟超时产生SIGALRM信号,向读端已关闭的管道写数据时产生SIGPIPE信号。

六、signal 函数


     
     
  1. #include <signal.h>
  2. typedef void (*sighandler_t)(int);
  3. sighandler_t signal( int signum, sighandler_t handler);

1、函数功能

设置对指定信号的指定处理方式

2、返回值

成功返回原信号处理方式,失败返回 SIG_ERR

3、参数解析

signum:信号编号
handler:信号处理方式,可取以下值
    SIG_IGN    忽略信号
    SIG_DFL    默认操作
    信号处理函数指针    捕获信号
查看系统头文件 /usr/include/i386-linux-gnu/bits/signum.h,则可以找到下列形式的声明:

     
     
  1. /* Fake signal functions.  */
  2. #define SIG_ERR ((__sighandler_t) -1)  /* Error return.  */
  3. #define SIG_DFL ((__sighandler_t) 0)  /* Default action.  */
  4. #define SIG_IGN ((__sighandler_t) 1)  /* Ignore signal.  */
这些常量可用于表示“ 指向函数的指针,该函数要求一个整型参数,而且无返回值”。signal 的第二个参数及其返回值就可用它们表示。

4、示例说明


     
     
  1. //示例一
  2. #include <stdio.h>
  3. #include <signal.h>
  4. int main (void)
  5. {
  6.  signal (SIGINT, SIG_IGN);
  7.   while ( 1);
  8.   return 0;
  9. }
  10. ctrl + c 将无法结束

     
     
  1. //示例二
  2. #include <stdio.h>
  3. #include <signal.h>
  4. int main (void)
  5. {
  6.  signal (SIGINT, SIG_DFL);
  7.   while ( 1);
  8.   return 0;
  9. }
  10. ctrl + c 可结束

     
     
  1. //示例三
  2. #include <stdio.h>
  3. #include <signal.h>
  4. //自定义信号处理函数
  5. void fa(int signo)
  6. {
  7.   printf( "捕获到了信号%d\n",signo);
  8. }
  9. int main(void)
  10. {
  11.  signal( 2,fa);
  12.   while( 1);
  13.   return 0;
  14. }
  15. ctrl + c 可打印 捕获到了信号 2

     
     
  1. //示例四
  2. #include <stdio.h> 
  3. #include <signal.h> 
  4. #include <stdlib.h>
  5.  
  6. int main (void) 
  7.   //if (signal (SIGKILL, SIG_IGN) == SIG_ERR)
  8.   //if (signal (SIGKILL, SIG_DFL) == SIG_ERR)
  9.   //if (signal (SIGSTOP, SIG_DFL) == SIG_ERR)
  10.   if (signal (SIGSTOP, SIG_IGN) == SIG_ERR)
  11.     perror ( "signal"), exit ( 1); 
  12.   while ( 1); 
  13.   return 0
  14. 输出结果:
  15. signal: Invalid argument

5、示例解析

示例一:使用 SIG_IGN 为忽略信号,所以使用 ctrl+c 终止进程失效
示例二:使用 SIG_DEL 为默认操作,所以有无 signal (SIGINT, SIG_DFL); 都可以使用ctrl+c 终止进程的。
示例三:使用自定义信号处理函数,注意该函数无返回值,参数为整型。SIGINT 可用信号编号 2 代替。
如果想结束,可用 ctrl+\ 即 SIGQUIT 退出。或打印进程号使用 kill -9 PID 杀死进程。 
示例四:说明 SIGKILL (9) 和 SIGSTOP (19) 信号即不能被忽略,也不能被捕获,只能按缺省方式终止或停止接收到信号的进程。如果返回失败则返回 SIG_ERR。

七、信号的分类

可以从两个不同的分类角度对信号进行分类:
(1)可靠性方面,分为可靠信号与不可靠信号
(2)与时间的关系上,分为实时信号与非实时信号。
上面也有提到了,编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。

1、不可靠信号

Linux信号机制基本上是从 UNIX 系统中继承过来的。早期 UNIX 系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做“不可靠信号”,信号小于 SIGRTMN 的信号都是不可靠信号。这就是“不可靠信号”的来源。它的主要问题是:
进程每次处理信号后,就将对信号的响应设为默认动作。在某些情况下,就导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用 signal ( ),重新安装该信号。
信号可能丢失,后面将对此详细阐述。因此,早期 UNIX 下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。
Linux支持不可靠信号,但是对不可靠信号机制做了改进,在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数时在可靠机制上的实现)。因此,Linux 下的不可靠信号问题主要是指的是信号可能丢失

2、可靠信号

随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。所以,后来出现的各种 UNIX 版本分别在这方面进行了研究,力图实现“可靠信号”。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。同时,信号的发送和安装也出现了新版本:信号发送函数 sigqueue() 及信号安装函数 sigaction() 。POSIX.4 对可靠信号机制做了标准化。但是,POSIX 只对可靠信号机制应具有的功能以及信号机制的对外接口做了标准化,对信号机制的实现没有作具体的规定。
信号值位于 SIGRTMIN 和 SIGRTMAX 之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数 sigation()以及信号发送函数 sigqueue ( ) 的同时,仍然支持早期的 signal()信号安装函数,支持信号发送函数 kill()。
注:不要有这样的误解:由 sigqueue() 发送、sigaction 安装的信号就是可靠的。事实上,可靠信号是指后来添加的新信号(信号值位于SIGRTMIN及SIGRTMAX之间);不可靠信号是信号值小于SIGRTMIN的信号信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。目前linux中的 signal() 是通过 sigation() 函数实现的,因此,即使通过 signal()安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由 signal() 安装的实时信号支持排队,同样不会丢失。
对于目前 linux 的两个信号安装函数: signal() 及 sigaction() 来说,它们都不能把 SIGRTMIN 以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对 SIGRTMIN 以后的信号都支持排队。这两个函数的最大区别在于,经过 sigaction 安装的信号都能传递信息给信号处理函数(对所有信号这一点都成立),而经过signal 安装的信号却不能向信号处理函数传递信息。对于信号发送函数来说也是一样的

3、实时信号与非实时信号

当多个相同的信号被发送到正在处理该信号的进程时,那些还来不及处理的信号会按先后顺序排成队列。等进程处理完手里的信号以后,再依次处理队列中的信号。整个过程中,所有发送给进程的信号一个也不会丢,都能得到处理。这样的信号就叫做可靠信号。实时信号都是可靠信号,都支持排队。
反之,不可靠信号不支持排队,可能丢失,在多个相同的信号里进程可能只收到一个。非实时信号都是不可靠信号。
                                   
                                   
               
                   
  •                                                
  •                                                     点赞                         1                        
  •                        
  •                                                     收藏
  •                        
  •                                                     分享
  •                                                                                                                        
  •                                                        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《TCP/IP详解,卷1:协议》(共3卷,其他卷请到我的空间下载)是一本完整而详细的TCP/IP协议指南。描述了属于每一层的各个协议以及它们如何在不同操作系统中运行。作者用Lawrence Berkeley实验室的tcpdump程序来捕获不同操作系统和TCP/IP实现之间传输的不同分组。对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的分层 4 1.4 互联网的地址 5 1.5 域名系统 6 1.6 封装 6 1.7 分用 8 1.8 客户-服务器模型 8 1.9 端口号 9 1.10 标准化过程 10 1.11 RFC 10 1.12 标准的简单服务 11 1.13 互联网 12 1.14 实现 12 1.15 应用编程接口 12 1.16 测试网络 13 1.17 小结 13 第2章 链路层 15 2.1 引言 15 2.2 以太网和IEEE 802封装 15 2.3 尾部封装 17 2.4 SLIP:串行线路IP 17 2.5 压缩的SLIP 18 2.6 PPP:点对点协议 18 2.7 环回接口 20 2.8 最大传输单元MTU 21 2.9 路径MTU 21 2.10 串行线路吞吐量计算 21 2.11 小结 22 第3章 IP:网际协议 24 3.1 引言 24 3.2 IP首部 24 3.3 IP路由选择 27 3.4 子网寻址 30 3.5 子网掩码 32 3.6 特殊情况的IP地址 33 3.7 一个子网的例子 33 3.8 ifconfig命令 35 3.9 netstat命令 36 3.10 IP的未来 36 3.11 小结 37 第4章 ARP:地址解析协议 38 4.1 引言 38 4.2 一个例子 38 4.3 ARP高速缓存 40 4.4 ARP的分组格式 40 4.5 ARP举例 41 4.5.1 一般的例子 41 4.5.2 对不存在主机的ARP请求 42 4.5.3 ARP高速缓存超时设置 43 4.6 ARP代理 43 4.7 免费ARP 45 4.8 arp命令 45 4.9 小结 46 第5章 RARP:逆地址解析协议 47 5.1 引言 47 5.2 RARP的分组格式 47 5.3 RARP举例 47 5.4 RARP服务器的设计 48 5.4.1 作为用户进程的RARP服务器 49 5.4.2 每个网络有多个RARP服务器 49 5.5 小结 49 第6章 ICMP:Internet控制报文协议 50 6.1 引言 50 6.2 ICMP报文的类型 50 6.3 ICMP地址掩码请求与应答 52 6.4 ICMP时间戳请求与应答 53 6.4.1 举例 54 6.4.2 另一种方法 55 6.5 ICMP端口不可达差错 56 6.6 ICMP报文的4.4BSD处理 59 6.7 小结 60 第7章 Ping程序 61 7.1 引言 61 7.2 Ping程序 61 7.2.1 LAN输出 62 7.2.2 WAN输出 63 7.2.3 线路SLIP链接 64 7.2.4 拨号SLIP链路 65 7.3 IP记录路由选项 65 7.3.1 通常的例子 66 7.3.2 异常的输出 68 7.4 IP时间戳选项 69 7.5 小结 70 第8章 Traceroute程序 71 8.1 引言 71 8.2 Traceroute 程序的操作 71 8.3 局域网输出 72 8.4 广域网输出 75 8.5 IP源站选路选项 76 8.5.1 宽松的源站选路的traceroute 程序示例 78 8.5.2 严格的源站选路的traceroute 程序示例 79 8.5.3 宽松的源站选路traceroute程序 的往返路由 80 8.6 小结 81 第9章 IP选路 83 9.1 引言 83 9.2 选路的原理 84 9.2.1 简单路由表 84 9.2.2 初始化路由表 86 9.2.3 较复杂的路由表 87 9.2.4 没有到达目
IT行业中必备技能之UNIX最佳学习资料 目 录 译者序 译者简介 前言 第1章 UNIX基础知识 1 1.1 引言 1 1.2 登录 1 1.2.1 登录名 1 1.2.2 shell 1 1.3 文件和目录 2 1.3.1 文件系统 2 1.3.2 文件名 2 1.3.3 路径名 2 1.3.4 工作目录 4 1.3.5 起始目录 4 1.4 输入和输出 5 1.4.1 文件描述符 5 1.4.2 标准输入、标准输出和标准 出错 5 1.4.3 不用缓存的I/O 5 1.4.4 标准I/O 6 1.5 程序和进程 7 1.5.1 程序 7 1.5.2 进程和进程ID 7 1.5.3 进程控制 7 1.6 ANSI C 9 1.6.1 函数原型 9 1.6.2 类属指针 9 1.6.3 原始系统数据类型 10 1.7 出错处理 10 1.8 用户标识 11 1.8.1 用户ID 11 1.8.2 组ID 12 1.8.3 添加组ID 12 1.9 信号 12 1.10 UNIX时间值 14 1.11 系统调用和库函数 14 1.12 小结 16 习题 16 第2章 UNIX标准化及实现 17 2.1 引言 17 2.2 UNIX标准化 17 2.2.1 ANSI C 17 2.2.2 IEEE POSIX 18 2.2.3 X/Open XPG3 19 2.2.4 FIPS 19 2.3 UNIX实现 19 2.3.1 SVR4 20 2.3.2 4.3+BSD 20 2.4 标准和实现的关系 21 2.5 限制 21 2.5.1 ANSI C限制 22 2.5.2 POSIX限制 22 2.5.3 XPG3限制 24 2.5.4 sysconf、pathconf 和fpathconf 函数 24 2.5.5 FIPS 151-1要求 28 2.5.6 限制总结 28 2.5.7 未确定的运行时间限制 29 2.6 功能测试宏 32 2.7 基本系统数据类型 32 2.8 标准之间的冲突 33 2.9 小结 34 习题 34 第3章 文件I/O 35 3.1 引言 35 3.2 文件描述符 35 3.3 open函数 35 3.4 creat函数 37 3.5 close函数 37 3.6 lseek函数 38 3.7 read函数 40 3.8 write函数 41 3.9 I/O的效率 41 3.10 文件共享 42 3.11 原子操作 45 3.11.1 添加至一个文件 45 3.11.2 创建一个文件 45 3.12 dup和dup2函数 46 3.13 fcntl函数 47 3.14 ioctl函数 50 3.15 /dev/fd 51 3.16 小结 52 习题 52 第4章 文件和目录 54 4.1 引言 54 4.2 stat, fstat和lstat函数 54 4.3 文件类型 55 4.4 设置-用户-ID和设置-组-ID 57 4.5 文件存取许可权 58 4.6 新文件和目录的所有权 60 4.7 access函数 60 4.8 umask函数 62 4.9 chmod和fchmod函数 63 4.10 粘住位 65 4.11 chown, fchown和 lchown函数 66 4.12 文件长度 67 4.13 文件截短 68 4.14 文件系统 69 4.15 link, unlink, remove和rename 函数 71 4.16 符号连接 73 4.17 symlink 和readlink函数 76 4.18 文件的时间 76 4.19 utime函数 78 4.20 mkdir和rmdir函数 79 4.21 读目录 80 4.22 chdir, fchdir和getcwd函数 84 4.23 特殊设备文件 86 4.24 sync和fsync函数 87 4.25 文件存取许可权位小结 88 4.26 小结 89 习题 89 第5章 标准I/O库 91 5.1 引言 91 5.2 流和FILE对象 91 5.3 标准输入、标准输出和标准出错 91 5.4 缓存 91 5.5 打开流 94 5.6 读和写流 96 5.6.1 输入函数 96 5.6.2 输出函数 97 5.7 每次一行I/O 98 5.8 标准I/O的效率 99 5.9 二进制I/O 100 5.10 定位流 102 5.11 格式化I/O 103 5.11.1 格式化输出 103 5.11.2 格式化输入 103 5.12 实现细节 104 5.13 临时文件 105 5.14 标准I/O的替代软件 108 5.15 小结 108 习题 108 第6章 系统数据文件和信息 110 6.1 引言 110 6.2 口令文件 110 6.3 阴影口令 112 6.4 组文件 113 6.5 添加组ID 114 6.6 其他数据文件 115 6.7 登录会计 116 6.8 系统标识 116 6.9 时间和日期例程 117 6.10 小结 121 习题 121 第7章 UNIX进程的环境 122 7.1 引言 122 7.2 main 函数 122 7.3 进程终止 122 7.3.1 exit和_exit函数 122 7.3.2 atexit函数 124 7.4 命令行参数 125 7.5 环境表 126 7.6 C程序的存储空间布局 126 7.7 共享库 127 7.8 存储器分配 128 7.9 环境变量 130 7.10 setjmp 和longjmp函数 132 7.10.1 自动、寄存器和易失变量 134 7.10.2 自动变量的潜在问题 136 7.11 getrlimit 和setrlimit函数 136 7.12 小结 139 习题 140 第8章 进程控制 141 8.1 引言 141 8.2 进程标识 141 8.3 fork函数 142 8.4 vfork 函数 145 8.5 exit函数 147 8.6 wait和waitpid函数 148 8.7 wait3和wait4函数 152 8.8 竞态条件 153 8.9 exec函数 156 8.10 更改用户ID和组ID 160 8.10.1 setreuid 和setregid函数 162 8.10.2 seteuid和 setegid函数 163 8.10.3 组ID 163 8.11 解释器文件 164 8.12 system函数 167 8.13 进程会计 171 8.14 用户标识 175 8.15 进程时间 176 8.16 小结 178 习题 178 第9章 进程关系 180 9.1 引言 180 9.2 终端登录 180 9.2.1 4.3+BSD终端登录 180 9.2.2 SVR4终端登录 182 9.3 网络登录 182 9.3.1 4.3+BSD网络登录 182 9.3.2 SVR4网络登录 183 9.4 进程组 183 9.5 对话期 184 9.6 控制终端 185 9.7 tcgetpgrp 和tcsetpgrp函数 187 9.8 作业控制 187 9.9 shell执行程序 189 9.10 孤儿进程组 193 9.11 4.3+BSD实现 195 9.12 小结 197 习题 197 第10章 信号 198 10.1 引言 198 10.2 信号的概念 198 10.3 signal函数 203 10.3.1 程序起动 205 10.3.2 进程创建 206 10.4 不可靠的信号 206 10.5 中断的系统调用 207 10.6 可再入函数 209 10.7 SIGCLD语义 211 10.8 可靠信号术语和语义 213 10.9 kill和raise函数 213 10.10 alarm和pause函数 214 10.11 信号集 219 10.12 sigprocmask 函数 220 10.13 sigpending函数 222 10.14 sigaction函数 223 10.15 sigsetjmp 和siglongjmp函数 226 10.16 sigsuspend函数 229 10.17 abort函数 234 10.18 system函数 235 10.19 sleep函数 240 10.20 作业控制信号 241 10.21 其他特征 243 10.21.1 信号名字 243 10.21.2 SVR4信号处理程序的附 加参数 244 10.21.3 4.3+BSD信号处理程序的附 加参数 244 10.22 小结 244 习题 244 第11章 终端I/O 246 11.1 引言 246 11.2 综述 246 11.3 特殊输入字符 250 11.4 获得和设置终端属性 254 11.5 终端选择标志 254 11.6 stty命令 258 11.7 波特率函数 259 11.8 行控制函数 260 11.9 终端标识 260 11.10 规范方式 263 11.11 非规范方式 266 11.12 终端的窗口大小 270 11.13 termcap, terminfo和 curses 271 11.14 小结 272 习题 272 第12章 高级I/O 273 12.1 引言 273 12.2 非阻塞I/O 273 12.3 记录锁 275 12.3.1 历史 276 12.3.2 fcntl记录锁 276 12.3.3 锁的隐含继承和释放 280 12.3.4 4.3+BSD的实现 281 12.3.5 建议性锁和强制性锁 284 12.4 流 288 12.4.1 流消息 289 12.4.2 putmsg和putpmsg函数 290 12.4.3 流ioctl操作 291 12.4.4 write至流设备 294 12.4.5 写方式 294 12.4.6 getmsg和getpmsg函数 294 12.4.7 读方式 295 12.5 I/O多路转接 296 12.5.1 select函数 298 12.5.2 poll函数 301 12.6 异步I/O 303 12.6.1 SVR4 303 12.6.2 4.3+BSD 303 12.7 readv和writev函数 304 12.8 readn和writen函数 306 12.9 存储映射I/O 307 12.10 小结 311 习题 311 第13章 精灵进程 312 13.1 引言 312 13.2 精灵进程的特征 312 13.3 编程规则 313 13.4 出错记录 314 13.4.1 SVR4流log驱动程序 315 13.4.2 4.3+BSD syslog设施 316 13.5 客户机-服务器模型 319 13.6 小结 319 习题 319 第14章 进程间通信 320 14.1 引言 320 14.2 管道 320 14.3 popen和pclose函数 325 14.4 协同进程 330 14.5 FIFO 333 14.6 系统V IPC 335 14.6.1 标识符和关键字 336 14.6.2 许可权结构 337 14.6.3 结构限制 337 14.6.4 优点和缺点 337 14.7 消息队列 338 14.8 信号量 342 14.9 共享存储 346 14.10 客户机-服务器属性 351 14.11 小结 353 习题 353 第15章 高级进程间通信 355 15.1 引言 355 15.2 流管道 355 15.3 传送文件描述符 358 15.3.1 SVR4 360 15.3.2 4.3BSD 361 15.3.3 4.3+BSD 364 15.4 open服务器第1版 366 15.5 客户机-服务器连接函数 371 15.5.1 SVR4 372 15.5.2 4.3+BSD 375 15.6 open服务器第2版 378 15.7 小结 385 习题 385 第16章 数据库函数库 386 16.1 引言 386 16.2 历史 386 16.3 函数库 386 16.4 实现概述 388 16.5 集中式或非集中式 390 16.6 并发 391 16.6.1 粗锁 391 16.6.2 细锁 391 16.7 源码 392 16.8 性能 409 16.8.1 单进程的结果 410 16.8.2 多进程的结果 410 16.9 小结 412 习题 412 第17章 与PostScript打印机通信 413 17.1 引言 413 17.2 PostScript通信机制 413 17.3 假脱机打印 415 17.4 源码 417 17.5 小结 434 习题 434 第18章 调制解调器拨号器 435 18.1 引言 435 18.2 历史 435 18.3 程序设计 436 18.4 数据文件 437 18.5 服务器设计 439 18.6 服务器源码 439 18.7 客户机设计 463 18.7.1 终端行规程 463 18.7.2 一个进程还是两个进程 464 18.8 客户机源码 465 18.9 小结 474 习题 474 第19章 伪终端 476 19.1 引言 476 19.2 概述 476 19.2.1 网络登录服务器 477 19.2.2 script程序 478 19.2.3 expect程序 479 19.2.4 运行协同进程 479 19.2.5 观看长时间运行程序的输出 479 19.3 打开伪终端设备 480 19.3.1 SVR4 481 19.3.2 4.3+BSD 482 19.4 pty_fork函数 484 19.5 pty程序 486 19.6 使用pty程序 489 19.6.1 utmp文件 489 19.6.2 作业控制交互 489 19.6.3 检查长时间运行程序的输出 491 19.6.4 script程序 491 19.6.5 运行协同进程 492 19.6.6 用非交互模式驱动交互式 程序 492 19.7 其他特性 494 19.7.1 打包模式 494 19.7.2 远程模式 494 19.7.3 窗口大小变化 495 19.7.4 信号发生 495 19.8 小结 495 习题 495 附录A 函数原型 497 附录B 其他源代码 512 附录C 习题答案 518 参考书目 536

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值