Bash-Linux.com : Le SHELL pour les nuls

  Actuellement 46 lignes de commande disponibles
login as: root
root@213.186.33.18's password:
Last login: Sun Aug 1 7:53:51 2010 from 38.107.191.102
[root@bash-linux ~] # echo "Bienvenue sur Bash-Linux.com"_
 Manuel des commandes UNIX (man) Version française

Indiquez la fonction :

Man Select_tut en français

SELECT_TUT(2) Manuel du programmeur Linux SELECT_TUT(2)
 
NOM


select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - Multiplexage d'E/S synchrones
 
SYNOPSIS


#include #include #include int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *utimeout); int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *ntimeout, sigset_t *sigmask); FD_CLR(int fd, fd_set *set); FD_ISSET(int fd, fd_set *set); FD_SET(int fd, fd_set *set); FD_ZERO(fd_set *set);
 
DESCRIPTION


select() (ou pselect()) est la fonction pivot de la plupart des pro- grammes en C qui gerent simultanement et de facon efficace plusieurs descripteurs de fichiers (ou sockets). Ses principaux arguments sont trois tableaux de descripteurs de fichiers : readfds, writefds, et exceptfds. select() est generalement utilise de facon a bloquer en attendant un changement d'etat d'un ou plusieurs descripteurs de fichiers. Un changement d'etat est signale lorsque de nouveaux carac- teres sont mis a disposition sur le descripteur de fichier ; ou bien lorsque de l'espace devient disponible au niveau des tampons internes du noyau permettant de nouvelles ecritures dans le descripteur de fichier, ou bien lorsqu'un descripteur de fichier rencontre une erreur (dans le cas d'une socket ou d'un tube, une telle erreur est levee lorsque l'autre extremite de la connexion est fermee). Pour resumer, select() surveille simplement de multiples descripteurs de fichiers, et constitue l'appel Unix standard pour realiser cette tche. Les tableaux de descripteurs de fichier sont appeles ensembles de descripteurs de fichiers. Chaque ensemble est de type fd_set, et son contenu peut etre modifie avec les macros FD_CLR(), FD_ISSET(), FD_SET(), et FD_ZERO(). On commence generalement par utiliser FD_ZERO() sur un ensemble venant d'etre cree. Ensuite, les descripteurs de fichiers individuels qui vous interessent peuvent etre ajoutes un a un a l'aide de FD_SET(). select() modifie le contenu de ces ensembles selon les regles ci-dessous. Apres un appel a select(), vous pouvez verifier si votre descripteur de fichier est toujours present dans l'ensemble a l'aide de la macro FD_ISSET(). FD_ISSET() renvoie zero si le descripteur de fichier est absent et une valeur non nulle sinon. FD_CLR() retire un descripteur de fichier de l'ensemble.
 
ARGUMENTS


