2014-09-28 148 views
-3

我正在寫一個程序,每5秒發送一個信號(警報)給自己。我也希望如果用戶鍵入被忽略的終端「kill -ALRM PID」。如果我理解的信號來電,我的程序應該這樣做,但是,當我使用該命令從終端proram認爲這是它的一個警報和犯規忽略它C編程 - 爲什麼這個信號不被忽略?

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次。但是,我也希望如果我試圖從終端向程序發送一個信號(kill -ALRM'pid'),那個信號會被忽略。這就是爲什麼我使用變量k:如果我從終端發送信號,k將不同於0(因爲來自警報(10)的10個secons沒有完全發生),所以信號將被忽略(信號(SIGALRM,SIG_IGN ))。

這就是我所理解的信號的使用,開始意識到我知道幾乎爲零

對不起,所有發送的潛在歧義

+4

k未初始化,可能不等於零。 – 2014-09-28 11:57:43

+1

未初始化的'k'上的'if'分支有什麼意義?那和'signal'有關呢? – mafso 2014-09-28 12:02:14

+0

如果我理解正確,如果全部時間已經轉換,則alarm()返回0,否則(當終端發生時)返回剩餘時間量。我嘗試初始化k = 1,但它也沒有工作。 – Ruben 2014-09-28 12:04:12

回答

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,所以這是僅用於演示目的。

示例輸出:

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

在這種情況下,通過alarm()發送的信號具有的SI_KERNELsi_code,而(在終端,同樣的事情或kill命令)經由kill()發送的那些具有si_codeSI_USER,所以你可以告訴源。

很明顯,您必須首先接收信號才能找到此信息,所以您實際上無法根據來源忽略它。一般情況下,這很好,但在你的情況下,它會導致問題,因爲你使用pause()來分隔你的電話到alarm(),pause()將返回任何信號被捕獲。因此,雖然您事實上可以忽略處理器中的kill()信號,但它仍會在十秒鐘內觸發另一個警報,因爲這就是您的主循環的工作原理。您仍然會收到alarm()發送的十個信號,您將只能處理這十個信號,但其時間將受終端使用kill的影響。

明顯的解決方案當然不是使用pause()來達到這個目的。如果你想使用這樣的定時器,那麼只需使用一個普通的POSIX定時器,你就不會遇到這個問題。你可以用一種替代方法來湊合它,但是當你只需要使用一個普通的POSIX定時器的時候,這樣做毫無意義。

+1

你不應該在信號處理程序中調用printf()。 (OP沒有犯此錯誤) – wildplasser 2014-09-28 21:34:31

+1

@wildplasser:有沒有關於「請注意,我們在信號處理程序中不能正常執行IO,所以這僅用於演示目的」,這是不明確的? – 2014-09-28 21:35:23

+0

不,它仍然是錯誤的。即使爲了演示目的。未來的讀者可能會重複使用您的代碼段(因爲它*看起來很實體),而忽略文本。 – wildplasser 2014-09-28 21:36:22