Zoeken…


Invoering

Buizen zijn een mechanisme voor unidirectionele interprocessen of interthread-communicatie binnen het bereik van een enkele machine. Logischerwijs bestaat een pipe uit twee verbonden termini, een waarnaar gegevens kunnen worden geschreven en een andere waaruit die gegevens vervolgens kunnen worden gelezen, met een gegevensbuffer daartussen zodat schrijven en lezen niet synchroon hoeven te zijn. Pijpen moeten worden onderscheiden van mantelpijpleidingen , die een toepassing van pijpen zijn.

Basis creatie en gebruik

Anonieme pijpen, of simpelweg pijpen, zijn door de kernel beheerde objecten die worden blootgesteld aan processen als een paar bestandsdescriptors, een voor het lees-eindpunt en een voor het schrijf-eindpunt. Ze worden gemaakt via de functie pipe (2):

int pipefds[2];
int result;

result = pipe(pipefds);

Bij succes neemt pipe() de descriptor voor het leesuiteinde van de pijp op bij index 0 van de verstrekte array, en de descriptor voor het schrijfeinde bij index 1; deze indices zijn analoog aan de conventionele bestandsdescriptornummers voor de standaardstreams.

Nadat een pipe is gemaakt, gebruikt men POSIX I / O-functies om naar het schrijfeinde te schrijven of vanaf het leeseinde te lezen:

Proces 1:

ssize_t bytes_written = write(pipefds[1], "Hello, World!", 14);

Proces 2:

char buffer[256];
ssize_t bytes_read = read(pipefds[0], buffer, sizeof(buffer));

Als alternatief kan men in plaats daarvan fdopen() een van de beide buiseinden in een wikkel FILE ten gebruike met C stdio functies:

FILE *write_end = fdopen(pipefds[1]);

if (write_end) {
    fputs("Hello, World!");
}

Pijpen hebben eindige I / O-buffers en gewone beschrijvingen naar pijpen met volledige buffers zullen blokkeren. Pijpen zijn daarom geen veilig mechanisme voor een draad om met zichzelf te communiceren, alsof de draad die naar een pijp schrijft ook de enige is die ervan leest, en zodra een schrijfblokkering die draad vastloopt.

Een pijp blijft bestaan zolang elk proces een open bestandsbeschrijving heeft voor elk van de pijpeinden. De functie close (2) kan worden gebruikt om een pijpeinde te sluiten dat wordt weergegeven door een bestandsdescriptor, en fclose (3) kan worden gebruikt om een pijpeinde te sluiten via een FILE dat eromheen is gewikkeld.

Een pijp maken naar een kindproces

Bestandsdescriptors en FILE objecten zijn bronnen per proces die zelf niet tussen processen kunnen worden uitgewisseld via gewone I / O. Daarom moeten, om twee verschillende processen via een anonieme pijp te laten communiceren, een of beide deelnemende processen een open pijpeinde erven van het proces dat de pijp heeft gecreëerd, wat daarom het ouderproces of een verder verwijderde voorouder moet zijn. Het eenvoudigste geval is het geval waarin een ouderproces wil communiceren met een kindproces.

Omdat het onderliggende proces de benodigde open bestandsbeschrijving van de bovenliggende moet erven, moet de pijp eerst worden gemaakt. De ouder vorken dan. Gewoonlijk zal elk proces strikt een lezer of strikt een schrijver zijn; in dat geval moet elk het buiseinde sluiten dat het niet van plan is te gebruiken.

void demo() {
    int pipefds[2];
    pid_t pid;

    // Create the pipe
    if (pipe(pipefds)) {
        // error - abort ...
    }

    switch (pid = fork()) {
        case -1:
            // error - abort ...
            break;
        case 0:   /* child */
            close(pipefds[0]);
            write(pipefds[1], "Goodbye, and thanks for all the fish!", 37);
            exit(0);
        default:  /* parent */
            close(pipefds[1]);

            char buffer[256];
            ssize_t nread = read(pipefds[0], sizeof(buffer) - 1);

            if (nread >= 0) {
                buffer[nread] = '\0';
                printf("My child said '%s'\n", buffer);
            }

            // collect the child
            wait(NULL);

            break;
    }
}

