IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Bibliothèques, systèmes et outils C Discussion :

Fonction bloquante et non bloquante


Sujet :

Bibliothèques, systèmes et outils C

  1. #1
    Membre régulier
    Homme Profil pro
    Lycéen
    Inscrit en
    mars 2014
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : mars 2014
    Messages : 76
    Points : 72
    Points
    72
    Par défaut Fonction bloquante et non bloquante
    Bonjour,

    je souhaite comprendre un peu mieux le mécanisme des fonctions bloquantes / non bloquantes.

    Au niveau des fonctions bloquantes : comment est-ce que ça fonctionne au niveau du kernel ? Ce que j'entends par là, c'est quand il y a une fonction bloquante, que se passe-t-il ? Est-ce que le kernel endort le processus et le réveil à l'aide d'un signal quand on en a besoin ?

    Et par exemple si je rends non bloquant une fonction bloquante. Et que je boucle dessus pour la vérifier, Si je comprends bien là je vais utiliser plus de ressources ? Et de temps CPU ?

    Merci de bien vouloir m'aider à mieux comprendre.

  2. #2
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    mai 2004
    Messages
    9 904
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : mai 2004
    Messages : 9 904
    Points : 27 035
    Points
    27 035
    Par défaut
    Bonjour,

    (tout ceci est sujet à discussion selon les approximations que je fais, et le fait que le week-end approche à grand pas).

    Dans le cas d'un programme "classique", tout est bloquant, c'est à dire que tu n'as qu'un seul processus, qui se voit attribuer des quantums de temps sur le processeur -- par exemple 20ms. C'est à dire qu'il va passer 20ms à exécuter des instructions sur le processeur, avant d'être remis à la fin de la file d'attente, et ainsi de suite (on parle d'ordonnancement).

    Dans le cas où un programme a besoin d'exécuter quelque chose en arrière-plan, il faut créer un 2nd processus (ou thread), et à partir de là, chacun vit sa vie, chacun est ordonnancé indépendamment, et ainsi de suite (avec les particularités des threads qui font partie du même processus et partagent une partie des informations).

    Maintenant, si tu fais un appel non-bloquant, c'est que tu dois (par exemple) lire quelque chose sur une entrée (une socket par exemple). Dans ce cas, le plus souvent, on fait un appel à une fonction qui nous dit si la source de lecture est prête (c'est à dire qu'il y a des données à lire) ou non. Par exemple, c'est le cas de select() que tu appelles avant de lire sur une socket.
    Si tu ne fais pas ce genre d'appel, tu vais faire (par exemple) un read() bloquant, et donc consommer pleins de ressources pour rien.

    Je ne sais pas si je répond à ta question ou pas ?
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  3. #3
    Membre régulier
    Homme Profil pro
    Lycéen
    Inscrit en
    mars 2014
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : mars 2014
    Messages : 76
    Points : 72
    Points
    72
    Par défaut
    Merci pour ta réponse,

    mais je ne comprends pas trop..
    Tu dis que dans le cas de fonctions non bloquante, il faut voir ça comme un second processus. Mais, par exemple, dans le cas d'un serveur multi-utilisateurs.. Si celui-ci est fait avec un fork est des appels bloquants, je pensais avoir compris que c'était moins gourmand, qu'un serveur sans fork mais avec des fonctions non bloquantes, qui vérifie tout le temps si une connexion est entrante par exemple.

  4. #4
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    mai 2004
    Messages
    9 904
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : mai 2004
    Messages : 9 904
    Points : 27 035
    Points
    27 035
    Par défaut
    J'ai édité ma réponse, j'avais un peu mélangé.

    Maintenant, si tu prends un serveur multi-utilisateurs, tu peux avoir 2 cas :

    Mono-processus (ou mono-thread) : tu fais le traitement pour le client 1 (C1), et les autres attendent. Puis tu traites la requête de C2, et ainsi de suite. Si tes traitements prennent du temps, tu vas avoir des soucis de temps de réponse, et donc tu vas commencer à penser à traiter partiellement la demande de C1, puis traiter celle de C2, puis revenir à C1, etc...

    Multi-processus (ou multi-thread) : tu as N processus, qui peuvent chacun servir M clients, mais ça prend plus de ressources, c'est plus compliqué à gérer, à maintenir, ...

    Et dans tous les cas, si un (ou plusieurs) processus n'a rien à faire, soit il est en attente active (il consomme des ressources pour rien), soit tu décides de les faire dormir un peu, et dans ce cas, oui, un signal est armé, qui est déclenché à la fin de la période de sommeil (enfin ça dépend de comment tu t'endors, mais c'est le mécanisme utilisé par la fonction sleep() par exemple).

    Suis-je plus clair ?
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  5. #5
    Membre régulier
    Homme Profil pro
    Lycéen
    Inscrit en
    mars 2014
    Messages
    76
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : mars 2014
    Messages : 76
    Points : 72
    Points
    72
    Par défaut
    Bonjour,

    oui merci, je viens de mieux comprendre au niveau du bloquant / non bloquant !

  6. #6
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    décembre 2015
    Messages
    1 254
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : décembre 2015
    Messages : 1 254
    Points : 6 096
    Points
    6 096
    Par défaut
    Bonjour,

    • Une fonction bloquante va rendre le CPU au moment du blocage donc ne consomme pas de CPU quand elle est attente.
    • Un mécanisme non bloquant est souvent plus optimum, il consiste à utiliser des threads qui se mettent en attente (donc sur une fonction bloquante), pour ensuite effectuer le traitement demandé en appelant des fonctions non bloquantes.
    • On peut aussi pour ne pas bloquer, créer un thread à chaque demande. C'est de loin, la solution la moins optimale en CPU car il faut beaucoup de ressource pour créer un thread.

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    septembre 2007
    Messages
    7 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2007
    Messages : 7 245
    Points : 23 235
    Points
    23 235
    Par défaut
    Bonsoir,

    Pour compléter ce qui dit gangsoleil, une fonction bloquante est une fonction qui ne rend pas la main au programme qui l'a appelée tant que l'opération qu'elle doit remplir n'est pas terminée. Ça peut être le cas de fgetc() par exemple. Si c'est l'entrée standard qu'on lit, et que cette entrée standard correspond au clavier du terminal, alors le programme va rester bloqué à la ligne qui appelle fgetc() tant que l'utilisateur n'aura pas tapé une touche au clavier. Lorsque que cela sera fait, la fonction retournera le caractère correspondant à la touche en question et le programme poursuivra normalement son exécution. D'une certaine manière, c'est le comportement normal a priori de toute fonction, mais la plupart d'entre elles sont indépendantes de l'extérieur et s'attachent donc à remplir leur tâche le plus vite possible.

    Les fonctions non bloquantes sont des fonctions qui sont faites pour rendre la main au programme avec un code d'erreur si jamais la donnée demandée est indisponible plutôt que l'attendre. Ce code est généralement EAGAIN sur systèmes UNIX ou POSIX. Dans le cas de fgetc(), la fonction renverra EOF si le buffer est vide et le code d'erreur se trouvera dans errno.

    Une fonction ne peut effectivement, in fine, être bloquante qu'au niveau du kernel. Et effectivement, si c'est le cas, alors le noyau considérera le processus comme en sommeil jusqu'à nouvel ordre (plus précisément : jusqu'à ce que la condition de déblocage soient remplie et que le processus réordonnancé ait attendu son tour) et accordera le temps machine à d'autres tâches. Par contre, ce n'est pas un signal qui va réveiller le processus : c'est le kernel lui-même qui se chargera de remettre le processus dans l'état runnable (« R ») et qui renverra la donnée demandée si nécessaire.

    En revanche, et c'est relativement important : un signal reçu va effectivement « réveiller » un processus s'il est sommeil, ne serait-ce que pour être traité, et cela va également concerner les processus qui sont en attente d'une fonction bloquante, ce qui signifie qu'un signal va systématiquement faire échouer un appel système bloquant. Ça va être notamment le cas avec sleep() dont on pourrait penser qu'il ne peut échouer, et cela précipitera la reprise de l'exécution avant le délai imparti. Les programmeurs doivent en tenir compte, surtout qu'il suffit à n'importe quel utilisateur ou processus d'envoyer un signal non destructeur comme SIGCHLD pour provoquer ce comportement. Les systèmes BSD ont pris le parti de relancer les appels systèmes ainsi interrompus mais cela n'est pas sans poser certains problèmes (ils sont bien « relancés »). sigaction() propose le flag SA_RESTART pour émuler ce comportement également, mais il est préférable de prendre correctement en charge l'échec éventuel de tout appel système au sein de son propre programme, généralement avec une boucle principale.

    Pour les serveurs multi-utilisateurs enfin : c'est le cas typique où ce système jusque là bien rodé atteint ses limites. Il est évident qu'on ne peut être en attente de deux appels systèmes à la fois et on ne peut pas se permettre de tourner perpétuellement en boucle pour faire du polling sur les appels systèmes jusqu'à ce que l'un d'eux aboutisse. L'avantage est les rares appels qui soient vraiment bloquants concernent à 99 % les entrées/sorties (accès disques, lecture/écriture fichier, clavier, écran ou même réseau). Celles-ci se font avec des opérations type read/write et donc quasi-systématiquement avec des descripteurs de fichiers ouverts par open(). L'exception à ce modèle concerne justement les attentes passives type sleep().

    Il faut donc demander au kernel de nous placer en état de sommeil jusqu'à ce qu'au moins un des descripteurs de fichiers surveillés devienne non bloquant. Et pour cela, il nous faut un appel système dédié. Cet appel est select(). Cet appel a été décliné en quelques variantes, à savoir poll(), puis pselect() et ppoll(). Ces appels te permettent en outre de passer un paramètre timeout qui leur demande de rendre la main dans tous les cas si jamais aucun descripteur ne s'est débloqué au bout d'un temps imparti. D'une certaine manière, ceci te permet d'ajouter l'équivalent d'un sleep() aux événements à attendre.

    Gérer un mini-serveur IRC, par exemple, est un exercice typique pour apprendre à les manipuler. Tu peux jeter un œil à ces anciennes discussions pour creuser un peu le sujet :

    http://www.developpez.net/forums/d11...interruptions/
    http://www.developpez.net/forums/d10...rt-l-utiliser/

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Fonction non bloquante
    Par NeoGeoo dans le forum Langage
    Réponses: 7
    Dernier message: 21/05/2009, 15h46
  2. [D2006][Socket]Mode bloquant vs non-bloquant
    Par femtosa dans le forum Delphi
    Réponses: 5
    Dernier message: 05/09/2007, 14h37
  3. Réponses: 6
    Dernier message: 09/08/2006, 15h45
  4. Réponses: 1
    Dernier message: 05/01/2006, 00h26
  5. [API] Communication série NON-bloquante : OVERLAPPED/Thread
    Par Rodrigue dans le forum C++Builder
    Réponses: 2
    Dernier message: 07/11/2003, 13h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo