教学课件第四章LINUX进程控制

上传人:ni****g 文档编号:571045336 上传时间:2024-08-08 格式:PPT 页数:87 大小:814KB
返回 下载 相关 举报
教学课件第四章LINUX进程控制_第1页
第1页 / 共87页
教学课件第四章LINUX进程控制_第2页
第2页 / 共87页
教学课件第四章LINUX进程控制_第3页
第3页 / 共87页
教学课件第四章LINUX进程控制_第4页
第4页 / 共87页
教学课件第四章LINUX进程控制_第5页
第5页 / 共87页
点击查看更多>>
资源描述

《教学课件第四章LINUX进程控制》由会员分享,可在线阅读,更多相关《教学课件第四章LINUX进程控制(87页珍藏版)》请在金锄头文库上搜索。

1、第四章 LINUX进程控制 UNIX进程简介 v进程的定义:进程是可并发执行的程序在一个数据集进程的定义:进程是可并发执行的程序在一个数据集合上的运行过程。合上的运行过程。进程的概念理解的核心:运行过程的理解,包括运进程的概念理解的核心:运行过程的理解,包括运行过程中的不同状态的理解。行过程中的不同状态的理解。正确理解进程和程序的关系。正确理解进程和程序的关系。v在在UNIXUNIX中,每个进程在创建时都会被分配一个数据结中,每个进程在创建时都会被分配一个数据结构,称为进程控制块(简称构,称为进程控制块(简称PCBPCB)。)。PCBPCB中包含了很中包含了很多重要的信息,其中最重要的进程多重

2、要的信息,其中最重要的进程IDID(process process IDID)了,在我们最常使用的了,在我们最常使用的X86X86架构上,其变化范围架构上,其变化范围是是0-327670-32767v一个或多个进程可以合起来构成一个进程组一个或多个进程可以合起来构成一个进程组(process groupprocess group),),一个或多个进程组可以合起来一个或多个进程组可以合起来构成一个会话(构成一个会话(sessionsession)。)。 进程号#include/*#include/*提供类型提供类型pid_tpid_t的定义的定义*/*/#include/*#include/*提

3、供函数的定义提供函数的定义*/*/pid_tgetpid(void);pid_tgetpid(void); /*/*获得调用进程的获得调用进程的IDID号号*/*/pid_tgetppid(void);pid_tgetppid(void);/*/*获得调用者父进程的获得调用者父进程的IDID号号*/*/v程序的标识是程序名或文件名;v进程的标识就是进程号,进程号建立了进程和用户之间的联系。LINUX用户标识相应的每一个用户也有一个用户相应的每一个用户也有一个用户ID.ID.通过系统调用通过系统调用getuidgetuid可以得到进程的可以得到进程的所有者的所有者的ID.ID.由于进程要用到一些

4、资源由于进程要用到一些资源, ,而而LinuxLinux对系统资源是进行保护的对系统资源是进行保护的, ,为了获取一定资源进程还有一个有效用户为了获取一定资源进程还有一个有效用户ID.ID.这个这个IDID和系统的资源使用有和系统的资源使用有关关, ,涉及到进程的权限涉及到进程的权限. . 通过系统调用通过系统调用geteuidgeteuid我们可以得到进程的有效用我们可以得到进程的有效用户户ID. ID. 和用户和用户IDID相对应进程还有一个组相对应进程还有一个组IDID和有效组和有效组IDID系统调用系统调用getgidgetgid和和getegidgetegid可以分别得到组可以分别得

5、到组IDID和有效组和有效组IDID创建子进程fork forkfork()系系统统调调用用创创建建一一个个新新的的进进程程, , 叫叫子子进进程程, , 是调用进程的一个复制品是调用进程的一个复制品. . 调用进程叫父进程。调用进程叫父进程。返回值返回值返回值返回值: : : : 调调用用成成功功则则对对子子进进程程返返回回0, 0, 对对父父进进程程返返回回子子进进程程号号, , 这这也也是是最最方方便便的的区区分分父父子子进进程程的的方方法法. . 若若调调用用失失败败则则返回返回-1-1给给父父进进程程, ,不生成子不生成子进进程程. . Fork继承信息fork()fork()的的

6、子子 进进 程程 继继 承承 了了 父父 进进 程程 的的 几几 乎乎 所所 有有 的的 属属 性,包括性,包括: :进程空间及其内容进程空间及其内容实际实际UID,GIDUID,GID和有效和有效UID,GIDUID,GID,附加附加GIDGID环境变量环境变量. .调用调用exec()exec()时的关闭标志时的关闭标志. .UIDUID设置模式比特位设置模式比特位. .GIDGID设置模式比特位设置模式比特位. .进程组号,会话进程组号,会话IDID,控制终端控制终端. .当前工作目录,根目录当前工作目录,根目录. .文件创建掩码文件创建掩码UMASK.UMASK.文件长度限制文件长度限

7、制ULIMIT.ULIMIT.预预定定值值, , 如如优优先先级级和和任任何何其其他他的的进进程程预预定定参参数数, , 根根据据种种类类不不同同决定是否可以继承决定是否可以继承. .还有一些其它属性还有一些其它属性. . Fork后子进程的不同信息 子进程也有与父进程不同的属性包括子进程也有与父进程不同的属性包括子进程也有与父进程不同的属性包括子进程也有与父进程不同的属性包括: : : :进程号进程号, , 子进程号不同与任何一个活动的进程组号子进程号不同与任何一个活动的进程组号. .父进程号父进程号. .子子进进程程继继承承父父进进程程的的文文件件描描述述符符或或流流时时, , 具具有有自

8、自己己的一个拷贝的一个拷贝并且与父进程和其它子进程共享该资源并且与父进程和其它子进程共享该资源. .子进程的用户时间和系统时间被初始化为子进程的用户时间和系统时间被初始化为0.0.子进程的超时时钟设置为子进程的超时时钟设置为0.0.子进程的信号处理函数指针组置为空子进程的信号处理函数指针组置为空. .子进程不继承父进程的记录锁子进程不继承父进程的记录锁. . 关于fork的说明 v在Linux中,创造新进程的方法只有一个,就是fork。其他一些库函数,如system(),实际上也在内部调用了fork。 vfork出错可能有两种原因:(1)当前的进程数已经达到了系统规定的上限,这时errno的值

9、被设置为EAGAIN。(2)系统内存不足,这时errno的值被设置为ENOMEM。 示例参考正文4.3Copy-On-Write的技术介绍vfork的实现并不做一个父进程数据段和堆的完全拷贝,因为在fork之后经常跟随着exec。作为替代,使用了在写时复制( Copy-On-Write, COW)的技术(注意不包括栈空间) 。这些区域由父、子进程共享,fork时内核将它们的存取许可权改变为只读。如果有进程试图修改这些区域,则内核为有关部分,典型的是虚存系统中的“页”,做一个拷贝。Exec替换进程映象vv第一个区别是前四个取路径名作为参数,后两个则取文件名作为参数。第一个区别是前四个取路径名作为

10、参数,后两个则取文件名作为参数。第一个区别是前四个取路径名作为参数,后两个则取文件名作为参数。第一个区别是前四个取路径名作为参数,后两个则取文件名作为参数。当指定当指定当指定当指定filenamefilenamefilenamefilename作为参数时:如果作为参数时:如果作为参数时:如果作为参数时:如果filenamefilenamefilenamefilename中包含中包含中包含中包含/ / / /,则就将其视为路径,则就将其视为路径,则就将其视为路径,则就将其视为路径名;否则就按名;否则就按名;否则就按名;否则就按PATHPATHPATHPATH环境变量,在有关目录中搜寻可执行文件环

11、境变量,在有关目录中搜寻可执行文件环境变量,在有关目录中搜寻可执行文件环境变量,在有关目录中搜寻可执行文件vv第二个区别与参数表的传递有关第二个区别与参数表的传递有关第二个区别与参数表的传递有关第二个区别与参数表的传递有关( ( ( ( l l l l表示列表表示列表表示列表表示列表,v v v v表示矢量表示矢量表示矢量表示矢量) ) ) )。函数函数函数函数execlexeclexeclexecl、execlpexeclpexeclpexeclp和和和和execleexecleexecleexecle要求将新程序的每个命令行参数都说明为一个单独的参要求将新程序的每个命令行参数都说明为一个单

12、独的参要求将新程序的每个命令行参数都说明为一个单独的参要求将新程序的每个命令行参数都说明为一个单独的参数。这种参数表以空指针结尾。对于另外三个函数数。这种参数表以空指针结尾。对于另外三个函数数。这种参数表以空指针结尾。对于另外三个函数数。这种参数表以空指针结尾。对于另外三个函数( ( ( (execveexecveexecveexecve,execvpexecvpexecvpexecvp和和和和execveexecveexecveexecve) ) ) ),则应先构造一个指向各参数的指针数组,然后将该数组地址作则应先构造一个指向各参数的指针数组,然后将该数组地址作则应先构造一个指向各参数的指针

13、数组,然后将该数组地址作则应先构造一个指向各参数的指针数组,然后将该数组地址作为这三个函数的参数。为这三个函数的参数。为这三个函数的参数。为这三个函数的参数。vv最后一个区别与向新程序传递环境表相关。以最后一个区别与向新程序传递环境表相关。以最后一个区别与向新程序传递环境表相关。以最后一个区别与向新程序传递环境表相关。以e e e e结尾的两个函数(结尾的两个函数(结尾的两个函数(结尾的两个函数(execleexecleexecleexecle和和和和execveexecveexecveexecve)可以传递一个指向环境字符串指针数组的指针。其他四个函可以传递一个指向环境字符串指针数组的指针。

14、其他四个函可以传递一个指向环境字符串指针数组的指针。其他四个函可以传递一个指向环境字符串指针数组的指针。其他四个函数则使用调用进程中的数则使用调用进程中的数则使用调用进程中的数则使用调用进程中的environenvironenvironenviron变量为新程序复制现存的环境。变量为新程序复制现存的环境。变量为新程序复制现存的环境。变量为新程序复制现存的环境。#include int execl(const char *path, const char *arg, .);int execv(const char *path, char *const argv);int execle(const

15、 char *path, const char *arg , ., char * const envp);int execve(const char *path, char *const argv , char *const envp);int execlp(const char *file, const char *arg, .);int execvp(const char *file, char *const argv);Exec的六个函数差异 这六个这六个这六个这六个execexec函数的参数很难记忆。函数名中的字符会给我们函数的参数很难记忆。函数名中的字符会给我们函数的参数很难记忆。函

16、数名中的字符会给我们函数的参数很难记忆。函数名中的字符会给我们一些帮助。字母一些帮助。字母一些帮助。字母一些帮助。字母p p表示该函数取表示该函数取表示该函数取表示该函数取filenamefilename作为参数,并且用作为参数,并且用作为参数,并且用作为参数,并且用PATHPATH环境变量寻找可执行文件。字母环境变量寻找可执行文件。字母环境变量寻找可执行文件。字母环境变量寻找可执行文件。字母l l表示该函数取一个参表示该函数取一个参表示该函数取一个参表示该函数取一个参数列表,它与字母数列表,它与字母数列表,它与字母数列表,它与字母v v互斥。互斥。互斥。互斥。v v表示该函数取一个参数数组表

17、示该函数取一个参数数组表示该函数取一个参数数组表示该函数取一个参数数组argvargv 。最后,字母。最后,字母。最后,字母。最后,字母e e表示该函数取表示该函数取表示该函数取表示该函数取e nvpe nvp数组,而不使用当前环数组,而不使用当前环数组,而不使用当前环数组,而不使用当前环境。下图显示了这六个函数之间的区别境。下图显示了这六个函数之间的区别境。下图显示了这六个函数之间的区别境。下图显示了这六个函数之间的区别Exec函数使用说明(一)1.ExecExec的基本实现思想的基本实现思想2.execexec新进程还继承原进程的如下属性新进程还继承原进程的如下属性: :3.附加附加GID

18、GID、进程号进程号、父进程号父进程号、进程组号进程组号、会话号会话号、控制控制终端终端、alarmalarm时钟信号剩下的时间时钟信号剩下的时间、当前工作目录当前工作目录、根目录根目录、文件创建掩码文件创建掩码、资源限制资源限制、用户时间用户时间、系统时间系统时间、子进程用户子进程用户时间时间、子进程系统时间子进程系统时间、记录锁记录锁、进程信号掩码进程信号掩码、信号屏蔽信号屏蔽、 优先级优先级、预定值预定值. .4.在调用这些系统调用前打开的文件指针对新进程来说也是打开在调用这些系统调用前打开的文件指针对新进程来说也是打开的的, ,除非它已定义了除非它已定义了close-on-execcl

19、ose-on-exec标志标志. .打开的文件指针在新进程打开的文件指针在新进程中保持不变中保持不变, ,所有相关的文件锁也被保留所有相关的文件锁也被保留. .调用进程设置并正被调用进程设置并正被捕俘的信号在新进程中被恢复为缺省设置捕俘的信号在新进程中被恢复为缺省设置, ,其它的则保持不变其它的则保持不变. .新进程启动时按文件的新进程启动时按文件的SUIDSUID和和SGIDSGID设置定义文件的设置定义文件的UIDUID和和GIDGID为有效为有效UIDUID和和GID.GID. Exec函数使用说明(二)vv在在execexec前后实际用户前后实际用户IDID和实际组和实际组IDID保持

20、不变,而有效保持不变,而有效IDID是否改变则取决于是否改变则取决于所执行程序的文件的设置所执行程序的文件的设置- -用户用户- -IDID位和设置位和设置- -组组- -IDID位是否设置。如果新位是否设置。如果新程序的该位已设置,则有效用户程序的该位已设置,则有效用户IDID变成程序文件所有者的变成程序文件所有者的IDID,否则有效否则有效用户用户IDID不变。对组不变。对组IDID的处理方式与此相同。的处理方式与此相同。v例例: :编译核心代码为下面的程序编译核心代码为下面的程序, ,形成目标代码形成目标代码a.outa.out printfprintf(uiduid=%d,=%d,eu

21、ideuid=%dn,=%dn,getuidgetuid(),(),geteuidgeteuid();();1.1.1.1.$ id$ id$ id$ id查看当前用户查看当前用户查看当前用户查看当前用户uiduiduiduid和和和和gidgidgidgiduiduiduiduid=1028(=1028(=1028(=1028(zjtzjtzjtzjt) ) ) ) gid gid gid gid=1028(=1028(=1028(=1028(zjtzjtzjtzjt) groups=1028() groups=1028() groups=1028() groups=1028(zjtzjtz

22、jtzjt) ) ) )2.2.2.2.$ $ $ $ lslslsls l a.out;a.out l a.out;a.out l a.out;a.out l a.out;a.out;执行程序,显示与执行程序,显示与执行程序,显示与执行程序,显示与idididid命令一样的结果命令一样的结果命令一样的结果命令一样的结果- - - -rwxrwxrrwxrwxrrwxrwxrrwxrwxr-x 1-x 1-x 1-x 1 zjt zjt zjt zjt zjt zjt zjt zjt 11585 Mar 25 11:38 a.out11585 Mar 25 11:38 a.out11585 M

23、ar 25 11:38 a.out11585 Mar 25 11:38 a.outuiduiduiduid=1028,=1028,=1028,=1028,euideuideuideuid=1028=1028=1028=10283.3.3.3.$ $ $ $su su su su root root root root;进入根用户进入根用户进入根用户进入根用户# # # #chmod chmod chmod chmod +s a.out+s a.out+s a.out+s a.out;chownchownchownchown root a.out;exit root a.out;exit roo

24、t a.out;exit root a.out;exit改变用户执行时改变用户执行时改变用户执行时改变用户执行时uiduiduiduid和和和和gidgidgidgid,设置用户的设置用户的设置用户的设置用户的ownerownerownerowner为根并退出根用户为根并退出根用户为根并退出根用户为根并退出根用户4.4.4.4.$ $ $ $ lslslsls l a.out l a.out l a.out l a.out重新显示程序执行权限重新显示程序执行权限重新显示程序执行权限重新显示程序执行权限- - - -rwsrwsrrwsrwsrrwsrwsrrwsrwsr-x 1 root -x

25、 1 root -x 1 root -x 1 root zjt zjt zjt zjt 11585 Mar 25 11:38 a.out11585 Mar 25 11:38 a.out11585 Mar 25 11:38 a.out11585 Mar 25 11:38 a.out5.5.5.5.$ a.out $ a.out $ a.out $ a.out 执行程序,发现程序的执行程序,发现程序的执行程序,发现程序的执行程序,发现程序的euideuideuideuid已经改变已经改变已经改变已经改变uiduiduiduid=1028,=1028,=1028,=1028,euideuideuid

26、euid=0=0=0=0 Exec函数使用说明(三)vLinuxLinux调用调用execexec的两个时机的两个时机vv每当有进程认为自己不能为系统和用户做出任何贡献了,每当有进程认为自己不能为系统和用户做出任何贡献了,就可以调用任何一个就可以调用任何一个execexec;vv或者一个进程想执行另一个程序,它就可以或者一个进程想执行另一个程序,它就可以forkfork出一个新出一个新进程,然后进程,然后立即立即调用任何一个调用任何一个execexec,这样看起来就好像这样看起来就好像通过执行应用程序而产生了一个新进程一样。通过执行应用程序而产生了一个新进程一样。一般,一般,forkfork后

27、立即调用后立即调用execexec,可以最少限度减少空间复制操作。可以最少限度减少空间复制操作。v常见错误常见错误vv找不到文件或路径,此时找不到文件或路径,此时errnoerrno被设置为被设置为ENOENTENOENT;vv数组数组argvargv和和envpenvp忘记用忘记用NULLNULL结束,此时结束,此时errnoerrno被设置为被设置为EFAULTEFAULT;vv没有对要执行文件的运行权限,此时没有对要执行文件的运行权限,此时errnoerrno被设置为被设置为EACCESEACCES。 实例实例4.94.9systemvsystemsystem将将 参参 数数 strin

28、gstring传传 递递 给给 一一 个个 命命 令令 解解 释释 器器 ( (一一 般般 为为/ /bin/bin/shsh) )执执行行, , 即即stringstring被被解解释释为为一一条条命命令令, , 由由shsh执执行行该该命命令令. .若若参参数数stringstring为为一一个个空空指指针针则则为为检检查查命命令令解解释释器器是是否否存存在在。该该命命令令可可以以同同命命令令行行命命令令相相同同形形式式, , 但但由由于于命命令令做做为为一一个个参参数数放放在在系系统统调调用用中中, , 应应注注意意编编译译时时对对特特殊殊意意义义字字符符的的处处理理. . 命命令令的的

29、查查找找是是按按PATHPATH环环境境变变量量的的定定义义的的,命命令令所生成的后果一般不会对父进程造成影响所生成的后果一般不会对父进程造成影响. .v命命令令执执行行期期间间,SIGCHLDSIGCHLD信信号号被被阻阻塞塞, , SIGINTSIGINT和和 SIGQUITSIGQUIT信号被忽略信号被忽略v返返返返回回回回值值值值: : : :当当参参数数为为空空指指针针时时, ,只只有有当当命命令令解解释释器器有有效效时时返返回回值值为为非非零零. .若若参参数数不不为为空空指指针针, ,返返回回值值为为该该命命令令的的返返回回状状态态( (同同waitpidwaitpid()()的

30、的返返回回值值. .命命令令无无效效或或语语法法错错误误则则返返回回非非零零值值, ,所执行的命令被终止所执行的命令被终止. .其他情况则返回其他情况则返回-1.-1. 注:注:systemsystem接口的本质是接口的本质是fork+execfork+exec,但其执行的开销更大但其执行的开销更大#include int system(const char *string);atexit()按照按照ANSI CANSI C的规定,一个进程可以登记多至的规定,一个进程可以登记多至3232个函数,这个函数,这些函数将由些函数将由exitexit自动调用。我们称这些函数为终止处理程自动调用。我们称

31、这些函数为终止处理程序(序(exit handlerexit handler),并用),并用atexitatexit函数来登记这些函数。函数来登记这些函数。其中,其中,atexitatexit的参数是一个函数地址,当调用此函数时无需的参数是一个函数地址,当调用此函数时无需向它传送任何参数,也不期望它返回一个值。向它传送任何参数,也不期望它返回一个值。vexitexit以登记这些函数的相反顺序调用它们(类似于压栈和以登记这些函数的相反顺序调用它们(类似于压栈和弹栈方式)。同一函数如若登记多次,则也被调用多次。弹栈方式)。同一函数如若登记多次,则也被调用多次。v终止处理程序这一机制由终止处理程序这

32、一机制由ANSI CANSI C最新引进。最新引进。SVR4SVR4和和4.3+4.3+BSDBSD都提供这种机制,都提供这种机制,LINUXLINUX同样支持。同样支持。v根据根据ANSICANSIC和和POSIX.1POSIX.1,exitexit首先调用各终止处理程序,然首先调用各终止处理程序,然后按需多次调用后按需多次调用fclosefclose,关闭所有打开流。关闭所有打开流。#include #include #include #include int atexit(void (* int atexit(void (* int atexit(void (* int atexit(v

33、oid (* f u n cf u n cf u n cf u n c) ( v o i d ) ) ;) ( v o i d ) ) ;) ( v o i d ) ) ;) ( v o i d ) ) ;返回:若成功则为返回:若成功则为0 0,若出错则为非,若出错则为非0 0atexit示例$ a . o u t$ a . o u tmain is donemain is donefirst exit handlerfirst exit handlerfirst exit handlerfirst exit handlersecond exit handlersecond exit hand

34、lerexit和_exit 两者的作用都是用来终止当前进程。无论在程序中的什么位两者的作用都是用来终止当前进程。无论在程序中的什么位置,只要执行到置,只要执行到exitexit系统调用,进程就会停止剩下的所有操系统调用,进程就会停止剩下的所有操作,清除包括作,清除包括PCBPCB在内的各种数据结构,终止本进程的运行。在内的各种数据结构,终止本进程的运行。v和和exitexit比较一下,比较一下,exit()exit()函函数定义在数定义在stdlibstdlib.h.h中,而中,而_ _exitexit()()定义在定义在unistdunistd.h.h中,其功中,其功能差别主要体现在两点:能

35、差别主要体现在两点:1)exitexit按后进先出的顺序依次按后进先出的顺序依次调用调用atexitatexit()()登记的函数并登记的函数并执行执行2)检查文件的打开情况,把文检查文件的打开情况,把文件缓冲区中的内容写回文件件缓冲区中的内容写回文件exitexit和和_exit_exit在在LinuxLinux函数库中的原型是:函数库中的原型是:#include#include#include#includevoidexit(intstatus);voidexit(intstatus);voidexit(intstatus);voidexit(intstatus);#include#inc

36、lude#include#includevoid_exit(intstatus);void_exit(intstatus);void_exit(intstatus);void_exit(intstatus); 为何?exit对程序的影响 在在LinuxLinux的标准函数库中,有一套称作的标准函数库中,有一套称作“高级高级I/O”I/O”的函数:的函数:printf()printf()、fopen()fopen()、fread()fread()、fwrite()fwrite(),它们也被称作,它们也被称作“缓冲缓冲I/OI/O(buffered I/Obuffered I/O)”,其特征是对应

37、每一个打开的文,其特征是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时,会多读出若件,在内存中都有一片缓冲区,每次读文件时,会多读出若干条记录,这样下次读文件时就可以直接从内存的缓冲区中干条记录,这样下次读文件时就可以直接从内存的缓冲区中读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足了一定的条件(达到一定数量,或遇到特定字符,如等满足了一定的条件(达到一定数量,或遇到特定字符,如换行符换行符nn和文件结束符和文件结束符EOFEOF),再将缓冲区中的内容一次性写),再将缓冲区中的内容一次性写入文件,这样就大大增加了文件读

38、写的速度,但也为我们编入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦。程带来了一点点麻烦。如果有一些数据,我们认为已经写入了文件,实际上因为没如果有一些数据,我们认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时我们有满足特定的条件,它们还只是保存在缓冲区内,这时我们用用_exit()_exit()函数直接将进程关闭,缓冲区中的数据就会丢失,函数直接将进程关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用反之,如果想保证数据的完整性,就一定要使用exit()exit()函数。函数。实例: 第一个输出:第一个输出:out

39、putbeginoutputbegincontentinbuffercontentinbuffer 第二个输出:第二个输出:outputbeginoutputbegin注:注:由于缓冲的大小不同和系统设置的刷新值不同,输出信息由于缓冲的大小不同和系统设置的刷新值不同,输出信息多少可能会有差异多少可能会有差异/*2.c*/#includemain()printf(outputbeginn);printf(contentinbuffer);_exit(0);_exit(0); /*1.c*/#includemain()printf(outputbeginn);printf(contentinbuf

