Question

Je suis intéressé par l'écriture des modules de programmes distincts qui fonctionnent comme des fils indépendants que je puisse accrocher ensemble avec des tuyaux. La motivation serait que je pourrais écrire et tester chaque module complètement indépendamment, peut-être même les écrire dans des langues différentes, ou d'exécuter les différents modules sur des machines différentes. Il existe une grande variété de possibilités ici. Je l'ai utilisé la tuyauterie pendant un certain temps, mais je ne suis pas familier avec les nuances de son comportement.

  • Il semble que l'extrémité de réception bloquera attente d'entrée, que je me attends, mais le bloc de fin d'envoyer parfois attendre quelqu'un pour lire du flux?
  • Si j'écris un EOF au flux puis-je conserver continuer à écrire à ce flux jusqu'à ce que je le fermer?
  • Y at-il des différences dans le comportement et les tuyaux nommés sans nom?
  • Est-ce important qui extrémité du tuyau, j'ouvre d'abord avec les pipes nommés?
  • Le comportement des tuyaux cohérents entre les différents systèmes Linux?
  • Est-ce que le comportement des tuyaux dépendent de l'enveloppe que je utilise ou la façon dont je l'ai configuré il?
  • Y at-il d'autres questions que je devrais demander ou les questions que je devrais être au courant si je veux utiliser des tuyaux de cette façon?
Était-ce utile?

La solution

Wow, c'est beaucoup de questions. Voyons voir si je peux couvrir tout ...

  

Il semble que l'extrémité de réception sera   bloc d'attente pour l'entrée, que je   attendre

Vous attendez correctement un appel réel « lire » bloquera jusqu'à ce que quelque chose est là. Cependant, je crois qu'il ya des fonctions C qui vous permettra de « coup d'oeil » à ce que (et combien) attend dans le tuyau. Malheureusement, je ne me souviens pas si cela bloque aussi bien.

  

sera le bloc de fin parfois l'envoi   en attendant que quelqu'un de la lire   courant

Non, l'envoi ne doit jamais bloquer. Pensez aux ramifications si cela était un tuyau à travers le réseau à un autre ordinateur. Voulez-vous attendre (par temps de latence éventuellement élevé) pour l'autre ordinateur pour répondre qu'il a reçu? Maintenant, c'est un cas différent si la poignée de lecteur de la destination a été fermée. Dans ce cas, vous devriez avoir une vérification d'erreur pour gérer cela.

  

Si j'écris un EOF au flux puis-je   garder continuer à écrire à ce flux   jusqu'à ce que je le fermer

Je pense que cela dépend de ce que la langue que vous utilisez et la mise en œuvre des tuyaux. En C, je dirais que non. Dans un shell linux, je dirais que oui. Quelqu'un d'autre avec plus d'expérience devrait répondre.

  

Y at-il des différences dans le comportement   nommé et tuyaux sans nom?   Pour autant que je sache, oui. Cependant, je n'ai pas beaucoup d'expérience avec le nom vs sans nom. Je crois que la différence est:

  • simple vs communication bidirectionnelle
  • Lecture et écriture à « dans » et cours d'eau « out » d'un fil
  

est-il important qui extrémité du tuyau I   ouvrir d'abord avec les pipes nommés?

En général non, mais vous pouvez rencontrer des problèmes lors de l'initialisation en essayant de créer et relier les fils entre eux. Vous aurez besoin d'avoir un fil conducteur qui crée tous les sous-fils et leurs tubes respectifs permet de synchroniser entre eux.

  

est le comportement des tuyaux cohérents   entre les différents systèmes linux?

Encore une fois, cela dépend de quelle langue, mais en général oui. Jamais entendu parler de Posix? C'est la norme (au moins pour Linux, Windows ne lui est propre chose).

  

t-elle le comportement des conduites dépendent   sur la coque que je utilise ou la façon dont je l'ai   configuré il?

Cela devient en un peu plus d'une zone grise. La réponse devrait être pas car la coquille devrait être essentiellement faire des appels système. Cependant, tout jusqu'à ce point est en jeu.

  

Y at-il d'autres questions que je devrait   se demander

