C编程 - 为什么这个信号不被忽略?

时间:2014-09-28 11:55:09

标签: c linux signals

我正在编写一个程序,每5秒向自己发送一个信号(警报)。我也希望如果用户键入终端" kill -ALRM PID"被忽略了。如果我理解信号调用,我的程序应该这样做,但是,当我从终端使用该命令时,proram认为它是它的一个警报并且不会忽略它

int s = 0;
void alarm_func(int s) {
    s = s + 10;
    char buff[256];
    sprintf(buff, "ALARM pid=%d, %d seconds, getpid(), s);
    write(1, buff, strlen(buff));    
}

int main(int argc, char* argv[]) {
    int k = 1; //just a different value from 0
    if (k == 0) signal(SIGALRM, alarm_func);
    else signal(SIGALRM, SIG_IGN); //this should ignore the terminal-created alarm (?)
    while (s<100) {
        k = alarm(10);
        pause();
    }  
    exit(1);
}

非常感谢

编辑:我的prgram应该每隔10秒向自己发送一个信号,持续100秒,即10次。但是,我还想要,如果我尝试从终端向程序发送信号(杀死-ALRM&#39; pid&#39;)信号无法识别。这就是我使用变量k的原因:如果我从终端发送信号,k将不同于0(因为来自警报(10)的10个secons没有完全发生)所以信号将被忽略(信号(SIGALRM,SIG_IGN) ))。

这就是我理解信号使用的方式,开始意识到我几乎一无所知

对于所有潜在的歧义感到抱歉

1 个答案:

答案 0 :(得分:1)

您可以通过向alarm()注册信号处理程序来区分kill发送的信号和终端sigaction()发送的信号。这将允许您编写一个信号处理程序,该信号处理程序可以获取更多信息,特别是接受指向struct siginfo_t的指针,该指针包含足够的信息供您确定信号的来源。

以下是一个例子:

#define _POSIX_C_SOURCE 200809L

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

sig_atomic_t s = 0;

void handler(int signum, siginfo_t * si, void * ucon) {
    switch ( si->si_code ) {
        case SI_USER:
            printf("Ignoring signal from user, s is %lu...\n",
                   (unsigned long) s);
            break;

        case SI_KERNEL:
            s += 10;
            printf("Processing signal from alarm(), s is %lu...\n",
                   (unsigned long) s);
            break;

        default:
            printf("Ignoring signal from unknown source, s is %lu...\n",
                   (unsigned long) s);
            break;
    }
}


int main(void) {

    /*  Set up struct sigaction  */

    struct sigaction sa;
    sa.sa_handler = NULL;
    sa.sa_sigaction = handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);

    /*  Register signal handler  */

    if ( sigaction(SIGALRM, &sa, NULL) == -1 ) {
        perror("Couldn't register signal handler.");
        return EXIT_FAILURE;
    }

    /*  Go into alarm loop  */

    while ( s < 100 ) {
        alarm(10);
        pause();
    }

    return 0;
}

请注意,我们通常不会在信号处理程序中执行IO,因此仅用于演示目的。

示例输出:

paul@local:~/src/sandbox$ ./alarm &
[1] 2042
paul@local:~/src/sandbox$ ps | grep alarm
 2042 pts/0    00:00:00 alarm
paul@local:~/src/sandbox$ Processing signal from alarm(), s is 10...
kill -ALRM 2042
Ignoring signal from user, s is 10...
paul@local:~/src/sandbox$ Processing signal from alarm(), s is 20...
kill -ALRM 2042
Ignoring signal from user, s is 20...
paul@local:~/src/sandbox$ kill -ALRM 2042
Ignoring signal from user, s is 20...
paul@local:~/src/sandbox$ kill -9 2042
paul@local:~/src/sandbox$ 
[1]+  Killed                  ./alarm
paul@local:~/src/sandbox$ 

在这种情况下,alarm()发送的信号si_codeSI_KERNEL,而kill()发送的信号(或kill命令发送的信号为si_code终端,同样的事情)SI_USER pause(),所以你可以告诉来源。

显然,您必须先收到信号才能找到此信息,因此您无法根据来源实际忽略该信息。一般来说这很好,但在你的情况下它会导致问题,因为你使用alarm()将你的电话分开到pause(),而kill()将在任何信号后返回被抓住了。因此,虽然您事实上可以忽略处理程序中的alarm()信号,但它仍会在十秒钟内触发另一个警报,因为这是您的主循环的工作方式。您仍然可以获得kill发送的十个信号,并且您只能处理这十个信号,但它们的时间安排将受到终端使用pause()的影响。

显而易见的解决方案当然是不要将{{1}}用于此目的。如果你想使用这样的计时器,那么只需使用常规POSIX计时器,你就不会遇到这个问题。你可以用另一种方法来解决这个问题,但是只要你使用普通的POSIX计时器就可以做到这一点。