readfds Cet ensemble est examine afin de determiner si des donnees sont disponibles en lecture a partir d'un de ses descripteurs de fichiers. Suite a un appel a select(), readfds ne contient plus aucun de ses descripteurs de fichiers a l'exception de ceux qui sont immediatement disponibles pour une lecture via un appel recv() (pour les sockets) ou read() (pour les tubes, fichiers et sockets). writefds Cet ensemble est examine afin de determiner s'il y a de l'espace afin d'ecrire des donnees dans un de ses descripteurs de fichiers. Suite a un appel a select(), writefds ne contient plus aucun de ses descripteurs de fichiers a l'exception de ceux qui sont immediatement disponibles pour une ecriture via un appel a send() (pour les sockets) ou write() (pour les tubes, fichiers et sockets). exceptfds Cet ensemble est examine pour les exceptions ou les erreurs sur- venues sur les descripteurs de fichiers. Neanmoins, ceci n'est veritablement rien d'autre qu'une rumeur. exceptfds est en fait utilise afin de detecter l'occurrence de donnees hors-bande (Out Of Band). Les donnees hors bande sont celles qui sont envoyees sur une socket en utilisant le drapeau MSG_OOB, ainsi exceptfds s'applique en realite uniquement aux sockets. Voir recv(2) et send(2) a ce sujet. Suite a un appel a select(), exceptfds ne contient plus aucun de ses descripteurs de fichiers a l'excep- tion de ceux qui sont disponibles pour une lecture de donnees hors-bande. Cependant, vous pouvez presque toujours lire unique- ment un octet de donnees hors bande (a l'aide de recv()), et l'ecriture de donnees hors bande (avec send) peut etre effectuee a n'importe quel moment et n'est pas bloquante. Il n'y a donc pas de besoin d'un quatrieme ensemble afin de verifier si une socket est disponible pour une ecriture de donnees hors bande. n Il s'agit d'un entier valant un de plus que n'importe lequel des descripteurs de fichiers de tous les ensembles. En d'autres ter- mes, lorsque vous ajoutez des descripteurs de fichiers a vos ensembles, vous devez determiner la valeur entiere maximale de tous ces derniers, puis ajouter un a cette valeur, et la passer en argument n a select(). utimeout Il s'agit du temps le plus long que select() doit attendre avant de rendre la main, meme si rien d'interessant n'est arrive. Si cette valeur est positionnee a NULL, alors, select() bloque indefiniment dans l'attente d'un evenement. utimeout peut etre positionne a zero seconde, ce qui provoque le retour immediat de select(). La structure struct timeval est definie comme struct timeval { long tv_sec; /* secondes */ long tv_usec; /* microsecondes */ }; ntimeout Cet argument a la meme signification que utimeout mais struct timespec a une precision a la nanoseconde comme explicite ci- dessous : struct timespec { long tv_sec; /* secondes */ long tv_nsec; /* nanosecondes */ }; sigmask Cet argument renferme un ensemble de signaux non bloques pendant un appel pselect() (voir sigaddset(3) et sigprocmask(2)). Il peut valoir NULL, et, dans ce cas, il ne modifie pas l'ensemble des signaux non bloques a l'entree et la sortie de la fonction. Il se comporte alors de facon identique a select(). COMBINAISON D'EVENEMENTS DE SIGNAUX ET DE DONNEES pselect() doit etre utilise si vous attendez tout aussi bien un signal que des donnees d'un descripteur de fichier. Les programmes qui recoivent les signaux comme des evenements utilisent generalement le gestionnaire de signal uniquement pour lever un drapeau global. Le dra- peau global indique que l'evenement doit etre traiter dans la boucle principale du programme. Un signal provoque l'arret de l'appel select() (ou pselect()) avec errno positionnee a EINTR. Ce comportement est essentiel afin que les signaux puissent etre traites dans la boucle principale du programme, sinon select() bloquerait indefiniment. Ceci etant, la boucle principale implante quelque part une condition verifi- ant le drapeau global, et l'on doit donc se demander : que se passe t'il si un signal est leve apres la condition mais avant l'appel a select() ? La reponse est que select() bloquerait indefiniment, meme si un signal est en fait en attente. Cette "race condition" est resolue par l'appel pselect(). Cet appel peut etre utilise afin de debloquer des signaux qui ne sont pas censes etre recus si ce n'est durant l'appel a pselect(). Par exemple, disons que l'evenement en question est la fin d'un processus fils. Avant le demarrage de la boucle princi- pale, nous bloquerions SIGCHLD en utilisant sigprocmask(). Notre appel pselect() debloquerait SIGCHLD en utilisant le masque de signal ini- tial. Le programme ressemblerait a ceci : int child_events = 0; void child_sig_handler (int x) { child_events++; signal (SIGCHLD, child_sig_handler); } int main (int argc, char **argv) { sigset_t sigmask, orig_sigmask; sigemptyset (&sigmask); sigaddset (&sigmask, SIGCHLD); sigprocmask (SIG_BLOCK, &sigmask, &orig_sigmask); signal (SIGCHLD, child_sig_handler); for (;;) { /* main loop */ for (; child_events > 0; child_events--) { /* do event work here */ } r = pselect (n, &rd, &wr, &er, 0, &orig_sigmask); /* corps principal du programme */ } }
 
PRATIQUE


Quelle est donc la finalite de select() ? Ne puis-je pas simplement lire et ecrire dans les descripteurs chaque fois que je le souhaite ? L'objet de select() est de surveiller de multiples descripteurs simul- tanement et d'endormir proprement le processus s'il n'y a pas d'activite. Il fait ceci tout en vous permettant de gerer de multiples tubes et sockets simultanement. Les programmeurs UNIX se retrouvent souvent dans une situation dans laquelle ils doivent gerer des E/S provenant de plus d'un descripteur de fichier et dans laquelle le flux de donnees est intermittent. Si vous deviez creer une sequence d'appels read() et write(), vous vous retrouveriez potentiellement bloque sur un de vos appels attendant pour lire ou ecrire des donnees a partir/vers un descripteur de fichier, alors qu'un autre descripteur de fichier est inutilise bien qu'il soit disponible pour lire/ecrire des donnees. select() gere efficacement cette situation. Un exemple simple de l'utilisation de select() peut etre trouve dans la page de manuel select(2).
 
EXEMPLE DE REDIRECTION DE PORT


Voici un exemple qui montre mieux l'utilite reelle de select(). Le code ci-dessous consiste en un programme de TCP forwarding qui redirige un port TCP vers un autre. #include #include #include #include #include #include #include #include #include #include #include static int forward_port; #undef max #define max(x,y) ((x) > (y) ? (x) : (y)) static int listen_socket (int listen_port) { struct sockaddr_in a; int s; int yes; if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) { perror ("socket"); return -1; } yes = 1; if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)) < 0) { perror ("setsockopt"); close (s); return -1; } memset (&a, 0, sizeof (a)); a.sin_port = htons (listen_port); a.sin_family = AF_INET; if (bind (s, (struct sockaddr *) &a, sizeof (a)) < 0) { perror ("bind"); close (s); return -1; } printf ("accepting connections on port %d\n", (int) listen_port); listen (s, 10); return s; } static int connect_socket (int connect_port, char *address) { struct sockaddr_in a; int s; if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) { perror ("socket"); close (s); return -1; } memset (&a, 0, sizeof (a)); a.sin_port = htons (connect_port); a.sin_family = AF_INET; if (!inet_aton (address, (struct in_addr *) &a.sin_addr.s_addr)) { perror ("bad IP address format"); close (s); return -1; } if (connect (s, (struct sockaddr *) &a, sizeof (a)) < 0) { perror ("connect()"); shutdown (s, SHUT_RDWR); close (s); return -1; } return s; } #define SHUT_FD1 { \ if (fd1 >= 0) { \ shutdown (fd1, SHUT_RDWR); \ close (fd1); \ fd1 = -1; \ } \ } #define SHUT_FD2 { \ if (fd2 >= 0) { \ shutdown (fd2, SHUT_RDWR); \ close (fd2); \ fd2 = -1; \ } \ } #define BUF_SIZE 1024 int main (int argc, char **argv) { int h; int fd1 = -1, fd2 = -1; char buf1[BUF_SIZE], buf2[BUF_SIZE]; int buf1_avail, buf1_written; int buf2_avail, buf2_written; if (argc != 4) { fprintf (stderr, "Utilisation\n\tfwd \ \n"); exit (1); } signal (SIGPIPE, SIG_IGN); forward_port = atoi (argv[2]); h = listen_socket (atoi (argv[1])); if (h < 0) exit (1); for (;;) { int r, n = 0; fd_set rd, wr, er; FD_ZERO (&rd); FD_ZERO (&wr); FD_ZERO (&er); FD_SET (h, &rd); n = max (n, h); if (fd1 > 0 && buf1_avail < BUF_SIZE) { FD_SET (fd1, &rd); n = max (n, fd1); } if (fd2 > 0 && buf2_avail < BUF_SIZE) { FD_SET (fd2, &rd); n = max (n, fd2); } if (fd1 > 0 && buf2_avail - buf2_written > 0) { FD_SET (fd1, &wr); n = max (n, fd1); } if (fd2 > 0 && buf1_avail - buf1_written > 0) { FD_SET (fd2, &wr); n = max (n, fd2); } if (fd1 > 0) { FD_SET (fd1, &er); n = max (n, fd1); } if (fd2 > 0) { FD_SET (fd2, &er); n = max (n, fd2); } r = select (n + 1, &rd, &wr, &er, NULL); if (r == -1 && errno == EINTR) continue; if (r < 0) { perror ("select()"); exit (1); } if (FD_ISSET (h, &rd)) { unsigned int l; struct sockaddr_in client_address; memset (&client_address, 0, l = sizeof (client_address)); r = accept (h, (struct sockaddr *) &client_address, &l); if (r < 0) { perror ("accept()"); } else { SHUT_FD1; SHUT_FD2; buf1_avail = buf1_written = 0; buf2_avail = buf2_written = 0; fd1 = r; fd2 = connect_socket (forward_port, argv[3]); if (fd2 < 0) { SHUT_FD1; } else printf ("connexion de %s\n", inet_ntoa (client_address.sin_addr)); } } /* NB : lecture des donnees hors bande avant les lectures normales */ if (fd1 > 0) if (FD_ISSET (fd1, &er)) { char c; errno = 0; r = recv (fd1, &c, 1, MSG_OOB); if (r < 1) { SHUT_FD1; } else send (fd2, &c, 1, MSG_OOB); } if (fd2 > 0) if (FD_ISSET (fd2, &er)) { char c; errno = 0; r = recv (fd2, &c, 1, MSG_OOB); if (r < 1) { SHUT_FD1; } else send (fd1, &c, 1, MSG_OOB); } if (fd1 > 0) if (FD_ISSET (fd1, &rd)) { r = read (fd1, buf1 + buf1_avail, BUF_SIZE - buf1_avail); if (r < 1) { SHUT_FD1; } else buf1_avail += r; } if (fd2 > 0) if (FD_ISSET (fd2, &rd)) { r = read (fd2, buf2 + buf2_avail, BUF_SIZE - buf2_avail); if (r < 1) { SHUT_FD2; } else buf2_avail += r; } if (fd1 > 0) if (FD_ISSET (fd1, &wr)) { r = write (fd1, buf2 + buf2_written, buf2_avail - buf2_written); if (r < 1) { SHUT_FD1; } else buf2_written += r; } if (fd2 > 0) if (FD_ISSET (fd2, &wr)) { r = write (fd2, buf1 + buf1_written, buf1_avail - buf1_written); if (r < 1) { SHUT_FD2; } else buf1_written += r; } /* Verifie si l'ecriture de donnees a provoque la lecture de donnees */ if (buf1_written == buf1_avail) buf1_written = buf1_avail = 0; if (buf2_written == buf2_avail) buf2_written = buf2_avail = 0; /* une extremite a ferme la connexion, continue d'ecrire vers l'autre extremite jusqu'a ce que ce soit vide */ if (fd1 < 0 && buf1_avail - buf1_written == 0) { SHUT_FD2; } if (fd2 < 0 && buf2_avail - buf2_written == 0) { SHUT_FD1; } } return 0; } Le programme ci-dessus redirige correctement la plupart des types de connexions TCP y compris les signaux de donnees hors bande OOB transmis par les serveurs telnet. Il gere le probleme epineux des flux de don- nees bidirectionnels simultanes. Vous pourriez penser qu'il est plus efficace d'utiliser un appel fork() et de dedier une tche a chaque flux. Cela devient alors plus delicat que vous ne l'imaginez. Une autre idee est de configurer les E/S comme non bloquantes en utilisant un appel ioctl(). Cela pose egalement probleme parce que vous finissez par avoir des timeouts inefficaces. Le programme ne gere pas plus d'une connexion a la fois bien qu'il soit aisement extensible a une telle fonctionnalite en utilisant une liste chainee de tampons - un pour chaque connexion. Pour l'instant, de nou- velles connexions provoquent l'abandon de la connexion courante.
 