Het verbinden van twee onderliggende processen via een pijp

Het verbinden van twee kindprocessen via een pijp wordt uitgevoerd door elk van twee kinderen via verschillende uiteinden van dezelfde pijp met de ouder te verbinden. Meestal zal de ouder geen partij zijn in het gesprek tussen de kinderen, dus sluit het zijn kopieën van beide pijpeinden.

int demo() {
    int pipefds[2];
    pid_t child1, child2;

    if (pipe(pipefds)) {
        // error - abort ...
    }

    switch (child1 = fork()) {
        case -1:
            // error - abort
            break;
        case 0:   /* child 1 */
            close(pipefds[0]);
            write(pipefds[1], "Hello, brother!", 15);
            exit(0);
        default:  /* parent */
            // nothing
    }

    switch (child1 = fork()) {
        case -1:
            // error - abort
            break;
        case 0:   /* child 2 */
            char buffer[256];
            ssize_t nread;

            close(pipefds[1]);
            nread = read(pipefds[0], buffer, sizeof(buffer) - 1);
            if (nread < 0) {
                // handle error
            } else {
                buffer[nread] = '\0';
                printf("My brother told me '%s'\n", buffer);
            }
            exit(0);
        default:  /* parent */
            // nothing
    }

    // Only the parent reaches this point
    close(pipefds[0]);
    close(pipefds[1]);
    if (child1 >= 0) {
        wait(NULL);
        if (child2 >= 0) {
            wait(NULL);
        }
    }
}

Een pijplijn in shell-stijl maken

Een shell-stijl pijplijn bestaat uit twee of meer processen, elk met zijn standaard output verbonden met de standaard input van de volgende. De output-naar-input-verbindingen zijn op pijpen gebouwd. Om een pijplijnachtige set processen tot stand te brengen, maakt men dup2 zoals beschreven in een ander voorbeeld, en gebruikt bovendien de functie dup2 (2) om elk pijpeinde te dupliceren naar de juiste standaardbestandsdescriptor van het proces. Het is over het algemeen een goed idee om vervolgens de oorspronkelijke pipe-end file descriptor te sluiten, vooral als, zoals vaak het geval is, het kind van plan is om daarna een ander commando uit te voeren.

// Most error handling omitted for brevity, including ensuring that pipe ends are
// always closed and child processes are always collected as needed; see other
// examples for more detail.
int demo() {
    int pipefds[2];
    pid_t child1 = -1, child2 = -1;

    pipe(pipefds);

    switch (child1 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 1 */
            close(pipefds[0]);
            dup2(pipefds[1], STDOUT_FILENO);
            close(pipefds[1]);
            execl("/bin/cat", "cat", "/etc/motd", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    switch (child2 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 2 */
            close(pipefds[1]);
            dup2(pipefds[0], STDIN_FILENO);
            close(pipefds[0]);
            execl("/bin/grep", "grep", "[Ww]ombat", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    // Only the parent reaches this point
    close(pipefds[0]);
    close(pipefds[1]);
    wait(NULL);
    wait(NULL);
}

Een van de meest voorkomende fouten bij het opzetten van een pijpleiding is om te vergeten dat een of meer van de betrokken processen de pijpeinden sluiten die niet worden gebruikt. Als vuistregel moet het eindresultaat zijn dat elk pijpeinde eigendom is van exact één proces en alleen open is in dat proces.

In een geval zoals de demo-functie hierboven, zal het mislukken van het tweede kind of van de ouder om pipefds[1] te sluiten pipefds[1] leiden dat het tweede kind blijft hangen, want grep blijft op invoer wachten totdat het EOF ziet, en dat zal niet zijn waargenomen zolang het schrijfuiteinde van de pijp open blijft in elk proces, zoals het ouderproces of het tweede kind zelf.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow