实验环境介绍

  • gcc:4.8.5
  • glibc:glibc-2.17-222.el7.x86_64
  • os:Centos7.4
  • kernel:3.10.0-693.21.1.el7.x86_64

信号概念

第10章——《信号》

函数signal
  • SIGKILL和SIGSTOP不能被捕获
  • fork之后,子进程继承父进程的处理方式
  • 测试代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

static void
sig_int(int signo)
{
    switch (signo) {
    case SIGINT: printf("pid: %d get sigint signal\n", getpid()); break;
    default: printf("pid: %d get other signal\n", getpid()); break;
    }
}

int
main(int argc, char *argv[])
{
    if (signal(SIGINT, sig_int) == SIG_ERR) {
        printf("register signal INT error");
        exit(EXIT_FAILURE);
    }

    fork();
    while (1) {
        sleep(1);
    }
    return (0);
}

result:
[[email protected] part_10]$ ./signal 
pid: 38328 get sigint signal
pid: 38329 get sigint signal

[[email protected] ~]$ ps -ef | grep signal
manjing+ 38328 21634 0 12:46 pts/3 00:00:00 ./signal
manjing+ 38329 38328 0 12:46 pts/3 00:00:00 ./signal
manjing+ 38331 38276 0 12:46 pts/2 00:00:00 grep --color=auto signal
[[email protected] ~]$ kill -2 38328
[[email protected] ~]$ kill -2 38329
  • fork exec之后,不继承信号注册

不可靠信号

# 35号信号以前的都是不可靠信号
 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
  • 不可靠信号每次调用完信号处理函数后,除非再次设置该信号的信号处理函数,否则就将该信号设置为默认处理方式

但是在我的实验环境中并不需要重新设置,也能继续使用该信号处理函数

  • 不可靠信号不支持排队
    • 比如一个进程将SIGINT信号设置为阻塞,那么发送多个SIGINT信号,对于前面那个进程而言都只算做只收到一个SIGINT信号
  • 测试代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

void catch_signal(int signo, siginfo_t *info, void *p);

int main(int arg, char *args[])
{
    pid_t pid = 0;
    struct sigaction act;
    act.sa_sigaction = catch_signal;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;

    //注册SIGINT信号
    if (sigaction(SIGINT, &act, NULL) != 0) {
        printf("sigaction SIGINT failed !\n");
        return -1;
    }
    //注册SIGTMIN信号
    if (sigaction(SIGRTMIN, &act, NULL) != 0) {
        printf("sigaction SIGINT failed !\n");
        return -1;
    }

    //注册SIGUSR1信号
    if (sigaction(SIGUSR1, &act, NULL) != 0) {
        printf("sigaction SIGINT failed !\n");
        return -1;
    }

    //阻塞SIGINT信号和SIGTMIN信号
    sigset_t bset;
    sigemptyset(&bset);
    sigaddset(&bset, SIGINT);
    sigaddset(&bset, SIGRTMIN);

    //更新进程屏蔽信号状态字
    if (sigprocmask(SIG_BLOCK, &bset, NULL) != 0) {
        printf("sigprocmask() failed !\n");
        return -1;
    }

    pid = fork();
    if (pid == -1) {
        printf("fork() failed ! error message:%s\n", strerror(errno));
        return -1;
    }

    if (pid == 0) {
        int i = 0, ret = 0;
        union sigval v1;
        union sigval v2;

        for (i = 0; i < 3; i++) {
            v1.sival_int = 201 + i;
            ret = sigqueue(getppid(), SIGINT, v1);
            if (ret != 0) {
                printf("发送不可靠信号SIGINT失败! error message:%s\n", strerror(errno));
            } else {
                printf("发送不可靠信号SIGINT成功!\n");
            }
        }

        for (i = 0; i < 3; i++) {
            v2.sival_int = 301 + i;
            ret = sigqueue(getppid(), SIGRTMIN, v2);
            if (ret != 0) {
                printf("发送可靠信号SIGTMIN失败! error message:%s\n", strerror(errno));
            } else {
                printf("发送可靠信号SIGTMIN成功!\n");
            }
        }
        //发送SIGUSR1信号
        if (kill(getppid(), SIGUSR1) != 0) {
            printf("kill() failed ! error message;%s\n", strerror(errno));
        }
        exit(0);
    } else {
        int res = 0, status = 0;
        while (1) {
            res = wait(&status);
            if (res == -1) {
                if (errno == EINTR)
                    continue;
            }
            break;
        }
    }

    return 0;
}

