msgrcv - SA_RESTART 标志不起作用

Posted

技术标签:

【中文标题】msgrcv - SA_RESTART 标志不起作用【英文标题】:msgrcv - SA_RESTART flag doesn't work 【发布时间】:2014-12-20 22:50:45 【问题描述】:

我使用 IPC 队列在线程之间进行通信的代码有问题。 我需要安全地处理 SIGINT - 让程序在 SIGINT 出现时完成所有活动线程,然后再关闭。不过,我在寻找解决方案方面遇到了严重的问题,因为即使对带有标志 SA_RESTART 的 SIGINT 进行 sigaction,msgrcv 函数也会出现 EINTR 错误。

我的问题是 - 除了使用 msgrcv 函数之外,还有什么方法可以避免 EINTR 在某些“如果”中指定错误条件,例如:

if (msgrcv<0)
  if (errno == EINTR)
    errno = 0;
  

这是我演示问题的程序的真正简化版本:

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/msg.h>

#define IPC_KEY 11000
#define BUF_SIZE 128

//structure of IPC message
typedef struct msgbuf
  long mtype;
  char mtext[BUF_SIZE];
 message_buf;


void handle_sigint(int sig)
  signal(SIGINT,SIG_IGN);
  /*
    some operation to handle sigint,
    here it's really simple setting 
    SIGINT to be ignored
  */ 



int main()

  long ipc_id;
  message_buf msg;
  struct sigaction setup_action;
  sigset_t block_mask;

  //setup sigaction for SIGINT
  sigemptyset(&block_mask);
  sigaddset(&block_mask,SIGINT);
  setup_action.sa_handler = handle_sigint;
  setup_action.sa_mask = block_mask;
  setup_action.sa_flags = SA_RESTART;
  if (sigaction(SIGINT, &setup_action, 0) < 0)
    perror("sigaction");
    exit(1);
  

  //creating the ipc queue
  if ((ipc_id = msgget(IPC_KEY, IPC_CREAT | IPC_EXCL | 0666))<0)
    perror("error in msgget");
    exit(1);
  

  for(;;)
    if (msgrcv(ipc_id,&msg,BUF_SIZE,1,0)<0)
      perror("error in msgrcv");
      exit(1);
    
    printf("received message : %s\n",msg.mtext);
  


这是清理 IPC 队列的简单程序:

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/msg.h>

#define IPC_KEY 11000


int main()

  long ipc_id;

  if ((ipc_id = msgget(IPC_KEY, IPC_CREAT | 0666 )) < 0) 
    perror("error in msgget");
    exit(1);
  

  if (msgctl(ipc_id, IPC_RMID, 0) != 0)
    perror("error in msgctl");
    exit(1);        
  


  return 0;


提前感谢您的帮助!我真的希望我没有提出重复的问题,但我试图寻找一段时间的解决方案,不幸的是除了使用 if 函数显式捕获 EINTR errno 之外没有找到任何其他解决方案。

【问题讨论】:

您不需要在 sigaction 调用中阻止 SIGINT — 默认情况下,无论 sa_mask 是什么,它都会被阻止,除非您使用 SA_NODEFER。 【参考方案1】:

来自(Linux) manual:

以下接口被中断后永不重启 由信号处理程序,无论是否使用 SA_RESTART;他们总是 被信号中断时失败并出现错误 EINTR 处理程序:

....

System V IPC 接口:msgrcv(2)、msgsnd(2)、semop(2) 和 semtimedop(2)。

SA_RESTART 的处理方式是位实现定义的。您没有使用特定的 Unix 风格进行标记,但我认为您的 Unix 根本不遵守 SA_RESTART 的特定系统调用。

【讨论】:

谢谢!我希望我可以通过 sigaction 以“更好”的方式做到这一点,但现在我发现这是不可能的【参考方案2】:

@cnicutar 比我早了 10 秒(所以 +1),但我要补充一点,您需要做的就是将对 msgrcv 的调用包装在 do/while 循环中,例如

int res;
do

  res = msgrcv(your parameters here);
 while ((res < 0 ) && (errno == EINTR));

如果你经常使用msgrcv,你当然可以定义一个小函数来为你做这件事。

【讨论】:

GNU unistd.h 提供了一个非标准的TEMP_FAILURE_RETRY 宏来帮助解决这个问题。 @cnicutar 我想你的意思是TEMP_FAILURE_RETRY(来自unistd.h)——我的至少没有NO_EINTR @abligh 还没有,我需要 15 个代表才能做到这一点。这不像我会乞求,我真的不需要那样,我只是认为能够为好的答案投票是一件好事,如果可以的话,我会这样做。

以上是关于msgrcv - SA_RESTART 标志不起作用的主要内容,如果未能解决你的问题,请参考以下文章

IPC 队列:- 接收错误数据

我的Android进阶之旅------&gt;Android中ListView中嵌套(ListView)控件时item的点击事件不起作的问题解决方法

msgrcv 收到空白消息

msgrcv 中的错误:参数无效

询问 msgrcv ,不适用于根据优先级接受消息

Linux进程间通信 -- 消息队列 msgget()msgsend()msgrcv()msgctl()