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

C编程-为什么这个信号没有被忽略?,c,linux,signals,C,Linux,Signals,我正在写一个程序,每5秒向自己发送一个信号警报。我还想知道,如果用户在terminal kill-ALRM PID中键入被忽略的内容。如果我理解信号调用,我的程序应该这样做,但是,当我从终端使用该命令时,程序会认为它是它的报警之一,并且不会忽略它 int s = 0; void alarm_func(int s) { s = s + 10; char buff[256]; sprintf(buff, "ALARM pid=%d, %d seconds, getpid(),

我正在写一个程序,每5秒向自己发送一个信号警报。我还想知道,如果用户在terminal kill-ALRM PID中键入被忽略的内容。如果我理解信号调用,我的程序应该这样做,但是,当我从终端使用该命令时,程序会认为它是它的报警之一,并且不会忽略它

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);
}
非常感谢


编辑:我的程序应该每10秒向自己发送一个信号,持续100秒,即10次。但是,如果我试图从终端kill-ALRM'pid'向程序发送一个信号,该信号就会被忽略。这就是为什么我使用变量k:如果我从终端发送信号,k将不同于0,因为报警10的10秒没有完全发生,因此信号将被忽略信号sigalrm,SIG_IGN

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


抱歉,所有潜在的歧义

通过向sigaction注册信号处理程序,您可以在终端区分alarm发送的信号和kill发送的信号。这将允许您编写一个获取更多信息的信号处理程序,特别是一个接受指向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;
}
在这种情况下,alarm发送的信号具有si_内核的si_代码,而通过kill或终端上的kill命令发送的信号具有si_用户的si_代码,因此您可以知道源代码

显然,你必须先收到信号才能找到这些信息,所以你不能根据信号的来源忽略它。一般来说,这很好,但在您的情况下,它会导致问题,因为您使用pause来分隔报警呼叫,并且pause将在捕获任何信号后返回。所以,尽管你可以忽略处理程序中的杀戮信号,但它仍然会在10秒内触发另一个警报,因为主循环就是这样工作的。你仍然会收到警报发出的整整十个信号,并且你只能处理这十个信号,但是它们的时间会受到kill终端使用的影响


当然,解决这个问题的显而易见的办法是,不要为此目的而使用暂停。如果您想使用这样的计时器,那么只需使用常规POSIX计时器,您就不会遇到这个问题。你可以用另一种方法来解决这个问题,但是如果你只需要使用一个常规的POSIX定时器,那么这样做是没有意义的。

k是未初始化的,并且可能不等于零。如果在未初始化的k上进行分支,那有什么意义呢?这和信号有什么关系?我试着初始化k=1,但也没用。什么也不起作用?这就是为什么我使用变量k:如果我从终端发送信号,k将不同于0-k仅在程序启动时使用。它怎么能改变任何事情呢?是的,你似乎对程序的执行方式有着非常混乱的理解。如果k==0,那么这个东西不会建立某种持久关系,在这种关系中,任何时候k==0,信号都会被忽略,而任何时候k!=0信号已被处理-它只在开始时测试一次,信号处理程序已注册,值k在程序中不再起任何作用。不应从信号处理程序中调用printf。OP没有这样做misstake@wildplasser:请注意,我们没有在信号处理程序中正常执行IO,所以这只是出于演示目的,不清楚吗?不,这仍然是错误的。甚至出于演示目的。未来的读者可能会重用您的代码段,因为它看起来很可靠,而忽略了文本。@wildplasser:胡说八道。@wildplasser:这是一个相当幼稚和简单的观点。如果你能保证它们不会被中断,那么非异步安全的函数就可以了,这在这个程序中是正确的,因为只有信号处理程序调用printf,它不能被第二个SIGALRM中断,所以它不需要重新进入。由于其他原因,即使是可重入函数也常常是不安全的。例如,如果主程序处于写入同一文件的中间,则在信号处理程序中写入的调用仍然会导致垃圾或无序输出的问题。
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$