void
catch_signal(int signo, siginfo_t *info, void *p)
{
    switch (signo) {
    case SIGINT:
        printf("accept SIGINT! recv data=%d\n",info->si_value.sival_int);
        break;
    case 34:
        //SIGRTMIN似乎不是一个确定的int类型
        printf("accept SIGRTMIN! recv data=%d\n",info->si_value.sival_int);
        break;
    case SIGUSR1:
        printf("accept SIGUSR1!\n");

        //取消信号阻塞
        sigset_t uset;
        sigemptyset(&uset);
        sigaddset(&uset, SIGINT);
        sigaddset(&uset, SIGRTMIN);
        sigprocmask(SIG_UNBLOCK, &uset, NULL);
        printf("阻塞解除了!\n");
        break;
    }
}
result:
[[email protected] part_10]$ ./signal 
发送不可靠信号SIGINT成功!
发送不可靠信号SIGINT成功!
发送不可靠信号SIGINT成功!
发送可靠信号SIGTMIN成功!
发送可靠信号SIGTMIN成功!
发送可靠信号SIGTMIN成功!
accept SIGUSR1!
accept SIGRTMIN! recv data=301
accept SIGRTMIN! recv data=302
accept SIGRTMIN! recv data=303
accept SIGINT! recv data=201
阻塞解除了!

中断的系统调用

  • 系统调用分为两种:低速系统调用和其他系统调用。低速系统调用是会使进程永远阻塞的一类系统调用,包括:
    • 某些类型文件(如读管道、终端设备和网络设备)的数据不存在,则可能使调用者永远被阻塞
    • 如果数据不能被相同的类型立即接受,则写操作可能会使调用者永远被阻塞
    • 在某种条件发生之前打开某些类型文件,可能发生阻塞(比如打开一个终端I/O,需要先等待与之连接的调制解调器应答)
    • pause函数和wait函数
    • 某些ioctl函数
    • 某些进程间通信函数(第15章再来讨论)

注意在这些低速系统调用中,一个值得注意的例外是与磁盘I/O有关的系统调用。虽然读写一个磁盘文件可能暂时性阻塞调用者(在磁盘驱动程序将请求排入队列,然后再适当时间执行请求期间)。但是除非发生硬件错误,I/O操作总会很快返回,并使调用者不再阻塞状态

重启系统调用

  • 与信号相关的函数的实现语义
    第10章——《信号》

可重入函数

  • 在进行信号处理时,进程正在执行的正常指令就被信号处理程序临时中断,他首先执行该信号处理程序中的指令。如果从信号处理程序返回(例如没有调用exit或longjmp),则继续执行在捕获到信号时进程正在执行的正常指令序列
  • 在信号处理程序中,不能判断捕捉到信号时进程时进程执行到何处。如果进程正在malloc,然后这时捕捉到信号,然后再信号处理程序中我们又进行malloc。这样会对进程造成破坏。这些函数会使用一些全局的变量,从而在信号处理程序中可能会破坏数据。
  • 在信号处理程序中可以调用的可重入的函数如下:
    第10章——《信号》
  • 上述没有列出的大多数函数都是不可重入的
    • 因为他们使用静态数据结构,无论是全局静态变量还是局部静态变量。
    • 他们调用malloc或者free
    • 他们是标准I/O函数,标准I/O库的很多实现都以不可重入方式使用全局数据结构

如果在信号处理函数中调用上图的函数时,应该在调用前保存errno,然后在调用后恢复(在捕获SIGCHLD信号的时候,通常会调用wait,此函数会更新errno,所以也得处理恢复)。如果主例程正在以非可重入方式更新一个数据结构可能产生信号。如果不是从信号处理函数返回,而是调用siglongjmp,那么那个数据结构可能是部分更新的。如果要做到全局更新这个数据结构,而同时要捕获信号进行处理,而这些信号处理函数又要调用siglongjmp,那么在更新这个数据结构的时候要阻塞此类信号
* 函数返回静态变量
* 函数中调用了不可重入函数
* 函数是singleton中的成员函数,而且使用了不使用线程独立存储的成员变量
* 在许多编译器/处理器中,浮点数是不可重入的。有些不允许在ISR中做浮点运算。

SIGCHLD信号

  • SIGCLD的早期处理方式:
    • 如果把这个信号设置为SIG_IGN,则调用进程的子进程将不产生僵尸进程。子进程终止的时,将其状态丢弃。如果调用进程随后调用一个wait函数,那么它将阻塞直到所有子进程都终止,然后wait会返回-1,并将其errno设置为ECHILD。
    • 如果将SIGCHLD设置为捕获,则内核立即检查是否有子进程准备好被等待,如果是这样则调用SIGCHLD的处理程序
  • 以下代码可能在早期SIGCLD、signal语义实现上运行有问题
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdarg.h>

static void	sig_cld(int);

#define MAXLINE 1024

static void
err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
 char	buf[MAXLINE];

 vsnprintf(buf, MAXLINE-1, fmt, ap);
 if (errnoflag)
  snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s",
    strerror(error));
 strcat(buf, "\n");
 fflush(stdout);	/* in case stdout and stderr are the same */
 fputs(buf, stderr);
 fflush(NULL);	/* flushes all stdio output streams */
}

void
err_msg(const char *fmt, ...)
{
 va_list	ap;

 va_start(ap, fmt);
 err_doit(0, 0, fmt, ap);
 va_end(ap);
}

int
main()
{
 pid_t	pid;

 if (signal(SIGCLD, sig_cld) == SIG_ERR)
  err_msg("signal error");
 if ((pid = fork()) < 0) {
  err_msg("fork error");
 } else if (pid == 0) {	/* child */
  sleep(2);
  _exit(0);
 }

 pause();	/* parent */
 exit(0);
}

static void
sig_cld(int signo)	/* interrupts pause() */
{
 pid_t	pid;
 int	status;

 printf("SIGCLD received\n");

// 这里可能有问题,早期的signal,
// 在调用的时候会检查内核中是否有子进程结束进行信号处理函数的调用
// 这里会一直循环调用sig_cld
// 解决方案是:先进行wait再用signal的注册
// 实际上现在的实现标准,应该不用再次注册信号处理函数
 if (signal(SIGCLD, sig_cld) == SIG_ERR)	/* reestablish handler */
  perror("signal error");

 if ((pid = wait(&status)) < 0)	/* fetch child status */
  perror("wait error");

 printf("pid = %d\n", pid);
}

可靠信号术语与语义

  • 向进程递送一个信号时,信号产生和递送之间的时间间隔内,称信号是未决的(pending)
  • 进程可以将信号阻塞在信号队列里面,尽管对该信号的处理是默认或者捕获。直到进程忽略该信号或者解除该信号的阻塞,该信号才解除了阻塞。
  • 内核在把信号递送给进程时,才决定对这个信号的处理
  • 调用sigpending函数可以返回队列中的未决信号
  • 如果在对某个信号进行了阻塞,如果该信号是不支持POSIX.1的实时扩展,那么尽管这个信号发生了多次,队列中仍然只有一个该种信号递送给该进程,则称该信号不支持排队。如果支持POSIX.1的实时扩展,则称这信号支持排队,内核会将产生的多个该信号都递送给该进程
  • 注意,不可排队信号已经在被处理的时候,如果此时又来一个相同的信号,那么这个信号会放在未决队列里面,只是该进程只能存在1个这种信号。测试代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdarg.h>


#define MAXLINE 1024

static void
sig_int(int signo);

static void
err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
 char	buf[MAXLINE];

 vsnprintf(buf, MAXLINE-1, fmt, ap);
 if (errnoflag)
  snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s",
    strerror(error));
 strcat(buf, "\n");
 fflush(stdout);	/* in case stdout and stderr are the same */
 fputs(buf, stderr);
 fflush(NULL);	/* flushes all stdio output streams */
}

void
err_msg(const char *fmt, ...)
{
 va_list	ap;

 va_start(ap, fmt);
 err_doit(0, 0, fmt, ap);
 va_end(ap);
}

int
main()
{
 pid_t	pid;

 if (signal(SIGINT, sig_int) == SIG_ERR)
  err_msg("signal error");
	
 while (1)
  pause();	/* parent */
 exit(0);
}

static void
sig_int(int signo)	/* interrupts pause() */
{
 pid_t	pid;
 int	status;

 printf("SIGINT received\n");

 sleep(10);
}

result:
[[email protected] part_10]# ./interrupt 
^CSIGINT received                // 这里立马处理了一个INT信号
^C^C^C^C^C^C^C^C^C^C^C^C^CSIGINT received //信号处理函数睡眠10秒,此时信号处理函数没有返回,发送多个INT信号
^CSIGINT received            // 尽管发送了多个,但是未决队列只能存在一个,
  • 关于sigprocmask介绍的一个理解
    • apue中有这么一句话:在调用sigprocmask后如果有任何未决的、不再阻塞的信号,则在sigprocmask返回前,至少会将其中一个信号递送给该进程。
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>

void sig_int( int ); 
void pr_mask( const char *);	//call to funtion pr_mask
void oops(void *msg);

int main(int argc , char *argv[]){
 
 setbuf( stdout , NULL );	//close buffer
 pr_mask( "before catching signal ,mask sig :" );
 
 if( signal( SIGINT , sig_int ) == SIG_ERR )
  oops( "signal" );
	
 //keep loop to catch signal interuption
 while(1)
 //	pause();
  sleep( 3 );
 
 pr_mask( "end catching signal ,mask sig :" );
 
 return 0;
}
 
//signal handler
void sig_int( int signo ){
 pr_mask( "in signal handler:" );
}

void pr_mask( const char *str ){
 sigset_t set;
 int errno_save;	//get the pre errno
 
 errno_save = errno;
 
 if( sigprocmask( 0, NULL , &set ) == -1 )
  oops( " sigmask" );
 else{
  printf( "\n%s" , str );
  if( sigismember( &set , SIGQUIT ) )
   printf( " SIGQUIT" );
  if( sigismember( &set , SIGINT ) )
   printf( " SIGINT" );
  if( sigismember( &set , SIGUSR1 ) )
   printf( " SIGUSR1" );
  if( sigismember( &set , SIGALRM ) )
   printf( " SIGALRM" );
 }
 errno = errno_save ;
}

 
void oops(void *msg){
 perror(msg);
 exit(1);
}

[[email protected] part_10]# ./interrupt 

before catching signal ,mask sig :^C
in signal handler: SIGINT^C
in signal handler: SIGINT^C
in signal handler: SIGINT^C
in signal handler: SIGINT^C
in signal handler: SIGINT^C
in signal handler: SIGINT^C
in signal handler: SIGINT^C
in signal handler: SIGINT^C
in signal handler: SIGINT^C
in signal handler: SIGINT^C
in signal handler: SIGINT
  • 也就是说,因为程序中我们并没有屏蔽中断信号,而当进程在处理中断信号的时候是屏蔽了接下来的中断信号,所以在信号处理函数中会有SIGINT出现, 而当信号处理函数完成,准确的说是sigprocmask完成前, 接下来的SIGINT都会被接收处理.

  • 对于sigsuspend函数的测试

#include <stdio.h>
#include <signal.h>
#include "unistd.h"

void 
un_int()
{
    printf("\nsigsuspend catch SIGINT\n");
    printf("The sigsuspend already restore the old mask for sigprocmask.Now still in the signal handle,next will sleep 5 seconds you can press ctrl-c to test\n");
    sleep(5);
    return;
}

