Linux下的signal信号机制

2019-02-20 16:22:36 浏览数 (2)

在Linux中,要发送一个信号相当容易。程序员需要知道两个信息:要发送哪个信号,将这个信号发送给哪个进程。可以用 man 7 signal 找到一个可以利用的信号的列表。用户可以只将信号发送给用户自己的进程,也可以以root身份运行从而将信号发送给任意一进程。

Source:

代码语言:javascript复制
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
void when_alarm();
void when_sigint();
void when_sigchld(int);
void when_sigusr1();
void when_sigio();
int main()
{
    int childpid;//子程序进程ID号
    printf("程序已经开始运行,5秒钟后将接收到时钟信号。/n");
    if ((childpid=fork())>0)//父进程
    {
        signal(SIGALRM,when_alarm);  //当接收到SIGALRM信号时,调用when_alarm函数
        signal(SIGINT,when_sigint);  //当接收到SIGINT信号时,调用when_sigint函数
        signal(SIGCHLD,when_sigchld);//当接收到SIGCHLD信号时,调用when_sigchld函数
        signal(SIGUSR1,when_sigusr1);//当接收到SIGUSR1信号时,调用when_sigusr1函数
        signal(SIGIO,when_sigio);//当接收到SIGIO信号时,调用when_sigio函数
        alarm(5);     //5秒钟之后产生SIGALRM信号
        raise(SIGIO); //向自己发送一个SIGIO信号
        pause(); //将父进程暂停下来,等待SIGALRM信号到来
        pause(); //将父进程暂停下来,等待SIGUSR1信号到来
        pause(); //将父进程暂停下来,等待SIGCHLD信号到来
        printf("------此时程序会停下来等待,请按下ctrl c送出SIGINT信号-------/n");
        pause(); //将父进程暂停下来,等待SIGINT信号到来        
    }
    else if(childpid==0) //子进程
    {
        int timer;
        for(timer=7;timer>=0;timer--) //时钟计时5秒产生SIGALRM信号,再过2秒子进程退出,产生SIGCHLD信号
        {
            if(timer>2)    
                printf("距离SIGALRM信号到来还有%d秒。/n",timer-2);
            if(timer==4)
                kill(getppid(),SIGUSR1); //向父进程发送一个SIGUSR1信号
            if((timer<=2)&&(timer>0))
                printf("子进程还剩%d秒退出,届时会产生SIGCHLD信号。/n",timer);
            if(timer==0) //子进程退出,产生SIGCHLD信号
                raise(SIGKILL); //子进程给自己发一个结束信号
            sleep(1); //每个循环延时1秒钟
        }        
    }
    else
        printf("fork()函数调用出现错误!/n");
    return 0;
}
void when_alarm()
{
    printf("5秒钟时间已到,系统接收到了SIGALRM信号!/n");
}
void when_sigint()
{
    printf("已经接收到了SIGINT信号,程序将退出!/n");
    exit(0);
}
void when_sigchld(int SIGCHLD_num)
{
    printf("收到SIGCHLD信号,表明我的子进程已经中止,SIGCHLD信号的数值是:%d。/n",SIGCHLD_num);
}
void when_sigusr1()
{
    printf("系统接收到了用户自定义信号SIGUSR1。/n");
}
void when_sigio()
{
    printf("系统接收到了SIGIO信号。/n");
}

Result:

[work@db-testing-com06-vm3.db01.baidu.com c ]$ ./signal_test 

程序已经开始运行,5秒钟后将接收到时钟信号。

距离SIGALRM信号到来还有5秒。

系统接收到了SIGIO信号。

距离SIGALRM信号到来还有4秒。

距离SIGALRM信号到来还有3秒。

距离SIGALRM信号到来还有2秒。

系统接收到了用户自定义信号SIGUSR1。

距离SIGALRM信号到来还有1秒。

5秒钟时间已到,系统接收到了SIGALRM信号!

子进程还剩2秒退出,届时会产生SIGCHLD信号。

子进程还剩1秒退出,届时会产生SIGCHLD信号。

收到SIGCHLD信号,表明我的子进程已经中止,SIGCHLD信号的数值是:17。

------此时程序会停下来等待,请按下ctrl c送出SIGINT信号-------

已经接收到了SIGINT信号,程序将退出!

信号参考对照表:

Signal

Description

SIGABRT

由调用abort函数产生,进程非正常退出

SIGALRM

用alarm函数设置的timer超时或setitimer函数设置的interval timer超时

SIGBUS

某种特定的硬件异常,通常由内存访问引起

SIGCANCEL

由Solaris Thread Library内部使用,通常不会使用

SIGCHLD

进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略

SIGCONT

当被stop的进程恢复运行的时候,自动发送

SIGEMT

和实现相关的硬件异常

SIGFPE

