Last child forked will not die - c

I have the main process forking two times and thus creating two children. The two children are piped with each other like this:
ls | more
Now the problem is that the second child never dies. Why is that? When does the last child in a pipe die really?
Removing one wait() call shows the expected result of ls | more but gives some further weird behaviours(stuck terminal etc).
Here is my code:
int main(){
printf("[%d] main\n", getpid());
int pip[2], i;
pipe(pip);
/* CHILDREN*/
for (i=0; i<2; i++){
if (fork()==0){
/* First child */
if (i==0){
printf("[%d] child1\n", getpid());
close(1); dup(pip[1]);
close(pip[0]);
execlp("ls", "ls", NULL);}
/* Second child */
if (i==1){
printf("[%d] child2\n", getpid());
close(0); dup(pip[0]);
close(pip[1]);
execlp("more", "more", NULL);}
}
}
wait(NULL); // wait for first child
wait(NULL); // wait for second child
return 0;
}

The read end of the pipe won't get an EOF mark until the write end has been closed by all its users. The parent of both children still has both ends of the pipe open, so more doesn't see an EOF (a return of 0 from read()), and keeps waiting for more input.

Check with ps axw that it's really the more that isn't dying. Then read the man page for wait. Look at the returned status for wait.
(Hint: look at what causes a 'zombie' process. I don't think the waits are doing what you think they are.)

Related

C Two child process sending signal to parent, parent replies

I have a problem sending multiple signals to parent process in c.
I've been trying for a long time now and I still end up in a deadlock or a waitlock.
But I think it shouldn't be that complicated, I simply can't understand what is going on..
So I have a parent process and two child processes. The children are signaling to the parent, it accepts them, then sends a message to the children in pipe. They write it out to console.
My code so far:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
void handler(int signumber) {
printf("Signal with number %i has arrived\n", signumber);
}
int main() {
signal(SIGUSR1, handler);
signal(SIGUSR2, handler);
int pipefd[2];
char sz[100];
if (pipe(pipefd) == -1)
{
perror("Pipe open error");
exit(EXIT_FAILURE);
}
pid_t child, child2;
child = fork();
if (child > 0)
{
pause();
printf("Signal1 arrived\n", SIGUSR1);
//pause();
printf("Signal2 arrived\n", SIGUSR2);
close(pipefd[0]); //Usually we close unused read end
write(pipefd[1], "Message", 13);
printf("Pipe write complete\n");
int status;
waitpid(child2, &status, 0);
waitpid(child, &status, 0);
printf("Parent process ended\n");
}
else
{
child2 = fork();
if(child2>0) //child 1
{
printf(" child 1 Waits 3 seconds, then send a SIGTERM %i signal\n", SIGUSR1);
sleep(3);
signal(SIGUSR1, handler);
close(pipefd[1]); //Usually we close the unused write end
printf("Child1 read start\n");
read(pipefd[0], sz, sizeof(sz)); // reading max 100 chars
printf("Child1 read end, Message: %s", sz);
printf("\n");
close(pipefd[0]); // finally we close the used read end
printf("Child1 process ended\n");
kill(child, SIGTERM);
}
else //child 2
{
printf("child 2 Waits 3 seconds, then send a SIGTERM %i signal\n", SIGUSR2);
sleep(3);
signal(SIGUSR2, handler);
close(pipefd[1]); //Usually we close the unused write end
printf("Child2 read start\n");
read(pipefd[0], sz, sizeof(sz)); // reading max 100 chars
printf("Child2 read end, Message: %s", sz);
printf("\n");
close(pipefd[0]); // finally we close the used read end
printf("Child2 process ended\n");
kill(child2, SIGTERM);
}
}
return 0;
}```
I think your code is laced with race conditions and memory errors. You're also not sending SIGUSR1 or SIGUSR2 to the parent. Speaking subjectively, your solution is implemented in a manner is that is difficult to follow. If you ordered your statements differently, I think you'd also spot the same errors I'm seeing (I mention this later).
I highly recommend starting small and building your way towards the goal you are aiming towards. Step 1, for instance, could be getting a process to fork, having the parent send a message over the pipe to the child, having the child exit and having the parent reap the child. In Step 2, add the signal handler you have in your example code above to the parent, only, and have the child send that signal (the exact signal the parent will handle) to the parent using kill(). At some later step, add another fork call to create another child.
As far as race conditions are concerned, your code is ordered such that after the first fork you have a parent process that is going to try to handle two child processes but only one child process has been created. Thus, you're trying to do something that is a no-no. Also, the first child is creating a second child. This means the parent of the first child doesn't even know the second child exists because the first child has a completely different memory space. The first child doesn't have a waitpid to wait for the second child that it created it because its "entry point" so to speak is after the waitpid call earlier in the code.
Also, beware of your write(pipefd[1], "Message", 13); is scary. The last parameter of write() is count. You supplied 13 but the length of the string "Message" is less than 13. The system call is going to spew garbage to the reader, causing more undesirable problems.

Forked child "prints" redirected stdout twice

Inside a while(1) I'm trying to:
spawn a child process with fork();
redirect the child process stdout so that the parent process can see it
print the result in the terminal from the parent process
repeat
Strangely, the output from the child process seems to be printed twice
// parentToChild and childToParent are the pipes I'm using
while(1) {
int pid = fork();
if(pid < 0) {
// error, get out
exit(0);
} else if(pid != 0) {
// parent process
close(parentToChild[0]); // don't need read end of parentToChild
close(childToParent[1]); // don't need write end of childToParent
sleep(4);
char respBuffer[400];
int respBufferLength = read(childToParent[0], respBuffer, sizeof(respBuffer));
printf("before\n");
printf("parent tried to read something from its child and got: %s\n", respBuffer);
printf("after\n");
} else if (pid == 0) {
if(dup2(childToParent[1], STDOUT_FILENO) < 0) {
// printf("dup2 error");
};
close(childToParent[1]);
close(childToParent[0]);
close(parentToChild[1]); // write end of parentToChild not used
printf("child message");
// if we don't exit here, we run the risk of repeatedly creating more processes in a loop
exit(0);
}
}
I would expect the ouput of the following loop at each iteration to be:
before
parent tried to read something from its child and got: child message
after
But instead, at each iteration I get:
before
parent tried to read something from its child and got: child message
after
child message
What's the reason behind the second print of "child message"?
Flushing the stdout buffers before calling fork() doesn't seem to solve the issue
Interestingly, removing the while loop and keeping everything else intact seems to work fine
In the first iteration of the loop, you close childToParent[1] in the parent, and you don't recreate the pipes, so in the second iteration of the loop, its trying to reuse those closed pipes, so the child's dup2 call fails, so its printf goes to the terminal. Meanwhile, in the parent, the read call returns 0 without writing anything to the buffer, so you just print the old contents.

Piping from parent process to child and back

I have found quite a few questions similar to this one (e.g. https://www.quora.com/How-can-I-write-a-code-for-PIPE-in-C-shell-script-python), which work in the cases described by those questions, but not mine for some reason. The answers I've found so far deal with either piping output from one child to another (the aforementioned parenthesized link) or from child to parent/parent to child. What I want to do is have the parent be able to write to the child's STDIN and read from its STDOUT. Or, in a more general case, given some number of children, have the parent write to the first child's STDIN, which will pipe its output to the second child, which will pipe its output to the third, and so on, until the last child pipes its output back to the parent. That is, I would like to be able to do something like prog0 | prog1 | prog 2 | ... from C/C++ code. My current half-attempt (half because I'm only trying to read from an echo's STDOUT before attempting a full input/output attempt using something simple like cat -) looks something like this (some error checking omitted for brevity):
void test() {
int pipe_fds[2];
pid_t cpid, wpid;
int status;
char buf[16] = {0};
status = pipe(pipe_fds);
cpid = fork();
if (cpid == 0) {
dup2(pipe_fds[1], STDOUT_FILENO);
close(pipe_fds[0]);
execlp("echo", "Hello world!", NULL);
printf("Child returned abnormally: %d\n", errno);
_exit(3);
}
close(pipe_fds[1]);
wpid = waitpid(cpid, &status, 0);
printf("Waited on pid %d, which exited with %d\n", wpid, WEXITSTATUS(status));
read(pipe_fds[0], buf, 16);
printf("Child said: %s\n", buf);
close(pipe_fds[0]);
exit(0);
}
This runs, but the parent gets no visible output from the child. How would I fix this, and, more importantly, how would I go about piping to [and from] a chain of processes as I had mentioned earlier?
In this case, the problem is your invocation of execlp. Try
execlp("echo", "echo", "Hello world!", NULL);

Why does this fork hang?

Why doesn't the program end? The child hangs after printing what it has to print. If the parent process slept instead of the child, It would have worked, but why is that? I have also tried calling exit at the end of each process, but with the same result. Do I always have to wait for the child to finish?
int main(){
int pid = fork();
char s[100] = "Hello";
if(pid > 0){
printf("FIRST PRINT IN PARENT: %s\n", s);
strcat(s, " - PARENT");
printf("SECOND PRINT IN PARENT: %s\n", s);
}
else if(pid == 0){
printf("IMMEDIATELY IN CHILD: %s\n", s);
sleep(2);
printf("AFTER 2 SCONDS IN CHILD: %s\n", s);
}
return 0;
}
When the parent exits it might send a signal (SIGHUP) to the child.
If it does, and if the child doesn't catch that signal, the child dies.
Historically, the default has been for a process to send SIGHUP to it's children when it exits. Nowadays, many Linux distributions don't send SIGHUP by default.
I tried your code on RHEL and the child process wasn't killed.
So the parent dies and control returns to the shell. The child continues and prints it's second output 2 seconds later.
If the child does receive a SIGHUP it won't hang. It dies, and the final string is never printed.
In Linux, you can turn on SIGHUP via the prctl system call:
#include <sys/prctl.h>
prctl(PR_SET_PDEATHSIG, SIGHUP);
Related question: how-to-make-child-process-die-after-parent-exits
The shell does give you the prompt back once its child process (i.e. the parent process in your code) exits. However,
it doesn't know about the child process your code started.
The source of problems you have observed is that your parent process doens't wait for its child.
Use wait(2) system call, such as wait(0);, in the parent process.
The general risk of not waiting for child process(es) is that you might end up with zombie processes; conversely, orphan processes (if you parent process exits first).

Sending AND receiving information through pipes?

I'm trying to better understand pipes between a parent and multiple child processes, so I made a simple program that spawns two child processes, gives them a value (i), has them change that value, and then prints it out.
However it's not working, as the program prints i as if it was unaltered, and prints the altered i inside the children. I'm obviously not sending the i variable through correctly, so how should I fix this?
int main ( int argc, char *argv[] ){
int i=0;
int pipefd[2];
int pipefd1[2];
pipe(pipefd);
pipe(pipefd1);
pid_t cpid;
cpid=fork();
cpid=fork();
if (cpid ==0) //this is the child
{
close(pipefd[1]); // close write end of first pipe
close(pipefd1[0]); // close read end of second pipe
read(pipefd[0], &i, sizeof(i));
i=i*2;
printf("child process i= %d\n",i); //this prints i as 20 twice
write(pipefd1[1],&i, sizeof(i));
close(pipefd[0]); // close the read-end of the pipe
close(pipefd1[1]);
exit(EXIT_SUCCESS);
}
else
{
close(pipefd[0]); // close read end of first pipe
close(pipefd1[1]); // close write end of second pipe
i=10;
write(pipefd[1],&i,sizeof(i));
read (pipefd1[1], &i, sizeof (i));
printf("%d\n",i); //this prints i as 10 twice
close(pipefd[1]);
close(pipefd1[0]);
exit(EXIT_SUCCESS);
}
}
The main problem is that you're not creating two child processes. You're creating three.
cpid=fork();
cpid=fork();
The first fork results in a child process being created. At that point, both the child and the parent execute the next statement, which is also a fork. So the parent creates a new child and the first child also creates a child. That's why everything is printing twice.
You need to check the return value of fork immediately before doing anything else.
If you were to remove one of the fork calls, you'd still end up with the wrong value for i in the parent. That's because it's reading from the wrong end of the pipe.
The child is writing to pipefd1[1], but the parent is then trying to read from pipefd1[1] as well. It should be reading from pipefd1[0].
EDIT:
Removed erroneous sample code which assumed pipes are bidirectional, which they are not.

Resources