2017-09-04 2 views
0

J'étudie actuellement les concepts de programmation réseau dans lesquels je suis tombé sur l'une des fonctions pselect() qui résout le problème de select ie. avec select(), il y a un risque de problème qui est celui entre le test de intr_flag et l'appel à sélectionner, si le signal se produit, il sera perdu si select bloque pour toujours.comment pselect bloque le signal en utilisant le masque de signal dans la programmation réseau

if (intr_flag) 
handle_intr(); /* handle the signal */ 
if ((nready = select(...)) < 0) { 
if (errno == EINTR) { 
if (intr_flag) 
handle_intr(); 
} 

Cependant, il est dit que Avec pselect, nous pouvons le code maintenant cet exemple de manière fiable que

sigset_t newmask, oldmask, zeromask; 
sigemptyset(&zeromask); 
sigemptyset(&newmask); 
sigaddset(&newmask, SIGINT); 
sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* block SIGINT */ 
if (intr_flag) //here 
handle_intr(); /* handle the signal */ 
if ((nready = pselect (... , &zeromask)) < 0) { 
if (errno == EINTR) {  //here 
if (intr_flag) 
handle_intr(); 
} 
... 
} 

L'explication qu'il donne pour que le code soit fiable est que - Avant de tester la variable intr_flag, nous bloquons SIGINT. Lorsque pselect est appelé, il remplace le masque de signal du processus par un ensemble vide (c'est-à-dire, zeromask), puis vérifie les descripteurs, en allant éventuellement au repos. Mais lorsque pselect revient, le masque de signal du processus est réinitialisé à sa valeur avant l'appel de pselect (c'est-à-dire, SIGINT est bloqué).

Mais dans le code avec pselect mentionné ci-dessus, nous bloquons les signaux alors comment pouvons-nous vérifier l'erreur EINTR? Puisque le pselect bloque tous les signaux, alors quand l'interruption se produit, il devrait empêcher que cela perturbe ou soit transmis au processus. Ce n'est que lorsque pselect revient que le signal peut être délivré.

Selon les lignes devant qui les commentent ici est mentionné, INTERRUPTION signal peut encore se produire pselect est appelé ou entre le premier chèque et pselect ou lorsque pselect est appelé contredisant le but de bloquer l'interruption et tous les autres signaux et devraient donc conduire à la condition de course comme avec select est là.

S'il vous plaît, n'importe qui explique comment cela est possible car je suis nouveau à ces concepts.

Répondre

1

Eh bien, l'idée principale est que faire ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask); équivaut à effectuer les opérations suivantes atomiquement:

sigset_t sigsaved; 

sigprocmask(SIG_SETMASK, &sigmask, &sigsaved); 
/* NB: NOTE-1 */ 
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout); 
sigprocmask(SIG_SETMASK, &sigsaved, NULL); 

Pour utiliser ce que nous premier bloc, disons, SIGINT:

sigset_t emptyset, blockset; 

sigemptyset(&blockset); 
sigaddset(&blockset, SIGINT); 
sigprocmask(SIG_BLOCK, &blockset, NULL); 

À ce stade, nous ne pouvons pas recevoir SIGINT et, par conséquent, nous ne pouvons pas le traiter. Mais nous n'en avons pas besoin jusqu'à ce que nous entrions pselect(). Ce que nous voulons faire après que nous avons bloqué SIGINT est de configurer un gestionnaire de signal approprié.

Dites, nous avons un drapeau static volatile int intr_flag = 0; déclaré en dehors de notre code principal et nous définissons un gestionnaire appelé handler() qui fait simplement intr_flag = 1;. Donc, nous avons mis en place en tant que gestionnaire:

sa.sa_handler = handler; 
sa.sa_flags = 0; 
sigemptyset(&sa.sa_mask); 
sigaction(SIGINT, &sa, NULL); 

Ensuite, nous configurons readfs (déclaration montre pas ici), initialiser un ensemble de signaux vide et appeler pselect():

sigemptyset(&emptyset); 
ready = pselect(nfds, &readfds, NULL, NULL, NULL, &emptyset); 

Alors, je vais décrire cela une fois de plus - nous ne recevons pas SIGINT jusqu'à ce que nous appelons pselect(). Nous n'en avons pas besoin.Dès que nous entrons pselect(), le signal SIGINT sera débloqué (en raison d'un ensemble de signaux vide fourni à pselect()), et pselect() peut être interrompu par SIGINT. Au moment où pselect() retours, nous avons de nouveau ne recevront aucune SIGINT plus loin, mais si SIGINT a eu lieu au cours pselect(), nous détectons ce que par errno étant EINTR et si nous vérifions intr_flag, nous allons le trouver pour être 1. Nous comprendrons que nous devons nous comporter en conséquence. Donc, il est assez clair que le gestionnaire de signal peut faire son travail dès que le signal est débloqué, et que ce dernier se passe à l'intérieur pselect() lui-même.

Le plus important détail ici est que si nous n'avons un appel pselect() spécial mis en œuvre atomique façon, nous devons faire les étapes autour /* NB: NOTE-1 */ étiquette dans l'extrait ci-dessus nous-mêmes lorsque vous utilisez un habituelle select() . Et, comme il ne sera pas atomique, nous aurons une chance que SIGINT nous sera livré entre les deux actions - où /* NB: NOTE-1 */ indice est situé, soit après que nous avons débloqué le signal d'être délivré et avant select() est entré . À ce stade, le signal sera en effet perdu. Voici pourquoi nous avons besoin d'un appel pselect() à la place.

Dans l'ensemble, atomicité de pselect() est l'explication de son utilisation. Si vous n'êtes pas familier avec un tel concept, vous pouvez consulter le article sur Wikipedia ou des livres spéciaux sur des sujets d'informatique.

Aussi, je fournirai ma réponse avec le lien vers un article sur LWN qui est beaucoup plus exhaustif.

+0

très belle explication :) Merci! –

+0

De rien. –