int 
main(int argc, char *argv[])
{
    sigset_t set;
    
    // include all signals
    sigfillset(&set);
    
    // register cb
    signal(SIGINT,fun_int);

    // bock all signals
    sigprocmask(SIG_SETMASK,&set,NULL);
    
    // 去除INT信号
    sigdelset(&set,SIGINT);
    printf("Here is the sigprocmask signal block, before use sigsuspend\n");
    sleep(5); // 这里就可以触发信号了
    printf("\nsleep over,next is sigsuspend\n");

    // 重新设置没有INT信号集的阻塞信号集,并等待信号递送到进程
    sigsuspend(&set);
    printf("\nI have return from the handler.I will sleep 5 seconds You can press ctrl-c to send sigint to test the mask of SIGINT restore from sigsuspend for sigprocmask\n");
    sleep(10);
    printf("\nTest finish\n");
    return 0;
}

result:
[[email protected] part_10]# 
[[email protected] part_10]# ./interrupt 
Here is the sigprocmask signal block, before use sigsuspend
^C^C^C^C^C^C^C
sleep over,next is sigsuspend

sigsuspend catch SIGINT
The sigsuspend already restore the old mask for sigprocmask.Now still in the signal handle,next will sleep 5 seconds you can press ctrl-c to test

I have return from the handler.I will sleep 5 seconds You can press ctrl-c to send sigint to test the mask of SIGINT restore from sigsuspend for sigprocmask

Test finish

函数kill和raise

  • 忽略

函数alarm和pause

  • 使用alarm可以设置一个定时器(闹钟时间),在将来的某个时间定时器会超时,超时后会产生一个SIGALRM信号
  • 每个进程只能有一个闹钟时间
  • pause函数使进程阻塞直至收到一个信号

信号集

  • 忽略

函数sigprocmask

  • 设置、获取该进程阻塞的信号集合

函数sigpending

  • 获取被阻塞的未决的信号集

函数sigaction

  • 可以保证在处理一个给定的信号时,如果这种用信号再次发生时,那么他会被阻塞到前一个信号被处理完为之
  • sigaction函数有阻塞的功能,比如SIGINT信号来了,进入信号处理函数,默认情况下,在信号处理函数未完成之前,如果又来了一个SIGINT信号,其将被阻塞,只有信号处理函数处理完毕,才会对后来的SIGINT再进行处理,同时后续无论来多少个SIGINT,仅处理一个SIGINT,sigaction会对后续SIGINT进行排队合并处理。

我的实验环境上的signal、sigaction实现,当对一个信号进行处理,如果该信号处理函数没有返回,那么该进程会阻塞该信号

  • signal和sigaction对于信号的阻塞测试代码如下:
// 测试signal函数
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void 
fun_int()
{
    printf("\ncatch SIGINT\n");
    sleep(5);
    return;
}

void signal_version(void)
{

    
    signal(SIGINT,fun_int);

    while (1)
        sleep(1);
}


void sigaction_version(void)
{
    struct sigaction act;
    act.sa_handler = fun_int;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGQUIT);
    
    sigaction(SIGINT, &act, 0);

    while (1)
        sleep(1);
}

int 
main(int argc, char *argv[])
{
    // sigaction_version();
    signal_version();
    
    return 0;
}

result:
[[email protected] part_10]# ./interrupt 
^C
catch SIGINT
^C^C^C^C^C^C^\Quit

// 这里说明signal函数在进行信号处理的时候,不会阻塞其他信号,但会阻塞当前信号,阻塞的信号在未决队列中(不可靠信号不支持排队)
// sigaction版本
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void 
fun_int()
{
    printf("\ncatch SIGINT\n");
    sleep(5);
    return;
}

void signal_version(void)
{

    
    signal(SIGINT,fun_int);

    while (1)
        sleep(1);
}


void sigaction_version(void)
{
    struct sigaction act;
    act.sa_handler = fun_int;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGQUIT);
    
    sigaction(SIGINT, &act, 0);

    while (1)
        sleep(1);
}

int 
main(int argc, char *argv[])
{
    sigaction_version();
    // signal_version();
    
    return 0;
}

