2015-03-25 109 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