2015-03-25 108 views
0

我正在開發一個應該像服務器一樣的程序,並不斷從消息隊列中讀取並處理收到的消息。C - 優雅地中斷msgrcv系統調用

主循環看起來是這樣的:

while (1) { 
    /* Receive message */ 
    if (msgrcv(msqid, &msg, sizeof(struct msgbuffer) - sizeof(long), 0, 0) == -1) { 
     perror("msgrcv"); 
     exit(1); 
    } 
    //more code here 
} 

我遇到的問題是,我不能想出一個辦法來正常退出這個循環不依靠客戶端上發送郵件服務器表明它應該停止。我在循環之後做了大量的資源清理工作,並且我的代碼永遠無法達到那個點,因爲循環不會結束。

有一件事我試圖做的是偵聽SIGINT結束循環....

volatile sig_atomic_t stop; 

void end(int signum) { 
    stop = 1; 
} 

int main(int argc, char* argv[]) { 
    signal(SIGINT, end); 
    //some code 
    while (!stop) { 
     /* Receive message */ 
     if (msgrcv(msqid, &msg, sizeof(struct msgbuffer) - sizeof(long), 0, 0) == -1) { 
      perror("msgrcv"); 
      exit(1); 
     } 
     //more code here 
    } 
    //cleanup 
} 

...但由於環是掛在系統中調用自身,這不起作用,並且僅僅導致perror打印出msgrcv: Interrupted system call,而不是終止循環並清理我的資源。

有沒有一種方法可以終止系統調用並正常退出我的循環?

SOLUTION:

感謝rivimey,我能解決我的問題。這是我做的工作:

volatile sig_atomic_t stop; 

void end(int signum) { 
    stop = 1; 
} 

int main(int argc, char* argv[]) { 
    signal(SIGINT, end); 
    //some code 
    while (!stop) { 
     /* Receive message */ 
     if (msgrcv(msqid, &msg, sizeof(struct msgbuffer) - sizeof(long), 0, 0) == -1) { 
      if (errno == EINTR) break; 
      else { 
       perror("msgrcv"); 
       exit(1); 
      } 
     } 
     //more code here 
    } 
    //I can now reach this code segment 
} 

回答

1

你會很好地去看看現有的軟件,這樣做;這是一種非常普遍的模式,並不像你希望的那樣簡單。但是基本是:

  • 信號處理程序 - 它不會幫助(如你發現)從msgrcv()== EINTR
  • 檢查errno回報;如果是的話,你看到了一箇中斷。
  • 請注意,如果msgrcv在中斷時收到了一些但不是全部的消息,則必須清理您自己的狀態和發件人。
  • 最後,您可能會發現有時會調用信號處理程序 - 如果在用戶代碼正在運行而不是系統調用時發生中斷。而且msgrcv不是唯一的系統調用...我確實說它很複雜。

對於一個非平凡的程序,你最好使用殺死循環的'毒'方法。用msgsend給自己發一條消息說殺了我。這樣,你可以得到可預見的結果。

HTH,露絲

+0

感謝您的信息。它幫助了很多。我會用我的問題的解決方案更新我的原始帖子。 – 2015-03-25 02:03:09

0
the code could have the following implemented: 

have the msgflg parameter contain 'IPC_NOWAIT' 
then, the next line in the code should check 'errno' 
for the value 'EAGIN' 
when errno is EAGIN, either loop to recall msgrcv() or exit 
    the loop due to some other criteria. 
    optionally the code could nanosleep() for a while 
    before jumping back to the top of the loop 

extracted from the man page for msgrcv() 

"EAGAIN No message was available in the queue 
     and IPC_NOWAIT was specified in msgflg." 
+0

繁忙輪詢是編寫程序非常糟糕的方式。 – rivimey 2015-03-25 11:08:43