当前位置: 首页 > news >正文

厦门旅游网站建设目的前端低代码平台开发

厦门旅游网站建设目的,前端低代码平台开发,国内wordpress插件,电商设备网站怎么做目录 基本概念信号的分类可靠信号与不可靠信号实时信号与非实时信号 常见信号与默认行为进程对信号的处理signal()函数sigaction()函数 向进程发送信号kill()函数raise() alarm()和pause()函数alarm()函数pause()函数 信号集初始化信号集测试信号是否在信号集中 获取信号的描述… 目录 基本概念信号的分类可靠信号与不可靠信号实时信号与非实时信号 常见信号与默认行为进程对信号的处理signal()函数sigaction()函数 向进程发送信号kill()函数raise() alarm()和pause()函数alarm()函数pause()函数 信号集初始化信号集测试信号是否在信号集中 获取信号的描述信息strsignal()函数psignal()函数 信号掩码(阻塞信号传递)阻塞等待信号sigsuspend()实时信号sigpending()函数发送实时信号 异常退出abort()函数 本章将讨论信号虽然信号的基本概念比较简单但是其所涉及到的细节内容比较多在很多应用程序当中都会存在处理异步事件这种需求而信号提供了一种处理异步事件的方法所以信号机制在Linux 早期版本中就已经提供了支持随着Linux 内核版本的更新迭代其对信号机制的支持更加完善。 基本概念 信号是事件发生时对进程的通知机制也可以把它称为软件中断。信号与硬件中断的相似之处在于能够打断程序当前执行的正常流程其实是在软件层次上对中断机制的一种模拟。大多数情况下是无法预测信号达到的准确时间所以信号提供了一种处理异步事件的方法。 信号的目的是用来通信的一个具有合适权限的进程能够向另一个进程发送信号信号的这一用法可作为一种同步技术甚至是进程间通信IPC的原始形式。 信号可以由“谁”发出呢以下列举的很多情况均可以产生信号 ⚫ 硬件发生异常即硬件检测到错误条件并通知内核随即再由内核发送相应的信号给相关进程。硬件检测到异常的例子包括执行一条异常的机器语言指令除数为0、数组访问越界导致引用了无法访问的内存区域等这些异常情况都会被硬件检测到并通知内核、然后内核为该异常情况发生时正在运行的进程发送适当的信号以通知进程。 ⚫ 用于在终端下输入了能够产生信号的特殊字符。譬如在终端上按下CTRL C 组合按键可以产生中断信号SIGINT通过这个方法可以终止在前台运行的进程按下CTRL Z 组合按键可以产生暂停信号SIGCONT通过这个方法可以暂停当前前台运行的进程。 ⚫ 进程调用kill()系统调用可将任意信号发送给另一个进程或进程组。当然对此是有所限制的接收信号的进程和发送信号的进程的所有者必须相同亦或者发送信号的进程的所有者是root 超级用户。 ⚫ 用户可以通过kill 命令将信号发送给其它进程。通过kill命令来“杀死”终止一个进程譬如在终端下执行kill -9 xxx来杀死PID 为xxx 的进程。kill命令其内部的实现原理便是通过kill()系统调用来完成的。 ⚫ 发生了软件事件即当检测到某种软件条件已经发生。这里指的不是硬件产生的条件如除数为0、引用无法访问的内存区域等而是软件的触发条件、触发了某种软件条件进程所设置的定时器已经超时、进程执行的CPU 时间超限、进程的某个子进程退出等等情况。 进程同样也可以向自身发送信号然而发送给进程的诸多信号中大多数都是来自于内核。 信号的目的都是用于通信的当发生某种情况下通过信号将情况“告知”相应的进程从而达到同步、通信的目的。 信号通常是发送给对应的进程当信号到达后该进程需要做出相应的处理措施通常进程会视具体信号执行以下操作之一 ⚫ 忽略信号。当信号到达进程后该进程并不会去理会它但有两种信号却决不能被忽略它们是SIGKILL 和SIGSTOP这两种信号不能被忽略的原因是它们向内核和超级用户提供了使进程终止或停止的可靠方法。另外如果忽略某些由硬件异常产生的信号则进程的运行行为是未定义的。 ⚫ 捕获信号。当信号到达进程后执行预先绑定好的信号处理函数。为了做到这一点要通知内核在某种信号发生时执行用户自定义的处理函数该处理函数中将会对该信号事件作出相应的处理Linux 系统提供了signal()系统调用可用于注册信号的处理函数后面介绍。 ⚫ 执行系统默认操作。进程不对该信号事件作出处理而是交由系统进行处理每一种信号都会有其对应的系统默认的处理方式8.3 小节中对此有进行介绍。需要注意的是对大多数信号来说系统默认的处理方式就是终止该进程。 信号是异步事件产生信号的事件对进程而言是随机出现的进程无法预测该事件产生的准确时间只有当产生中断事件时才会告知程序、然后打断当前程序的正常执行流程、跳转去执行中断服务函数这就是异步处理方式。 信号本质上是int 类型的数字编号这就好比硬件中断所对应的中断号。内核针对每个信号都给其定义了一个唯一的整数编号从数字1 开始顺序展开。并且每一个信号都有其对应的名字其实就是一个宏信号名字与信号编号乃是一一对应关系但是由于每个信号的实际编号随着系统的不同可能会不一样所以在程序当中一般都使用信号的符号名也就是宏定义。 这些信号在signum.h头文件中定义每个信号都是以SIGxxx 开头如下所示 /* Signals. */ #define SIGHUP 1 /* Hangup (POSIX). */ #define SIGINT 2 /* Interrupt (ANSI). */ #define SIGQUIT 3 /* Quit (POSIX). */ #define SIGILL 4 /* Illegal instruction (ANSI). */ #define SIGTRAP 5 /* Trace trap (POSIX). */ #define SIGABRT 6 /* Abort (ANSI). */ #define SIGIOT 6 /* IOT trap (4.2 BSD). */ #define SIGBUS 7 /* BUS error (4.2 BSD). */ #define SIGFPE 8 /* Floating-point exception (ANSI). */ #define SIGKILL 9 /* Kill, unblockable (POSIX). */ #define SIGUSR1 10 /* User-defined signal 1 (POSIX). */ #define SIGSEGV 11 /* Segmentation violation (ANSI). */ #define SIGUSR2 12 /* User-defined signal 2 (POSIX). */ #define SIGPIPE 13 /* Broken pipe (POSIX). */ #define SIGALRM 14 /* Alarm clock (POSIX). */ #define SIGTERM 15 /* Termination (ANSI). */ #define SIGSTKFLT 16 /* Stack fault. */ #define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ #define SIGCHLD 17 /* Child status has changed (POSIX). */ #define SIGCONT 18 /* Continue (POSIX). */ #define SIGSTOP 19 /* Stop, unblockable (POSIX). */ #define SIGTSTP 20 /* Keyboard stop (POSIX). */ #define SIGTTIN 21 /* Background read from tty (POSIX). */ #define SIGTTOU 22 /* Background write to tty (POSIX). */ #define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ #define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ #define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ #define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ #define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ #define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ #define SIGPOLL SIGIO /* Pollable event occurred (System V). */ #define SIGIO 29 /* I/O now possible (4.2 BSD). */ #define SIGPWR 30 /* Power failure restart (System V). */ #define SIGSYS 31 /* Bad system call. */ #define SIGUNUSED 31不存在编号为0 的信号从示例代码8.1.1 中也可以看到信号编号是从1 开始的事实上kill()函数对信号编号0 有着特殊的应用后面介绍。 信号的分类 Linux 系统下可对信号从两个不同的角度进行分类从可靠性方面将信号分为可靠信号与不可靠信号而从实时性方面将信号分为实时信号与非实时信号。 可靠信号与不可靠信号 Linux 信号机制基本上是从UNIX 系统中继承过来的早期UNIX 系统中的信号机制比较简单后来在实践中暴露出一些问题它的主要问题是 ⚫ 进程每次处理信号后就将对信号的响应设置为系统默认操作。在某些情况下将导致对信号的错误处理因此用户如果不希望这样的操作那么就要在信号处理函数结尾再一次调用signal()重新为该信号绑定相应的处理函数。 ⚫ 早期UNIX 下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失处理信号时又来了新的信号则导致信号丢失。 Linux 支持不可靠信号但是对不可靠信号机制做了改进在调用完信号处理函数后不必重新调用signal()。因此Linux 下的不可靠信号问题主要指的是信号可能丢失。在Linux 系统下信号值小于SIGRTMIN34的信号都是不可靠信号这就是不可靠信号的来源所以示例代码8.1.1 中所列举的信号都是不可靠信号。 随着时间的发展实践证明有必要对信号的原始机制加以改进和扩充所以后来出现的各种UNIX版本分别在这方面进行了研究力图实现可靠信号。由于原来定义的信号已有许多应用不好再做改动最终只好又新增加了一些信号(SIGRTMIN~SIGRTMAX)并在一开始就把它们定义为可靠信号在Linux 系统下使用kill -l命令可查看到所有信号如下所示 Tips括号 ) 前面的数字对应该信号的编号编号1 ~ 31 所对应的是不可靠信号34~64 对应的是可靠信号从图中可知可靠信号并没有一个具体对应的名字而是使用了SIGRTMINN 或SIGRTMAX-N 的方式来表示。 可靠信号支持排队不会丢失同时信号的发送和绑定也出现了新版本信号发送函数sigqueue()及信号绑定函数sigaction()。 早期UNIX 系统只定义了31 种信号而Linux 3.x 支持64 种信号编号1-64(SIGRTMIN34SIGRTMAX64)将来可能进一步增加这需要得到内核的支持。前31 种信号已经有了预定义值每个信号有了确定的用途、含义以及对应的名字并且每种信号都有各自的系统默认操作。如按键盘的CTRLC时会产生SIGINT 信号对该信号的系统默认操作就是终止进程后32 个信号表示可靠信号。 实时信号与非实时信号 实时信号与非实时信号其实是从时间关系上进行的分类与可靠信号与不可靠信号是相互对应的非实时信号都不支持排队都是不可靠信号实时信号都支持排队都是可靠信号。实时信号保证了发送的多个信号都能被接收实时信号是POSIX 标准的一部分可用于应用进程。 一般我们也把非实时信号不可靠信号称为标准信号如果文档中用到了这个词那么大家要知道这里指的就是非实时信号不可靠信号。关于更多实时信号相关内容将会在8.10 小节中介绍。 常见信号与默认行为 前面说到Linux 下对标准信号不可靠信号、非实时信号的编号为1~31如示例代码8.1.1 所示接下来将介绍这些信号以及这些信号所对应的系统默认操作。 ⚫ SIGINT 当用户在终端按下中断字符通常是CTRL C时内核将发送SIGINT 信号给前台进程组中的每一个进程。该信号的系统默认操作是终止进程的运行。所以通常我们都会使用CTRL C 来终止一个占用前台的进程原因在于大部分的进程会将该信号交给系统去处理从而执行该信号的系统默认操作。 ⚫ SIGQUIT 当用户在终端按下退出字符通常是CTRL \时内核将发送SIGQUIT 信号给前台进程组中的每一个进程。该信号的系统默认操作是终止进程的运行、并生成可用于调试的核心转储文件。进程如果陷入无限循环、或不再响应时使用SIGQUIT 信号就很合适。所以对于一个前台进程既可以在终端按下中断字符CTRL C、也可以按下退出字符CTRL \来终止当然前提条件是此进程会将SIGINT 信号或SIGQUIT信号交给系统处理也就是没有将信号忽略或捕获进入执行该信号所对应的系统默认操作。 ⚫ SIGILL 如果进程试图执行非法即格式不正确的机器语言指令系统将向进程发送该信号。该信号的系统默认操作是终止进程的运行。 ⚫ SIGABRT 当进程调用abort()系统调用时进程异常终止系统会向该进程发送SIGABRT 信号。该信号的系统默认操作是终止进程、并生成核心转储文件。 ⚫ SIGBUS 产生该信号总线错误bus error表示发生了某种内存访问错误。该信号的系统默认操作是终止进程。 ⚫ SIGFPE 该信号因特定类型的算术错误而产生譬如除以0。该信号的系统默认操作是终止进程。 ⚫ SIGKILL 此信号为“必杀sure kill”信号用于杀死进程的终极办法此信号无法被进程阻塞、忽略或者捕获故而“一击必杀”总能终止进程。使用SIGINT 信号和SIGQUIT 信号虽然能终止进程但是前提条件是该进程并没有忽略或捕获这些信号如果使用SIGINT 或SIGQUIT 无法终止进程那就使用“必杀信号”SIGKILL 吧。Linux 下有一个kill 命令kill 命令可用于向进程发送信号我们会使用kill -9 xxx命令来终止一个进程xxx 表示进程的pid这里的-9 其实指的就是发送编号为9 的信号也就是SIGKILL 信号。 ⚫ SIGUSR1 该信号和SIGUSR2 信号供程序员自定义使用内核绝不会为进程产生这些信号在我们的程序中可以使用这些信号来互通通知事件的发生或是进程彼此同步操作。该信号的系统默认操作是终止进程。 ⚫ SIGSEGV 这一信号非常常见当应用程序对内存的引用无效时操作系统就会向该应用程序发送该信号。引起对内存无效引用的原因很多C 语言中引发这些事件往往是解引用的指针里包含了错误地址譬如未初始化的指针或者传递了一个无效参数供函数调用等。该信号的系统默认操作是终止进程。 ⚫ SIGUSR2 与SIGUSR1 信号相同。 ⚫ SIGPIPE 涉及到管道和socket当进程向已经关闭的管道、FIFO 或套接字写入信息时那么系统将发送该信号给进程。该信号的系统默认操作是终止进程。 ⚫ SIGALRM 与系统调用alarm()或setitimer()有关应用程序中可以调用alarm()或setitimer()函数来设置一个定时器当定时器定时时间到那么内核将会发送SIGALRM 信号给该应用程序关于alarm()或setitimer()函数的使用后面将会进行讲解。该信号的系统默认操作是终止进程。 ⚫ SIGTERM 这是用于终止进程的标准信号也是kill 命令所发送的默认信号kill xxxxxx 表示进程pid有时我们会直接使用kill -9 xxx显式向进程发送SIGKILL 信号来终止进程然而这一做法通常是错误的精心设计的应用程序应该会捕获SIGTERM 信号、并为其绑定一个处理函数当该进程收到SIGTERM 信号时会在处理函数中清除临时文件以及释放其它资源再而退出程序。如果直接使用SIGKILL 信号终止进程从而跳过了SIGTERM 信号的处理函数通常SIGKILL 终止进程是不友好的方式、是暴力的方式这种方式应该作为最后手段应首先尝试使用SIGTERM实在不行再使用最后手段SIGKILL。 ⚫ SIGCHLD 当父进程的某一个子进程终止时内核会向父进程发送该信号。当父进程的某一个子进程因收到信号而停止或恢复时内核也可能向父进程发送该信号。注意这里说的停止并不是终止你可以理解为暂停。该信号的系统默认操作是忽略此信号如果父进程希望被告知其子进程的这种状态改变则应捕获此信号。 ⚫ SIGCLD 与SIGCHLD 信号同义。 ⚫ SIGCONT 将该信号发送给已停止的进程进程将会恢复运行。当进程接收到此信号时并不处于停止状态系统默认操作是忽略该信号但如果进程处于停止状态则系统默认操作是使该进程继续运行。 ⚫ SIGSTOP 这是一个“必停”信号用于停止进程注意停止不是终止停止只是暂停运行、进程并没有终止应用程序无法将该信号忽略或者捕获故而总能停止进程。 ⚫ SIGTSTP 这也是一个停止信号当用户在终端按下停止字符通常是CTRL Z那么系统会将SIGTSTP 信号发送给前台进程组中的每一个进程使其停止运行。 ⚫ SIGXCPU 当进程的CPU 时间超出对应的资源限制时内核将发送此信号给该进程。 ⚫ SIGVTALRM 应用程序调用setitimer()函数设置一个虚拟定时器当定时器定时时间到时内核将会发送该信号给进程。 ⚫ SIGWINCH 在窗口环境中当终端窗口尺寸发生变化时譬如用户手动调整了大小应用程序调用ioctl()设置了大小等系统会向前台进程组中的每一个进程发送该信号。 ⚫ SIGPOLL/SIGIO 这两个信号同义。这两个信号将会在高级IO 章节内容中使用到用于提示一个异步IO 事件的发生譬如应用程序打开的文件描述符发生了I/O 事件时内核会向应用程序发送SIGIO 信号。 ⚫ SIGSYS 如果进程发起的系统调用有误那么内核将发送该信号给对应的进程。 以上就是关于这些信号的简单介绍内容以上所介绍的这些信号并不包括Linux 下所有的信号仅给大家介绍了一下常见信号表8.3.1 将对这些信号进行总结。 Tips上表中term 表示终止进程core 表示生成核心转储文件核心转储文件可用于调试这个便不再给介绍了ignore表示忽略信号cont 表示继续运行进程stop 表示停止进程注意停止不等于终止而是暂停。 进程对信号的处理 当进程接收到内核或用户发送过来的信号之后根据具体信号可以采取不同的处理方式忽略信号、捕获信号或者执行系统默认操作。Linux 系统提供了系统调用signal()和sigaction()两个函数用于设置信号的处理方式本小节将向大家介绍这两个系统调用的使用方法。 signal()函数 signal()函数是Linux 系统下设置信号处理方式最简单的接口可将信号的处理方式设置为捕获信号、忽略信号以及系统默认操作此函数原型如下所示 #include signal.h typedef void (*sig_t)(int);sig_t signal(int signum, sig_t handler);函数参数和返回值含义如下 signum此参数指定需要进行设置的信号可使用信号名宏或信号的数字编号建议使用信号名。 handlersig_t 类型的函数指针指向信号对应的信号处理函数当进程接收到信号后会自动执行该处理函数参数handler 既可以设置为用户自定义的函数也就是捕获信号时需要执行的处理函数也可以设置为SIG_IGN 或SIG_DFLSIG_IGN 表示此进程需要忽略该信号SIG_DFL 则表示设置为系统默认操作。 sig_t 函数指针的int 类型参数指的是当前触发该函数的信号可将多个信号绑定到同一个信号处理函数上此时就可通过此参数来判断当前触发的是哪个信号。 SIG_IGN、SIG_DFL 分别取值如下 /* Fake signal functions. */ #define SIG_ERR ((sig_t) -1) /* Error return. */ #define SIG_DFL ((sig_t) 0) /* Default action. */ #define SIG_IGN ((sig_t) 1) /* Ignore signal. */返回值此函数的返回值也是一个sig_t 类型的函数指针成功情况下的返回值则是指向在此之前的信号处理函数如果出错则返回SIG_ERR并会设置errno。 由此可知signal()函数可以根据第二个参数handler 的不同设置情况可对信号进行不同的处理。 signal()函数测试 #include stdio.h #include stdlib.h #include signal.h static void sig_handler(int sig) {printf(Received signal: %d\n, sig); } int main(int argc, char *argv[]) {sig_t ret NULL;ret signal(SIGINT, (sig_t)sig_handler);if (SIG_ERR ret){perror(signal error);exit(-1);}/* 死循环*/for (;;){}exit(0); }在上述示例代码中我们通过signal()函数将SIGINT2信号绑定到了一个用户自定的处理函数上sig_handler(int sig)当进程收到SIGINT 信号后会执行该函数然后运行printf 打印语句。当运行程序之后程序会卡在for 死循环处此时在终端按下中断符CTRL C系统便会给前台进程组中的每一个进程发送SIGINT 信号我们测试程序便会收到该信号。 运行测试 当运行程序之后程序会占用终端称为一个前台进程此时按下中断符便会打印出信息^C 表示按下了中断符。平时大家使用CTRL C 可以终止一个进程而这里却不能通过这种方式来终止这个测试程序原因在于测试程序中捕获了该信号而对应的处理方式仅仅只是打印一条语句、而并不终止进程。 那此时该怎么关闭这个测试程序呢前面给大家介绍了“一击必杀”信号SIGKILL编号为9可向该进程发送SIGKILL 暴力终止该进程当然一般不推荐大家这样使用如果实在没办法才采取这种措施。新打开一个终端使用ps 命令找到该进程的pid 号再使用kill 命令如下所示 此时测试程序就会强制终止 Tips普通用户只能杀死该用户自己的进程无权限杀死其它用户的进程。 我们再执行一次测试程序这里将测试程序放在后台运行然后再按下中断符 按下中断符发现进程并没有收到SIGINT 信号原因很简单因为进程并不是前台进程、而是一个后台进程按下中断符时系统并不会给后台进程发送SIGINT 信号。可以使用kill 命令手动发送信号给我们的进程 如果程序中没有调用signal()函数为信号设置相应的处理方式亦或者程序刚启动起来并未运行到signal()处那么这时进程接收到一个信号后是如何处理的呢 ⚫ 程序启动 当一个应用程序刚启动的时候或者程序中没有调用signal()函数通常情况下进程对所有信号的处理方式都设置为系统默认操作。所以如果在我们的程序当中没有调用signal()为信号设置处理方式则默认的处理方式便是系统默认操作。 所以为什么大家平时都可以使用CTRL C 中断符来终止一个进程因为大部分情况下应用程序中并不会为SIGINT 信号设置处理方式所以该信号的处理方式便是系统默认操作当接收到信号之后便执行系统默认操作而SIGINT 信号的系统默认操作便是终止进程。 ⚫ 进程创建 当一个进程调用fork()创建子进程时其子进程将会继承父进程的信号处理方式因为子进程在开始时复制了父进程的内存映像所以信号捕获函数的地址在子进程中是有意义的。 sigaction()函数 sigaction()允许单独获取信号的处理函数而不是设置并且还可以设置各种属性对调用信号处理函数时的行为施以更加精准的控制其函数原型如下所示 #include signal.h int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);函数参数和返回值含义如下 signum需要设置的信号除了SIGKILL 信号和SIGSTOP 信号之外的任何信号。 actact 参数是一个struct sigaction 类型指针指向一个struct sigaction 数据结构该数据结构描述了信号的处理方式稍后介绍该数据结构如果参数act 不为NULL则表示需要为信号设置新的处理方式如果参数act 为NULL则表示无需改变信号当前的处理方式。 oldactoldact 参数也是一个struct sigaction 类型指针指向一个struct sigaction 数据结构。如果参数oldact 不为NULL则会将信号之前的处理方式等信息通过参数oldact 返回出来如果无意获取此类信息那么可将该参数设置为NULL。 返回值成功返回0失败将返回-1并设置errno。 struct sigaction 结构体 struct sigaction {void (*sa_handler)(int);void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;int sa_flags;void (*sa_restorer)(void); };结构体成员介绍 ⚫ sa_handler指定信号处理函数与signal()函数的handler 参数相同。 ⚫ sa_sigaction也用于指定信号处理函数这是一个替代的信号处理函数他提供了更多的参数可以通过该函数获取到更多信息这些信号通过siginfo_t 参数获取稍后介绍该数据结构sa_handler 和sa_sigaction 是互斥的不能同时设置对于标准信号来说使用sa_handler 就可以了可通过SA_SIGINFO 标志进行选择。 ⚫ sa_mask参数sa_mask 定义了一组信号当进程在执行由sa_handler 所定义的信号处理函数之前会先将这组信号添加到进程的信号掩码字段中当进程执行完处理函数之后再恢复信号掩码将这组信号从信号掩码字段中删除。当进程在执行信号处理函数期间可能又收到了同样的信号或其它信号从而打断当前信号处理函数的执行这就像中断嵌套通常我们在执行信号处理函数期间不希望被另一个信号所打断就是通过信号掩码来实现如果进程接收到了信号掩码中的这些信号那么这个信号将会被阻塞暂时不能得到处理直到这些信号从进程的信号掩码中移除。在信号处理函数调用时进程会自动将当前处理的信号添加到信号掩码字段中这样保证了在处理一个给定的信号时如果此信号再次发生那么它将会被阻塞。如果用户还需要在阻塞其它的信号则可以通过设置参数sa_mask 来完成此参数是sigset_t 类型变量关于该类型的介绍信息请看8.6.1 小节内容关于信号掩码还会在8.7.1 小节中进一步介绍信号掩码可以避免一些信号之间的竞争状态也称为竞态。 ⚫ sa_restorer该成员已过时不再使用了。 ⚫ sa_flags参数sa_flags 指定了一组标志这些标志用于控制信号的处理过程可设置为如下这些标志多个标志使用位或 | 组合 SA_NOCLDSTOP 如果signum 为SIGCHLD则子进程停止时即当它们接收到SIGSTOP、SIGTSTP、SIGTTIN 或SIGTTOU中的一种时或恢复即它们接收到SIGCONT时不会收到SIGCHLD 信号。 SA_NOCLDWAIT 如果signum 是SIGCHLD则在子进程终止时不要将其转变为僵尸进程。 SA_NODEFER 不要阻塞从某个信号自身的信号处理函数中接收此信号。也就是说当进程此时正在执行某个信号的处理函数默认情况下进程会自动将该信号添加到进程的信号掩码字段中从而在执行信号处理函数期间阻塞该信号默认情况下我们期望进程在处理一个信号时阻塞同种信号否则引起一些竞态条件如果设置了SA_NODEFER 标志则表示不对它进行阻塞。 SA_RESETHAND 执行完信号处理函数之后将信号的处理方式设置为系统默认操作。 SA_RESTART 被信号中断的系统调用在信号处理完成之后将自动重新发起。 SA_SIGINFO 如果设置了该标志则表示使用sa_sigaction 作为信号处理函数、而不是sa_handler关于sa_sigaction信号处理函数的参数信息。 以上就是关于struct sigaction 结构体相关的内容介绍了接下编写程序进行实战测试。 siginfo_t 结构体 siginfo_t {int si_signo; /* Signal number */int si_errno; /* An errno value */int si_code; /* Signal code */int si_trapno; /* Trap number that caused hardware-generated signal(unused on most architectures) */pid_t si_pid; /* Sending process ID */uid_t si_uid; /* Real user ID of sending process */int si_status; /* Exit value or signal */clock_t si_utime; /* User time consumed */clock_t si_stime; /* System time consumed */sigval_t si_value; /* Signal value */int si_int; /* POSIX.1b signal */void *si_ptr; /* POSIX.1b signal */int si_overrun; /* Timer overrun count; POSIX.1b timers */int si_timerid; /* Timer ID; POSIX.1b timers */void *si_addr; /* Memory location which caused fault */long si_band; /* Band event (was int in glibc 2.3.2 and earlier) */int si_fd; /* File descriptor */short si_addr_lsb; /* Least significant bit of address(since Linux 2.6.32) */void *si_call_addr; /* Address of system call instruction(since Linux 3.5) */int si_syscall; /* Number of attempted system call(since Linux 3.5) */unsigned int si_arch; /* Architecture of attempted system call(since Linux 3.5) */ }这个结构体就不给大家介绍了使用man 手册查看sigaction()函数帮助信息时在下面会有介绍。 测试 这里使用sigaction()函数实现与示例代码8.4.1 相同的功能。 #include stdio.h #include stdlib.h #include signal.hstatic void sig_handler(int sig) {printf(Received signal: %d\n, sig); }int main(int argc, char *argv[]) {struct sigaction sig {0};int ret;sig.sa_handler sig_handler;sig.sa_flags 0;ret sigaction(SIGINT, sig, NULL);if (-1 ret){perror(sigaction error);exit(-1);}/* 死循环*/for (;;){}exit(0); }运行测试 一般而言将信号处理函数设计越简单越好这就好比中断处理函数越快越好不要在处理函数中做大量消耗CPU 时间的事情这一个重要的原因在于设计的越简单这将降低引发信号竞争条件的风险。 向进程发送信号 与kill 命令相类似Linux 系统提供了kill()系统调用一个进程可通过kill()向另一个进程发送信号除了kill()系统调用之外Linux 系统还提供了系统调用killpg()以及库函数raise()也可用于实现发送信号的功能本小节将向大家进行介绍。 kill()函数 kill()系统调用可将信号发送给指定的进程或进程组中的每一个进程其函数原型如下所示 #include sys/types.h #include signal.hint kill(pid_t pid, int sig);函数参数和返回值含义如下 pid参数pid 为正数的情况下用于指定接收此信号的进程pid除此之外参数pid 也可设置为0 或-1 以及小于-1 等不同值稍后给说明。 sig参数sig 指定需要发送的信号也可设置为0如果参数sig 设置为0 则表示不发送信号但任执行错误检查这通常可用于检查参数pid 指定的进程是否存在。 返回值成功返回0失败将返回-1并设置errno。 参数pid 不同取值含义 ⚫ 如果pid 为正则信号sig 将发送到pid 指定的进程。 ⚫ 如果pid 等于0则将sig 发送到当前进程的进程组中的每个进程。 ⚫ 如果pid 等于-1则将sig 发送到当前进程有权发送信号的每个进程但进程1init除外。 ⚫ 如果pid 小于-1则将sig 发送到ID 为-pid 的进程组中的每个进程。 进程中将信号发送给另一个进程是需要权限的并不是可以随便给任何一个进程发送信号超级用户root 进程可以将信号发送给任何进程但对于非超级用户普通用户进程来说其基本规则是发送者进程的实际用户ID 或有效用户ID 必须等于接收者进程的实际用户ID 或有效用户ID。 从上面介绍可知当sig 为0 时仍可进行正常执行的错误检查但不会发送信号这通常可用于确定一个特定的进程是否存在如果向一个不存在的进程发送信号kill()将会返回-1errno 将被设置为ESRCH表示进程不存在。 测试 (1)使用kill()函数向一个指定的进程发送信号。 #include stdio.h #include stdlib.h #include sys/types.h #include signal.h #include stdlib.hint main(int argc, char *argv[]) {int pid;/* 判断传参个数*/if (2 argc)exit(-1);/* 将传入的字符串转为整形数字*/pid atoi(argv[1]);printf(pid: %d\n, pid);/* 向pid 指定的进程发送信号*/if (-1 kill(pid, SIGINT)){perror(kill error);exit(-1);}exit(0); }以上代码通过kill()函数向指定进程发送SIGINT 信号可通过外部传参将接收信号的进程pid 传入到程序中再执行该测试代码之前需要运行先一个用于接收此信号的进程接收信号的进程直接使用示例代码8.4.4 程序。 运行测试 testApp1 是示例代码8.4.4 对应的程序testApp 则是示例代码8.5.1 对应的程序首先执行./testApp1 “将接收信号的程序置于后台运行其进程pid 为21825接着执行”./testApp 21825向接收信号的进程发送SIGINT 信号。 raise() 有时进程需要向自身发送信号raise()函数可用于实现这一要求raise()函数原型如下所示此函数为C库函数 #include signal.h int raise(int sig);使用该函数需要包含头文件signal.h。 函数参数和返回值含义如下 sig需要发送的信号。 返回值成功返回0失败将返回非零值。 raise()其实等价于 kill(getpid(), sig);Tipsgetpid()函数用于获取进程自身的pid。 测试 #include stdio.h #include stdlib.h #include signal.h #include unistd.h static void sig_handler(int sig) {printf(Received signal: %d\n, sig); } int main(int argc, char *argv[]) {struct sigaction sig {0};int ret;sig.sa_handler sig_handler;sig.sa_flags 0;ret sigaction(SIGINT, sig, NULL);if (-1 ret){perror(sigaction error);exit(-1);}for (;;){/* 向自身发送SIGINT 信号*/if (0 ! raise(SIGINT)){printf(raise error\n);exit(-1);}sleep(3); // 每隔3 秒发送一次}exit(0); }alarm()和pause()函数 本小节向大家介绍两个系统调用alarm()和pause()。 alarm()函数 使用alarm()函数可以设置一个定时器闹钟当定时器定时时间到时内核会向进程发送SIGALRM信号其函数原型如下所示 #include unistd.h unsigned int alarm(unsigned int seconds);函数参数和返回值 seconds设置定时时间以秒为单位如果参数seconds 等于0则表示取消之前设置的alarm 闹钟。 返回值如果在调用alarm()时之前已经为该进程设置了alarm 闹钟还没有超时则该闹钟的剩余值作为本次alarm()函数调用的返回值之前设置的闹钟则被新的替代否则返回0。 参数seconds 的值是产生SIGALRM 信号需要经过的时钟秒数当这一刻到达时由内核产生该信号每个进程只能设置一个alarm 闹钟虽然SIGALRM 信号的系统默认操作是终止进程但是如果程序当中设置了alarm 闹钟但大多数使用闹钟的进程都会捕获此信号。 需要注意的是alarm 闹钟并不能循环触发只能触发一次若想要实现循环触发可以在SIGALRM 信号处理函数中再次调用alarm()函数设置定时器。 测试 使用alarm()来设计一个闹钟。 #include stdio.h #include stdlib.h #include signal.h #include unistd.h static void sig_handler(int sig) {puts(Alarm timeout);exit(0); } int main(int argc, char *argv[]) {struct sigaction sig {0};int second;/* 检验传参个数*/if (2 argc)exit(-1);/* 为SIGALRM 信号绑定处理函数*/sig.sa_handler sig_handler;sig.sa_flags 0;if (-1 sigaction(SIGALRM, sig, NULL)){perror(sigaction error);exit(-1);}/* 启动alarm 定时器*/second atoi(argv[1]);printf(定时时长: %d 秒\n, second);alarm(second);/* 循环*/for (;;)sleep(1);exit(0); }运行测试 pause()函数 pause()系统调用可以使得进程暂停运行、进入休眠状态直到进程捕获到一个信号为止只有执行了信号处理函数并从其返回时pause()才返回在这种情况下pause()返回-1并且将errno 设置为EINTR。其函数原型如下所示 #include unistd.h int pause(void);测试 通过alarm()和pause()函数模拟sleep 功能。 #include stdio.h #include stdlib.h #include signal.h #include unistd.h static void sig_handler(int sig) {puts(Alarm timeout); } int main(int argc, char *argv[]) {struct sigaction sig {0};int second;/* 检验传参个数*/if (2 argc)exit(-1);/* 为SIGALRM 信号绑定处理函数*/sig.sa_handler sig_handler;sig.sa_flags 0;if (-1 sigaction(SIGALRM, sig, NULL)){perror(sigaction error);exit(-1);}/* 启动alarm 定时器*/second atoi(argv[1]);printf(定时时长: %d 秒\n, second);alarm(second);/* 进入休眠状态*/pause();puts(休眠结束);exit(0); }运行测试 信号集 通常我们需要有一个能表示多个信号一组信号的数据类型—信号集signal set很多系统调用都使用到了信号集这种数据类型来作为参数传递譬如sigaction()函数、sigprocmask()函数、sigpending()函数等。本小节向大家介绍信号集这个数据类型。 信号集其实就是sigset_t 类型数据结构来看看 # define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) typedef struct {unsigned long int __val[_SIGSET_NWORDS]; } sigset_t;使用这个结构体可以表示一组信号将多个信号添加到该数据结构中当然Linux 系统了用于操作sigset_t 信号集的API譬如sigemptyset()、sigfillset()、sigaddset()、sigdelset()、sigismember()接下来向大家介绍。 初始化信号集 sigemptyset()和sigfillset()用于初始化信号集。sigemptyset()初始化信号集使其不包含任何信号而sigfillset()函数初始化信号集使其包含所有信号包括所有实时信号函数原型如下 #include signal.h int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set);使用这些函数需要包含头文件signal.h。 函数参数和返回值含义如下 set指向需要进行初始化的信号集变量。 返回值成功返回0失败将返回-1并设置errno。 使用示例 初始化为空信号集 sigset_t sig_set; sigemptyset(sig_set);初始化信号集使其包含所有信号 sigset_t sig_set; sigfillset(sig_set);向信号集中添加/删除信号 分别使用sigaddset()和sigdelset()函数向信号集中添加或移除一个信号函数原型如下 #include signal.h int sigaddset(sigset_t *set, int signum); int sigdelset(sigset_t *set, int signum);函数参数和返回值含义如下 set指向信号集。 signum需要添加/删除的信号。 返回值成功返回0失败将返回-1并设置errno。 使用示例 向信号集中添加信号 sigset_t sig_set; sigemptyset(sig_set); sigaddset(sig_set, SIGINT);从信号集中移除信号 sigset_t sig_set; sigfillset(sig_set); sigdelset(sig_set, SIGINT);测试信号是否在信号集中 使用sigismember()函数可以测试某一个信号是否在指定的信号集中函数原型如下所示 #include signal.h int sigismember(const sigset_t *set, int signum);函数参数和返回值含义如下 set指定信号集。 signum需要进行测试的信号。 返回值如果信号signum 在信号集set 中则返回1如果不在信号集set 中则返回0失败则返回- 1并设置errno。 使用示例 判断SIGINT 信号是否在sig_set 信号集中 sigset_t sig_set; ...... if (1 sigismember(sig_set, SIGINT)) puts(信号集中包含SIGINT 信号); else if (!sigismember(sig_set, SIGINT)) puts(信号集中不包含SIGINT 信号);获取信号的描述信息 在Linux 下每个信号都有一串与之相对应的字符串描述信息用于对该信号进行相应的描述。这些字符串位于sys_siglist 数组中sys_siglist 数组是一个char *类型的数组数组中的每一个元素存放的是一个字符串指针指向一个信号描述信息。譬如可以使用sys_siglist[SIGINT]来获取对SIGINT 信号的描述。我们编写一个简单地程序进行测试 Tips使用sys_siglist 数组需要包含signal.h头文件。 #include signal.h #include stdio.h #include stdlib.h int main(void) {printf(SIGINT 描述信息: %s\n, sys_siglist[SIGINT]);printf(SIGQUIT 描述信息: %s\n, sys_siglist[SIGQUIT]);printf(SIGBUS 描述信息: %s\n, sys_siglist[SIGBUS]);exit(0); }运行结果 从图中打印信息可知这个描述信息其实非常简洁没什么太多的信息。 strsignal()函数 除了直接使用sys_siglist 数组获取描述信息之外还可以使用strsignal()函数。较之于直接引用sys_siglist 数组更推荐使用strsignal()函数其函数原型如下所示 #include string.h char *strsignal(int sig);使用strsignal()函数需要包含头文件string.h这是一个库函数。 调用strsignal()函数将会获取到参数sig 指定的信号对应的描述信息返回该描述信息字符串的指针函数会对参数sig 进行检查若传入的sig 无效则会返回Unknown signal信息。 使用示例 #include signal.h #include stdio.h #include stdlib.h #include string.h int main(void) {printf(SIGINT 描述信息: %s\n, strsignal(SIGINT));printf(SIGQUIT 描述信息: %s\n, strsignal(SIGQUIT));printf(SIGBUS 描述信息: %s\n, strsignal(SIGBUS));printf(编号为1000 的描述信息: %s\n, strsignal(1000));exit(0); }测试结果 psignal()函数 psignal()可以在标准错误stderr上输出信号描述信息其函数原型如下所示 #include signal.h void psignal(int sig, const char *s);调用psignal()函数会将参数sig 指定的信号对应的描述信息输出到标准错误并且还允许调用者添加一些输出信息由参数s 指定所以整个输出信息由字符串s、冒号、空格、描述信号编号sig 的字符串和尾随的换行符组成。 使用示例 #include signal.h #include stdio.h #include stdlib.h #include string.h int main(void) {psignal(SIGINT, SIGINT 信号描述信息);psignal(SIGQUIT, SIGQUIT 信号描述信息);psignal(SIGBUS, SIGBUS 信号描述信息);exit(0); } 运行结果 信号掩码(阻塞信号传递) 内核为每一个进程维护了一个信号掩码其实就是一个信号集即一组信号。当进程接收到一个属于信号掩码中定义的信号时该信号将会被阻塞、无法传递给进程进行处理那么内核会将其阻塞直到该信号从信号掩码中移除内核才会把该信号传递给进程从而得到处理。 向信号掩码中添加一个信号通常有如下几种方式 ⚫ 当应用程序调用signal()或sigaction()函数为某一个信号设置处理方式时进程会自动将该信号添加到信号掩码中这样保证了在处理一个给定的信号时如果此信号再次发生那么它将会被阻塞当然对于sigaction()而言是否会如此需要根据sigaction()函数是否设置了SA_NODEFER 标志而定当信号处理函数结束返回后会自动将该信号从信号掩码中移除。 ⚫ 使用sigaction()函数为信号设置处理方式时可以额外指定一组信号当调用信号处理函数时将该组信号自动添加到信号掩码中当信号处理函数结束返回后再将这组信号从信号掩码中移除通过sa_mask 参数进行设置参考8.4.2 小节内容。 ⚫ 除了以上两种方式之外还可以使用sigprocmask()系统调用随时可以显式地向信号掩码中添加/ 移除信号。 sigprocmask()函数原型如下所示 #include signal.h int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);使用该函数需要包含头文件signal.h。 函数参数和返回值含义如下 how参数how 指定了调用函数时的一些行为。 set将参数set 指向的信号集内的所有信号添加到信号掩码中或者从信号掩码中移除如果参数set 为 NULL则表示无需对当前信号掩码作出改动。 oldset如果参数oldset 不为NULL在向信号掩码中添加新的信号之前获取到进程当前的信号掩码存放在oldset 所指定的信号集中如果为NULL 则表示不获取当前的信号掩码。 返回值成功返回0失败将返回-1并设置errno。 参数how 可以设置为以下宏 ⚫ SIG_BLOCK将参数set 所指向的信号集内的所有信号添加到进程的信号掩码中。换言之将信号掩码设置为当前值与set 的并集。 ⚫ SIG_UNBLOCK将参数set 指向的信号集内的所有信号从进程信号掩码中移除。 ⚫ SIG_SETMASK进程信号掩码直接设置为参数set 指向的信号集。 使用示例 将信号SIGINT 添加到进程的信号掩码中 int ret; /* 定义信号集*/ sigset_t sig_set; /* 将信号集初始化为空*/ sigemptyset(sig_set); /* 向信号集中添加SIGINT 信号*/ sigaddset(sig_set, SIGINT); /* 向进程的信号掩码中添加信号*/ ret sigprocmask(SIG_BLOCK, sig_set, NULL); if (-1 ret) {perror(sigprocmask error);exit(-1); }从信号掩码中移除SIGINT 信号 int ret; /* 定义信号集*/ sigset_t sig_set; /* 将信号集初始化为空*/ sigemptyset(sig_set); /* 向信号集中添加SIGINT 信号*/ sigaddset(sig_set, SIGINT); /* 从信号掩码中移除信号*/ ret sigprocmask(SIG_UNBLOCK, sig_set, NULL); if (-1 ret) {perror(sigprocmask error);exit(-1); }下面我们编写一个简单地测试代码验证信号掩码的作用测试代码如下所示 #include stdio.h #include stdlib.h #include signal.h #include unistd.h static void sig_handler(int sig) {printf(执行信号处理函数...\n); } int main(void) {struct sigaction sig {0};sigset_t sig_set;/* 注册信号处理函数*/sig.sa_handler sig_handler;sig.sa_flags 0;if (-1 sigaction(SIGINT, sig, NULL))exit(-1);/* 信号集初始化*/sigemptyset(sig_set);sigaddset(sig_set, SIGINT);/* 向信号掩码中添加信号*/if (-1 sigprocmask(SIG_BLOCK, sig_set, NULL))exit(-1);/* 向自己发送信号*/raise(SIGINT);/* 休眠2 秒*/sleep(2);printf(休眠结束\n);/* 从信号掩码中移除添加的信号*/if (-1 sigprocmask(SIG_UNBLOCK, sig_set, NULL))exit(-1);exit(0); }上述代码中我们为SIGINT 信号注册了一个处理函数sig_handler当进程接收到该信号之后就会执行它然后调用sigprocmask 函数将SIGINT 信号添加到信号掩码中然后再调用raise(SIGINT)向自己发送一个SIGINT 信号如果信号掩码没有生效、也就意味着SIGINT 信号不会被阻塞那么调用raise(SIGINT)之后应该就会立马执行sig_handler 函数从而打印出执行信号处理函数…字符串信息如果设置的信号掩码生效了则并不会立马执行信号处理函数而是在2 秒后才执行因为程序中使用sleep(2)休眠了2 秒钟之后才将SIGINT 信号从信号掩码中移除故而进程才会处理该信号在移除之前接收到该信号会将其阻塞。 编译测试结果如下 阻塞等待信号sigsuspend() 上一小节已经说明更改进程的信号掩码可以阻塞所选择的信号或解除对它们的阻塞。使用这种技术可以保护不希望由信号中断的关键代码段。如果希望对一个信号解除阻塞后然后调用pause()以等待之前被阻塞的信号的传递这将如何譬如有如下代码段 sigset_t new_set, old_set; /* 信号集初始化*/ sigemptyset(new_set); sigaddset(new_set, SIGINT); /* 向信号掩码中添加信号*/ if (-1 sigprocmask(SIG_BLOCK, new_set, old_set))exit(-1); /* 受保护的关键代码段*/ ....../**********************//* 恢复信号掩码*/if (-1 sigprocmask(SIG_SETMASK, old_set, NULL))exit(-1); pause(); /* 等待信号唤醒*/执行受保护的关键代码时不希望被SIGINT 信号打断所以在执行关键代码之前将SIGINT 信号添加到进程的信号掩码中执行完毕之后再恢复之前的信号掩码。最后调用了pause()阻塞等待被信号唤醒如果此时发生了信号则会被唤醒、从pause 返回继续执行考虑到这样一种情况如果信号的传递恰好发生在第二次调用sigprocmask()之后、pause()之前如果确实发生了这种情况就会产生一个问题信号传递过来就会导致执行信号的处理函数而从处理函数返回后又回到主程序继续执行从而进入到pause()被阻塞知道下一次信号发生时才会被唤醒这有违代码的本意。 虽然信号传递发生在这个时间段的可能性并不大但并不是完全没有可能这必然是一个缺陷要避免这个问题需要将恢复信号掩码和pause()挂起进程这两个动作封装成一个原子操作这正是sigsuspend()系统调用的目的所在sigsuspend()函数原型如下所示 #include signal.h int sigsuspend(const sigset_t *mask);使用该函数需要包含头文件#include signal.h。 函数参数和返回值含义如下 mask参数mask 指向一个信号集。 返回值sigsuspend()始终返回-1并设置errno 来指示错误通常为EINTR表示被信号所中断如果调用失败将errno 设置为EFAULT。 sigsuspend()函数会将参数mask 所指向的信号集来替换进程的信号掩码也就是将进程的信号掩码设置为参数mask 所指向的信号集然后挂起进程直到捕获到信号被唤醒如果捕获的信号是mask 信号集中的成员将不会唤醒、继续挂起、并从信号处理函数返回一旦从信号处理函数返回sigsuspend()会将进程的信号掩码恢复成调用前的值。 调用sigsuspend()函数相当于以不可中断原子操作的方式执行以下操作 sigprocmask(SIG_SETMASK, mask, old_mask); pause(); sigprocmask(SIG_SETMASK, old_mask, NULL);使用示例 #include stdio.h #include stdlib.h #include signal.h #include unistd.h static void sig_handler(int sig) {printf(执行信号处理函数...\n); } int main(void) {struct sigaction sig {0};sigset_t new_mask, old_mask, wait_mask;/* 信号集初始化*/sigemptyset(new_mask);sigaddset(new_mask, SIGINT);sigemptyset(wait_mask);/* 注册信号处理函数*/sig.sa_handler sig_handler;sig.sa_flags 0;if (-1 sigaction(SIGINT, sig, NULL))exit(-1);/* 向信号掩码中添加信号*/if (-1 sigprocmask(SIG_BLOCK, new_mask, old_mask))exit(-1);/* 执行保护代码段*/puts(执行保护代码段);/******************//* 挂起、等待信号唤醒*/if (-1 ! sigsuspend(wait_mask))exit(-1);/* 恢复信号掩码*/if (-1 sigprocmask(SIG_SETMASK, old_mask, NULL))exit(-1);exit(0); }在上述代码中我们希望执行受保护代码段时不被SIGINT 中断信号打断所以在执行保护代码段之前将SIGINT 信号添加到进程的信号掩码中执行完受保护的代码段之后调用sigsuspend()挂起进程等待被信号唤醒被唤醒之后再解除SIGINT 信号的阻塞状态。 实时信号 如果进程当前正在执行信号处理函数在处理信号期间接收到了新的信号如果该信号是信号掩码中的成员那么内核会将其阻塞将该信号添加到进程的等待信号集等待被处理处于等待状态的信号中为了确定进程中处于等待状态的是哪些信号可以使用sigpending()函数获取。 sigpending()函数 其函数原型如下所示 #include signal.h int sigpending(sigset_t *set);使用该函数需要包含头文件signal.h。 函数参数和返回值含义如下 set处于等待状态的信号会存放在参数set 所指向的信号集中。 返回值成功返回0失败将返回-1并设置errno。 使用示例 判断SIGINT 信号当前是否处于等待状态 /* 定义信号集*/ sigset_t sig_set; /* 将信号集初始化为空*/ sigemptyset(sig_set); /* 获取当前处于等待状态的信号*/ sigpending(sig_set); /* 判断SIGINT 信号是否处于等待状态*/ if (1 sigismember(sig_set, SIGINT))puts(SIGINT 信号处于等待状态); else if (!sigismember(sig_set, SIGINT))puts(SIGINT 信号未处于等待状态);发送实时信号 等待信号集只是一个掩码仅表明一个信号是否发生而不能表示其发生的次数。换言之如果一个同一个信号在阻塞状态下产生了多次那么会将该信号记录在等待信号集中并在之后仅传递一次仅当做发生了一次这是标准信号的缺点之一。 实时信号较之于标准信号其优势如下 ⚫ 实时信号的信号范围有所扩大可应用于应用程序自定义的目的而标准信号仅提供了两个信号可用于应用程序自定义使用SIGUSR1 和SIGUSR2。 ⚫ 内核对于实时信号所采取的是队列化管理。如果将某一实时信号多次发送给另一个进程那么将会多次传递此信号。相反对于某一标准信号正在等待某一进程而此时即使再次向该进程发送此信号信号也只会传递一次。 ⚫ 当发送一个实时信号时可为信号指定伴随数据一整形数据或者指针值供接收信号的进程在它的信号处理函数中获取。 ⚫ 不同实时信号的传递顺序得到保障。如果有多个不同的实时信号处于等待状态那么将率先传递具有最小编号的信号。换言之信号的编号越小其优先级越高如果是同一类型的多个信号在排队那么信号以及伴随数据的传递顺序与信号发送来时的顺序保持一致。 Linux 内核定义了31 个不同的实时信号信号编号范围为34~64使用SIGRTMIN 表示编号最小的实时信号使用SIGRTMAX 表示编号最大的实时信号其它信号编号可使用这两个宏加上一个整数或减去一个整数。 应用程序当中使用实时信号需要有以下的两点要求 ⚫ 发送进程使用sigqueue()系统调用向另一个进程发送实时信号以及伴随数据。 ⚫ 接收实时信号的进程要为该信号建立一个信号处理函数使用sigaction 函数为信号建立处理函数并加入SA_SIGINFO这样信号处理函数才能够接收到实时信号以及伴随数据也就是要使用 sa_sigaction 指针指向的处理函数而不是sa_handler当然允许应用程序使用sa_handler但这样就不能获取到实时信号的伴随数据了。 使用sigqueue()函数发送实时信号其函数原型如下所示 #include signal.h int sigqueue(pid_t pid, int sig, const union sigval value);使用该函数需要包含头文件signal.h。 函数参数和返回值含义如下 pid指定接收信号的进程对应的pid将信号发送给该进程。 sig指定需要发送的信号。与kill()函数一样也可将参数sig 设置为0用于检查参数pid 所指定的进程是否存在。 value参数value 指定了信号的伴随数据union sigval 数据类型。 返回值成功将返回0失败将返回-1并设置errno。 union sigval 数据类型共用体如下所示 typedef union sigval {int sival_int;void *sival_ptr; } sigval_t;携带的伴随数据既可以指定一个整形的数据也可以指定一个指针。 使用示例 (1)发送进程使用sigqueue()系统调用向另一个进程发送实时信号 #include stdio.h #include stdlib.h #include signal.h int main(int argc, char *argv[]) {union sigval sig_val;int pid;int sig;/* 判断传参个数*/if (3 argc)exit(-1);/* 获取用户传递的参数*/pid atoi(argv[1]);sig atoi(argv[2]);printf(pid: %d\nsignal: %d\n, pid, sig);/* 发送信号*/sig_val.sival_int 10; // 伴随数据if (-1 sigqueue(pid, sig, sig_val)){perror(sigqueue error);exit(-1);}puts(信号发送成功!);exit(0); }(2)接收进程使用sigaction()函数为信号绑定处理函数 #include stdio.h #include stdlib.h #include signal.h #include unistd.h static void sig_handler(int sig, siginfo_t *info, void *context) {sigval_t sig_val info-si_value;printf(接收到实时信号: %d\n, sig);printf(伴随数据为: %d\n, sig_val.sival_int); } int main(int argc, char *argv[]) {struct sigaction sig {0};int num;/* 判断传参个数*/if (2 argc)exit(-1);/* 获取用户传递的参数*/num atoi(argv[1]);/* 为实时信号绑定处理函数*/sig.sa_sigaction sig_handler;sig.sa_flags SA_SIGINFO;if (-1 sigaction(num, sig, NULL)){perror(sigaction error);exit(-1);}/* 死循环*/for (;;)sleep(1);exit(0); }异常退出abort()函数 在3.3 小节中给大家介绍了应用程序中结束进程的几种方法譬如使用exit()、_exit()或_Exit()这些函数来终止进程然后这些方法使用于正常退出应用程序而对于异常退出程序则一般使用abort()库函数使用abort()终止进程运行会生成核心转储文件可用于判断程序调用abort()时的程序状态。 abort()函数原型如下所示 #include stdlib.h void abort(void);函数abort()通常产生SIGABRT 信号来终止调用该函数的进程SIGABRT 信号的系统默认操作是终止进程运行、并生成核心转储文件当调用abort()函数之后内核会向进程发送SIGABRT 信号。 使用示例 #include stdio.h #include stdlib.h #include signal.h #include unistd.h static void sig_handler(int sig) {printf(接收到信号: %d\n, sig); } int main(int argc, char *argv[]) {struct sigaction sig {0};sig.sa_handler sig_handler;sig.sa_flags 0;if (-1 sigaction(SIGABRT, sig, NULL)){perror(sigaction error);exit(-1);}sleep(2);abort(); // 调用abortfor (;;)sleep(1);exit(0); }运行测试 从打印信息可知即使在我们的程序当中捕获了SIGABRT 信号但是程序依然会无情的终止无论阻塞或忽略SIGABRT 信号abort()调用均不收到影响总会成功终止进程。
http://www.w-s-a.com/news/680806/

相关文章:

  • 天津高端品牌网站建设韶关网站建设墨子
  • Wordpress多站点为什么注册不了2008iis搭建网站
  • 天津高端网站制作建网站的公司服务
  • 温州网站推广优化类似淘宝的网站怎么做的
  • 网站建设实训考试什么网站做玩具的比较多
  • 上海网站建设特点怎样给公司做一个网站做推广
  • 流量网站怎么做的济南优化排名公司
  • 保定网站制作套餐设计师导航网站大全
  • 惠州 商城网站建设石家庄新闻广播在线收听
  • 洪山网站建设域名购买之后怎么做网站
  • 北京网站建设公司服务哪家好wap是什么意思?
  • 怎么看公司网站做的好不好哦wordpress页面目录下
  • 做装修业务呢有多少网站平台搭建是什么
  • 潍坊优化网站排名淘宝做网站被骗
  • 建设专业网站的利弊免费logo设计生成器下载
  • 怎么在备案号添加网站网页设计动画网站
  • 网站开发 只要wordpress滑动注册
  • 跨境电商运营主要做什么静态网站如何做优化
  • 南充网站建设网站网站备案安全责任书是谁盖章
  • 怎么将网站设置为首页网站子目录怎么做
  • 做网站交互wordpress信息导出
  • 如何自己做企业网站做外贸登录国外网站
  • 郑州炫彩网站建设网站集约化建设调研报告
  • 2016年两学一做教育网站优良的定制网站建设制作商
  • 自己做网站需要哪些流程网站建设服务费如何做会计分录
  • 莆田建站培训用手机制作游戏的app软件
  • 中山建网站找哪家wordpress采集图片插件
  • 网站首页做后台链接有什么好用的模拟建站软件
  • 宁波有没有开发网站的公司网站上线除了备案还需要什么
  • 网站备案授权wordpress默认主体设置