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

标签 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 ,而不是终止循环并清理我的资源。

有没有办法可以终止系统调用并优雅地退出循环?

解决方案:

感谢 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
}

最佳答案

您最好看看现有的软件可以做到这一点;这是一种非常常见的模式,但并不像您希望的那样简单。但基本原理是:

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

对于一个不平凡的程序,你最好使用“毒药”方法来终止循环。使用 msgsend 向自己发送一条消息,内容是“杀掉我”。这样,您就可以获得可预测的结果。

HTH,露丝

关于C - 优雅地中断 msgrcv 系统调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29245596/

相关文章:

c - 使用 Malloc (char & int) 分配内存

linux - 为 tar 文件创建新脚本返回错误

node.js - 如何使用 NVM 将全局 cordova 包(acorn-node)迁移到旧 Node 版本?

bash - UNIX:从 CSV 文件中获取数字的出现次数

c - 如何多次向 child 发送信号以避免僵尸状态? C语言

c - 我应该如何以非 root 身份读取 Linux 上的英特尔 PCI 非核心性能计数器?

c - 为什么下面的程序给出输出 :A

c - 仅使用 1 个参数定义

audio - 示波器上显示的音频信号上的振铃伪影

qt - Qt 中插件的自定义信号