数学相关的异常,如被0除,浮点溢出,等等

SIGFREEZE

Solaris专用,Hiberate或者Suspended时候发送

SIGHUP

发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送

SIGILL

非法指令异常

SIGINFO

BSD signal。由Status Key产生,通常是CTRL T。发送给所有Foreground Group的进程

SIGINT

由Interrupt Key产生,通常是CTRL C或者DELETE。发送给所有ForeGround Group的进程

SIGIO

异步IO事件

SIGIOT

实现相关的硬件异常,一般对应SIGABRT

SIGKILL

无法处理和忽略。中止某个进程

SIGLWP

由Solaris Thread Libray内部使用

SIGPIPE

在reader中止之后写Pipe的时候发送

SIGPOLL

当某个事件发送给Pollable Device的时候发送

SIGPROF

Setitimer指定的Profiling Interval Timer所产生

SIGPWR

和系统相关。和UPS相关。

SIGQUIT

输入Quit Key的时候(CTRL /)发送给所有Foreground Group的进程

SIGSEGV

非法内存访问

SIGSTKFLT

Linux专用,数学协处理器的栈异常

SIGSTOP

中止进程。无法处理和忽略。

SIGSYS

非法系统调用

SIGTERM

请求中止进程,kill命令缺省发送

SIGTHAW

Solaris专用,从Suspend恢复时候发送

SIGTRAP

实现相关的硬件异常。一般是调试异常

SIGTSTP

Suspend Key,一般是Ctrl Z。发送给所有Foreground Group的进程

SIGTTIN

当Background Group的进程尝试读取Terminal的时候发送

SIGTTOU

当Background Group的进程尝试写Terminal的时候发送

SIGURG

当out-of-band data接收的时候可能发送

SIGUSR1

用户自定义signal 1

SIGUSR2

用户自定义signal 2

SIGVTALRM

setitimer函数设置的Virtual Interval Timer超时的时候

SIGWAITING

Solaris Thread Library内部实现专用

SIGWINCH

当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程

SIGXCPU

当CPU时间限制超时的时候

SIGXFSZ

进程超过文件大小限制

SIGXRES

Solaris专用,进程超过资源限制的时候发送

==========================================================================

signal学习推荐:

信号(signal)介绍(Linux中国)

http://www.linux-cn.com/html/linux/system/20070505/27605.shtml

Linux 信号signal处理函数(CSDN)

http://blog.csdn.net/Sunboy_2050/archive/2010/10/16/5945535.aspx

Linux 信号signal处理机制(CSDN)

http://blog.csdn.net/Sunboy_2050/archive/2010/10/16/5945380.aspx

==========================================================================          程序员可以调用 int raise(int signo) 将一个信号发送给它自己。这个函数只带有一个参数,既要发送信号的编号。如:raise(SIGINT); raise(SIGKILL);          让人感兴趣的是函数 unsigned int alarm(unsigned int seconds) 它可以让用户进程在将来某个指定的时间接收到一个信号。alarm()的唯一参数是将来信号SIGALRM应该在多少秒以后发送给用户进程。当用户调用alarm()时,前面任何一个请求的报警信号(不包括悬挂起来被阻塞的SIGALRM信号)都将被取消,调用的返回值是前面请求的剩余时间。alarm()范例如下: if(signal(SIGALRM,alarmhandler)==SIG_ERR) {      printf("Couldn't register signal handler./n");  } alarm(5);      // 5秒钟以后,程序将会收到一个SIGALRM信号 for(i=0;i<10;i ) {      sleep(3); } void alarmhandler(int signum) {      printf("alarmhandler./n");  }          也可以使用 int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue) 来实现更精确更方便的定时控制。          参数which一般取ITIMER_REAL,它使得用户的计时器根据系统时钟来计算时间。当计数时间到期时,它将发送一个SIGALRM信号。其功能和alarm()一样,所以用户不能将两者同时使用。          结构itimerval的定义如下: struct itimerval  {       struct timeval it_interval; // 每一次触发报警后应该被复位的值,为0报警被禁止      struct timeval it_value;     // 下一次触发报警的时间,为0报警将只触发一次 };          结构timeval的定义如下: strut timeval {       long tv_sec;       // 秒数       long tv_usec;     // 微秒数 };          setitimer()范例如下: struct itimerval itimer; itimer.it_interval.tv_usec = 0;     // it_interval字段指定了每一次触发后应该被复位的值 itimer.it_interval.tv_sec     = 2; itimer.it_value.tv_usec = 0;        // it_value字段指定了直到下一次触发的时间    itimer.it_value.tv_sec     = 5; setitimer(ITIMER_REAL,&itimer,NULL); for(i=0;i<10;i ) {      sleep(3); } void alarmhandler(int signum) {      printf("alarmhandler./n");  }

0 人点赞