C - 优雅地中断msgrcv系统调用

时间:2015-03-25 00:43:33

标签: c unix signals system-calls

我正在开发一个应该像服务器一样运行的程序,并不断从消息队列中读取并处理收到的消息。

主循环看起来像这样:

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
}

2 个答案:

答案 0 :(得分:1)

你最好去看一下这样做的现有软件;这是一种非常普遍的模式,并不像你希望的那样简单。但基本要素是:

  • 信号处理程序 - 它没有帮助(正如您已找到的)
  • 检查errno从msgrcv()== EINTR返回;如果是这样你就看到了中断。
  • 请注意,如果msgrcv在中断时收到了部分但不是全部的消息,则必须清理自己的状态和发送者。
  • 最后,您可能会发现有时会调用信号处理程序 - 如果在用户代码运行时发生中断而不是系统调用。并且msgrcv不是唯一的系统调用...我确实说它很复杂。

对于一个非平凡的计划,你最好使用毒药#39;杀死循环的方法。使用msgsend给自己发一条消息说杀了我。这样,您就可以获得可预测的结果。

HTH,露丝

答案 1 :(得分: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."