When a parent writes to a child, the read port should be closed. The child process should also close the write port, otherwise the read function will be blocked.
However, in my code, why is it possible to send and receive messages with redundant pipe ports without blocking
if(fork()==0) //child process
{
char buf[7];
close(p1[1]);
close(p2[0]);
read(p1[0],buf,7);
fprintf(1, "%d:received ping %s\n", getpid(),buf);
write(p2[1],buf,7);
close(p2[1]);
}
else{
char buf[7];
close(p1[0]);
close(p2[1]);
write(p1[1],"justts",6);
close(p1[1]);
read(p2[0],buf,7);
fprintf(1, "%d:received pong %s\n", getpid(),buf);
}
result of the code
Related
C newbie here. I am trying to write a simple program in xv6 that utilizes pipes, forks, and file descriptors.
int
main(int argc, char *argv[])
{
int p[2]; // file descriptors for pipe
char recv_buf[5];
pipe(p);
if (fork() == 0) { // child
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
write(p[1], "pong", 5);
close(p[1]);
} else { // parent
write(p[1], "ping", 5);
close(p[1]);
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
}
exit(0);
}
I thought that the program would succeed in printing
$ ./pingpong
$ "3: received ping"
$ "4: received pong"
to the terminal output.
Instead the output comes out as:
$ ./pingpong
$ "3: received ping"
Can anyone explain what's happening here? I thought that each process had its own file descriptor copy, and that read/write would hang the parent process until there is output on the other end of the pipe. So why is it that that the child process is not receiving the "pong" call?
Note that if I add a wait(0) in the parent, the problem goes away.
int
main(int argc, char *argv[])
{
int p[2]; // file descriptors for pipe
char recv_buf[5];
pipe(p);
if (fork() == 0) { // child
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
write(p[1], "pong", 5);
close(p[1]);
} else { // parent
write(p[1], "ping", 5);
wait(0); // this fixes the problem. but why?
close(p[1]);
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
}
exit(0);
}
$ ./pingpong
$ "3: received ping"
$ "4: received pong"
Can anyone explain why the wait(0) causes the program to succeed?
I actually figured out the answer shortly after typing this.
The program with the first program is that inside the parent block I am writing to the output file descriptor, then reading from the input file descriptor immediately after, which means the parent process doesn't hang.
The result is that the parent process exits before the child process is able to make any read/writes.
if (fork() == 0) { // child process never reached
read(p[0], recv_buf, 5);
printf("%d: received %s\n", getpid(), recv_buf);
close(p[0]);
write(p[1], "pong", 5);
close(p[1]);
} else { // parent
write(p[1], "ping", 5); // write to output fd
close(p[1]); // close output fd
read(p[0], recv_buf, 5); // read "ping" from input fd
printf("%d: received %s\n", getpid(), recv_buf); // print "ping"
close(p[0]); // close input fd
}
exit(0); // parent process exists immediately
wait(0) fixes this by allow the child process to read/write prior to the parent reaching the read block
I am trying to rewrite a c program that do something like ls|wc|wc , I already did it for ls|wc , it worked fine, but I can't figure out why my program stops at the child in the indicated line. please help!
int main (void)
{
pid_t pid_fils, pid_pfils;
int fd[2], fd2[2];
if(pipe(fd)==-1 || pipe(fd2)==-1)
{
printf("pipe failed!");
return 1;
}
printf("program started\n");
pid_fils=fork();
if(pid_fils==0)
{
pid_pfils=fork();
if(pid_pfils==0)
{
//action3
printf("I am the grandson\n");
close(fd[0]);//close read side
dup2(fd[1],1);//connect write with stdout
close(fd[1]);//close write side
execlp("ls","ls",(char*)0);
//execvp("ls",argv3);
return 0;/*actions grandson*/
}
else
{
//action2
printf("I am the son\n");
wait();
printf("son, wait ok\n");
>close(fd[1]); //close read side
>dup2(fd[0],0); //connect write with stdin
>close(fd[0]); //close read side
///////pipe2////
> close(fd2[0]); //close read side
>dup2(fd2[1],1); //connect write with stdout/*it stops here -can't display "ok!"*/
printf("ok!\n");
>close(fd2[1]); //close write side
execlp("wc","wc",(char*)0);
printf("error exec returned!\n");
return 0;
}
}
else
{
///action1
printf("I am the parent\n");
wait();
printf("parent,wait ok\n");
close(fd2[1]); //close write side,
dup2(fd2[0],0); //connect read with stdin
close(fd2[0]); //close read side
execlp("wc","wc",(char*)0);
return 0;/*the parent*/
}
return 1;
}
Make sure you close all unused descriptors. In your case, the easiest solution is to move the creation of pipe(fd) into the first if block (in the first sub-process). The problem is that as long as any process could possibly write to the pipe, the reader won't get EOF and so won't terminate.
if(pipe(fd2)==-1)
{
printf("pipe failed!");
return 1;
}
printf("program started\n");
pid_fils=fork();
if(pid_fils==0)
{
if(pipe(fd)==-1)
{
printf("pipe failed!");
return 1;
}
pid_pfils=fork();
I should also mention that you may want to reconsider the wait calls. Not sure what you are intending to do with them but you don't want the "ls" process to block on output because the reader hasn't been started yet.
dup2(fd2[1],1);
Above line will first close the file at descriptor 1 and then duplicate the decriptor from fd2[1] into 1.
1 is stdout. Meaning that call closed stdout.
printf prints to stdout, meaning printf prints to 1 which is now assigned to the pipe fd2
So your ok went into the pipe and not on the screen.
try
//action2
printf("I am the son\n");
wait();
printf("son, wait ok\n");
close(fd[1]); //close read side
dup2(fd[0],0); //connect write with stdin
close(fd[0]); //close read side
///////pipe2////
int my_terminal_out = dup(1);
close(fd2[0]); //close read side
dup2(fd2[1],1); //connect write with stdout/*it stops here -can't display "ok!"*/
fprintf(my_terminal_out, "ok!\n");
close(fd2[1]); //close write side
Not tested. Also you should test the rest of your code for similar missteps.
+What DrC said.
Currently am doing two forks to pipeline two process, but I think am doing my wait(&status) wrong because after the command my shell just hangs and does not return to my prompt. I know my pipe is working because I can see the result if I remove the wait.
Any tips?
pipe(mypipe);
pid1=fork();
if(pid1==0)
{
pid2=fork();
if(pid2==0)
{
close(0);
dup(mypipe[0]);
close(mypipe[1]);
execv(foundnode2->path_dir,arv2);
exit(0);
}
close(1);
dup(mypipe[1]);
close(mypipe[0]);
pid2 = wait(&status2);
execv(foundnode1->path_dir,arv1);
exit(0);
}
pid1 = wait(&status2);
Rule of Thumb: if you use dup() or dup2() to map one end of a pipe to standard input or standard output, you should close() both ends of the pipe itself. You're not doing that; your waits are waiting for the programs to finish but the programs will not finish because there is still a proess with the pipe open that could write to the pipe. Also, the process which created the pipe needs to close both ends of the pipe since it is not, itself, using the pipe (the child processes are using it). See also C MiniShell — Adding Pipelines.
Also, you should not be waiting for the first child to finish before launching the second (so the pid2 = wait(&status2); line is a bad idea). Pipes have a fairly small capacity; if the total data to be transferred is too large, the writing child may block waiting for the reading child to read, but the reading child hasn't started yet because it is waiting for the writing child to exit (and it takes a long time for this deadlock to resolve itself). You're seeing the output appear without the wait() calls because the second part of the pipeline executes and processes the data from the first part of the pipeline, but it is still waiting for more data to come from the shell.
Taking those tips into account, you might end up with:
pipe(mypipe);
pid1 = fork();
if (pid1 == 0)
{
pid2 = fork();
if (pid2 == 0)
{
close(0);
dup(mypipe[0]);
close(mypipe[1]);
close(mypipe[0]);
execv(foundnode2->path_dir, arv2);
fprintf(stderr, "Failed to exec %s\n", foundnode2->path_dir);
exit(1);
}
close(1);
dup(mypipe[1]);
close(mypipe[0]);
close(mypipe[1]);
execv(foundnode1->path_dir, arv1);
fprintf(stderr, "Failed to exec %s\n", foundnode1->path_dir);
exit(1);
}
close(mypipe[0]);
close(mypipe[1]);
pid1 = wait(&status1);
Notice the error reporting to standard error when the commands fail to execv(). Also, the exit status of 0 should be reserved for success; 1 is a convenient error exit status, or you can use EXIT_FAILURE from <stdlib.h>.
There is a lot of error checking omitted still; the fork() operations could fail; the pipe() might fail. One consequence is that if the second fork() fails, you still launch the second child (identified by foundnode1->path_dir).
And I note that you could save yourself a little work by moving the pipe creation into the first child process (the parent then does not need to — indeed, cannot — close the pipe):
int pid1 = fork();
if (pid1 == 0)
{
int mypipe[2];
pipe(mypipe);
int pid2 = fork();
if (pid2 == 0)
{
close(0);
dup(mypipe[0]);
close(mypipe[1]);
close(mypipe[0]);
execv(foundnode2->path_dir, arv2);
fprintf(stderr, "Failed to exec %s\n", foundnode2->path_dir);
exit(1);
}
close(1);
dup(mypipe[1]);
close(mypipe[0]);
close(mypipe[1]);
execv(foundnode1->path_dir, arv1);
fprintf(stderr, "Failed to exec %s\n", foundnode1->path_dir);
exit(1);
}
pid1 = wait(&status1);
If it's just a pipe with two processes, I wouldn't wait at all. Just fork and do an exec in parent and child.
int fd[2];
pipe(fd);
int pid = fork();
if (pid == -1) {
/* error handling */
} else if (pid == 0) {
dup2(fd[0], 0);
close(fd[1]);
execv(foundnode2->path_dir,arv2);
/* error handling for failed exec */
exit(1);
} else {
dup2(fd[1], 1);
close(fd[0]);
execv(foundnode1->path_dir,arv1);
/* error handling for failed exec */
exit(1);
}
I wrote code for concurrent server. I want to see output in terminal for child server socket that is generated by fork() call. Client is communicating with child server socket and i want to see data written by client on server. So how can i see it in terminal?
my server code:
while(1)
{
clilen=sizeof(cliaddr);
connfd=accept(sockfd,(struct sockaddr*)&cliaddr,&clilen);
printf("connection accepted\n");
if((childpid=fork())==0)
{
close(sockfd);
printf("child process\n");
str_echo(connfd);
// str_echo(connfd);
exit(0);
}
printf("connection established\n");
str_echo()
void str_echo(int sockfd)
{
char buff[20];
ssize_t n;
while(1)
{
if((n=read(sockfd,buff,20))>=0)
write(sockfd,buff,20);
else
write(sockfd,"blank",20);
//else
printf("%s",buff);
// write(sockfd,buff,20);
//return;
//else
// writen(sockfd,buff,n);
}
}
but i am not getting output for above printf("%s",buff); in str_echo() in terminal
Usually a forked() process shares its parents file descriptors, in particular it should have the same stdout. So I think that you should be able to see the output without doing any special setup.
What I am implementing is a (simpler) version of bash. One of the tasks is to accept the command :
$ bg <command> <arguments>
This will then fork a new process and then run execvp() in the new process using <command> and <arguments> as parameters. The problem is that when I capture the output from the child process, I use pipe(), and after getting the output from the child process and outputting it when I want, I can't seem to switch back to STDIN for my parent (shell) process, which results in a segfault the next time I need to accept input.
Here is part of my "bg" function.
ChildPID = fork();
if (ChildPID < 0) {
/* There is an error */
printf("Error forking the process.\n");
exit(1);
}
if (ChildPID >= 0) {
if (ChildPID == 0) { /* Child Process */
close(m_pipefd[0]);
dup2(m_pipefd[1], STDOUT_FILENO);
close(m_pipefd[1]);
//sleep(5);
err = execvp(optionsPTR[0], optionsPTR);
switch (errno) {
case ENOENT:
printf("RSI: %s: command not found\n", optionsPTR[0]);
break;
case EACCES:
printf("RSI: %s: Permission denied\n", optionsPTR[0]);
break;
}
exit(1);
}
else { /* Parent Process */
WaitErr = waitpid(ChildPID, &status, WNOHANG | WUNTRACED);
return(0); /* to main */
}
}
return 0;
And the code for when I get the output from the pipe after the process finishes.
close(m_pipefd[1]);
dup2(m_pipefd[0], STDIN_FILENO);
while(fgets(buffer, sizeof(buffer), stdin)) {
buf = buffer;
printf("%s\n", buf);
}
close(m_pipefd[0]);
So the tl;dr version is that I need to reset back to stdin for the parent process after capturing the child processes output.
Thanks,
Braden
There is usually no need to mess with stdin and stdout in your parent. After you connect your pipes in the child to stdin and stdout, the other ends of the pipes should be able to send data or get data from the child.
Just read from m_pipefd[1] in your parent.