Les questions que vous avez posées montre que vous avez une compréhension correcte du système. Gardez la recherche et se concentrer sur ce niveau, vous allez travailler sur (shell, C, etc.). Vous apprendrez beaucoup plus en juste essayer cependant.

Autres conseils

est basé sur un système de type UNIX; Je ne connais pas le comportement spécifique des versions récentes de Windows.

Il semble que l'extrémité de réception bloquera attente d'entrée, que je me attends, mais le bloc de fin d'envoyer parfois attendre quelqu'un pour lire du flux?

Oui, bien sur une machine moderne, il ne peut pas arriver souvent. Le tuyau comporte un tampon intermédiaire qui peut remplir potentiellement vers le haut. Dans le cas contraire, le côté écriture du tube sera en effet bloquer. Mais si vous pensez à ce sujet, il n'y a pas beaucoup de fichiers qui sont assez grands pour risquer cela.

Si j'écris un EOF au flux puis-je conserver continuer à écrire à ce flux jusqu'à ce que je le fermer?

Euh, tu veux dire comme un CTRL-D, 0x04? Bien sûr, tant que le flux est configuré de cette façon. Viz.

506 # cat | od -c
abc
^D
efg
0000000    a   b   c  \n 004  \n   e   f   g  \n                        
0000012

Y at-il des différences dans le comportement et les tuyaux nommés sans nom?

Oui, mais ils sont subtils et dépendent de l'implémentation. Le plus important est que vous pouvez écrire à un tube nommé avant l'autre extrémité est en cours d'exécution; avec des tuyaux sans nom, les descripteurs de fichiers sont partagés au cours du processus fork / exec, donc il n'y a aucun moyen d'accéder à la mémoire tampon transitoire sans les processus étant en place.

Est-il important extrémité du tuyau, j'ouvre d'abord avec les pipes nommés?

Non.

Le comportement des tuyaux cohérents entre les différents systèmes linux?

Dans la raison, oui. Tampon tailles etc peuvent varier.

Est-ce que le comportement des tuyaux dépendent de l'enveloppe que je utilise ou la façon dont je l'ai configuré il?

Non. Lorsque vous créez un tuyau, sous les couvertures ce qui se passe est votre processus parent (la coquille) crée un tuyau qui a une paire de descripteurs de fichiers, puis fait un exec fourchette, comme cette pseudo-code:

Parent :

create pipe, returning two file descriptors, call them fd[0] and fd[1]
fork write-side process
fork read-side process

écriture côté :

close fd[0]
connect fd[1] to stdout
exec writer program

Lire côté :

close fd[1]
connect fd[0] to stdin
exec reader program

Y at-il d'autres questions que je devrais demander ou les questions que je devrais être au courant si je veux utiliser des tuyaux de cette façon?

est tout ce que vous voulez faire va vraiment mettre en ligne comme celui-ci? Sinon, vous voudrez peut-être penser à une architecture plus générale. Mais l'idée que d'avoir beaucoup de processus distincts qui interagissent via l'interface « étroite » d'un tuyau est souhaitable est un bon.

