域名领域,南昌网站排名优化报,西宁到青海湖,网站建设的实践目的文章目录 前言信号处理signal()函数关于异步环境 信号处理函数示例raise()函数 前言 信号处理
关于信号#xff0c;信号是一种进程间通信的机制#xff0c;用于在程序执行过程中通知进程发生了一些事件。在Unix和类Unix系统中#xff0c;信号是一种异步通知机制#xff0c… 文章目录 前言信号处理signal()函数关于异步环境 信号处理函数示例raise()函数 前言 信号处理
关于信号信号是一种进程间通信的机制用于在程序执行过程中通知进程发生了一些事件。在Unix和类Unix系统中信号是一种异步通知机制通过发送信号一个进程可以通知另一个进程发生了某个事件如按下 CtrlC、除零错误等。
在C中可以使用 头文件提供的信号处理机制来捕获和处理信号。
信号的基本概念
信号编号每个信号都有一个唯一的编号用来标识不同的事件。例如SIGINT 是表示中断的信号。信号处理器 信号处理器是一个函数用于处理接收到的信号。你可以为每种信号指定一个处理函数。
常见的信号
SIGINT中断信号通常由用户按下 CtrlC 生成。SIGSEGV段错误信号表示非法内存访问。SIGTERM终止信号表示进程被要求终止。SIGKILL强制终止信号表示进程被强制终止。 signal()函数
C使用 signal 函数可以为特定的信号注册信号处理函数。
语法使用
void (*signal(int signum, void (*handler)(int)))(int);也可以写成
signal(SIGINT, signalHandler);在上述代码中
signumSIGINT要注册的信号的编号。handlersignalHandler要注册的信号处理函数的指针。
信号处理函数的声明
void handlerFunction(int signum)。关于信号处理函数的定义应该尽量简单因为它在异步环境中执行同时有一些函数例如‘printf’‘malloc’不是异步安全的所以尽量不要在信号处理函数中使用它们。
关于异步环境 异步环境是指程序执行时存在多个同时运行的线程或进程这些线程或进程在执行过程中可能会相互干扰因为它们共享某些资源如内存、文件描述符等。在异步环境中执行顺序是不确定的因此程序的行为可能受到非常复杂的影响。 printf 和 malloc 不是异步安全的主要是因为它们在执行时可能涉及到对共享资源的访问而这样的访问在异步环境中是不安全的。
printf print函数通常会使用标准输出stdout而在异步环境中多个线程或进程可能会同时尝试写入标准输出导致输出内容混乱。在标准库中的输出函数如 printf通常使用全局锁mutex来保护对输出流的访问但这并不能解决所有的异步安全问题。在信号处理函数中使用 printf 可能导致死锁或其他竞态条件。 malloc malloc 函数用于动态分配内存而在异步环境中多个线程或进程可能同时尝试分配或释放内存这可能导致内存管理错误。
因此在异步环境中为了确保代码的正确性应该尽量避免在信号处理函数或多线程环境中使用不可重入non-reentrant的函数。不可重入函数是指在执行过程中依赖于全局状态或静态变量的函数而这在异步环境中可能导致不确定的结果。
为了在异步环境中安全使用输出函数和内存分配函数通常建议使用异步安全的替代版本。例如在信号处理函数中可以使用 write 函数代替 printf而在多线程环境中可以使用 pthread 库提供的线程安全的输出函数和内存分配函数。 信号处理函数示例
#include iostream
#include csignal// 信号处理函数
void signalHandler(int signum) {std::cout Received signal: signum std::endl;// 自定义处理逻辑可以在这里添加// ...// 恢复对 SIGINT 的默认处理signal(SIGINT, SIG_DFL);
}int main() {// 注册信号处理函数signal(SIGINT, signalHandler);std::cout Press CtrlC to trigger the signal. std::endl;// 一个简单的循环使程序保持运行while (true) {// 等待信号的到来}return 0;
}
在上述代码中声明了一个自定义的信号处理函数signalHandler之后的main函数中用signal注册信号处理函数来处理SIGINT信号。signal函数的第一个参数就是要识别的信号的编号第二个参数就是指向信号处理函数的指针。
而在信号处理函数signalHandler中可以添加自定义的处理逻辑。
上述代码运行后因为whileture程序会一直保持运行直到我们按下CtrlC发生中断后signal函数在捕获信号后信号处理函数发挥作用打印了Received signal: 2
因为SIGINT 的信号编号是 2所以signum的值是2
如果想要在信号处理完成后恢复对该信号的默认处理可以使用 signal(SIGINT, SIG_DFL)。
忽略和恢复信号
使用 signal(SIGINT, SIG_IGN) 可以忽略 SIGINT 信号。使用 signal(SIGINT, SIG_DFL) 可以恢复对 SIGINT 的默认处理。
关于恢复信号当你按下 CtrlC 触发 SIGINT 信号时如果没有 signal(SIGINT, SIG_DFL); 这一行那么程序将继续执行 signalHandler 函数但不会将 SIGINT 的处理方式恢复为默认。这意味着如果再次按下 CtrlCsignalHandler 函数将再次被调用而不会终止程序。
实际上如果不将 SIGINT 恢复为默认处理方式程序可能会对多次 CtrlC 信号作出相应而不是默认的行为终止程序。 raise()函数
raise 函数是用于在程序中手动触发一个信号的函数。
声明
int raise(int sig);sig要触发的信号的编号。
raise 函数返回一个整数值表示函数调用的结果。如果成功发送信号返回 0如果失败返回非零值。
示例
#include csignal
#include iostream// 信号处理函数
void signalHandler(int signum) {std::cout Received signal: signum std::endl;
}int main() {// 注册信号处理函数signal(SIGINT, signalHandler);std::cout Press CtrlC to trigger the signal. std::endl;// 模拟其他程序逻辑int count 0;while (true) {// 模拟其他程序逻辑std::cout Working... ( count ) std::endl;// 在某个条件下手动触发 SIGINT 信号if (count 500) {std::cout Manually triggering SIGINT... std::endl;raise(SIGINT);}// 模拟其他程序逻辑// ...// 增加计数count;}return 0;
}
上述代码的运行结果 可以看到在进行俩次的模拟生成信号后程序就停止了这是因为没有重新注册 SIGINT 的处理函数程序将使用默认的信号处理方式即终止程序。
如果想要第一次信号处理后继续运行可以重新注册 SIGINT 的处理函数。 在signalHandler函数中添加
// 重新注册信号处理函数signal(SIGINT, signalHandler);