I tried to run the following simple code:
int main(int argc, char* argv[]) {
int fds[2];
pipe(fds);
int pid1, pid2;
if ((pid1 = fork()) == 0){
close(fds[0]); // close read end
dup2(fds[1], 1); // connect write end
char *argv1[2] = {"echo", "hi"};
execvp(argv1[0], argv1);
}
if ((pid2 = fork()) == 0){
close(fds[1]); // close write end
dup2(fds[0], 0); // connect read end
char *argv2[2] = {"wc", "-c"};
execvp(argv2[0], argv2);
}
int status;
waitpid(pid1, &status, 0);
printf("pid=%d terminated with status=%d\n", pid1, status);
waitpid(pid2, &status, 0);
printf("pid=%d terminated with status=%d\n", pid2, status);
return 0;
}
I was expecting both the echo and wc to terminate, but the wc never does.
The output I get for the above code is just something like below (and the main process gets suspended waiting for the wc to finish).
pid=2802 terminated with status=0
Any ideas why the second forked process doesn't finish?
I have checked, by trying to read from fds[0] after a short sleep at the main process, that the wc command has indeed read the hi message from the echo.
You need to close all of the write ends of the pipe. There are 4 copies of that file descriptor, and wc is not going to terminate until all of them are closed. The echo will close both of its copies when it terminates, but it's best practice to explicitly close the unused copy before you exec with:
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
You need to also close that file descriptor in the parent before you call waitpid
Related
Here is my pipe to pipe function:
int *pip is the pipe that I want to read from
char **cmd is the command that I want to execute
char **envp points to the environment variables
I want to take the pipe (pip) as STDIN with dup2
and take the new pipe (fd) as STDOUT also with dup2
The problem is that when it comes to execve the command is executed but the program is stuck in an infinite loop
int *pipe_to_pipe(int *pip, char **cmd, char **envp) {
int *fd;
int pid;
fd = (int *)malloc(sizeof(int) * 2);
if (!fd || pipe(fd) == -1)
error(1);
pid = fork();
if (pid == -1)
error(1);
if (pid == 0)
{
dup2(pip[0], STDIN_FILENO);
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(pip[1]);
execve((command_path), cmd, envp);
exit(0);
}
wait(NULL);
free(pip);
return (fd);
}
As an example, if I have "hello world" in my int *pip
as a char **cmd I have cmd[0] = "cat", cmd[1] = "-e".
When I make execve(path, cmd, envp);
the program cat "hello world" and infinite loop here.
How can I fix this?
The problem comes from the fact that I didn't close the pipe "pip" in the parent process adding those lines that would be executed by the parent process fix everything
if (pid != 0){
close(pip[0]);
close(pip[1]);
wait(NULL);
}
Otherwise it fixes my problem I don't understand why if someone can explain to me I will be gratefull
Thanks to everyone that answered me <3
I want to simulate the pipe command : ls | sort | wc -l by writing some code but I comfront a strange problem ( I can solve it by remove the line but I don't know the reason why it is an error)
When I remove the last wait command it run exactly and finish. But when I add this, it will wait infinity .
int main()
{
int pfds[2];
int pfds1[2];
pipe(pfds); // create a pipe
pid_t pid = fork(); // create child proess
if (pid == 0) // child process
{
dup2(pfds[1],1); // change the stdout to pipe
close(pfds[0]);
execlp("ls", "ls", NULL); // run the ls command
}
else { // main process
wait(0); // wait for the child process
pipe(pfds1); // make a pipe
pid_t npid = fork(); // create child process
if (npid == 0) // child process
{
dup2(pfds[0],0); // change the stdin to pipe
close(pfds[1]);
dup2(pfds1[1], 1); // get data from pipe
close(pfds1[0]);
execlp("sort", "sort", NULL); // run sort command ( combine with above is ls | sort )
}
else { // main process
wait(0); // this line will make infinity wait
dup2(pfds[0], 0);
close(pfds[1]);
execlp("wc", "wc", "-l", NULL); // run wc -l command
}
}
return 0;
}
You aren't closing fds you should be closing. Remember that a file handle is only closed once all file descriptors to it are closed. For example, if you're not careful, it's possible for ls to have completed but sort not realizing this because of a missing close.
Also, your code is susceptible to deadlocks. If ls were to output a large amount, it would write to the first pipe until it's full and block. The parent, rather than emptying the pipe, is waiting for the child to end. Deadlock.
Make sure to close fds you don't need anymore, and start every children before waiting for any to end.
int pfds1[2]; pipe(pfds1);
pid_t pid1 = fork();
if (!pid1) {
close(pfds1[0]);
dup2(pfds1[1], 1); close(pfds1[1]);
execlp("ls", "ls", NULL);
}
close(pfds1[1]);
int pfds2[2]; pipe(pfds2);
pid_t pid2 = fork();
if (!pid2) {
close(pfds2[0]);
dup2(pfds1[0], 0); close(pfds1[0]);
dup2(pfds2[1], 1); close(pfds2[1]);
execlp("sort", "sort", NULL);
}
close(pfds2[1]);
pid_t pid3 = fork();
if (!pid3) {
dup2(pfds2[0], 0); close(pfds2[0]);
execlp("wc", "wc", "-l", NULL);
}
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
waitpid(pid3, NULL, 0);
I am working to make a shell like bash, but i have trouble solving heredoc << so i made a test code as simple as possible for this question.
void pipeline()
{
int i = 0;
int fd[2];
pid_t pid;
int fdd = 0;
while (i < 2)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
//dup2(fd[1],1); if i dup in the first pipe cat dont finalize
if (i == 0)
dup2(fd[0],0);
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[0]);
close(fd[1]);
dup2(fdd, 0);
if (i == 0)
execlp("cat", "cat", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
else
{
close(fd[1]);
fdd = fd[0];
wait(NULL);
i++;
}
}
}
int main(int *argc, char **argv, char **env)
{
pipeline();
}
I know that cat and grep need an EOF to run; what I'm doing is writing in stdin and running cat, but my question is: how do I save stdout for grep without duping stdout on the first pipe?
If I dup on dup2(fd[1],1) cat does not work in the first pipe, could someone help me out to make this code work? And make it as similar to bash heredoc as well if possible.
how do I save stdout for grep without duping stdout on the first pipe?
I'd rearrange the creation of the child processes from rightmost to leftmost - then grep is created first and can output to the initial output descriptor. A necessary change is to run all child processes before waiting on one as well as before writing, so that there's no deadlock even if the pipe buffer wouldn't suffice for the heredoc.
void pipeline()
{
int i = 2; // create children from last to first
int fd[2];
pid_t pid;
int fdd = 1; // output of last child is STDOUT
while (i--)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
dup2(fdd, 1); // child's output
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
if (i == 0)
execlp("cat", "cat", "-A", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
if (fdd != 1) close(fdd); // close if a pipe write end
fdd = fd[1]; // preceding child's output is pipe write end
close(fd[0]);
}
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[1]); // signal EOF to child
while (wait(NULL) > 0) ; // wait for all children
}
I have taken a look at this and also this stack overflow links.
I am having trouble understanding the process for closing write ends of pipes. In the code below, I have 3 processes, one parent, a child of the parent, and a child of the child. I am trying to simulate a pipe for the command - cat xxx | grep 28 | sort. I have written some code for this, and it essentially creates the sorts, "grips"/filters and prints my input, but it hangs at the end. I have to ctrl + c to exit. My code is a little messy, but if you can help me spot the problem that would be nice.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/**
* Executes the command "cat scores | grep Lakers". In this quick-and-dirty
* implementation the parent doesn't wait for the child to finish and
* so the command prompt may reappear before the child terminates.
*
*/
int main(int argc, char **argv)
{
int pipefd[2];
int pipefd2[2];
int pid;
char *cat_args[] = {"cat", "scores", NULL};
char *grep_args[] = {"grep", "28", NULL};
char *sort_args[] = {"sort", NULL};
// make a pipe (fds go in pipefd[0] and pipefd[1])
if (pipe(pipefd) != 0){
return 1;
}
if (pipe(pipefd2) != 0){
return 1;
}
pid = fork();
if (pid < 0)
{
fprintf(stderr, "fork Failed" );
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
int pid2;
pid2 = fork();
if (pid2 < 0){
fprintf(stderr, "fork Failed" );
return 1;
}
else if (pid2 == 0){
// replace standard input with input part of pipe
// close(0);
// close(pipefd[1]);
// close(pipefd2[1]);
dup2(pipefd2[0], 0);
// close unused hald of pipe
close(pipefd2[0]);
close(pipefd[1]);
close(pipefd2[1]);
close(pipefd[0]);
// execute grep
execvp("sort", sort_args);
close(pipefd[1]);
close(pipefd2[1]);
exit(0);
}else{
// replace standard input with input part of pipe
// close(pipefd[1]);
// close(pipefd2[1]);
dup2(pipefd[0], 0);
dup2(pipefd2[1], 1);
// close unused hald of pipe
close(pipefd[0]);
close(pipefd2[1]);
close(pipefd[1]);
close(pipefd2[0]);
// execute grep
execvp("grep", grep_args);
waitpid(pid2, NULL, 0);
close(pipefd[1]);
close(pipefd[0]);
close(pipefd2[1]);
close(pipefd2[0]);
exit(0);
waitpid(pid2, NULL, 0);
}
}
else
{
// close(pipefd[1]);
// close(pipefd2[1]);
dup2(pipefd[1], 1);
// close unused unput half of pipe
close(pipefd[1]);
close(pipefd[0]);
close(pipefd2[1]);
close(pipefd2[0]);
// execute cat
execvp("cat", cat_args);
exit(0);
waitpid(pid, NULL, 0);
}
close(pipefd[1]);
close(pipefd[0]);
close(pipefd2[1]);
close(pipefd2[0]);
}
here is the output I am getting. Not sure it is relevant but as you can see, the result is sorted by team name. It just doesn't terminate.
Houston 44 28 .611
Indiana 45 28 .616
Oklahoma City 44 28 .611
Utah 44 28 .611
^C
Calling execvp replaces the current process image with a new process image. If no error occured, your code will never reach any line after that, so your close() and waitpid() function calls are useless.
EDIT
Here's a fully functional code to your problem. The comments should be self explanatory. Notice that the command executing order is different and I'm waiting for processes to finish.
Reading from an empty pipe will block until there is some data to read or an error occured, so this is not the only solution.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
static void die (const char *msg) {
perror (msg);
exit (EXIT_FAILURE);
}
int main (int argc, char** argv) {
int pipefd[2];
int pid;
char *cat_args[] = {"cat", "scores", NULL};
char *grep_args[] = {"grep", "28", NULL};
char *sort_args[] = {"sort", NULL};
//make a pipe (file descriptor to read is pipefd[0], fd to write is pipefd[1])
if (pipe (pipefd) < 0)
die ("creating a pipe failed");
pid = fork();
if (pid < 0)
die ("fork");
else if (pid == 0) {
//child process
int pipefd2[2]; //only visible to the affected processes
if (pipe (pipefd2) < 0)
die ("pipe");
int pid2;
pid2 = fork();
if (pid2 < 0)
die ("fork");
else if (pid2 == 0) {
//child of child will execute cat command
close (pipefd2[0]); //we only need to write to the second pipe. close its reading end
//first pipe is for communication between parent and grandparent only
close (pipefd[0]);
close (pipefd[1]);
dup2 (pipefd2[1], STDOUT_FILENO); //write the output to the second pipe instead of the standard output
close (pipefd2[1]); //close writing end of second pipe
execvp("cat", cat_args); //execute cat command
die ("execvp should never return");
}
else {
//child process will execute the grep command
close (pipefd2[1]); // we only need to read from the second pipe. close its writing end
close(pipefd[0]); //we won't read from the first pipe
waitpid (pid2, NULL, 0); //wait for cat command to finish
dup2 (pipefd2[0], STDIN_FILENO); //read from the second pipe instead of the stdin
close (pipefd2[0]); //child finished. close reading end of second pipe
dup2 (pipefd[1], STDOUT_FILENO); //write the results of grep command to first pipe instead of standard output
close (pipefd[1]); //we dup2 the output, close the writing end of first pipe
execvp ("grep", grep_args);
die ("execvp should never return");
}
} else {
//parent process will execute the sort command
close (pipefd[1]); //we won't write to the first pipe
waitpid (pid, NULL, 0); //wait for child to write grep output to the first pipe
dup2 (pipefd[0], STDIN_FILENO); //read from the first pipe instead of stdin
close (pipefd[0]); //child finished. close reading end of first pipe
execvp ("sort", sort_args); //execute command
die ("execvp should never return");
}
//exit (EXIT_SUCCESS); we don't need this. the programm will never reach this line
}
Let's say within a program I want to execute two processes, one to execute a ls -al command, then piping the result into the wc command, and displaying the output on the terminal. How can I do this using pipe file descriptors? So far the code I have written:
int main(int argc, char* argv[]) {
int pipefd[2];
int pipefd2[2];
pipe(pipefd2);
if ((fork()) == 0) {
dup2(pipefd2[1], STDOUT_FILENO);
close(pipefd2[0]);
close(pipefd2[1]);
execl("ls", "ls", "-al", NULL);
exit(EXIT_FAILURE);
}
if ((fork()) == 0){
dup2(pipefd2[0], STDIN_FILENO);
close(pipefd2[0]);
close(pipefd2[1]);
execl("/usr/bin/wc", "wc", NULL);
exit(EXIT_FAILURE);
}
close(pipefd[0]);
close(pipefd[1]);
close(pipefd2[0]);
close(pipefd2[1]);
}
An example would be greatly helpful.
Your example code was syntactically and semantically broken (e.g. pipefd2 not decared, confusion between pipefd and pipefd2, etc.) Since this smells like homework, please make sure you understand my annotations below and ask more if you need to. I've omitted error checks on pipe, fork and dup, but they should be there, ideally.
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t ls_pid, wc_pid;
pipe(pipefd);
// this child is generating output to the pipe
//
if ((ls_pid = fork()) == 0) {
// attach stdout to the left side of pipe
// and inherit stdin and stdout from parent
dup2(pipefd[1],STDOUT_FILENO);
close(pipefd[0]); // not using the right side
execl("/bin/ls", "ls","-al", NULL);
perror("exec ls failed");
exit(EXIT_FAILURE);
}
// this child is consuming input from the pipe
//
if ((wc_pid = fork()) == 0) {
// attach stdin to the right side of pipe
// and inherit stdout and stderr from parent
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[1]); // not using the left side
execl("/usr/bin/wc", "wc", NULL);
perror("exec wc failed");
exit(EXIT_FAILURE);
}
// explicitly not waiting for ls_pid here
// wc_pid isn't even my child, it belongs to ls_pid
return EXIT_SUCCESS;
}