msgrcv – L’indicateur SA_RESTART ne fonctionne pas

Il y a quelque chose qui cloche dans mon code qui utilise la queue IPC pour communiquer entre les threads. J’ai besoin de manipuler SIGINT en toute sécurité – laissez le programme terminer tous les threads actifs lorsque SIGINT est apparu avant de s’éteindre. Cependant, j’ai un grave problème pour trouver une solution car même avec sigaction pour SIGINT avec l’indicateur SA_RESTART, la fonction msgrcv obtient une erreur EINTR.

Ma question est la suivante: existe-t-il un moyen d’éviter EINTR avec la fonction msgrcv autre que la spécification de la condition d’erreur dans certains “if” tels que:

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

Voici une version très simplifiée de mon programme pour démontrer le problème:

 #include  #include  #include  #include  #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); } } 

Et voici un programme simple pour nettoyer la file d’attente IPC:

 #include  #include  #include  #include  #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; } 

Merci d’avance pour votre aide! J’espère vraiment que ma question n’a pas été dupliquée, mais j’ai essayé de chercher une solution pendant un certain temps et, malheureusement, je n’ai trouvé rien d’autre que de saisir explicitement l’erreur EINTR avec if, fonction.

Du manuel (Linux) :

Les interfaces suivantes ne sont jamais redémarrées après avoir été interrompues par un gestionnaire de signaux, quelle que soit l’utilisation de SA_RESTART; ils échouent toujours avec l’erreur EINTR quand ils sont interrompus par un gestionnaire de signal:

….

  • Interfaces IPC System V: msgrcv(2) , msgsnd (2), semop (2) et semtimedop (2).

La façon dont SA_RESTART est traité est un bit d’implémentation défini. Vous n’avez pas SA_RESTART de type Unix spécifique mais je suppose que votre système Unix n’obéit tout simplement pas à SA_RESTART pour votre appel système spécifique.

@cnicutar m’a battu à cela par 10 secondes (donc +1), mais j’appendais que tout ce que vous avez à faire est d’envelopper l’appel à msgrcv dans une boucle do / while, par exemple

 int res; do { res = msgrcv(your parameters here); } while ((res < 0 ) && (errno == EINTR)); 

Vous pouvez bien sûr définir une petite fonction pour le faire si vous utilisez msgrcv .