Unix channel - reading data from stdin in a child descriptor

I am trying to implement a unix pipeline in c (i.e. execute ls | wc). I found an appropriate solution to my problem ( C Unix Pipes Example ), however I am not sure why a certain part of the sparse code fragment works.

Here is the code:

/* Run WC. */ int filedes[2]; pipe(filedes); /* Run LS. */ pid_t pid = fork(); if (pid == 0) { /* Set stdout to the input side of the pipe, and run 'ls'. */ dup2(filedes[1], 1); char *argv[] = {"ls", NULL}; execv("/bin/ls", argv); } else { /* Close the input side of the pipe, to prevent it staying open. */ close(filedes[1]); } /* Run WC. */ pid = fork(); if (pid == 0) { dup2(filedes[0], 0); char *argv[] = {"wc", NULL}; execv("/usr/bin/wc", argv); } 

In the child process that executes the wc command, although it gives stndin to the file descriptor, it seems that we are not explicitly reading the output generated by ls in the first child process. Thus, it seems to me that ls starts independently and wc works independently, since we do not explicitly use the ls output when wc is executed. How does this code work (i.e., it executes ls | wc)?

+2
source share
1 answer

The code shown is practically for work (it cuts several angles, but it works), because the branched children guarantee that the file descriptor of the executed process will write (in the case of ls ) and read from (in the case of wc ) - the corresponding end of the pipe . You no longer have to do; the standard input is file descriptor 0, so wc with no (filename) arguments is read from standard input. ls always writes to standard output, file descriptor 1, if it does not write an error message.

There are three processes in the code snippet; parent process and two children, one from each fork() . The parent process should also cover both ends of the pipe; he closes only one.

In general, after calling dup() or dup2() in the pipe file descriptor, you must close both ends of the pipe. You leave here because ls generates data and exits; you would not be under any circumstances.

A comment:

 /* Set stdout to the input side of the pipe, and run 'ls'. */ 

is inaccurate; you set stdout to the output side of the channel, not to the input side.

After execv() calls, you should have an error output; if they fail, they return, and the process can lead to chaos (for example, if ls fails, you get two copies of wc .

Sscce

Pay attention to carefully closing both ends of the pipe in each of the processes. The parent process is not used for the channel once it has launched both children. I left the code that closes filedes[1] at an early stage (but removed it from the explicit else block, since the following code was also executed only if else was executed). I could store closes() pairs in each of the three code paths where the files should be closed.

 #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> int main(void) { int filedes[2]; int corpse; int status; pipe(filedes); /* Run LS. */ pid_t pid = fork(); if (pid == 0) { /* Set stdout to the output side of the pipe, and run 'ls'. */ dup2(filedes[1], 1); close(filedes[1]); close(filedes[0]); char *argv[] = {"ls", NULL}; execv("/bin/ls", argv); fprintf(stderr, "Failed to execute /bin/ls\n"); exit(1); } /* Close the input side of the pipe, to prevent it staying open. */ close(filedes[1]); /* Run WC. */ pid = fork(); if (pid == 0) { /* Set stdin to the input side of the pipe, and run 'wc'. */ dup2(filedes[0], 0); close(filedes[0]); char *argv[] = {"wc", NULL}; execv("/usr/bin/wc", argv); fprintf(stderr, "Failed to execute /usr/bin/wc\n"); exit(1); } close(filedes[0]); while ((corpse = waitpid(-1, &status, 0)) > 0) printf("PID %d died 0x%.4X\n", corpse, status); return(0); } 

Output Example:

 $ ./pipes-14312939 32 32 389 PID 75954 died 0x0000 PID 75955 died 0x0000 $ 
+3
source

All Articles