[Mise à jour: j'avais les indices de descripteurs de fichiers inversés au début. Ils sont maintenant correct, voir man 2 pipe.]

Dashogun et Charlie Martin a noté, c'est une grande question. Certaines parties de leurs réponses sont inexactes, donc je vais répondre aussi.

  

Je suis intéressé par l'écriture des modules de programmes distincts qui fonctionnent comme des fils indépendants que je puisse accrocher ensemble avec des tuyaux.

Méfiez d'essayer d'utiliser des tuyaux en tant que mécanisme de communication entre les fils d'un même processus. Parce que vous auriez à la fois lire et écrire extrémités du tube ouvert dans un seul processus, vous jamais l'indication EOF (zéro octets).

Si vous étiez vraiment référence aux processus, alors c'est la base de l'approche classique aux outils Unix de construction. La plupart des programmes Unix standard sont des filtres qui lisent l'entrée standard, la transforment en quelque sorte, et d'écrire le résultat sur la sortie standard. Par exemple, tr, sort, grep, et tous les filtres sont cat, pour ne citer que quelques-uns. C'est un excellent modèle à suivre lorsque les données que vous manipulez le permet. Pas toutes les manipulations de données sont propices à cette approche, mais il y a beaucoup qui sont.

  

La motivation serait que je pourrais écrire et tester chaque module complètement indépendamment, peut-être même les écrire dans des langues différentes, ou d'exécuter les différents modules sur des machines différentes.

Les bons points. Sachez qu'il n'y a pas vraiment un mécanisme de conduite entre les machines, mais vous pouvez approcher avec des programmes tels que ou rsh (mieux) ssh. Cependant, en interne, de tels programmes peuvent lire les données locales des tuyaux et envoyer ces données à des machines distantes, mais ils communiquent entre machines sur des sockets, ne pas utiliser des tuyaux.

  

Il existe une grande variété de possibilités ici. Je l'ai utilisé la tuyauterie pendant un certain temps, mais je ne suis pas familier avec les nuances de son comportement.

OK; poser des questions est une (bonne) façon d'apprendre. Expérimenter est une autre, bien sûr.

  

Il semble que l'extrémité de réception bloquera attente d'entrée, que je me attends, mais le bloc de fin d'envoyer parfois attendre quelqu'un pour lire du flux?

Oui. Il y a une limite à la taille d'un tampon de tuyau. Classiquement, c'était assez petite - 4096 ou 5120 étaient des valeurs communes. Vous trouverez peut-être que Linux moderne utilise une plus grande valeur. Vous pouvez utiliser et _PC_PIPE_BUF pour fpathconf() connaître la taille d'un tampon de tuyau. POSIX ne nécessite que la mémoire tampon pour être 512 (qui est, _POSIX_PIPE_BUF est 512).

  

Si j'écris un EOF au flux puis-je conserver continuer à écrire à ce flux jusqu'à ce que je le fermer?

Techniquement, il n'y a aucun moyen d'écrire EOF à un cours d'eau; vous fermez le descripteur de tuyau pour indiquer EOF. Si vous envisagez de contrôle-D ou de contrôle-Z comme un caractère EOF, alors ce ne sont que des personnages réguliers dans la mesure où les tuyaux sont concernés - ils ont seulement un effet comme EOF lorsque tapé à un terminal qui est en cours d'exécution en mode canonique (cuit , ou normal).

  

Y at-il des différences dans le comportement et les tuyaux nommés sans nom?

Oui et non. Les plus grandes différences sont que les tuyaux nommés doivent être mis en place par un processus et ne peuvent être utilisés par ce processus et les enfants qui partagent ce processus comme un ancêtre commun. En revanche, les pipes nommés peuvent être utilisés par des processus précédemment non associés. La prochaine grande différence est une conséquence de la première; avec un tuyau sans nom, vous revenez deux descripteurs de fichiers à partir d'une seule fonction (système) appel à pipe(), mais vous ouvrez un FIFO ou tube nommé en utilisant la fonction régulière open(). (Quelqu'un doit créer un FIFO avec l'appel avant mkfifo() pouvez l'ouvrir, pipes sans nom ne nécessitent pas de telle configuration préalable.) Cependant, une fois que vous avez un descripteur de fichier ouvert, il est précieux peu de différence entre un tuyau et un nom tuyau anonyme.

  

Est-ce que ce qui importe bout du tuyau, j'ouvre d'abord avec les pipes nommés?

Non. Les premiers procès-s pour ouvrir le FIFO (normalement) bloquera jusqu'à ce qu'il ya un processus avec l'autre extrémité ouverte. Si vous l'ouvrez pour la lecture et l'écriture (aconventional mais possible), alors vous ne serez pas bloqué; si vous utilisez le drapeau O_NONBLOCK, vous ne serez pas bloqué.

  

Le comportement des tuyaux cohérents entre les différents systèmes Linux?

Oui. Je n'ai pas entendu parler ou eu des problèmes avec des tuyaux sur l'un des systèmes où je les ai utilisés.

  

Est-ce que le comportement des tuyaux dépendent de l'enveloppe que je utilise ou la façon dont je l'ai configuré il?

Non: les tuyaux et les FIFOs sont indépendants de la coque que vous utilisez

.
  

Y at-il d'autres questions que je devrais demander ou les questions que je devrais être au courant si je veux utiliser des tuyaux de cette façon?