40、fer);exit(0); 典型C进程终止状态图 v下图显示了一个下图显示了一个C C程序是如何起动的,以及它终止的各种方式程序是如何起动的,以及它终止的各种方式 进程的僵尸体状态vv事实上,在一个进程调用了事实上,在一个进程调用了exitexit之后,该进程并非马上就消失掉,而是留之后,该进程并非马上就消失掉,而是留下一个称为僵尸进程(下一个称为僵尸进程(ZombieZombie)的数据结构。在的数据结构。在LinuxLinux进程的进程的5 5种状态中,种状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何

41、可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何进程空间。从这点来看,僵尸进程对系统没有影响。进程空间。从这点来看,僵尸进程对系统没有影响。示例示例示例示例 vv僵尸进程的由来:僵尸进程的由来:LINUXLINUX中僵尸进程的概念也是从传统中僵尸进程的概念也是从传统UNIXUNIX上继承而来上继承而来vv僵尸进程的作用僵尸进程的作用僵尸进程中保存着很多对程序员和系统管理员非常重要的信息,包

42、括:僵尸进程中保存着很多对程序员和系统管理员非常重要的信息,包括: 这个进程是怎么死亡的?是正常退出呢,还是出现了错误,还是被其它进程这个进程是怎么死亡的?是正常退出呢,还是出现了错误,还是被其它进程强迫退出的?强迫退出的? 这个进程占用的总系统这个进程占用的总系统CPUCPU时间和总用户时间和总用户CPUCPU时间分别是多少?发生页错误的时间分别是多少?发生页错误的数目和收到信号的数目。数目和收到信号的数目。这些信息都被存储在僵尸进程中,试想如果没有僵尸进程,进程一退出,所有与这些信息都被存储在僵尸进程中,试想如果没有僵尸进程,进程一退出,所有与之相关的信息都立刻归于无形,而此时程序员或系统

43、管理员需要用到,就没之相关的信息都立刻归于无形,而此时程序员或系统管理员需要用到,就没有办法了有办法了 问题:由于系统中的主存空间一定,如果系统中僵尸进程一直驻留的的话,问题:由于系统中的主存空间一定,如果系统中僵尸进程一直驻留的的话,那么,系统资源将很快耗尽(特别是早期计算机系统中内存都很小)!那么,系统资源将很快耗尽(特别是早期计算机系统中内存都很小)!UNIXUNIX系统中怎样解决此问题?系统中怎样解决此问题? /*zombie.c*/*zombie.c*/#include#include#include#includemain()main()pid_tpid;pid_tpid;pid=

44、fork();pid=fork();if(pid0)if(pid0)/*/*如果出错如果出错*/*/printf(erroroccurred!n);printf(erroroccurred!n);elseif(pid=0)/*elseif(pid=0)/*如果是子进程如果是子进程*/*/exit(0);exit(0);elseelse/*/*如果是父进程如果是父进程*/*/sleep(60);sleep(60);/*/*休眠休眠6060秒,这段时间里,父进程什么秒,这段时间里,父进程什么也干不了也干不了*/*/wait(NULL);wait(NULL); /*/*收集僵尸进程收集僵尸进程*/*

45、/ $cczombie.c-ozombie$cczombie.c-ozombie$./zombie&11577$./zombie&11577$ps-ax$ps-ax1177pts/0S0:00-bash1177pts/0S0:00-bash1577pts/0S0:00./zombie1577pts/0S0:00./zombie1578pts/0Z0:00zombie1578pts/0Z0:00zombie 1579pts/0R0:00psax1579pts/0R0:00psax说明:说明:进程状态一栏中间的进程状态一栏中间的Z状态就是状态就是僵尸进程的标志,它表示僵尸进程的标志,它表示1578

46、号进程现在号进程现在就是一个僵尸进程就是一个僵尸进程 示例示例WaitvvWaitWaitWaitWait的作用是等待子进程结束,由的作用是等待子进程结束,由的作用是等待子进程结束,由的作用是等待子进程结束,由waitwaitwaitwait自动分析是否当前进程的某个子进自动分析是否当前进程的某个子进自动分析是否当前进程的某个子进自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,waitwai

47、twaitwait就会就会就会就会收集这个子进程的信息,并把它彻底回收后返回;如果没有找到这样一个收集这个子进程的信息,并把它彻底回收后返回;如果没有找到这样一个收集这个子进程的信息,并把它彻底回收后返回;如果没有找到这样一个收集这个子进程的信息,并把它彻底回收后返回;如果没有找到这样一个子进程,子进程,子进程,子进程,waitwaitwaitwait就会一直阻塞在这里,直到有一个出现为止。就会一直阻塞在这里,直到有一个出现为止。就会一直阻塞在这里,直到有一个出现为止。就会一直阻塞在这里,直到有一个出现为止。vv参数参数参数参数statusstatus用来保存被收集进程退出时的一些状态,它是一

48、个指向用来保存被收集进程退出时的一些状态,它是一个指向用来保存被收集进程退出时的一些状态,它是一个指向用来保存被收集进程退出时的一些状态,它是一个指向intint类型类型类型类型的指针。但如果对这个子进程是如何死掉的毫不在意,只想把这个僵尸进的指针。但如果对这个子进程是如何死掉的毫不在意,只想把这个僵尸进的指针。但如果对这个子进程是如何死掉的毫不在意,只想把这个僵尸进的指针。但如果对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,我们就可以设定这个参数为程消灭掉,我们就可以设定这个参数为程消灭掉,我们就可以设定这个参数为程消灭掉,我们就可以设定这个参数为NULLNULL,如果成功,如

49、果成功,如果成功,如果成功,waitwait会返回被会返回被会返回被会返回被收集的子进程的进程收集的子进程的进程收集的子进程的进程收集的子进程的进程IDID,如果调用进程没有子进程,调用就会失败,此时如果调用进程没有子进程,调用就会失败,此时如果调用进程没有子进程,调用就会失败,此时如果调用进程没有子进程,调用就会失败,此时waitwait返回返回返回返回-1-1,同时,同时,同时,同时errnoerrno被置为被置为被置为被置为ECHILDECHILD。 vv用用用用waitwaitwaitwait的功能我们可以实现父进程和子进程之间的同步,如:父进程的运的功能我们可以实现父进程和子进程之间

50、的同步,如:父进程的运的功能我们可以实现父进程和子进程之间的同步,如:父进程的运的功能我们可以实现父进程和子进程之间的同步,如:父进程的运算结果进行下一步的运算,或者子进程的功能是为父进程提供了下一步执算结果进行下一步的运算,或者子进程的功能是为父进程提供了下一步执算结果进行下一步的运算,或者子进程的功能是为父进程提供了下一步执算结果进行下一步的运算,或者子进程的功能是为父进程提供了下一步执行的先决条件(如:子进程建立文件,而父进程写入数据),此时父进程行的先决条件(如:子进程建立文件,而父进程写入数据),此时父进程行的先决条件(如:子进程建立文件,而父进程写入数据),此时父进程行的先决条件(

51、如:子进程建立文件,而父进程写入数据),此时父进程就必须在某一个位置停下来,等待子进程运行结束。这种情况称为进程之就必须在某一个位置停下来,等待子进程运行结束。这种情况称为进程之就必须在某一个位置停下来,等待子进程运行结束。这种情况称为进程之就必须在某一个位置停下来,等待子进程运行结束。这种情况称为进程之间的同步,更准确地说,这是进程同步的一种特例。间的同步,更准确地说,这是进程同步的一种特例。间的同步,更准确地说,这是进程同步的一种特例。间的同步,更准确地说,这是进程同步的一种特例。进程回收的两个重要提示:进程回收的两个重要提示:进程回收的两个重要提示:进程回收的两个重要提示:vvShell

52、Shell负责回收所有前台进程负责回收所有前台进程负责回收所有前台进程负责回收所有前台进程vvInitInit进程负责回收所有后台进程和其它用户未回收的进程进程负责回收所有后台进程和其它用户未回收的进程进程负责回收所有后台进程和其它用户未回收的进程进程负责回收所有后台进程和其它用户未回收的进程#include/*提供类型pid_t的定义*/#includepid_twait(int*status) Wait中的status参数 如果参数如果参数statusstatus的值不是的值不是NULLNULL,waitwait就会把子进程退出时的状态取出并存就会把子进程退出时的状态取出并存入其中,这是一

53、个整数值(入其中,这是一个整数值(intint),指出了子进程是正常退出还是被非正常),指出了子进程是正常退出还是被非正常结束的(一个进程也可以被其他进程用信号结束),以及正常结束时的返结束的(一个进程也可以被其他进程用信号结束),以及正常结束时的返回值,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的回值,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏来完成这项工作,下面介绍其中最常用的两个:专门的宏来完成这项工作,下面介绍其中最常用的

54、两个:vvWIFEXITED(status)WIFEXITED(status)这个宏用来指出子进程是否为正常退出的,如果是,这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。它会返回一个非零值。注意:注意:虽然名字一样,这里的参数虽然名字一样,这里的参数statusstatus并不同于并不同于waitwait唯一的参数唯一的参数- -指向整指向整数的指针数的指针statusstatus,而是那个指针所指向的整数,切记不要搞混了。)而是那个指针所指向的整数,切记不要搞混了。)vvWEXITSTATUS(status)WEXITSTATUS(status)当当WIFEXITEDWI

55、FEXITED返回非零值时,我们可以用这个宏返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用来提取子进程的返回值,如果子进程调用exit(5)exit(5)退出,退出,WEXITSTATUS(status)WEXITSTATUS(status)就会返回就会返回5 5;如果子进程调用;如果子进程调用exit(7)exit(7),WEXITSTATUS(status)WEXITSTATUS(status)就会返回就会返回7 7。请注意,如果进程不是正常退出的,。请注意,如果进程不是正常退出的,也就是说,也就是说,WIFEXITEDWIFEXITED返回返回0 0,这个值就毫无意义

56、。,这个值就毫无意义。 waitpid 从本质上讲,系统调用从本质上讲,系统调用waitpidwaitpid和和waitwait的作用是完全相同的,的作用是完全相同的,但但waitpidwaitpid多出了两个可由用户控制的参数多出了两个可由用户控制的参数pidpid和和optionsoptions,从,从而为我们编程提供了另一种更灵活的方式。而为我们编程提供了另一种更灵活的方式。参数参数pidpid定义:定义:1.pidpid00时,只等待进程时,只等待进程IDID等于等于pidpid的子进程,不管其它已经的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有有多少子进程运

57、行结束退出了,只要指定的子进程还没有结束,结束,waitpidwaitpid就会一直等下去。就会一直等下去。 2.pidpid=-1=-1时,等待任何一个子进程退出,没有任何限制,此时,等待任何一个子进程退出,没有任何限制,此时时waitpidwaitpid和和waitwait的作用一模一样。的作用一模一样。 3.pidpid=0=0时,等待同一个进程组中的任何子进程,如果子进程时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,已经加入了别的进程组,waitpidwaitpid不会对它做任何理睬。不会对它做任何理睬。4.pidpid-1-1时,等待一个指定进程组中的任何子进程

58、,这个进时,等待一个指定进程组中的任何子进程,这个进程组的程组的IDID等于等于pidpid的绝对值。的绝对值。#include/*提供类型pid_t的定义*/#includepid_twaitpid(pid_tpid,int*status,intoptions) waitpid 参数参数optionsoptions定义:定义: optionsoptions提供了一些额外的选项来控制提供了一些额外的选项来控制waitpidwaitpid,LinuxLinux中主要的有中主要的有WNOHANGWNOHANG和和WUNTRACEDWUNTRACED两个选项两个选项 vvWNOHANGWNOHANG

59、:指示即使没有子进程退出,它也会立即返回,不会像指示即使没有子进程退出,它也会立即返回,不会像waitwait那样永远等下去。可用于在程序中插入检测。那样永远等下去。可用于在程序中插入检测。vvWUNTRACEDWUNTRACED:指示子进程被跟踪停止时也返回,用于实现调试控制。指示子进程被跟踪停止时也返回,用于实现调试控制。 waitpidwaitpid的返回值比的返回值比waitwait稍微复杂一些,一共有稍微复杂一些,一共有3 3种情况:种情况:vv当正常返回的时候,当正常返回的时候,waitpidwaitpid返回收集到的子进程的进程返回收集到的子进程的进程IDID; vv如果设置了选

60、项如果设置了选项WNOHANGWNOHANG,而调用中而调用中waitpidwaitpid发现没有已退出的子进发现没有已退出的子进程可收集,则返回程可收集,则返回0 0; vv如果调用中出错,则返回如果调用中出错,则返回-1-1,这时,这时errnoerrno会被设置成相应的值以指示错误会被设置成相应的值以指示错误所在;所在; 当当pidpid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpidwaitpid就会出错返回,这时就会出错返回,这时errnoerrno被设置为被设置为ECHILDECHILD; #in

61、clude/*提供类型pid_t的定义*/#includepid_twaitpid(pid_tpid,int*status,intoptions) 更改用户和组ID setuidsetuid函数设置实际用户函数设置实际用户IDID和有效用户和有效用户IDID。setgidsetgid函数设置实函数设置实际组际组IDID和有效组和有效组IDID ,两个函数返回:若成功则为两个函数返回:若成功则为0 0,若出错,若出错则为则为-1-1 。关于更改用户关于更改用户IDID的的若干规则(同样适用于组若干规则(同样适用于组IDID):):1)1)若进程具有超级用户特权,则若进程具有超级用户特权,则set

62、uidsetuid函数可将实际用户函数可将实际用户IDID、有效用户有效用户IDID,以及保存的设置,以及保存的设置- -用户用户-ID-ID设置为设置为uiduid参数值参数值。2)2)若进程没有超级用户特权,但若进程没有超级用户特权,但uiduid等于实际用户等于实际用户IDID或保存的设或保存的设置置- -用户用户-ID-ID,则,则setuidsetuid只将有效用户只将有效用户IDID设置为设置为uiduid。不改变实。不改变实际用户际用户IDID和保存的设置和保存的设置- -用户用户-ID-ID。3)3)如果上面两个条件都不满足,则如果上面两个条件都不满足,则errnoerrno设

63、置为设置为EPERMEPERM,并返回,并返回出错。出错。 #include#includeintsetuid(uid_tuid);intsetgid(gid_tgid);关于设置用户和组ID的注意事项关于内核所维护的三个用户关于内核所维护的三个用户IDID,还要注意下列几点:,还要注意下列几点:1)1)只有超级用户进程可以更改实际用户只有超级用户进程可以更改实际用户IDID。通常,实际用户通常,实际用户IDID是在用户登是在用户登录时,由录时,由login(1)login(1)程序设置的,而且决不会改变它。因为程序设置的,而且决不会改变它。因为loginlogin是一个是一个超级用户进程,当

64、它调用超级用户进程,当它调用setuidsetuid时,设置所有三个用户时,设置所有三个用户IDID。2)2)仅当对程序文件设置了设置仅当对程序文件设置了设置- -用户用户- -IDID位时,位时,execexec函数才改变有效用户函数才改变有效用户IDID。如果设置如果设置- -用户用户- -IDID位没有设置,则位没有设置,则execexec函数不会改变有效用户函数不会改变有效用户IDID,而将而将其维持为原先值。任何时候都可以调用其维持为原先值。任何时候都可以调用setuidsetuid,将有效用户将有效用户IDID设置为实设置为实际用户际用户IDID或保存的设置或保存的设置- -用户用

65、户- -IDID。3)3)保存的设置保存的设置- -用户用户- -IDID是由是由execexec从有效用户从有效用户IDID复制的。在复制的。在execexec按文件按文件用户用户IDID设置了有效用户设置了有效用户IDID后,即进行这种复制,并将此副本保存起来。后,即进行这种复制,并将此副本保存起来。下表列出了改变这三个用户下表列出了改变这三个用户IDID的不同方法。的不同方法。setreuid/setregidseteuid/setegid vSetreuidSetreuid设置当前进程的实际和有效设置当前进程的实际和有效ididvSetregidSetregid设置当前进程的实际和有效

66、组设置当前进程的实际和有效组ididvSeteuidSeteuid设置有效用户设置有效用户IDIDvSetegidSetegid设置有效用户组设置有效用户组IDID这些函数的设置原则与这些函数的设置原则与setuidsetuid基本一致基本一致说明说明说明说明:vvsetuidsetuid/ /setgidsetgid的典型用法是根用户设置普通用户的的典型用法是根用户设置普通用户的uiduid/ /gidgid,以普通用户身以普通用户身份执行某些操作,如:创建用户临时文件等。份执行某些操作,如:创建用户临时文件等。vv一般在编写具一般在编写具setuidsetuid root root的程序时

