Pipes and processes

Prerequisite: Write a program to query the user for two input lines. Each input line must be a unix command with arguments allowed. For example, input 1 may be ls -l , and input 2 may be more . Then the program will create a channel and two child processes. The first child process will run the command specified in the first input. It will be output to the pipe instead of the standard output. The second child process will run the command specified in the second input. It will accept its input from the pipe, not from standard input. The parent process will wait until his two children are finished, then all this will happen again. Execution stops when the โ€œ@โ€ character is entered as the first command. Here is the code I have:

 #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main(){ /* Program Termination Symbol */ const char terminate = '@'; /* String delimiter */ const char delimiter = ' '; /* Pipe file ID */ int fileID[2]; /* Parent ID */ int pid1, pid2; /* String token */ char * token, * token2; /* User input */ char * user_input, line[100]; user_input = (char *) malloc(100); /* Unix Commands */ char * command1[10], *command2[10]; for (int i=0; i<10; i++) { command1[i] = (char *)malloc(100*sizeof(char)); command2[i] = (char *)malloc(100*sizeof(char)); } /* Begin main program logic */ printf("Please enter the first command: \n"); user_input = gets(line); while (user_input[0] != terminate) { token = (char *) malloc(100*sizeof(char)); for (int i=0; i<10; i++) { if (i == 0) { token = strtok(user_input, &delimiter); } else { token = strtok(NULL, &delimiter); } if (token != NULL) { strcpy(command1[i], token); } else { command1[i] = 0; } } printf("Please enter the second command: \n"); user_input = gets(line); token2 = (char *) malloc(100*sizeof(char)); for (int i=0; i<10; i++) { if (i == 0) { token2 = strtok(user_input, &delimiter); } else { token2 = strtok(NULL, &delimiter); } if (token2 != NULL) { strcpy(command2[i], token2); } else { command2[i] = 0; } } /* Pipe and execute user commands */ /* Create pipe */ pipe(fileID); /* Create child processes */ pid1 = fork(); if (pid1 != 0) { pid2 = fork(); } /* First child process */ if (pid1 == 0) { dup2(fileID[1], 1); execvp(command1[0], command1); } /* Second child process */ if (pid2 == 0) { dup2(fileID[0], 0); execvp(command2[0], command2); } /* Wait for children to terminate */ wait(&pid1); wait(&pid2); /* Repeat */ printf("Please enter the first command: \n"); user_input = gets(line); } return 0; } 

The problem I am facing is related to my expectations. If I have one and the other that makes sense for me (one wait for one child), then the program freezes after the first channel is completed. If I delete the second wait, the program will start the cycle again, but will not accept keyboard input except input, and will issue segfault. So, with both expectations, entry and exit ...

 Please enter the first command: ls Please enter the second command: more Pipe Pipe.c Pipe.c~ 

... and then it blocks. If I remove the second wait, the input / output will be ...

 Please enter the first command: ls Please enter the second command: more Pipe Pipe.c Pipe.c~ Please enter the first command: (I hit enter, nothing else will work) Segmentation fault 

Anyone have any suggestions? This is clearly related to the expectation of two processes, but I do not understand how to handle this.


This program is now 100% functional - thank you very much for your help, everyone! Stack overflow was one of the best resources on the Internet. Thank you so much for taking the time to look at my code and give me your suggestions.

+6
c process pipe
source share
3 answers

I agree with everything that torak said, but to solve your problem you need to close your pipes. I think you hung because the pipe is still open.

So, in the parent element, right before the โ€œexpectationsโ€, I would close the pipes.

 close(fileID[0]); close(fileID[1]); wait(&pid_status); wait(&pid_status); 

Then, right before each execvp, I would close the ends of the channel that the child would not use:

 close(fileID[0]); dup2(fileID[1], 1); execvp(command1[0], command1); close(fileID[1]); dup2(fileID[0], 0); execvp(command2[0], command2); 

This should solve your hanging. In addition to the suggestions made by torak, I would also recommend fgets instead of preventing buffer overflows.

+5
source share

A few things. Not sure if they are the cause of your problems, but still need to consider before submitting your homework.

  • I do not think you are using wait properly. According to http://linux.die.net/man/2/wait , it does not accept the pid pointer as an argument.

  • Each time in the loop, you call malloc for token and token2 , but I do not see the corresponding memory release.

  • You have created a single monolithic function. Good coding practice would suggest breaking it down into a set of routines

  • Finally, and possibly related to clause 3, two lines of code appear twice in your code. Again, this is not a mistake, but unnecessary duplication and inelegant.

    printf ("Please enter the first command: \ n"); user_input = gets (string);

+4
source share

First of all, you call wait from child processes [edit: no, you're not, since every child calls execvp].

In addition, wait does not accept a pointer to a child pid, but to a variable in which the status of the process will be written (which means that you throw away the child pid).

Finally, try using waitpid with the "WNOHANG" option. It will not hang, you can put both loops while you are doing other things, and you can check if the child processes have completed checking state variables. man waitpid .

+1
source share

All Articles