REGLES DE SELECT


De nombreuses personnes qui essaient d'utiliser select() obtiennent un comportement difficile a comprendre et produisent des resultats non portables ou des effets de bord. Par exemple, le programme ci-dessus est ecrit avec precaution afin de ne bloquer nulle part, meme s'il ne positionne pas du tout ses descripteurs de fichiers en mode non blo- quant (voir ioctl(2)). Il est facile d'introduire des erreurs subtiles qui annuleraient l'avantage de l'utilisation de select(), aussi, vais- je presenter une liste de points essentiels a controler lors de l'util- isation de l'appel select(). 1. Vous devriez toujours essayer d'utiliser select() sans timeout. Votre programme ne devrait rien avoir a faire s'il n'y a pas de donnees disponibles. Le code dependant de timeouts n'est en gen- eral pas portable et difficile a deboguer. 2. La valeur n doit etre calculee correctement pour des raisons d'efficacite comme explique plus haut. 3. Aucun descripteur de fichier ne doit etre ajoute a un quelconque ensemble si vous ne projetez pas de verifier son etat apres un appel a select(), et de reagir de facon adequate. Voir la regle suivante. 4. Apres qu'un appel select() ait rendu la main, tous les descrip- teurs de fichiers de tous les ensembles devraient etre verifies pour voir s'ils sont prets. 5. Les fonctions read(), recv(), write(), et send() ne lisent ou n'ecrivent pas forcement la quantite totale de donnees speci- fiee. Si elles lisent/ecrivent la quantite totale, c'est parce que vous avez une faible charge de trafic et un flux rapide. Ce n'est pas toujours le cas. Vous devriez gerer le cas ou vos fonctions traitent seulement l'envoi ou la reception d'un unique octet. 6. Ne lisez/N'ecrivez jamais seulement quelques octets a la fois a moins que vous ne soyez absolument sr de n'avoir qu'une faible quantite de donnees a traiter. Il est parfaitement inefficace de ne pas lire/ecrire autant de donnees que vous pouvez en stocker a chaque fois. Les tampons de l'exemple ci-dessus font 1024 octets bien qu'ils aient facilement pu etre rendus plus grands. 7. Les fonctions read(), recv(), write(), et send() tout comme l'appel select() peuvent renvoyer -1 avec errno valant EINTR ou EAGAIN (EWOULDBLOCK). Ces resultats doivent etre correctement geres (cela n'est pas fait correctement ci-dessus.) Si votre programme n'est pas cense recevoir de signal, alors, il est hautement improbable que vous obteniez EINTR. Si votre programme n'a pas configure les E/S en mode non bloquant, vous n'obtien- drez pas de EAGAIN. Neanmoins, vous devriez tout de meme gerer ces erreurs dans un soucis de completude. 8. N'appelez jamais read(), recv(), write(), ou send() avec un tampon de taille nulle. 9. Si les fonctions read(), recv(), write() et send() echouent avec des erreurs autres que celles indiquees en7., ou si l'une des fonctions d'entree renvoie 0, indiquant la fin de fichier, vous ne devriez pas passer ce descripteur de fichier a select(). again. Dans l'exemple ci-dessus, je ferme le descripteur imme- diatement, et ensuite, je le positionne a -1 afin qu'il ne soit pas inclus dans un ensemble. 10. La valeur de timeout doit etre initialisee a chaque nouvel appel a select(), puisque des systemes d'exploitation modifient la structure. Cependant, pselect() ne modifie pas sa structure de timeout. 11. J'ai entendu que le niveau socket Windows ne traite pas cor- rectement les donnees hors bande (OOB). Il ne gere pas non plus les appels select() lorsqu'aucun descripteur de fichier n'est positionne. N'avoir aucun descripteur de fichier positionne est un moyen utile afin d'endormir le processus avec une precision inferieure a la seconde en utilisant le timeout. (Voir plus loin.)
 