67、,为减少此类程序带来的系统安全风的程序时,为减少此类程序带来的系统安全风险,在使用完险,在使用完rootroot权限后建议马上执行权限后建议马上执行setuidsetuid( (getuidgetuid();();来抛弃来抛弃rootroot权权限。限。vv进程进程uiduid和和euideuid不一致时不一致时LinuxLinux系统将不会产生系统将不会产生core dumpcore dump#include#includeintsetreuid(uid_truid,uid_teuid);intsetregid(gid_trgid,gid_tegid);用户ID转换关系图 取用户标识getl

68、ogin GetloginGetlogin()返回用户登陆名()返回用户登陆名v一般情况下,我们可以调用一般情况下,我们可以调用getpwuidgetpwuid( (getuidgetuid()(),得到用户得到用户名、真实姓名及名、真实姓名及HOMEHOME目录等信息。目录等信息。v存在一种情况:如果一个用户有多个登录名,这些登录名又存在一种情况:如果一个用户有多个登录名,这些登录名又对应着同一个用户对应着同一个用户IDID,那么又将如何呢那么又将如何呢? ?(一个人在口令文件(一个人在口令文件中可以有多个登录项,它们的用户中可以有多个登录项,它们的用户IDID相同,但登录相同,但登录she

69、llshell则不则不同。)系统通常保存用户的登录名,用同。)系统通常保存用户的登录名,用getlogingetlogin函数可以存取函数可以存取此登录名。此登录名。v如果调用此函数的进程没有连接到用户登录时所用的终端,如果调用此函数的进程没有连接到用户登录时所用的终端,则本函数会失败。用户得到了登录名,就可用则本函数会失败。用户得到了登录名,就可用getpwnamgetpwnam在口在口令文件中查找相应记录以确定其登录令文件中查找相应记录以确定其登录shellshell等。等。 #include char *getlogin(void);进程统计 参数非空时,参数非空时,acctacct打开

70、系统记帐并把终止进程的信息追加到该文件中,打开系统记帐并把终止进程的信息追加到该文件中,参数空时,关闭系统记帐功能。参数空时,关闭系统记帐功能。vvLINUXLINUX系统提供了一个选择项以进行进程事务统计处理。当取了这种系统提供了一个选择项以进行进程事务统计处理。当取了这种选择项后,每当进程结束时内核就写一个会计记录。典型的会计记录选择项后,每当进程结束时内核就写一个会计记录。典型的会计记录是是3232字节长的二进制数据,包括命令名、所使用的字节长的二进制数据,包括命令名、所使用的CPUCPU时间总量、时间总量、用户用户IDID和组和组IDID、起动时间等。起动时间等。vv记帐记录所需的各个

71、数据都由内核保存在进程表中,并在一个新进程记帐记录所需的各个数据都由内核保存在进程表中,并在一个新进程被创建时置初值被创建时置初值( (例如例如forkfork之后在子进程中之后在子进程中) )。进程终止时写一个会计。进程终止时写一个会计记录。这就意味着在记帐文件中记录的顺序对应于进程终止的顺序,记录。这就意味着在记帐文件中记录的顺序对应于进程终止的顺序,而不是它们起动的顺序。为了确定起动顺序,需要读全部记帐文件,而不是它们起动的顺序。为了确定起动顺序,需要读全部记帐文件,并按起动日历时间进行排序。这不是一种很完善的方法,因为日历时并按起动日历时间进行排序。这不是一种很完善的方法,因为日历时间

72、的单位是秒,在一个给定的秒中可能起动了多个进程。间的单位是秒,在一个给定的秒中可能起动了多个进程。vv记帐记录对应于进程而不是程序。在记帐记录对应于进程而不是程序。在forkfork之后,内核为子进程初始之后,内核为子进程初始化一个记录,而不是在一个新程序被执行时。虽然化一个记录,而不是在一个新程序被执行时。虽然execexec并不创建一并不创建一个新的记帐记录,但相应记录中的命令名改变了,个新的记帐记录,但相应记录中的命令名改变了,AFORKAFORK标志则标志则被清除。这意味着,如果一个进程顺序执行了三个程序被清除。这意味着,如果一个进程顺序执行了三个程序( (AexecB,BAexecB

73、,BexecC,execC,最后最后Cexit)Cexit),但只写一个会计记录。在该记录中的命令名对应但只写一个会计记录。在该记录中的命令名对应于程序于程序C C,但但CPUCPU时间是程序时间是程序A A、B B、C C之和。之和。 #include int acct(const char *filename);进程执行时间 用户进程可以通过调用用户进程可以通过调用timestimes函数获得它自己及终止子进函数获得它自己及终止子进程的时间值程的时间值 。此函数填写由此函数填写由bufbuf指向的指向的tmstms结构,该结构定义如下:结构,该结构定义如下:struct tms struc

74、t tms clock_t tms_utime; /* user time */ clock_t tms_utime; /* user time */ clock_t tms_stime; /* system time */ clock_t tms_stime; /* system time */ clock_t tms_cutime; clock_t tms_cutime;/* user time of children */* user time of children */ clock_t tms_cstime; /* system time of children*/ clock_t

75、tms_cstime; /* system time of children*/ ; ;vv结构中两个针对子进程的字段包含了此进程已等待到的各子进程的值结构中两个针对子进程的字段包含了此进程已等待到的各子进程的值vv由此函数返回的由此函数返回的clock_tclock_t值都用值都用_ _SC_CLK_TCK(SC_CLK_TCK(由由sysconfsysconf函数返回的每秒函数返回的每秒时钟滴答数)变换成秒数。时钟滴答数)变换成秒数。 #include clock_t times(struct tms *buf);小结 对在对在LINUXLINUX环境中的高级程序设计而言,完整地了解环境中

76、的高级程序设计而言,完整地了解LINUXLINUX的的进程控制非常重要。进程控制非常重要。其中必须熟练掌握的只有几个其中必须熟练掌握的只有几个forkfork、execexec族、族、_exit_exit、waitwait和和waitpidwaitpid。通过对这些接口的理解,我们应该了解。通过对这些接口的理解,我们应该了解LINUXLINUX下新下新进程是怎么产生的?不同的程序又是如何执行的?进程是怎么产生的?不同的程序又是如何执行的?以及进程控制的一些基本内容。以及进程控制的一些基本内容。对各种不同的用户对各种不同的用户IDID和组和组ID(ID(实际,有效和保存的实际,有效和保存的) )

77、的理解和的理解和编写安全的设置编写安全的设置- -用户用户-ID-ID程序是至关重要的。程序是至关重要的。本章的最后,我们还介绍了本章的最后,我们还介绍了LINUXLINUX下的几个辅助系统调用。下的几个辅助系统调用。第六章 LINUX下的信号 信号的本质v信号是在软件层次上是对中断机制的一种模拟,信号是在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。一个中断请求可以说是一样的。v信号是异步的,一个进程不必通过任何操作来等信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知

78、道信号到底待信号的到达,事实上,进程也不知道信号到底什么时候到达。什么时候到达。v信号是进程间通信机制中唯一的异步通信机制,信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪可以看作是异步通知,通知接收信号的进程有哪些事情发生了。些事情发生了。v信号机制经过信号机制经过POSIXPOSIX实时扩展后,功能更加强大,实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。除了基本通知功能外,还可以传递附加信息。 信号的来源信号事件的发生有两个来源:v硬件来源硬件中断触发,如键盘硬件中断触发,如键盘其它硬件故障,如其它硬件故障,如IO BUS error

79、IO BUS error硬件触发的程序异常硬件触发的程序异常v软件来源程序或命令控制的信号,如:程序或命令控制的信号,如:kill,raisekill,raise等等系统闹钟系统闹钟程序调试程序调试程序异常,如:段违例、非法运算等操作程序异常,如:段违例、非法运算等操作信号的种类可以从两个不同的分类角度对信号进行分类:1)可靠性方面:可靠信号与不可靠信号;2)与时间的关系上:实时信号与非实时信号。 不可靠信号 LinuxLinux信号机制基本上是从信号机制基本上是从信号机制基本上是从信号机制基本上是从UnixUnix系统中继承过来的。早期系统中继承过来的。早期系统中继承过来的。早期系统中继承过

80、来的。早期UnixUnix系统中的信号机制比较简单和原始,后来在实践中暴露出一系统中的信号机制比较简单和原始,后来在实践中暴露出一系统中的信号机制比较简单和原始,后来在实践中暴露出一系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做些问题,因此,把那些建立在早期机制上的信号叫做些问题,因此,把那些建立在早期机制上的信号叫做些问题,因此,把那些建立在早期机制上的信号叫做“ “不可靠不可靠不可靠不可靠信号信号信号信号” ”,信号值小于,信号值小于,信号值小于,信号值小于SIGRTMIN(RedhatSIGRTMIN(RedhatLINUXLINUX中

81、,中,中,中,SIGRTMIN=32SIGRTMIN=32,SIGRTMAX=63)SIGRTMAX=63)的信号都是不可靠信号。的信号都是不可靠信号。的信号都是不可靠信号。的信号都是不可靠信号。这就是这就是这就是这就是“ “不可靠信号不可靠信号不可靠信号不可靠信号” ”的来源。它的主要问题是:的来源。它的主要问题是:的来源。它的主要问题是:的来源。它的主要问题是:v进程每次处理信号后,就将对信号的响应设置为默认动作。进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么

82、就要在信号处理函数结尾再一次调不希望这样的操作,那么就要在信号处理函数结尾再一次调用用signal()signal(),重新安装该信号。重新安装该信号。v信号可能丢失。早期信号可能丢失。早期unixunix下的不可靠信号主要指的是进程可下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。这种情况主要能对信号做出错误的反应以及信号可能丢失。这种情况主要出现在:当出现在:当信号处理函数执行过程中到来的所有相同信号,信号处理函数执行过程中到来的所有相同信号,都被合并为一个信号。都被合并为一个信号。 不可靠信号示例说明:说明:这这段段代代码码段段的的一一个个问问题题是是:在在信信号号

83、发发生生之之后后到到信信号号处处理理程程序序中中调调用用signalsignal函函数数之之间间有有一一个个时时间间窗窗口口。在在此此段段时时间间中中,可可能能发发生生另另一一次次中中断断信信号号。第第二二个个信信号号会会造造成成执执行行默默认认动动作作,而而对对中中断断信信号号则则是是终终止止该该进进程程。这这种种类类型型的的程程序序段段在在大大多多数数情情况况下下会会正正常常工工作作,使使得得我我们们认认为为它它们们正正确确,而实际上却并不是如此。而实际上却并不是如此。LinuxLinux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号支持不可靠信号,但是对不可靠信号机制做了改进

84、:在调用完信号处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠处理函数后,不必重新调用该信号的安装函数(信号安装函数是在可靠机制上的实现)。因此,机制上的实现)。因此,LinuxLinux下的不可靠信号问题主要指的是信号可能下的不可靠信号问题主要指的是信号可能丢失。丢失。 可靠信号 v随着时间的发展,实践证明了有必要对信号的原始机制加以随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。所以,后来出现的各种改进和扩充。所以,后来出现的各种UnixUnix版本分别在这方面版本分别在这方面进行了研究,力图实现进行了研究,力图实现“可靠信号可靠信号”。由于原来定义的信号。由

85、于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。队,不会丢失。v信号的发送和安装也出现了新版本:信号发送函数信号的发送和安装也出现了新版本:信号发送函数sigqueuesigqueue()()及信号安装函数及信号安装函数sigactionsigaction()()。POSIXPOSIX对可靠信号机制做了对可靠信号机制做了标准化。但是,标准化。但是,POSIXPOSIX只对可靠信号机制应具有的功能以及信只对可

86、靠信号机制应具有的功能以及信号机制的对外接口做了标准化,对信号机制的实现没有作具号机制的对外接口做了标准化,对信号机制的实现没有作具体的规定。体的规定。v信号值位于信号值位于SIGRTMINSIGRTMIN和和SIGRTMAXSIGRTMAX之间的信号都是可靠信之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。号,可靠信号克服了信号可能丢失的问题。LinuxLinux在支持新版在支持新版本的信号安装函数本的信号安装函数sigationsigation() ()以及信号发送函数以及信号发送函数sigqueuesigqueue() ()的同的同时,仍然支持早期的时,仍然支持早期的signa

87、lsignal()()信号安装函数和信号安装函数和kill()kill()函数函数。 一点说明v可靠信号是指后来添加的新信号(信号值位于可靠信号是指后来添加的新信号(信号值位于SIGRTMINSIGRTMIN及及SIGRTMAXSIGRTMAX之间);不可靠信号是信号值小于之间);不可靠信号是信号值小于SIGRTMINSIGRTMIN的信号。信号的可靠与不可靠只与信号值有关,与信号的的信号。信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。发送及安装函数无关。v目前目前linuxlinux中的中的signal()signal()是通过是通过sigationsigation() ()

88、函数实现的,因此,函数实现的,因此,即使通过即使通过signalsignal()()安装的信号,在信号处理函数的结尾也安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由不必再调用一次信号安装函数。同时,由signal()signal()安装的实安装的实时信号支持排队,同样不会丢失。时信号支持排队,同样不会丢失。v对于目前对于目前linuxlinux的两个信号安装函数的两个信号安装函数: :signal()signal()及及sigactionsigaction() ()来来说,它们都不能把说,它们都不能把SIGRTMINSIGRTMIN以前的信号变成可靠信号(都以前的信号变

89、成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且不支持排队,仍有可能丢失,仍然是不可靠信号),而且对对SIGRTMINSIGRTMIN以后的信号都支持排队。以后的信号都支持排队。这两个函数的最大区这两个函数的最大区别在于,别在于,经过经过sigactionsigaction安装的信号都能传递信息安装的信号都能传递信息安装的信号都能传递信息安装的信号都能传递信息给信号处理给信号处理函数(对所有信号这一点都成立),而经过函数(对所有信号这一点都成立),而经过signalsignal安装的信安装的信安装的信安装的信号却不能向信号处理函数传递信息号却不能向信号处理函数传递信息号却不能向

90、信号处理函数传递信息号却不能向信号处理函数传递信息, ,对于信号发送函数来说对于信号发送函数来说也是一样的。也是一样的。 实时信号和非实时信号 vLINUXLINUX中信号的编号为中信号的编号为0-630-63,将来可能进一步增加,将来可能进一步增加,这需要得到内核的支持。这需要得到内核的支持。v前前3232种信号已经有了预定义值,每个信号有了确定的种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。用途及含义,并且每种信号都有各自的缺省动作。v后后3232个信号个信号( (SIGRTMIN=31SIGRTMIN=31,SIGRTMAX=63)SIGRTMAX

91、=63)表示实时信表示实时信号,等同于前面阐述的可靠信号。这保证了发送的号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。实时信号是多个实时信号都被接收。实时信号是POSIXPOSIX标准的一标准的一部分,可用于应用进程。部分,可用于应用进程。v非实时信号都不支持排队,都是不可靠信号;实时信非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。号都支持排队,都是可靠信号。 进程对信号的响应进程可以通过三种方式来响应一个信号:进程可以通过三种方式来响应一个信号:1)忽略信号忽略信号( (SIG_DFL)SIG_DFL),即对信号不做任何处理,其中,有即对信号不

92、做任何处理,其中,有两个信号不能忽略:两个信号不能忽略:SIGKILLSIGKILL及及SIGSTOPSIGSTOP;2) 捕捉信号捕捉信号 ( (CATCH)CATCH)。定义信号处理函数,当信号发生时,定义信号处理函数,当信号发生时,执行相应的处理函数;执行相应的处理函数;3) 执行缺省操作执行缺省操作( (SIG_IGN)SIG_IGN),LinuxLinux对每种信号都规定了默对每种信号都规定了默认操作,详细情况请参考下表。注意,进程对实时信号认操作,详细情况请参考下表。注意,进程对实时信号的缺省反应是进程终止。的缺省反应是进程终止。LinuxLinux究竟采用上述三种方式的哪一个来响

93、应信号,取决究竟采用上述三种方式的哪一个来响应信号,取决于传递给相应于传递给相应APIAPI函数的参数。函数的参数。 关于上表的说明在在系系统统默默认认动动作作列列,“ “终终止止ww/ /c co or re”e”表表示示在在进进程程当当前前工工作作目目录录的的产产生生了了c co or re e文文件件,该该文文件件中中记记录录了了该该进进程程非非正正常常退退出出时时的的存存储储图图像像,大大多多数数UUNNI IX X调调试试程程序序都都使使用用c co or re e文文件件以以检检查查进进程程在在终终止时的状态。止时的状态。在下列条件下不产生在下列条件下不产生corecore文件:文

94、件:(a)(a)进程是设置进程是设置- -用户用户-ID-ID,而且当前用户并非程序文件的所有者,而且当前用户并非程序文件的所有者(b)(b)进程是设置进程是设置- -组组-ID-ID,而且当前用户并非该程序文件的组所有者,而且当前用户并非该程序文件的组所有者(c)(c)用户没有写当前工作目录的许可权用户没有写当前工作目录的许可权(d)(d)文件太大。文件太大。corecore文件的许可权文件的许可权( (假定该文件在此之前并不存在假定该文件在此之前并不存在) )通常是用户读通常是用户读写,组读和其他读。写,组读和其他读。 LINUX信号列表(按序号排列) vvSIGHUPSIGHUP:如果终