result:
[[email protected] part_10]# ./interrupt 
^C
catch SIGINT
^\^\^\^\^\^\^\^\^C
catch SIGINT
Quit
// 这里处理INT信号的时候同时阻塞INT信号和QUIT信号,但是信号处理函数返回后,继续处理INT信号,但是最后比较疑惑的是,为啥先处理INT信号再处理QUIT信号

函数sigsetjmp和函数siglongjmp

  • 当调用某信号的信号处理函数的时候,该信号会自动加入到该进程的信号屏蔽字中,这阻止了后来的信号中断该信号处理程序。如果使用longjmp跳出信号处理函数,那么对信号屏蔽字的处理得看具体实现(Linux 3.2.0的longjmp和setjmp并不对信号屏蔽字进行保存和恢复)
  • 测试代码如下:
#include <errno.h>
#include <setjmp.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

static void sig_usr1(int);
static void sig_alrm(int);
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;

void
pr_mask(const char *str)
{
    sigset_t sigset;
    int errno_save;

    errno_save = errno; /* we can be called by signal handlers */
    if (sigprocmask(0, NULL, &sigset) < 0) {
        printf("sigprocmask error\n");
        exit(1);
    } else {
        printf("%s", str);
        if (sigismember(&sigset, SIGINT))
            printf(" SIGINT");
        if (sigismember(&sigset, SIGQUIT))
            printf(" SIGQUIT");
        if (sigismember(&sigset, SIGUSR1))
            printf(" SIGUSR1");
        if (sigismember(&sigset, SIGALRM))
            printf(" SIGALRM");

        /* remaining signals can go here */

        printf("\n");
    }

    errno = errno_save; /* restore errno */
}

int
main(void)
{
    if (signal(SIGUSR1, sig_usr1) == SIG_ERR) {
        printf("signal(SIGUSR1) error");
        exit(1);
    }
    if (signal(SIGALRM, sig_alrm) == SIG_ERR) {
        printf("signal(SIGALRM) error");
        exit(1);
    }

    pr_mask("starting main: "); /* {Prog prmask} */

    if (sigsetjmp(jmpbuf, 1)) {

        pr_mask("ending main: ");

        exit(0);
    }
    canjump = 1; /* now sigsetjmp() is OK */

    for ( ; ; )
        pause();
}

static void
sig_usr1(int signo)
{
    time_t starttime;

    if (canjump == 0)
        return; /* unexpected signal, ignore */

    pr_mask("starting sig_usr1: ");

    alarm(3); /* SIGALRM in 3 seconds */
    starttime = time(NULL);
    for ( ; ; ) /* busy wait for 5 seconds */
        if (time(NULL) > starttime + 5)
            break;

    pr_mask("finishing sig_usr1: ");

    canjump = 0;
    siglongjmp(jmpbuf, 1); /* jump back to main, don't return */
}

static void
sig_alrm(int signo)
{
    pr_mask("in sig_alrm: ");
}

result:
[[email protected] part_10]# ./interrupt 
starting main: 
starting sig_usr1: SIGUSR1
in sig_alrm: SIGUSR1 SIGALRM
finishing sig_usr1: SIGUSR1
ending main:

第10章——《信号》

  • sigsetjmp和siglongjmp是为了处理信号处理函数中非局部跳转中信号屏蔽的问题

sigsuspend函数

  • 忽略

abort函数

  • 忽略

system函数

  • 忽略

函数sleep、nanosleep、clock_nanosleep

  • 忽略

sigqueue函数

  • 类似于kill,可以发送附带的信息

作业控制信号

  • 相关信号
    • SIGCHLD
    • SIGCONT
    • SIGSTOP
    • SIGTSTP
    • SIGTTIN
    • SIGTTOU

相关文章:

  • 2021-12-24
  • 2021-12-07
  • 2021-04-13
  • 2021-05-03
  • 2021-05-08
  • 2021-10-10
  • 2021-08-04
  • 2022-12-23
猜你喜欢
  • 2022-03-08
  • 2021-11-23
  • 2021-05-31
  • 2021-07-02
  • 2022-12-23
  • 2021-10-01
  • 2021-08-10
相关资源
相似解决方案