EMULATION DE USLEEP


Sur les systemes qui ne possedent pas la fonction usleep(), vous pouvez appeler select() avec un timeout a valeur finie et sans descripteur de fichier de la facon suivante : struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 200000; /* 0.2 secondes */ select (0, NULL, NULL, NULL, &tv); Le fonctionnement n'est cependant garanti que sur les systemes Unix.
 
VALEUR RENVOYEE


En cas de succes, select() renvoie le nombre total de descripteurs de fichiers encore presents dans les ensembles de descripteurs de fichiers. En cas de timeout echu, la valeur de retour sera zero. Les descrip- teurs de fichiers devraient tous etre vides (mais peuvent ne pas l'etre sur certains systemes). Une valeur de retour egale a -1 indique une erreur, errno est alors positionne de facon adequate. En cas d'erreur, les ensembles renvoyes et le contenu de la structure de timeout sont indefinis et ne devraient pas etre exploites. pselect() ne modifie cependant jamais ntimeout.
 
NOTES


De facon generale, tous les systemes d'exploitation qui gerent les sockets, implantent egalement select(). De nombreux types de pro- grammes deviennent extremement compliques sans cette fonction. select() peut etre utilise pour resoudre de facon portable et efficace de nombreux problemes que des programmeurs naifs essaient de resoudre de maniere plus compliquee avec des threads, des forks, des IPCs, des signaux, des memoires partagees et ainsi de suite. L'appel systeme poll(2) a les memes fonctionnalites que select(), et est quelque peu plus efficace lors de la surveillance d'ensembles de descripteurs de fichiers parsemes. Il est aujourd'hui largement disponible mais etait considere historiquement comme moins portable que select(). L'API epoll(7), specifique a Linux, fournit une interface plus efficace que select(2) et poll(2) pour la surveillance d'un grand nombre de descripteurs de fichiers.
 
VOIR AUSSI


accept(2), connect(2), ioctl(2), poll(2), read(2), recv(2), select(2), send(2), sigprocmask(2), write(2), sigaddset(3), sigdelset(3), sigemptyset(3), sigfillset(3), sigismember(3), epoll(7)
 
TRADUCTION


Ce document est une traduction realisee par Stephan Rafin le 16 juin 2002 et revisee le 7 decem- bre 2006. L'equipe de traduction a fait le maximum pour realiser une adaptation francaise de qualite. La version anglaise la plus a jour de ce document est toujours consultable via la commande : LANG=C man 2 select_tut . N'hesitez pas a signaler a l'auteur ou au traducteur, selon le cas, toute erreur dans cette page de manuel. Linux 2.4 21 octobre 2001 SELECT_TUT(2)


 Dernières recherches
Man  en anglais Man select_tut en anglaisMan  en français Man select_tut en français
Man  en anglais Man wait en anglaisMan  en français Man wait en français
Man  en anglais Man mmap en anglaisMan  en français Man mmap en français
Man  en anglais Man expr en anglaisMan  en français Man expr en français
Man  en anglais Man write en anglaisMan  en français Man write en français
Man  en anglais Man forkpty en anglaisMan  en français Man forkpty en français
Man  en anglais Man grantpt en anglaisMan  en français Man grantpt en français
Man  en anglais Man tr en anglaisMan  en français Man tr en français
Man  en anglais Man sh en anglaisMan  en français Man sh en français
Man  en anglais Man shmctl en anglaisMan  en français Man shmctl en français
Man  en anglais Man ld en anglaisMan  en français Man ld en français
Man  en anglais Man connect en anglaisMan  en français Man connect en français
Man  en anglais Man confstr en anglaisMan  en français Man confstr en français
Man  en anglais Man master en anglaisMan  en français Man master en français
Man  en anglais Man wc en anglaisMan  en français Man wc en français

 Recherche

Dans ce moteur de recherche, vous pouvez taper directement votre besoin, en une phrase normale, humaine.
Exemple : vous cherchez comment remplacer un mot par un autre dans tous les fichiers d'un certain dossier. Vous pouvez écrire "Comment remplacer un mot par un autre dans tous les fichiers d'un dossier". Le moteur vous ramenera les résultats en fonction de leur pertinence.
Vous pouvez bien sûr ne chercher qu'un seul mot-clé, par exemple "find".
 Toutes les lignes de code
Par popularité
Par fonction
Recherche avancée
 Les logiciels SHELL/SSH
Putty
Astuces Bash
Faire du SHELL avec PHP!
 La doc officielle
Les man Linux en français
Les man Linux en anglais
 Proposer vos bash
Partagez vos lignes!
 Les requêtes
Déposer une requête
Voir/répondre à une requête
 Quelques sites interessants
Bons sites pour apprendre
 Rechercher