95、端界面检测到一个连接断开,则将此信号送给与该终端相如果终端界面检测到一个连接断开,则将此信号送给与该终端相关的控制进程(对话期首进程)。关的控制进程(对话期首进程)。vvSIGINTSIGINT:当用户按中断键(一般采用当用户按中断键(一般采用DELETEDELETE或或Ctrl-CCtrl-C)时,终端驱动程序时,终端驱动程序产生此信号并送至前台进程组中的每一个进程。当一个进程在运行时失控,产生此信号并送至前台进程组中的每一个进程。当一个进程在运行时失控,特别是它正在屏幕上产特别是它正在屏幕上产生大量不需要的输出时,常用此信号终止它。生大量不需要的输出时,常用此信号终止它。vvSIGQUIT

96、SIGQUIT:当用户在终端上按退出键(一般采用当用户在终端上按退出键(一般采用Ctrl-Ctrl-)时,产生此信号,时,产生此信号,并送至前台进程组中的所有进程。此信号不仅终止前台进程组(如并送至前台进程组中的所有进程。此信号不仅终止前台进程组(如SIGINTSIGINT所做的那样),同时产生一个所做的那样),同时产生一个corecore文件文件vvSIGILLSIGILL:此信号指示进程已执行一条非法硬件指令此信号指示进程已执行一条非法硬件指令vvSIGTRAPSIGTRAP:指示一个实现定义的硬件故障,一般用于跟踪调试自陷指示一个实现定义的硬件故障,一般用于跟踪调试自陷vvSIGASIG

97、ABRTBRT:调用调用abortabort函数时产生此信号。进程异常终止函数时产生此信号。进程异常终止vvSIGIOTSIGIOT:IOTIOT自陷,指示一个实现定义的硬件故障自陷,指示一个实现定义的硬件故障vvSIGBUSSIGBUS:总线错,指示一个实现定义的硬件故障总线错,指示一个实现定义的硬件故障vvSIGFPESIGFPE:浮点相关异常浮点相关异常vvSIGKILLSIGKILL:这这是两个不能被捕捉或忽略信号中的一个。它向系是两个不能被捕捉或忽略信号中的一个。它向系统统管理管理员员提提供了一种可以供了一种可以杀杀死任一死任一进进程的可靠方法。程的可靠方法。vvSIGUSR1SIG

98、USR1:用用户户自定自定义义信号信号LINUX信号列表(续)vvSIGSEGVSIGSEGV:存储访问相关的异常,一般为非法存储空间读写引起存储访问相关的异常,一般为非法存储空间读写引起vvSIGUSR2SIGUSR2:用户自定义信号用户自定义信号vvSIGPIPESIGPIPE:如果在读进程已终止时写管道,则产生此信号。当套接口如果在读进程已终止时写管道,则产生此信号。当套接口的一端已经终止时,若进程写该套接口也产生此信号的一端已经终止时,若进程写该套接口也产生此信号vvSIGALRMSIGALRM:用用alarmalarm函数设置的时间到达时产生此信号。若由函数设置的时间到达时产生此信号

99、。若由setitimsetitimer(2)er(2)函数设置的间隔时间已经过时,那么也产生此信号函数设置的间隔时间已经过时,那么也产生此信号vvSIGTERMSIGTERM:终端信号,终端信号,是由是由kill(1)kill(1)命令命令发送的系统默认终止信号发送的系统默认终止信号vvSIGSTKFLTSIGSTKFLT:栈故障相关信号栈故障相关信号vvSIGCHLDSIGCHLD:在一个进程终止或停止在一个进程终止或停止时,时,SIGCHLDSIGCHLD信号被送给其父进程。信号被送给其父进程。按系统默认,将忽略此信号。如果父进程希望了解其子进程的这种状按系统默认,将忽略此信号。如果父进程

100、希望了解其子进程的这种状态改变,则应捕捉此信号。信号捕捉函数中通常要调用态改变,则应捕捉此信号。信号捕捉函数中通常要调用waitwait函数以取函数以取得子进程得子进程IDID和其终止状态。和其终止状态。vvSIGCONTSIGCONT:跟踪调试时的继续信号跟踪调试时的继续信号vvSIGSTOPSIGSTOP:是一个作业控制信号,它停止一个进程,是一个作业控制信号,它停止一个进程,SIGSTOPSIGSTOP不能被不能被捕捉或忽略捕捉或忽略vvSIGTSTPSIGTSTP:交互停止信号,当用户在终端上按挂起键(一般采用交互停止信号,当用户在终端上按挂起键(一般采用Ctrl-Ctrl-Z Z)时

101、,终端驱动程序产生此信号。时,终端驱动程序产生此信号。 LINUX信号列表(续)vvSIGTTINSIGTTIN:当一个后台进程组进程试图读其控制终端时,终端驱动程当一个后台进程组进程试图读其控制终端时,终端驱动程序产生此信号。在下列例外情形下不产生此信号,此时读操作返回出序产生此信号。在下列例外情形下不产生此信号,此时读操作返回出错,错, errnoerrno设置为设置为EIOEIO:(a)(a)读进程忽略或阻塞此信号,或读进程忽略或阻塞此信号,或( (b)b)读进程读进程所属的进程组是孤儿进程组。所属的进程组是孤儿进程组。vvSIGTTOUSIGTTOU:当一个后台进程组进程试图写其控制终

102、端时产生此信号。当一个后台进程组进程试图写其控制终端时产生此信号。与上面所述的与上面所述的SIGTTINSIGTTIN信号不同,一个进程可以选择为允许后台进程信号不同,一个进程可以选择为允许后台进程写控制终端。如果不允许后台进程写,则与写控制终端。如果不允许后台进程写,则与SIGTTINSIGTTIN相似也有两种特相似也有两种特殊情况:殊情况:( ( a )a )写进程忽略或阻塞此信写进程忽略或阻塞此信不幸的是,术语停止不幸的是,术语停止( (stop)stop)有不同的意义。在讨论作业控制和信号时我们需提及停止和继续作业。有不同的意义。在讨论作业控制和信号时我们需提及停止和继续作业。但是终端

103、驱动程序一直用术语停止表示用但是终端驱动程序一直用术语停止表示用Ctrl-SCtrl-S和和Ctrl-QCtrl-Q字符停字符停止和起动终端输出。因此,终端驱动程序将产生交互停止信号和字符止和起动终端输出。因此,终端驱动程序将产生交互停止信号和字符称之为挂起字符而非停止字符。号,或称之为挂起字符而非停止字符。号,或( (b)b)写进程所属进程组是孤儿写进程所属进程组是孤儿进程组。在这两种情况下不产生此信号,写操作返回出错,进程组。在这两种情况下不产生此信号,写操作返回出错,errnoerrno设设置为置为EIOEIO。vvSIGURGSIGURG:该信号通知进程已经发生一个紧急情况。在网络连接

104、上,接该信号通知进程已经发生一个紧急情况。在网络连接上,接到非规定波特率的到非规定波特率的数据时,此信号可选择地产生数据时,此信号可选择地产生vvSIGXCPUSIGXCPU:如果进程超过了其软如果进程超过了其软CPUCPU时间限制,则产生此信号。时间限制,则产生此信号。vvSIGXFSZSIGXFSZ:如果进程超过了其软文件长度限制,则产生此如果进程超过了其软文件长度限制,则产生此信号。信号。LINUX信号列表(续)vSIGXFSZSIGXFSZ:如果进程超过了其软文件长度限制,则产生此如果进程超过了其软文件长度限制,则产生此信号。信号。vSIGVTALRMSIGVTALRM:当一个当一个由

105、由setitimersetitimer(2)(2)函函数设置的虚拟间隔时数设置的虚拟间隔时间已经超过时产生此信号间已经超过时产生此信号vSIGPROFSIGPROF:当当setitimersetitimer(2)(2)函数设置的梗概统计间隔时间已函数设置的梗概统计间隔时间已经超过时产生此信号。经超过时产生此信号。vSIGWINCHSIGWINCH:如果一个进程用如果一个进程用ioctlioctl的设置的设置- -窗口窗口- -大大小命令小命令更改了窗口大小,则内核将更改了窗口大小,则内核将SIGWINCHSIGWINCH信号送至前台进程信号送至前台进程组。组。vSIGPOLLSIGPOLL:当

106、在一个可轮询设备上发生一特定事件时产生当在一个可轮询设备上发生一特定事件时产生此信号。此信号。vSIGIOSIGIO:该信号指示一个异步该信号指示一个异步I/OI/O事件。事件。 LINUX信号列表(续)vvSIGPWRSIGPWR:这是一种这是一种SVR4SVR4信号,它依赖于系统,信号,它依赖于系统,LINUXLINUX也支持。它主要用于具也支持。它主要用于具有不间断电源有不间断电源( (UPS)UPS)的系统上,如果电源失效,的系统上,如果电源失效,则则UPSUPS起作用,而且通常软件起作用,而且通常软件会接到通知。在这种情况下,系统依靠蓄电池电源继续运行,所以无须作任会接到通知。在这种

107、情况下,系统依靠蓄电池电源继续运行,所以无须作任何处理。但是如果蓄电池也将不能支持工作,则软件通常会再次接到通何处理。但是如果蓄电池也将不能支持工作,则软件通常会再次接到通知,知,此时,它在此时,它在15153030秒内使系统各部分都停止运行。此时应当传递秒内使系统各部分都停止运行。此时应当传递SIGPWRSIGPWR信号。在大多数系统中使接到蓄电池电压过低的进程将信号信号。在大多数系统中使接到蓄电池电压过低的进程将信号SIGPWRSIGPWR发送给发送给initinit进程,然后由进程,然后由initinit处理停机操作。很多系统处理停机操作。很多系统V V的的initinit实现在实现在i

108、nittabinittab文件中提文件中提供了两个记录项用于此种目的;供了两个记录项用于此种目的;powerfailpowerfail以及以及powerwaitpowerwait。# When our UPS tells us power has failed, assume we have a few minutes# When our UPS tells us power has failed, assume we have a few minutes# When our UPS tells us power has failed, assume we have a few minutes

109、# When our UPS tells us power has failed, assume we have a few minutes# of power left. Schedule a shutdown for 2 minutes from now.# of power left. Schedule a shutdown for 2 minutes from now.# of power left. Schedule a shutdown for 2 minutes from now.# of power left. Schedule a shutdown for 2 minutes

110、 from now.# This does, of course, assume you have# This does, of course, assume you have# This does, of course, assume you have# This does, of course, assume you have powerd powerd powerd powerd installed and your installed and your installed and your installed and your# UPS connected and working co

111、rrectly.# UPS connected and working correctly.# UPS connected and working correctly.# UPS connected and working correctly.pf:pf:pf:pf:powerfailpowerfailpowerfailpowerfail:/:/:/:/sbinsbinsbinsbin/shutdown -f -h +2 Power Failure; System Shutting Down/shutdown -f -h +2 Power Failure; System Shutting Do

112、wn/shutdown -f -h +2 Power Failure; System Shutting Down/shutdown -f -h +2 Power Failure; System Shutting Down# If power was restored before the shutdown kicked in, cancel it.# If power was restored before the shutdown kicked in, cancel it.# If power was restored before the shutdown kicked in, cance

113、l it.# If power was restored before the shutdown kicked in, cancel it.pr:12345:pr:12345:pr:12345:pr:12345:powerokwaitpowerokwaitpowerokwaitpowerokwait:/:/:/:/sbinsbinsbinsbin/shutdown -c Power Restored; Shutdown Cancelled/shutdown -c Power Restored; Shutdown Cancelled/shutdown -c Power Restored; Shu

114、tdown Cancelled/shutdown -c Power Restored; Shutdown Cancelled由于目前已能获得低价格的由于目前已能获得低价格的UPSUPS系统,它用系统,它用RS-232RS-232串行连接能够很容易地将串行连接能够很容易地将蓄电池电压过低的条件通知系统,于是这种信号也就更加重要了。蓄电池电压过低的条件通知系统,于是这种信号也就更加重要了。 信号的发送LINUX中发送信号的主要函数有:kill()、raise()、sigqueue()、alarm()、setitimer()以及abort() kill signosigno是信号值,当为是信号值,当

115、为0 0时(即空信号),实际不发送任何信号,时(即空信号),实际不发送任何信号,但照常进行错误检查,因此,可用于检查目标进程是否存在,但照常进行错误检查,因此,可用于检查目标进程是否存在,以及当前进程是否具有向目标发送信号的权限(以及当前进程是否具有向目标发送信号的权限(rootroot权限的进权限的进程可以向任何进程发送信号,非程可以向任何进程发送信号,非rootroot权限的进程只能向属于同权限的进程只能向属于同一个一个sessionsession或者同一个用户的进程发送信号)或者同一个用户的进程发送信号) 调用成功返回调用成功返回0 0; 否则,返回否则,返回-1-1。 #include

116、#includekill(pid_tpid,intsigno) 参数参数参数参数pidpidpidpid的值的值的值的值信号的接收进程信号的接收进程信号的接收进程信号的接收进程pid0pid0pid0pid0进程进程进程进程IDIDIDID为为为为pidpidpidpid的进程的进程的进程的进程pid=0pid=0pid=0pid=0同一个进程组的进程同一个进程组的进程同一个进程组的进程同一个进程组的进程pid0 pid!=-1pid0 pid!=-1pid0 pid!=-1pid0 pid!=-1进程组进程组进程组进程组IDIDIDID为为为为 -pid -pid -pid -pid的所有进

117、程的所有进程的所有进程的所有进程pid=-1pid=-1pid=-1pid=-1除发送进程自身外,所有进程除发送进程自身外,所有进程除发送进程自身外,所有进程除发送进程自身外,所有进程IDIDIDID大于大于大于大于1 1 1 1的进程的进程的进程的进程raise向进程本身发送信号,参数为即将发送的信号值。向进程本身发送信号,参数为即将发送的信号值。调用成功返回调用成功返回 0 0;否则,返回;否则,返回 -1 -1。raise()raise()等价于等价于kill(getpid(),sig)kill(getpid(),sig)#includeintraise(intsigno) sigque

118、uesigqueue()sigqueue()是比是比较较新的新的发发送信号系送信号系统调统调用,主要是用,主要是针对实时针对实时信号提出的信号提出的(当然也支持前(当然也支持前3232种),支持信号种),支持信号带带有参数,与函数有参数,与函数sigaction()sigaction()配合配合使用。使用。vvsigqueuesigqueue的第一个参数是指定接收信号的进程的第一个参数是指定接收信号的进程IDID,第二个参数确定即将第二个参数确定即将发送的信号,第三个参数是一个联合数据结构发送的信号,第三个参数是一个联合数据结构unionunionsigvalsigval,指定了信号指定了信号

119、传递的参数,即通常所说的传递的参数,即通常所说的4 4字节值。字节值。 typedeftypedef union union sigval sigval int sivalint sival_ _intint; /*; /*用于传送一个整形数用于传送一个整形数*/*/ void *void *sivalsival_ _ptrptr; /*; /*用于传送一批数据用于传送一批数据:数组或结构或其它数组或结构或其它*/*/ sigvalsigval_t;_t; sigqueue sigqueue()()可以比可以比kill()kill()传递了更多的附加信息,但传递了更多的附加信息,但sigque

120、uesigqueue()()只能向一只能向一个进程发送信号,而不能发送信号给一个进程组。如果个进程发送信号,而不能发送信号给一个进程组。如果signosigno=0=0,将会将会执行错误检查,但实际上不发送任何信号,执行错误检查,但实际上不发送任何信号,0 0值信号可用于检查值信号可用于检查pidpid的有的有效性以及当前进程是否有权限向目标进程发送信号。效性以及当前进程是否有权限向目标进程发送信号。#include#includeintsigqueue(pid_tpid,intsig,constunionsigvalval) sigqueue1.在调用在调用sigqueuesigqueue时

121、,时,sigvalsigval_t_t指定的信息会拷贝到指定的信息会拷贝到3 3参数信参数信号处理函数的号处理函数的siginfosiginfo_t_t结构中(结构中(3 3参数信号处理函数指的是参数信号处理函数指的是信号处理函数由信号处理函数由sigactionsigaction安装,并设定了安装,并设定了sasa_ _sigactionsigaction指指针,稍后将阐述针,稍后将阐述),这样信号处理函数就可以根据这些信息),这样信号处理函数就可以根据这些信息进行不同的处理。由于进行不同的处理。由于sigqueuesigqueue系统调用支持发送带参数信系统调用支持发送带参数信号,所以比号

122、,所以比kill()kill()系统调用的功能要灵活和强大得多。系统调用的功能要灵活和强大得多。2.注:注:sigqueuesigqueue()()发送非实时信号时,第三个参数包含的信发送非实时信号时,第三个参数包含的信息仍然能够传递给信号处理函数;息仍然能够传递给信号处理函数; sigqueuesigqueue()()发送非实时发送非实时信号时,仍然不支持排队,即在信号处理函数执行过程中到信号时,仍然不支持排队,即在信号处理函数执行过程中到来的所有相同信号,都被合并为一个信号。来的所有相同信号,都被合并为一个信号。 #include#includeintsigqueue(pid_tpid,i

123、ntsig,constunionsigvalval) alarmv专门为专门为SIGALRMSIGALRM信号而信号而设设,调用此函数后,调用此函数后,在指定在指定的的时间时间secondsseconds秒后,将向秒后,将向进进程本身程本身发发送送SIGALRMSIGALRM信号,信号,所以,所以,又称又称secondsseconds为闹钟时间为闹钟时间。进进程程调调用用alarmalarm后,任何以前的后,任何以前的alarm()alarm()调调用都将无效用都将无效。v如果参数如果参数secondsseconds为为零,零,将清除闹钟时间,即将清除闹钟时间,即进进程程内将不再包含任何内将不

124、再包含任何闹钟时间闹钟时间。v返回值,如果调用返回值,如果调用alarmalarm()()前,进程中已经设置前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回否则返回0 0。 #includeunsignedintalarm(unsignedintseconds) setitimersetitimer()setitimer()的功能的功能为设为设置置间间隔定隔定时时器,它比器,它比alarmalarm功能功能强强大,大,whichwhich支持支持3 3种种类类型的定型的定时时器:器:ITIMER_REALITIMER_REAL:

125、 设定绝对时间(设定绝对时间(realreal时间);经过指定的时时间);经过指定的时间后,内核将发送间后,内核将发送SIGALRMSIGALRMSIGALRMSIGALRM信号给本进程;信号给本进程; ITIMER_VIRTUALITIMER_VIRTUAL: 设定程序执行时间;经过指定的时间后,设定程序执行时间;经过指定的时间后,内核将发送内核将发送SIGVTALRMSIGVTALRMSIGVTALRMSIGVTALRM信号给本进程;信号给本进程; ITIMER_PROFITIMER_PROF: 设定进程执行以及内核因本进程而消耗的时设定进程执行以及内核因本进程而消耗的时间和,经过指定的时

126、间后,内核将发送间和,经过指定的时间后,内核将发送SIG_PROFSIG_PROFSIG_PROFSIG_PROF信号给本进信号给本进程;程; SetitimerSetitimer() ()第一个参数第一个参数whichwhich指定定时器类型(上面三种之一);指定定时器类型(上面三种之一);第二个参数是结构第二个参数是结构itimervalitimerval的一个实例,结构的一个实例,结构itimervalitimerval形式形式见下面。第三个参数指示旧的时间设置值,可不做处理见下面。第三个参数指示旧的时间设置值,可不做处理 。 #includeintsetitimer(intwhich,

127、conststructitimerval*value,structitimerval*ovalue);setitimerstruct itimerval struct itimerval struct timeval it_interval; /* next value */ struct timeval it_interval; /* next value */ struct timeval it_value; /* current value */ struct timeval it_value; /* current value */ ; ;struct timeval struct t

128、imeval long tv_sec; /* seconds */ long tv_sec; /* seconds */ long tv_usec; /* microseconds */ long tv_usec; /* microseconds */;setitimersetitimer的的功功能能是是把把valuevalue中中值值设设置置为为指指定定的的定定时时器器值值,若若ovalueovalue值值为为非非“ “0 0” ”,则则定定时时器器的的旧旧值值存存在在该该单单元元中中。定定时时器器中中值值从从it_valueit_value减减至至0 0时时,产产生生一一信信号号,并并把把

129、it_valueit_value值值复复位位至至it_intervalit_interval,对对于于设设置置为为0 0的的定定时时器器则停止计时(则停止计时(it_valueit_value为为0 0或定时器时间已经到达并且或定时器时间已经到达并且it_intervalit_interval为为0 0)。)。tv_sectv_sec和和tv_usectv_usec在在决决定定定定时时器器的的存存在在( (持持续续) )时时间间上上非非常常重重要要,定定时时器器在在请请求求的的时时间间内内永永远远不不会会过过期期,但但会会在在超超过过定定时时器器规规定定的的时时间间后后触触发发定定时时器器,这

130、这个个时时间间差差一一般般都都很很短短,通通常常它它与与系系统统定定时时器器的的最最小小粒粒度度有有关关( (通通常常为为系系统短时钟大小统短时钟大小) )。一旦定时器到达,一个信号将产生并复位定时器,若进程处于活动状态,一旦定时器到达,一个信号将产生并复位定时器,若进程处于活动状态,信号处理立即触发,否则根据系统的负载情况,信号处理将会稍被推延。信号处理立即触发,否则根据系统的负载情况,信号处理将会稍被推延。 #includeintsetitimer(intwhich,conststructitimerval*value,structitimerval*ovalue);abort向进程发送向

131、进程发送SIGABORTSIGABORT信号,默认情况下进程会信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。即异常退出,当然可定义自己的信号处理函数。即使使SIGABORTSIGABORT被进程设置为阻塞信号,调用被进程设置为阻塞信号,调用abortabort() ()后,后,SIGABORTSIGABORT仍然能被进程接收。该函数无返仍然能被进程接收。该函数无返回值。回值。 #includevoidabort(void); 信号的安装(设置信号关联动作) v如果如果进进程要程要处处理某一信号,那么就要在理某一信号,那么就要在进进程中安装程中安装该该信号。信号。安装信号主要用来

132、确定信号安装信号主要用来确定信号值值及及进进程程针对该针对该信号信号值值的的动动作作之之间间的映射关系,即的映射关系,即进进程将要程将要处处理哪个信号;理哪个信号;该该信号被信号被传传递给进递给进程程时时,将,将执执行何种操作。行何种操作。vlinuxlinux主要有两个函数主要有两个函数实现实现信号的安装:信号的安装:signal()signal()、sigactionsigaction()()。其中其中signal()signal()在可靠信号系在可靠信号系统调统调用的基用的基础础上上实现实现。它。它只有两个参数,不支持信号只有两个参数,不支持信号传递传递信息,主要是用于前信息,主要是用于

133、前3232种种非非实时实时信号的安装;信号的安装;vsigactionsigaction() ()是较新的函数,有三个参数,支持信号传递信息,是较新的函数,有三个参数,支持信号传递信息,主要用来与主要用来与 sigqueuesigqueue()()系统调用配合使用,当然,系统调用配合使用,当然,sigactionsigaction() ()同样支持非实时信号的安装。同样支持非实时信号的安装。sigactionsigaction() ()优于优于signal()signal()主要主要体现在支持信号带有参数。体现在支持信号带有参数。 signalv第一个参数指定信号的第一个参数指定信号的值值,第

134、二个参数指定,第二个参数指定针对针对前面信号前面信号值值的的处处理,可以忽略理,可以忽略该该信号(参数信号(参数设为设为SIG_IGNSIG_IGN););可以采可以采用系用系统统默默认认方式方式处处理信号理信号( (参数参数设为设为SIG_DFL)SIG_DFL);也可以自也可以自己己实现处实现处理方式理方式( (参数指定一个函数地址参数指定一个函数地址) )。v对对于一个信号使用了信号于一个信号使用了信号处处理函数理函数则则称称为为捕俘了信号,捕俘了信号,SIGKILL SIGKILL 和和 SIGSTOPSIGSTOP不能被捕俘或忽略。不能被捕俘或忽略。v如果如果signal()sign

135、al()调用成功,返回最后一次为安装信号调用成功,返回最后一次为安装信号signumsignum而而调用调用signal()signal()时的时的handlerhandler值;失败则返回值;失败则返回SIG_ERRSIG_ERR。 #include #include typedef void (*sighandler_t)(int); typedef void (*sighandler_t)(int); sighandler_tsignal(intsignum,sighandler_thandler);sigactionsigactionsigaction函数用于改函数用于改变进变进程接收

136、到特定信号后的行程接收到特定信号后的行为为。v第一个参数第一个参数为为信号的信号的值值,可以,可以为为除除SIGKILLSIGKILL及及SIGSTOPSIGSTOP外的外的任何一个特定有效的信号。任何一个特定有效的信号。v第二个参数是指向第二个参数是指向结结构构sigactionsigaction的指的指针针,在,在结结构构sigactionsigaction的的实实例中,指定了例中,指定了对对特定信号的特定信号的处处理,可以理,可以为为空,空,进进程会以缺省方式程会以缺省方式对对信号信号处处理;理;v第三个参数第三个参数oldactoldact指向的指向的对对象用来保存原来象用来保存原来对

137、对相相应应信号的信号的处处理,可指定理,可指定oldactoldact为为NULLNULL。v如果把第二、第三个参数都如果把第二、第三个参数都设为设为NULLNULL,那么那么该该函数可用于函数可用于检查检查信号的有效性。信号的有效性。第二个参数最为重要,其中包含了对指定信号的处理、信号第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等,该值为非空时,信号数等,该值为非空时,信号signumsignum的新的处理行为被设置的新的处理行为被设置为为actact中内容。中内容。 #includ

138、e #include intsigaction(intsignum,conststructsigaction*act,structsigaction*oldact); Sigaction参数struct sigaction struct sigaction struct sigaction struct sigaction void (*sa_handler)(int); void (*sa_handler)(int); void (*sa_handler)(int); void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *

139、, void *); void (*sa_sigaction)(int, siginfo_t *, void *); void (*sa_sigaction)(int, siginfo_t *, void *); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; sigset_t sa_mask; sigset_t sa_mask; sigset_t sa_mask; int sa_flags; int sa_flags; int sa_flags; int sa_flags; void (*sa_restore

140、r)(void); void (*sa_restorer)(void); void (*sa_restorer)(void); void (*sa_restorer)(void); SigactionSigactionSigactionSigaction结构中的结构中的结构中的结构中的sa_handlersa_handlersa_handlersa_handler和和和和sa_sigactionsa_sigactionsa_sigactionsa_sigaction是一个联合结是一个联合结是一个联合结是一个联合结vv_sa_handler_sa_handler_sa_handler_sa_ha

