信号与不可重入

信号

信号是软件中断。提供了一种处理异步事件的方法。例如当用户在终端输入中断键(ctrl+c),会向前台进程发送一个中止信号SIGINT,以此来中止一个进程。

信号通常以SIG3个字符开头作为命名,例如SIGABRT是夭折信号;当进程调用abort函数时发出这种信号。SIGALARM是闹钟信号,由alarm函数设定的定时器超时后将产生这种信号。

几种常见的信号类型:

①当用户在终端输入某些键时,引起终端产生相应的信号。例如按下DELETE键或者Ctrl+C键将产生中断信号SIGINT。该信号用于停止一个失去控制的程序。

②硬件产生信号,区别于硬件中断。例如发生除0,引用无效地址。这些事件由硬件检测到,通知内核,然后内核向正在执行的进程产生相应的信号。

③进程调用kill(2)函数可以将任意信号发送给另外一个进程。但是其限制是接收信号进程和发送信号进程的所有者相同。或者发送该信号的进程为超级用户root

④当检测到某种软件条件发生,应向有关进程产生信号。例如SIGURG (网络链接上传来带外的数据)、SIGPIPE(在管道的读进程已结束,一个进程在写管道)以及SIGALRM(进程设定的定时器超时)

信号处理动作

①忽略此信号。大部分信号采取这种方式被处理,但SIGKILL和SIGSTOP这两种信号不能被忽略:因为它们是内核和超级用户使得进程终止或者停止的可靠方法。另外,如果引用错误的内存地址等异常产生的信号也不该被忽略,否则造成程序运行行为未定义。

②捕捉信号。应用程序必需提供一个函数,使得当相应的信号发生时,该事件能够按照定义被处理。例如如果捕获到SIGCHLD信号(子进程已经终止),那么此信号处理函数很可能调用waitpid获得子进程的进程ID和它的终止状态。

执行系统默认动作。值得注意的是,对于大多数信号的默认动作,是终止该进程,

函数signal

void (*signal(int signo,void (*func)(int)))(int);  

其中signo是信号名,而func函数指针则是当相应的信号发生时的信号处理函数。
两个函数地址的宏定义:

  • SIG_IGN 向内核表示发生该信号时,忽略该信号(SIGKILL和SIGSTOP不能忽略)
  • SIG_DFL表示接收到此信号时执行系统默认动作。
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>
static void sig_usr(int signo)
{
    if(signo==SIGUSR1)
        printf("received SIGUSR1\n");
    else if(signo==SIGUSR2)
        printf("received SIGUSR2\n");
    else
        printf("received signal %d\n",signo);
}


int main()
{
    if(signal(SIGUSR1,sig_usr)==SIG_ERR)
        printf("can't catch SIGUSR1\n");
    if(signal(SIGUSR2,sig_usr)==SIG_ERR)
        printf("can't catch SIGUSR2\n");
    for(;;)
        pause();   //该函数将使得调用者进程被挂起,直到某个收到某个信号
}

编译后,通过&将改程序运行于后台,通过kill命令向该进程发送相应的USR1 USR2信号。

在这里插入图片描述

值得注意的是,通过shell在后台启动的进程,终端输入的中断和退出信号会被设置为忽略,因此,当按下中断字符时不会影响到后台进程。否则,通过shell启动的后台进程将会被终止。

信号处理函数必须是可重入函数

可重入函数是线程安全函数,含义为可以完全的暂停和重新回到函数(可重入),可重入函数特点是通常只使用本地函数栈资源,意味者不会访问全局结构,其特点是不同的执行流在并发执行该函数时是安全的。

关于线程安全性与可重入:

线程安全指的是在多线程环境下,多个线程并发的执行能够得到正确的结果。例如,通过加锁的方式来实现对共享临界资源的并发访问。从这个角度上看,线程安全的实现包含多种方式,其中之一就是可重入,即函数可完全暂停和继续,并且不使用共享资源。

malloc函数是一个线程安全函数,调用时将陷入内核,内部维护一个空闲区链表来满足堆内存的申请获取和释放回收。因此malloc函数是不可重入的,因为其在执行过程中如果被信号打断,之后继续执行时,可能内部的空闲区链表已经发生变化。

在这里插入图片描述

因此信号处理函数需要是可重入的,通常不可重入函数有以下特点:
①使用了静态数据结构,那么不可重入,因此继续执行时,结构可能被修改

②调用了malloc函数。free函数,因为会访问共享数据结构

③使用了标准IO函数,标准IO函数会维护共享的缓冲区

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页