Rappelez-vous simplement que vous devez fermer l'extrémité la lecture d'un tuyau dans le processus qui sera en train d'écrire, et la fin de l'écriture de la conduite dans le processus qui sera en train de lire. Si vous voulez une communication bidirectionnelle sur les tuyaux, utilisez deux tuyaux séparés. Si vous créez des arrangements de plomberie compliquées, méfiez-vous de l'impasse - il est possible. Un pipeline linéaire n'interblocage cependant pas (bien que si le premier processus ne ferme jamais sa sortie, les processus en aval peuvent attendre indéfiniment).


J'ai observé au-dessus et dans les commentaires à d'autres réponses que les tampons de tuyauterie sont classiquement limités aux dimensions assez petites. @Charlie Martin contre-fait remarquer que certaines versions d'Unix ont des tampons de tuyaux dynamiques et ceux-ci peuvent être assez grand.

Je ne suis pas sûr de ceux qu'il a en tête. J'ai utilisé le programme de test qui suit sur Solaris, AIX, HP-UX, Mac OS X, Linux et Cygwin / Windows XP (résultats ci-dessous):

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

static const char *arg0;

static void err_syserr(char *str)
{
    int errnum = errno;
    fprintf(stderr, "%s: %s - (%d) %s\n", arg0, str, errnum, strerror(errnum));
    exit(1);
}

int main(int argc, char **argv)
{
    int pd[2];
    pid_t kid;
    size_t i = 0;
    char buffer[2] = "a";
    int flags;

    arg0 = argv[0];

    if (pipe(pd) != 0)
        err_syserr("pipe() failed");
    if ((kid = fork()) < 0)
        err_syserr("fork() failed");
    else if (kid == 0)
    {
        close(pd[1]);
        pause();
    }
    /* else */
    close(pd[0]);
    if (fcntl(pd[1], F_GETFL, &flags) == -1)
        err_syserr("fcntl(F_GETFL) failed");
    flags |= O_NONBLOCK;
    if (fcntl(pd[1], F_SETFL, &flags) == -1)
        err_syserr("fcntl(F_SETFL) failed");
    while (write(pd[1], buffer, sizeof(buffer)-1) == sizeof(buffer)-1)
    {
        putchar('.');
        if (++i % 50 ==  0)
            printf("%u\n", (unsigned)i);
    }
    if (i % 50 !=  0)
        printf("%u\n", (unsigned)i);
    kill(kid, SIGINT);
    return 0;
}

Je serais curieux d'obtenir des résultats supplémentaires d'autres plates-formes. Voici les tailles que j'ai trouvé. Tous les résultats sont plus grandes que prévu, je dois avouer, mais Charlie et moi peut-être débattre de la signification du terme « assez grand » en matière de tampon tailles.

  • 8196 - HP-UX 11,23 pour IA-64 (fcntl (F_SETFL) n'a pas)
  • 16384 - Solaris 10
  • 16384 - Mac OS X 10.5 (O_NONBLOCK ne fonctionne pas, bien que fcntl (F_SETFL) n'a pas manqué)
  • 32768 - AIX 5.3
  • 65536 - Cygwin / Windows XP (O_NONBLOCK ne fonctionne pas, bien que fcntl (F_SETFL) n'a pas manqué)
  • 65536 - SuSE Linux 10 (et CentOS) (fcntl (F_SETFL) a échoué)

Un point qui ressort de ces essais est que O_NONBLOCK fonctionne avec des tuyaux sur certaines plates-formes et non sur d'autres.

Le programme crée un tuyau, et des fourches. L'enfant ferme l'extrémité d'écriture du tuyau, puis va dormir jusqu'à ce qu'il obtienne un signal - qui est ce que la pause () fait. Le parent se ferme alors la fin lecture du tube, et fixe les drapeaux sur le descripteur d'écriture afin qu'il ne bloque pas sur une tentative d'écriture sur un tuyau plein. Il boucle ensuite, l'écriture d'un caractère à la fois, et l'impression d'un point pour chaque caractère écrit, et un compte et tous les 50 caractères saut de ligne. Quand il détecte un problème d'écriture (mémoire tampon pleine, puisque l'enfant ne lit pas une chose), il arrête la boucle, écrit le décompte final, et tue l'enfant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top