141、ndler指定与信号指定与信号指定与信号指定与信号signumsignumsignumsignum相关联的函数,即用户指定的信号处理相关联的函数,即用户指定的信号处理相关联的函数,即用户指定的信号处理相关联的函数,即用户指定的信号处理函数,除了可以是用户自定义的处理函数外,还可以为函数,除了可以是用户自定义的处理函数外,还可以为函数,除了可以是用户自定义的处理函数外,还可以为函数,除了可以是用户自定义的处理函数外,还可以为SIG_DFL(SIG_DFL(SIG_DFL(SIG_DFL(采用采用采用采用缺省的处理方式缺省的处理方式缺省的处理方式缺省的处理方式) ) ) ),也可以为,也可以为,也

142、可以为,也可以为SIG_IGNSIG_IGNSIG_IGNSIG_IGN(忽略信号)。(忽略信号)。(忽略信号)。(忽略信号)。vvsa_masksa_masksa_masksa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,以防止信号的嵌套发送,除非指定情况下当前信号本身被阻塞,以防止信号的嵌套发送,除非指定情况下当前信号本身被阻塞,以防止信号的嵌套发送,除非指定情况下当前信号本身被阻塞,以

143、防止信号的嵌套发送,除非指定SA_NODEFERSA_NODEFERSA_NODEFERSA_NODEFER或者或者或者或者SA_NOMASKSA_NOMASKSA_NOMASKSA_NOMASK标志位。标志位。标志位。标志位。vv注意:注意:注意:注意:sa_masksa_mask指定的信号阻塞的条件是,在由指定的信号阻塞的条件是,在由指定的信号阻塞的条件是,在由指定的信号阻塞的条件是,在由sigactionsigaction()安装信号()安装信号()安装信号()安装信号的处理函数执行过程中由的处理函数执行过程中由的处理函数执行过程中由的处理函数执行过程中由sa_masksa_mask指定

144、的信号才被阻塞。指定的信号才被阻塞。指定的信号才被阻塞。指定的信号才被阻塞。 Sigaction参数sa_flagssa_flags中包含了许多标志位,它指定了修改信号处理进程行为的一组标志。中包含了许多标志位,它指定了修改信号处理进程行为的一组标志。中包含了许多标志位,它指定了修改信号处理进程行为的一组标志。中包含了许多标志位,它指定了修改信号处理进程行为的一组标志。这些标志可以以位或形式存在。标志包括:这些标志可以以位或形式存在。标志包括:这些标志可以以位或形式存在。标志包括:这些标志可以以位或形式存在。标志包括: SA_NOCLDSTOPSA_NOCLDSTOP:若信号为:若信号为SIG

