Impossible d’attraper un signal SIGINT lors de l’utilisation de pthreads

J’ai créé un serveur de discussion qui utilise le multi-threading pour traiter plusieurs clients. J’ai une boucle while qui fonctionne à l’infini et attend de nouveaux clients. Je veux en sortir après avoir appuyé sur ctrl + c. Donc, j’essaie d’attraper le signal SIGINT comme cela a été mentionné ici . Mais je ne peux pas sortir du programme. Je travaille dans un terminal sous Linux.

serveur.c

//for running type ./a.out anyportnumber #include  #include  #include  #include  #include  #include  #include  #include  #include  int s2; int arr[100]; int tc = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; volatile sig_atomic_t flag = 1; void handler(int signal) { flag = 0; } void sendtoall(char *msg,int s1) { int i; pthread_mutex_lock(&mutex); for(i = 0; i  0) { rmsg[n] = '\0'; sendtoall(rmsg,s1); bzero(rmsg,500); } pthread_exit(NULL); } int main(int arrc,char *argv[]) { struct sockaddr_in server,client; int s1,len; int n; int port; pthread_t t1; char message[500]; port = atoi(argv[1]); bzero((char *)&server,sizeof(server)); server.sin_port = htons(port); server.sin_addr.s_addr = INADDR_ANY; server.sin_family = AF_INET; s1 = socket(AF_INET,SOCK_STREAM,0); if(s1 == -1) { perror("socket not created\n"); exit(1); } if(bind(s1,(struct sockaddr *)&server,sizeof(struct sockaddr)) == -1) { perror("socket not binded\n"); exit(1); } if(listen(s1,5) == -1) { perror("unable to listen"); exit(1); } len = sizeof(struct sockaddr_in); signal(SIGINT, handler); while(flag) { s2 = accept(s1,(struct sockaddr *)&client,&len); pthread_create(&t1,NULL,function,(void *)&s2); arr[tc] = s2; tc++; } close(s1); close(s2); return 0; } 

Le signal de capture via un indicateur défini par le gestionnaire d’interruption ne convient pas dans les cas où le signal doit interrompre de manière fiable un appel système bloquant ( accept dans votre cas). Le problème est que le signal peut arriver juste avant que le système de blocage ne soit entré: après avoir vérifié l’indicateur mais avant l’état où le signal s’interrompt, appel système. Ainsi, même flag est défini, l’appel système bloque l’exécution du programme.

De plus, lorsque le signal est autorisé par plusieurs threads , seul un des threads intercepte le signal, et celui-ci n’est pas spécifié. Dans votre cas, il est possible que le signal ne soit pas capté par le thread principal, donc accept ne soit pas interrompu du tout.

Alors que le second problème (lié aux programmes multithread) est facilement surmonté en bloquant le signal dans tous les threads sauf le principal, le premier problème nécessite des approches spéciales. Possibles:

signalfd () en conjonction avec poll () / select ()

L’approche la plus complexe, mais fonctionne dans presque tous les cas. Le signal est “transformé” en descripteur de fichier, qui est combiné avec le descripteur de fichier sur lequel l’appel du système attend. L’ensemble de descripteurs de fichiers résultant est utilisé pour l’ interrogation :

 // Preparations sigset_t s; sigemptyset(&s); sigaddset(&s, SIGINT); sigprocmask(SIGBLOCK, &s, NULL); // For multithreaded program *pthread_sigmask* should be used. int fd_int = signalfd(0, &s, 0); // When signal arises, this file becomes readable // Usage fd_set fds; FD_ZERO(&fds); FD_SET(fd_int, &fds); FD_SET(s1, &fds); int nfds = MAX(fd_int, s1) + 1; select(nfds, &fds, NULL, NULL, NULL); if(FD_ISSET(fd_int, &fds)) { // Signal is arrived ... } else { // New connection request is received accept(s1, ...); ... } 

Notez que ce signal est bloqué pour tous les threads, y compris le principal.

Finalisation à l’intérieur du gestionnaire de signal et sortie

L’approche la plus simple mais avec un usage limité. Si toutes les actions de finalisation sont sans danger pour le signal (voir le manuel (7) pour la liste complète des fonctions dont l’appel est autorisé dans le gestionnaire de signal), les actions peuvent être exécutées par le gestionnaire de signal qui quitte alors le programme:

 void handler(int signal) { close(s1); close(s2); _exit(0); // This function is thread-safe, unlike to *exit*. } 

Mais dans le cas d’un programme multithread, cette approche ne convient normalement pas, car la fonction thread_join n’est pas sûre pour le signal.

Modification de l’état des parameters de l’appel système dans le gestionnaire de signaux

donc l’appel système retournera immédiatement, sans blocage. Le changement d’état le plus simple est la fermeture du descripteur de fichier avec lequel l’appel système fonctionne:

 void handler(int signal) { flag = 0; close(s1); // Close file descriptor which is used by the system call. } while(flag) { s2 = accept(s1, ...); if(s2 == -1 && !flag) { // Signal is catched break; } ... } 

Notez que dans le cas d’un programme multithread, le signal devrait être explicitement bloqué pour tous les threads, sauf le principal. Sinon, la fermeture du fichier dans un thread pendant que d’autres threads le lit n’est pas nécessaire pour interrompre le thread de lecture.

En outre, dans le cas d’un programme multithread, il faut prendre en compte que si un autre thread crée (ouvre) un descripteur de fichier, il peut en réutiliser un, fermé dans le gestionnaire de signal, juste avant son utilisation dans l’appel système.