145、CHLDSIGCHLD时,当子进程停止时,不接收通知时,当子进程停止时,不接收通知(即子进程接收下列之一信号时:(即子进程接收下列之一信号时:SIGSTOP,SIGTSTP,SIGTTINSIGSTOP,SIGTSTP,SIGTTIN或或 SIGTTOUSIGTTOU)SA_ONESHOTorSA_RESETHANDSA_ONESHOTorSA_RESETHAND:一旦信号处理程序被调用,恢复信号:一旦信号处理程序被调用,恢复信号处理为缺省状态处理为缺省状态SA_ONSTACKSA_ONSTACK:在一个可替换的信号栈上(:在一个可替换的信号栈上(sigaltstack(2)sigaltsta

146、ck(2)提供)调用信号处提供)调用信号处理函数,若可替换的信号栈不可用,使用缺省栈理函数,若可替换的信号栈不可用,使用缺省栈SA_RESTARTSA_RESTART:提供与:提供与BSDBSD信号语义兼容的行为,使得某些系统调用通过信号语义兼容的行为,使得某些系统调用通过信号可以重新执行信号可以重新执行(restartable)(restartable)SA_NOMASKorSA_NODEFERSA_NOMASKorSA_NODEFER:不阻止:不阻止( (屏蔽屏蔽) )信号处理程序中收到的信号信号处理程序中收到的信号SA_SIGINFOSA_SIGINFO:这是一个比较重要的标志位,当设定

147、了该标志位时,表示信:这是一个比较重要的标志位,当设定了该标志位时,表示信号附带的参数可以被传递到信号处理函数中。因此,联合中分量应该指号附带的参数可以被传递到信号处理函数中。因此,联合中分量应该指定为定为sigactionsigaction结构中的结构中的sa_sigactionsa_sigaction指定处理函数,而不应该为指定处理函数,而不应该为sa_handlersa_handler指定信号处理函数,否则,设置该标志变得毫无意义。即使为指定信号处理函数,否则,设置该标志变得毫无意义。即使为sa_sigactionsa_sigaction指定了信号处理函数,如果不设置指定了信号处理函数,

148、如果不设置SA_SIGINFOSA_SIGINFO,信号处理,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误(的访问都将导致段错误(SegmentationfaultSegmentationfault) Sigaction由由_sa_handler_sa_handler指定的处理函数只有一个参数,即信号值,所以信号不能传递除信号指定的处理函数只有一个参数,即信号值,所以信号不能传递除信号值之外的任何信息;由值之外的任何信息;由_sa_sigaction_sa_sigaction是指定的信号

149、处理函数带有三个参数,是为实时是指定的信号处理函数带有三个参数,是为实时信号而设的(当然同样支持非实时信号),它指定一个信号而设的(当然同样支持非实时信号),它指定一个3 3参数信号处理函数。第一个参数信号处理函数。第一个参数为信号值,第三个参数没有使用(参数为信号值,第三个参数没有使用(posixposix没有规范使用该参数的标准),第二个没有规范使用该参数的标准),第二个参数是指向参数是指向siginfo_tsiginfo_t结构的指针,结构中包含信号携带的数据值,结构如下:结构的指针,结构中包含信号携带的数据值,结构如下:siginfo_tsiginfo_tintsi_signo;int

150、si_signo;/*/*信号值,对所有信号有意义信号值,对所有信号有意义*/*/intsi_errno;intsi_errno;/*errno/*errno值,对所有信号有意义值,对所有信号有意义*/*/intsi_code;intsi_code;/*/*信号产生的原因,对所有信号有意义信号产生的原因,对所有信号有意义*/*/pid_tsi_pid;pid_tsi_pid;/*/*发送信号的进程发送信号的进程ID*/ID*/uid_tsi_uid;uid_tsi_uid;/*/*发送信号进程的真实用户发送信号进程的真实用户ID*/ID*/intsi_status;intsi_status;/

151、*/*退出状态,对退出状态,对SIGCHLDSIGCHLD有意义有意义*/*/clock_tsi_utime;clock_tsi_utime; /*/*用户消耗的时间,对用户消耗的时间,对SIGCHLDSIGCHLD有意义有意义*/*/clock_tsi_stime;clock_tsi_stime; /*/*内核消耗的时间,对内核消耗的时间,对SIGCHLDSIGCHLD有意义有意义*/*/sigval_tsigval_tsi_valuesi_value; ; /*/*信信号号值值,对对所所有有实实时时有有意意义义,是是一一个个联联合合数数据据结结构构,可可以以为一个整数(由为一个整数(由si

152、_intsi_int标示,也可以为一个指针,由标示,也可以为一个指针,由si_ptrsi_ptr标示)标示)*/*/void*si_addr;void*si_addr;/*/*触触发发faultfault内内存存地地址址,对对SIGILL,SIGFPE,SIGSEGV,SIGBUSSIGILL,SIGFPE,SIGSEGV,SIGBUS信号有意义信号有意义*/*/intsi_band;intsi_band;/*/*对对SIGPOLLSIGPOLL信号有意义信号有意义*/*/intsi_fd;intsi_fd;/*/*对对SIGPOLLSIGPOLL信号有意义信号有意义*/*/ Sigactio

153、n 前面在讨论系统调用前面在讨论系统调用sigqueuesigqueue发送信号时,发送信号时,sigqueuesigqueue的第三的第三个参数就是个参数就是sigvalsigval联合数据结构,当调用联合数据结构,当调用sigqueuesigqueue时,该数时,该数据结构中的数据就将拷贝到信号处理函数的第二个参数中。据结构中的数据就将拷贝到信号处理函数的第二个参数中。这样,在发送信号同时,就可以让信号传递一些附加信息。这样,在发送信号同时,就可以让信号传递一些附加信息。信号可以传递信息对程序开发是非常有意义的。信号可以传递信息对程序开发是非常有意义的。信号参数的传递过程可图示如下:信号参

154、数的传递过程可图示如下: 信号集及信号集操作函数信号集及信号集操作函数信号集被定义为一种数据类型信号集被定义为一种数据类型typedef structtypedef struct unsigned long int _val_SIGSET_NWORDS; unsigned long int _val_SIGSET_NWORDS; _sigset_t; _sigset_t;# # define define _SIGSET_NWORDS _SIGSET_NWORDS (1024 (1024 / / (8 (8 * * sizeof sizeof (unsigned (unsigned long

155、int)long int) 信信号号集集用用来来描描述述信信号号的的集集合合,linuxlinux所所支支持持的的所所有有信信号号可可以以全全部部或或部部分分的的出出现现在在信信号号集集中中,主主要要与与信信号号阻阻塞塞相相关关函函数数配合使用。下面是为信号集操作定义的相关函数:配合使用。下面是为信号集操作定义的相关函数: 信号集及信号集操作函数信号集及信号集操作函数#includeintsigemptyset(sigset_t*set);intsigfillset(sigset_t*set);intsigaddset(sigset_t*set,intsignum)intsigdelset(s

156、igset_t*set,intsignum);intsigismember(constsigset_t*set,intsignum);sigemptyset(sigset_t*set):初始化由:初始化由set指定的信号集,信号集里面的所有信指定的信号集,信号集里面的所有信号被清空;号被清空;sigfillset(sigset_t*set):调用该函数后,:调用该函数后,set指向的信号集中将包含指向的信号集中将包含linux支持支持的的64种信号;种信号;sigaddset(sigset_t*set,intsignum):在:在set指向的信号集中加入指向的信号集中加入signum信号;信号

157、;sigdelset(sigset_t*set,intsignum):在:在set指向的信号集中删除指向的信号集中删除signum信号;信号;sigismember(constsigset_t*set,intsignum):判定信号:判定信号signum是否在是否在set指向指向的信号集中。的信号集中。信号阻塞和信号未决 每个进程都有一个用来描述哪些信号递送到进程时每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。到进程后都将被阻塞。vsigpendingsigpending( (sigset

158、sigset_t *set)_t *set)获获得当前已得当前已递递送到送到进进程,程,却被阻塞的所有信号,在却被阻塞的所有信号,在setset指向的信号集中返回指向的信号集中返回结结果。果。vsigsuspendsigsuspend(const(const sigset sigset_t *mask)_t *mask)用于在接收到用于在接收到某个信号之前某个信号之前, , 临时用临时用maskmask替换进程的信号掩码替换进程的信号掩码, , 并暂停进程执行,直到收到信号为止。并暂停进程执行,直到收到信号为止。sigsuspend sigsuspend 返回后将恢复调用之前的信号掩码。信号处

159、理函数返回后将恢复调用之前的信号掩码。信号处理函数完成后,进程将继续执行。该系统调用始终返回完成后,进程将继续执行。该系统调用始终返回-1-1,并将,并将errnoerrno设置为设置为EINTREINTR。 #includeintsigpending(sigset_t*set);intsigsuspend(constsigset_t*mask);intsigprocmask(inthow,constsigset_t*set,sigset_t*oldset);信号阻塞和信号未决 vsigprocmasksigprocmask()()的功能为根据参数的功能为根据参数howhow来实现对信号集来实

160、现对信号集的操作。若的操作。若oldsetoldset是非空指针,进程的当前信号屏是非空指针,进程的当前信号屏蔽字通过蔽字通过oldsetoldset返回。当返回。当setset是一个非空指针,操作是一个非空指针,操作主要有三种:主要有三种: #includeintsigpending(sigset_t*set);intsigsuspend(constsigset_t*mask);intsigprocmask(inthow,constsigset_t*set,sigset_t*oldset);参数参数参数参数howhowhowhow进程当前信号集进程当前信号集进程当前信号集进程当前信号集set

161、setsetsetSIG_BLOCKSIG_BLOCKSIG_BLOCKSIG_BLOCK在进程当前阻塞信号集中添加在进程当前阻塞信号集中添加setset指向信号集中的信号,即指向信号集中的信号,即该该进程新的信号屏蔽字是其当前信号屏蔽字和进程新的信号屏蔽字是其当前信号屏蔽字和setset指向信号集的指向信号集的并集并集并集并集。SetSet包含了我们希望阻塞的附加信号包含了我们希望阻塞的附加信号SIG_UNBLOCKSIG_UNBLOCKSIG_UNBLOCKSIG_UNBLOCK该该进进程程新新的的信信号号屏屏蔽蔽字字是是其其当当前前信信号号屏屏蔽蔽字字和和setset所所指指向向信信号号

162、集的集的交集交集交集交集。SetSet包含了我们希望解除阻塞的信号。包含了我们希望解除阻塞的信号。SIG_SETMASKSIG_SETMASKSIG_SETMASKSIG_SETMASK更新进程阻塞信号集为更新进程阻塞信号集为setset指向的信号集指向的信号集Sigsuspend示例上上例例中中,如如果果在在解解除除对对SIGINTSIGINT的的阻阻塞塞和和pausepause之之间间发发生生了了SIGINTSIGINT信信号号,则则此信号被丢失。这是早期的不可靠信号机制的另一个问题。此信号被丢失。这是早期的不可靠信号机制的另一个问题。为了纠正此问题,需要在一个原子操作中实现恢复信号屏蔽字

163、,然后为了纠正此问题,需要在一个原子操作中实现恢复信号屏蔽字,然后使进程睡眠,这种功能是由使进程睡眠,这种功能是由sigsuspendsigsuspend函数所提供的,上例中把后一函数所提供的,上例中把后一个个sigprocmasksigprocmask设置为设置为sigsuspend()sigsuspend()即可。即可。 我们可以通过更改进程的信号屏蔽字以阻塞或解除阻塞所选择的信号。使用这种技术可以保护不希望由信号中断的临界代码。信号生命周期(一)对于一个完整的信号生命周期对于一个完整的信号生命周期对于一个完整的信号生命周期对于一个完整的信号生命周期( (从信号发送到相应的处理函数执行完毕

164、从信号发送到相应的处理函数执行完毕从信号发送到相应的处理函数执行完毕从信号发送到相应的处理函数执行完毕) )来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:信号诞生;信号在进程中注册完毕;信号在进程中的注销完毕;信:信号诞生;信号在进程中注册完毕;信号在进程中的注销完毕;信:信号诞生;信号在进程中注册完毕;信号在进程中的注销完毕;信:信号诞生;信号在进程中注册完毕;信号在进程中的注销完毕;信号处理函数执

165、行完毕。相邻两个事件的时间间隔构成信号生命周期的号处理函数执行完毕。相邻两个事件的时间间隔构成信号生命周期的号处理函数执行完毕。相邻两个事件的时间间隔构成信号生命周期的号处理函数执行完毕。相邻两个事件的时间间隔构成信号生命周期的一个阶段。一个阶段。一个阶段。一个阶段。 1.1.1.1.信号信号信号信号“诞生诞生诞生诞生”。信号的诞生指的是触发信号的事件发生(如检测到。信号的诞生指的是触发信号的事件发生(如检测到。信号的诞生指的是触发信号的事件发生(如检测到。信号的诞生指的是触发信号的事件发生(如检测到硬件异常、定时器超时以及调用信号发送函数硬件异常、定时器超时以及调用信号发送函数硬件异常、定时

166、器超时以及调用信号发送函数硬件异常、定时器超时以及调用信号发送函数kill()kill()kill()kill()或或或或sigqueuesigqueuesigqueuesigqueue()()()()等)。等)。等)。等)。2.2.2.2.信号在目标进程中信号在目标进程中信号在目标进程中信号在目标进程中 注册注册注册注册 ;进程的;进程的;进程的;进程的task_task_task_task_structstructstructstruct结构中有关于本进程结构中有关于本进程结构中有关于本进程结构中有关于本进程中未决信号的数据成员:中未决信号的数据成员:中未决信号的数据成员:中未决信号的数据

167、成员: struct sigpendingstruct sigpendingstruct sigpendingstruct sigpending pending pending pending pending:struct sigpendingstruct sigpendingstruct sigpendingstruct sigpending struct sigqueuestruct sigqueuestruct sigqueuestruct sigqueue *head, *tail; *head, *tail; *head, *tail; *head, *tail;sigsetsigs

168、etsigsetsigset_t signal;_t signal;_t signal;_t signal; 信号生命周期(二)第第三三个个成成员员是是进进程程中中所所有有未未决决信信号号集集,第第一一、第第二二个个成成员员分分别别指指向向一一个个sigqueuesigqueue类类型型的的结结构构链链(称称之之为为 未未决决信信号号信信息息链链 )的的首首尾尾,信信息息链链中中的的每每个个sigqueuesigqueue结结构构刻刻画画一一个个特特定定信信号号所携带的信息,并指向下一个所携带的信息,并指向下一个sigqueuesigqueue结构结构: : struct sigqueuest

169、ruct sigqueuestruct sigqueue *next;struct sigqueue *next;siginfo_t info;siginfo_t info; 信号在进程中注册指的就是信号值加入到进程的未决信号集信号在进程中注册指的就是信号值加入到进程的未决信号集中(中(sigpendingsigpending结构的第二个成员结构的第二个成员sigset_t signalsigset_t signal),并且),并且信号所携带的信息被保留到未决信号信息链的某个信号所携带的信息被保留到未决信号信息链的某个sigqueuesigqueue结构中。只要信号在进程的未决信号集中,表明进

170、程已经知结构中。只要信号在进程的未决信号集中,表明进程已经知道这些信号的存在,但还没来得及处理,或者该信号被进程道这些信号的存在,但还没来得及处理,或者该信号被进程阻塞。阻塞。 信号生命周期(三) 注注注注:v当当一一个个实实时时信信号号发发送送给给一一个个进进程程时时,不不管管该该信信号号是是否否已已经经在在进进程程中中注注册册,都都会会被被再再注注册册一一次次,因因此此,信信号号不不会会丢丢失失,因因此此,实实时时信信号号又又叫叫做做“ “可可靠靠信信号号” ”。这这意意味味着着同同一一个个实实时时信信号号可可以以在在同同一一个个进进程程的的未未决决信信号号信信息息链链中中占占有有多多个个

171、sigqueuesigqueue结结构构(进进程程每每收收到到一一个个实实时时信信号号,都都会会为为它它分分配配一一个个结结构构来来登登记记该该信信号号信信息息,并并把把该该结结构构添添加加在在未未决决信信号号链链尾尾,即即所所有有诞诞生生的实时信号都会在目标进程中注册);的实时信号都会在目标进程中注册);v当当一一个个非非实实时时信信号号发发送送给给一一个个进进程程时时,如如果果该该信信号号已已经经在在进进程程中中注注册册,则则该该信信号号将将被被丢丢弃弃,造造成成信信号号丢丢失失。这这意意味味着着同同一一个个非非实实时时信信号号在在进进程程的的未未决决信信号号信信息息链链中中,至至多多占占

172、有有一一个个sigqueuesigqueue结结构构(一一个个非非实实时时信信号号诞诞生生后后,(1 1)、如如果果发发现现相相同同的的信信号号已已经经在在目目标标结结构构中中注注册册,则则不不再再注注册册,对对于于进进程程来来说说,相相当当于于不不知知道道本本次次信信号号发发生生,信信号号丢丢失失;(2 2)、如如果果进进程的未决信号中没有相同信号,则在进程中注册自己)程的未决信号中没有相同信号,则在进程中注册自己) 信号生命周期(四)3.3.信号在进程中的注销信号在进程中的注销。在目标进程执行过程中,会检测是否有信号。在目标进程执行过程中,会检测是否有信号等待处理。如果存在未决信号等待处理

173、且该信号没有被进程阻塞,等待处理。如果存在未决信号等待处理且该信号没有被进程阻塞,则在运行相应的信号处理函数前,进程会把信号在未决信号链中占则在运行相应的信号处理函数前,进程会把信号在未决信号链中占有的结构卸掉。是否将信号从进程未决信号集中删除对于实时与非有的结构卸掉。是否将信号从进程未决信号集中删除对于实时与非实时信号是不同的。对于非实时信号来说,由于在未决信号信息链实时信号是不同的。对于非实时信号来说,由于在未决信号信息链中最多只占用一个中最多只占用一个sigqueuesigqueue结构,因此该结构被释放后,应该把信结构,因此该结构被释放后,应该把信号在进程未决信号集中删除(号在进程未决

174、信号集中删除(信号注销完毕信号注销完毕);而对于实时信号来);而对于实时信号来说,可能在未决信号信息链中占用多个说,可能在未决信号信息链中占用多个sigqueuesigqueue结构,因此应该针结构,因此应该针对占用对占用sigqueuesigqueue结构的数目区别对待:如果只占用一个结构的数目区别对待:如果只占用一个sigqueuesigqueue结结构(进程只收到该信号一次),则应该把信号在进程的未决信号集构(进程只收到该信号一次),则应该把信号在进程的未决信号集中删除(信号注销完毕)。否则,不应该在进程的未决信号集中删中删除(信号注销完毕)。否则,不应该在进程的未决信号集中删除该信号。

175、进程在执行信号相应处理函数之前,首先要把信号在进除该信号。进程在执行信号相应处理函数之前,首先要把信号在进程中注销。程中注销。4.4.信号生命终止。信号生命终止。进程注销信号后,立即执行相应的信号处理函数,进程注销信号后,立即执行相应的信号处理函数,执行完毕后,信号的本次发送对进程的影响彻底结束。执行完毕后,信号的本次发送对进程的影响彻底结束。 几点说明 1) 用户程序的信号函数执行有一定的时机,而并非用户感觉到用户程序的信号函数执行有一定的时机,而并非用户感觉到的立即发生的立即发生 2)2)当用户程序当前正在执行时,用户程序由于系统调用或中断会进入系统当用户程序当前正在执行时,用户程序由于系

176、统调用或中断会进入系统态,当完成系统服务时并从系统态退出到用户态前,系统会检测用户可态,当完成系统服务时并从系统态退出到用户态前,系统会检测用户可能存在的信号,若存在,则触发相应处理函数。能存在的信号,若存在,则触发相应处理函数。3)3)当用户程序没有在执行时,则其它进程或系统发向该进程的信号需要等当用户程序没有在执行时,则其它进程或系统发向该进程的信号需要等到该进程被调度到后才会被触发。到该进程被调度到后才会被触发。2)信号注册与否,与发送信号的函数(如信号注册与否,与发送信号的函数(如kill()kill()或或sigqueuesigqueue()()等)以及信号安装函数(等)以及信号安装

177、函数(signal()signal()及及sigactionsigaction()())无关,无关,只与信号值有关(信号值小于只与信号值有关(信号值小于SIGRTMINSIGRTMIN的信号最多只注册一的信号最多只注册一次,信号值在次,信号值在SIGRTMINSIGRTMIN及及SIGRTMAXSIGRTMAX之间的信号,只要被进程之间的信号,只要被进程接收到就被注册)。接收到就被注册)。3)在信号被注销到相应的信号处理函数执行完毕这段时间内,在信号被注销到相应的信号处理函数执行完毕这段时间内,如果进程又收到同一信号多次,则对实时信号来说,每一次如果进程又收到同一信号多次,则对实时信号来说,每

178、一次都会在进程中注册;而对于非实时信号来说,无论收到多少都会在进程中注册;而对于非实时信号来说,无论收到多少次信号,都会视为只收到一个信号,只在进程中注册一次。次信号,都会视为只收到一个信号,只在进程中注册一次。 内核对信号的基本处理方法(一)v内内核核给给一一个个进进程程发发送送软软中中断断信信号号的的方方法法,是是在在进进程程所所在在的的进进程程表表项项的的信信号号域域设设置置对对应应于于该该信信号号的的位位。如如果果信信号号发发送送给给一一个个正正在在睡睡眠眠的的进进程程,那那么么要要看看该该进进程程进进入入睡睡眠眠的的优优先先级级,如如果果进进程程睡睡眠眠在在可可被被中中断断的的优优先

179、先级级上上,则则唤唤醒醒进进程程;否否则则仅仅设设置置进进程程表表中中信信号号域域相相应应的的位位,而而不不唤唤醒醒进进程程。这这一一点点比比较较重重要要,因因为为进进程程检检查查是是否否收收到到信信号号的的时时机机是是:一一个个进进程程在在即即将将从从内内核核态态返返回回到到用用户户态态时时;或或者者,在在一一个个进进程要进入或离开一个适当的低调度优先级睡眠状态时。程要进入或离开一个适当的低调度优先级睡眠状态时。v内内核核处处理理一一个个进进程程收收到到的的信信号号的的时时机机是是在在一一个个进进程程从从内内核核态态返返回回用用户户态态时时。所所以以,当当一一个个进进程程在在内内核核态态下下

180、运运行行时时,软软中中断断信信号号并并不不立立即即起起作作用用,要要等等到到将将返返回回用用户户态态时时才才处处理理。进进程程只只有有处处理理完完信信号号才才会会返返回回用用户户态态,进进程程在在用用户户态态下不会有未处理完的信号。下不会有未处理完的信号。 内核对信号的基本处理方法(二)v如果进程收到一个要捕捉的信号,那么进程从内如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。核态返回用户态时执行用户定义的函数。执行用执行用户定义的函数的方法是:户定义的函数的方法是:内核是在用户栈上创建内核是在用户栈上创建一个新的层,该层中将返回地址的值设置成用户一个新的层,该层

181、中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方。回再弹出栈顶时,才返回原先进入内核的地方。这样做的原因是用户定义的处理函数不能且不允这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。态下运行的话,用户就可以获得任何权限)。 内核对信号的基本处理方法(三)在信号的处理方法中有几点特别要引起注意。在

182、信号的处理方法中有几点特别要引起注意。1)1)在在一一些些系系统统中中,当当一一个个进进程程处处理理完完中中断断信信号号返返回回用用户户态态之之前前,内内核核清清除除用用户户区区中中设设定定的的对对该该信信号号的的处处理理例例程程的的地地址址,即即下下一一次次进进程程对对该该信信号号的的处处理理方方法法又又改改为为默默认认值值,除除非非在在下下一一次次信信号号到到来来之之前前再再次次使使用用signalsignal系系统统调调用用。这这可可能能会会使使得得进进程程在在调调用用signalsignal之之前前又又得得到到该该信信号号而而导导致致退退出出。在在LINUXLINUX、BSDBSD中中

183、,内内核核不不再再清清除除该该地地址址。但但不不清清除除该该地地址址可可能能使使得得进进程程因因为为过过多多过过快快的的得得到到某某个个信信号号而而导导致致堆堆栈栈溢溢出出。为为了了避避免免出出现现上上述述情情况况。在在BSDBSD系系统统中中,内内核核模模拟拟了了对对硬硬件件中中断的处理方法,即在处理某个中断时,阻止接收新的该类中断。断的处理方法,即在处理某个中断时,阻止接收新的该类中断。2)2)如如果果要要捕捕捉捉的的信信号号发发生生于于进进程程正正在在一一个个系系统统调调用用中中时时,并并且且该该进进程程睡睡眠眠在在可可中中断断的的优优先先级级上上,这这时时该该信信号号引引起起进进程程作

184、作一一次次longjmplongjmp,跳跳出出睡睡眠眠状状态态,返返回回用用户户态态并并执执行行信信号号处处理理例例程程。当当从从信信号号处处理理例例程程返返回回时时,进进程程就就象象从从系系统统调调用用返返回回一一样样,但但返返回回了了一一个个错错误误代代码码,指指出出该该次次系系统统调调用用曾曾经经被被中中断断。这这要要注注意意的的是是,BSDBSD系系统统中中内内核核可可以以自动地重新开始系统调用。自动地重新开始系统调用。( (前面有说明前面有说明SA_RESTARTSA_RESTART) )3)3)若若进进程程睡睡眠眠在在可可中中断断的的优优先先级级上上,则则当当它它收收到到一一个个

185、要要忽忽略略的的信信号号时时,该该进进程程被被唤唤醒醒,但但不不做做longjmplongjmp,一一般般是是继继续续睡睡眠眠。但但用用户户感感觉觉不不到到进程曾经被唤醒,而是象没有发生过该信号一样。进程曾经被唤醒,而是象没有发生过该信号一样。 信号在核心中的处理4)内内核核对对子子进进程程终终止止(SIGCLDSIGCLD)信信号号的的处处理理方方法法与与其其他他信信号号有有所所区区别别。当当进进程程检检查查出出收收到到了了一一个个子子进进程程终终止止的的信信号号时时,缺缺省省情情况况下下,该该进进程程就就象象没没有有收收到到该该信信号号似似的的,如如果果父父进进程程执执行行了了系系统统调调

186、用用waitwait,进进程程将将从从系系统统调调用用waitwait中中醒醒来来并并返返回回waitwait调调用用,执执行行一一系系列列waitwait调调用用的的后后续续操操作作(找找出出僵僵死死的的子子进进程程,释释放放子子进进程程的的进进程程表表项项),然然后后从从waitwait中中返返回回。SIGCLDSIGCLD信信号号的的作作用用是是唤唤醒醒一一个个睡睡眠眠在在可可被被中中断断优优先先级级上上的的进进程程。如如果果该该进进程程捕捕捉捉了了这这个个信信号号,就就象象普普通通信信号号处处理理一一样样转转到到处处理理例例程程。如如果果进进程程忽忽略略该该信信号号,那那么么系系统统调

187、调用用waitwait的的动动作作就就有有所所不不同同,因因为为SIGCLDSIGCLD的的作作用用仅仅仅仅是是唤唤醒醒一一个个睡睡眠眠在在可可被被中中断断优优先先级级上上的的进进程程,那那么么执执行行waitwait调调用用的的父父进进程程被被唤唤醒醒继继续续执执行行waitwait调调用用的的后后续续操操作作,然然后后等等待待其他的子进程。其他的子进程。 如如果果一一个个进进程程调调用用signalsignal系系统统调调用用,并并设设置置了了SIGCLDSIGCLD的的处处理理方方法法,并并且且该该进进程程有有子子进进程程处处于于僵僵死死状状态态,则则内内核核将将向向该进程发一个该进程发

188、一个SIGCLDSIGCLD信号。信号。 信号编程注意事项1.防防止止不不该该丢丢失失的的信信号号丢丢失失。如如果果对对上上面面所所提提到到的的信信号号生生命命周周期期理理解解深深刻刻的的话话,很很容容易易知知道道信信号号会会不不会会丢丢失失,以以及在哪里丢失。及在哪里丢失。 2.2.程程程程序序序序的的的的可可可可移移移移植植植植性性性性考考虑虑到到程程序序的的可可移移植植性性, ,应应该该尽尽量量采采用用POSIXPOSIX信信号号函函数数,POSIXPOSIX信号函数主要分为两类:信号函数主要分为两类: qPOSIX POSIX 1003.11003.1信信号号函函数数: Kill()K

189、ill()、sigactionsigaction()()、sigaddsetsigaddset()()、sigdelsetsigdelset()()、sigemptysetsigemptyset()()、sigfillsetsigfillset()()、sigismembersigismember()()、sigpendingsigpending()()、sigprocmasksigprocmask()()、sigsuspendsigsuspend()()。oPOSIX POSIX 1003.1b1003.1b信信号号函函数数。POSIX POSIX 1003.1b1003.1b在在信信号号的

190、的实实时时性性方方面面对对POSIX POSIX 1003.11003.1做做了了扩扩展展,包包括括以以下下三三个个函函数数: sigqueuesigqueue()()、 sigtimedwaitsigtimedwait()()、 sigwaitinfosigwaitinfo()()。 其其 中中 ,sigqueuesigqueue主主要要针针对对信信号号发发送送,而而sigtimedwaitsigtimedwait及及sigwaitinfosigwaitinfo()()主要用于取代主要用于取代sigsuspendsigsuspend()()函数函数 信号编程注意事项3.3.3.3.程序的稳定

191、性。程序的稳定性。程序的稳定性。程序的稳定性。为了增强程序的稳定性,在信号处理函数中应使用可重入函数。为了增强程序的稳定性,在信号处理函数中应使用可重入函数。 vv信信号号处处理理程程序序中中应应当当使使用用可可重重入入函函数数(注注:所所谓谓可可重重入入函函数数是是指指一一个个可可以以被被多多个个任任务务调调用用的的过过程程,任任务务在在调调用用时时不不必必担担心心数数据据是是否否会会出出错错)。因因为为进进程程在在收收到到信信号号后后,就就将将跳跳转转到到信信号号处处理理函函数数去去接接着着执执行行。如如果果信信号号处处理理函函数数中中使使用用了了不不可可重重入入函函数数,那那么么信信号号

192、处处理理函函数数可可能能会会修修改改原原来来进进程程中中不不应应该该被被修修改改的的数数据据,这这样样进进程程从从信信号号处处理理函函数数中中返返回回接接着着执执行行时时,可可能能会会出出现现不不可可预预料料的的后后果果。不不可可再再入入函数在信号处理函数中被视为不安全函数。函数在信号处理函数中被视为不安全函数。vv满满足足下下列列条条件件的的函函数数多多数数是是不不可可再再入入的的:(1 1)使使用用静静态态的的数数据据结结构构,如如 getlogingetlogin()(), gmtimegmtime()(), getgrgidgetgrgid()(), getgrnamgetgrnam(

193、)(), getpwuidgetpwuid()()以以及及getpwnamgetpwnam()()等等等等;(2 2)函函数数实实现现时时,调调用用了了mallocmalloc()或或者者freefree()()函数;(函数;(3 3)实现时使用了标准)实现时使用了标准I/OI/O函数的。函数的。vv即即使使信信号号处处理理函函数数使使用用的的都都是是 安安全全函函数数 ,同同样样要要注注意意进进入入处处理理函函数数时时,首首先先要要保保存存errnoerrno的的值值,结结束束时时,再再恢恢复复原原值值。因因为为,信信号号处处理理过过程程中中,errnoerrno值值随随时时可可能能被被改改

194、变变。另另外外,longjmplongjmp()()以以及及siglongjmpsiglongjmp()()没没有有被被列列为为可可再再入入函函数数,因因为为不不能能保保证证紧紧接接着着两两个个函函数数的的其其它它调调用是安全的。用是安全的。 信号应用实例linuxlinux下下的的信信号号应应用用并并没没有有想想象象的的那那么么复复杂杂,实实际上程序员所要做的最多只有三件事情:际上程序员所要做的最多只有三件事情:1.安装信号(推荐使用安装信号(推荐使用sigactionsigaction()());); 2.实实 现现 三三 参参 数数 信信 号号 处处 理理 函函 数数 , handler

195、(handler(intint signal,signal,struct siginfostruct siginfo *info, void *) *info, void *); 3.发送信号,推荐使用发送信号,推荐使用sigqueuesigqueue()()。 对对有有些些信信号号来来说说,只只要要安安装装信信号号就就足足够够了了(信信号号处处理理方方式式采采用用缺缺省省或或忽忽略略)。其其他他可可能能要要做做的无非是与信号集相关的几种操作。的无非是与信号集相关的几种操作。 实例,参考实例,参考5.125.12小结 信号用于很多复杂的应用程序中,理解进行信号处理的原因和方式对于高级LINUX程序设计极其重要。本章对LINUX信号进行了详细而且比较深入的介绍。首先介绍了信号的产生、分类,解释了LINUX下常用的32个用户信号的语义,分析了早期的信号实施存在的主要问题。然后逐一介绍了LINUX下信号发送和安装的主要接口,简要介绍了内核对信号的处理思想。最后我们分析了一些典型信号处理程序,有助于大家快速理解信号处理的大致方法。The End

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 建筑/环境 > 施工组织

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号