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.
Related
I tried to answer this question:
Write a program C that creates two children. The second child process
is blocked until the reception of the signal SIGUSR1 sent from the
parent process. While the first child process is blocked until the
reception of the signal SIGUSR2 (that will kill him) sent from the
second child process. The parent is terminated after the termination
of his children.
However the execution is not working as intended with my code below, and only the parent printfs are displayed. Can you tell me what's wrong with my code?
My code:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
void this(int sig) {
printf("this is this");
}
int main() {
int pid = fork();
int pid2;
if (pid < 0) {
exit(-1);
} else if (pid == 0) {
printf("FIrst child is paused");
pause();
printf("ERror");
} else {
pid2 = fork();
if (pid2 < 0) {
exit(-2);
} else if (pid2 == 0) {
signal(SIGUSR1, &this);
printf("Second child is paused");
pause();
kill(pid,SIGUSR2);
printf("signal sent to first child");
} else {
printf("this is the parent");
kill(pid2, SIGUSR1);
printf("signal sent to second child");
wait(NULL);
exit(-3);
}
}
}
You make no provision to ensure that the parent's signal is delivered to the second child only when that child is ready for it. Because process startup takes some time, chances are good that the signal is indeed delivered sooner. In that case, the second child will be terminated (default disposition of SIGUSR1) or it will block indefinitely in pause() (if the signal is received after the handler is installed but before pauseing). In neither case will the second child signal the first.
Signal masks and signal dispositions are inherited across a fork, so you can address that by blocking SIGUSR1 in the parent before forking, and then using sigsuspend() in the child instead of pause(), which will enable you to atomically unblock the signal and start waiting for it.
The same is not an issue for the first child because you're looking for it to exercise the default disposition for SIGUSR2 (termination), and it does not matter for the specified behavior whether that happens before that child reaches or blocks in pause().
Additionally,
the parent waits only for one child, but the prompt seems to say that it must wait for both. Perhaps you dropped the second wait() because the parent was not terminating, but if so, that was a missed clue that one of the children was not terminating.
printf is not async-signal-safe, so calling it from a signal handler invokes undefined behavior.
you should put a newline at the end of your printf formats. This will make your output much more readable, and it will also ensure that the output is delivered to the screen promptly. That could end up being useful as you debug. Alternatively, use puts() instead of printf() since you are outputting only fixed strings. puts() will add a newline automatically.
The absence of newlines probably explains why the first child's output from before it pauses is never printed. If the second child were reaching the indefinite pause state then it would also explain why that child's pre-pause output was not being printed.
I want to catch all child processes forked by a parent process, then collect the last child's exit status. To that end, I called sigsuspend() to wait for a SIGCHLD signal. When I receive the SIGCHLD signal, then the handler will call waitpid in a loop until it indicates there are no children left to reap. The exit status will be set, and the main will break out of the loop and terminate.
However, I noticed that this is not correct, as all the children aren't always reaped. How can I fix this?
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
volatile sig_atomic_t exit_stat;
// Signal Handler
void sigchld_handler(int sig) {
pid_t pid;
int status;
while(1) {
pid = waitpid(-1, &status, WNOHANG);
if(pid <= 0) {break;}
if(WIFEXITED(status)) {
printf("%s", "Exited correctly.");
}
else {
printf("%s", "Bad exit.");
}
}
exit_stat = status;
}
// Executing code.
int main() {
signal(SIGCHLD, sigchld_handler);
sigset_t mask_child;
sigset_t old_mask;
sigemptyset(&mask_child);
sigaddset(&mask_child, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask_child, &old_mask);
for(int i = 0; i < 5; i++) {
int child_pid = fork();
if(child_pid != 0) {
//Perform execvp call.
char* argv[] = {"echo", "hi", NULL};
execvp(argv[0], argv);
}
}
while(!exit_stat) {
sigsuspend(&old_mask);
}
return 0;
}
Transferring lightly modified comments into an answer.
The WNOHANG option to waitpid() means "return immediately if there are no children left, OR if there are children left but they're still running". If you really want to wait for all children to exit, either omit the WNOHANG option to waitpid() or simply use wait() instead. Note that if there were tasks launched in the background, they may not terminate for a very long time, if ever. It also depends on the context whether 'the last child to die' is the correct one to report on. It is possible to imagine scenarios where that is not appropriate.
You're right, in this instance, I meant that "the last child to die" is the last child that was forked. Can I fix this by adding a simple condition to check if the returned pid of wait == the pid of the last forked child?
If you're interested in the last child in the most recent pipeline (e.g. ls | grep … | sort … | wc and you want to wait for wc), then you know the PID for wc, and you can use waitpid(wc_pid, &status, 0) to wait for that process specifically to die. Or you can use your loop to collect bodies until you either find the body of wc or get 'no dead processes left'. At that point, you can decide to wait specifically for the wc PID, or (better) use waitpid() without WNOHANG (or use wait()) until some process dies — and again you can decide whether it was wc or not, and if not, repeat the WNOHANG corpse collection process to collect any zombies. Repeat until you do find the corpse of wc.
And also, you said that background tasks may not terminate for a long time. By this, do you mean that waitpid(-1, &status, 0) will completely suspend all processes until a child is ready to be reaped?
waitpid(-1, &status, 0); will make the parent process wait indefinitely until some child process dies, or it will return because there are no children left to wait for (which indicates there was a housekeeping error; children should not die without the parent knowing).
Note that using a 'wait for any child' loop avoids leaving zombies around (children that have died but not been waited for). This is generally a good idea. But capturing when the child you're currently interested in dies ensures that your shell doesn't hang around waiting when it wasn't necessary. So, you need to capture both the PID and the exit status of the dead child processes.
In the case of establishing a pipe between two processes, if those two have a brother to brother relationship rather than a father-child, will they be more error prone ?
My question for this arose when I investigated the code example below:
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
void runpipe();
int
main(int argc, char **argv)
{
int pid, status;
int fd[2];
pipe(fd);
switch (pid = fork())
{
case 0: /* child */
runpipe(fd);
exit(0);
default: /* parent */
while ((pid = wait(&status)) != -1) {
fprintf(stderr, "process %d exits with %d\n", pid, WEXITSTATUS(status));
exit(0);
}
case -1:
perror("fork");
exit(1);
}
exit(0);
}
char *cmd1[] = { "ls", "-al", "/", 0 };
char *cmd2[] = { "tr", "a-z", "A-Z", 0 };
void
runpipe(int pfd[])
{
int pid;
switch (pid = fork())
{
case 0: /* child */
dup2(pfd[0], 0);
close(pfd[1]); /* the child does not need this end of the pipe */
execvp(cmd2[0], cmd2);
perror(cmd2[0]);
default: /* parent */
dup2(pfd[1], 1);
close(pfd[0]); /* the parent does not need this end of the pipe */
execvp(cmd1[0], cmd1);
perror(cmd1[0]);
case -1:
perror("fork");
exit(1);
}
}
In the example above, parent(grandpa) forks a child(parent), which then forks another child(grandchild). Grandpa waits for dad but dad does not wait for grandson because they both execute execvp. What happens if child finishes earlier than dad (zombie) or dad finishes earlier than child (orphan) ? On the other hand if we had two brothers connected with the pipe and one father and waiting for them (total three processes), even if they both brothers executed execvp, ones exit would not harm the other.
In the case of establishing a pipe between two processes, if those two have a brother to brother relationship rather than a father-child, will they be more error prone ?
As far as the pipe is concerned, everything depends on the I/O operations that each performs. If the process at the read end tries to read data that the process at the other end is not prepared to write, then it will block until the writer writes or exits. In the latter case, the read will either report an error or return short data.
What happens if child finishes earlier than dad (zombie) or dad finishes earlier than child (orphan) ?
If the father calls an exec() function after forking a child and before collecting it via wait() or waitpid(), as in the example code, then it is unlikely ever to wait on the child.
Regardless, child and dad each become zombies when they terminate. This is true of the child whether or not it is orphaned first. If dad never collects child (as it won't in your example), then once dad terminates, the child (whether live or zombie) is inherited by process 0 (init), which can be relied upon to clean up all its zombie children. Similarly, if grandpa never collects dad then init eventually will do.
Under certain circumstances it is possible for zombies to build up uncollected. This is a form of resource leak, but it will ultimately be cleaned up when the zombies are inherited by init. That is slightly exacerbated by the grandpa -> parent -> child topology you've set up, but I wouldn't characterize it as "error prone."
What happens if child finishes earlier than dad (zombie)...
It will be a zombie process. Once the parent finishes without waiting on the child, the child will be re-parented to init. init will then wait on the child, retrieving its exit code and allowing it to finally exit.
...or dad finishes earlier than child (orphan) ?
Orphaned processes are re-parented to init. The process will then be the same as above.
It seems that if I create a process, fork it and send a SIGHUP from the parent to the child, the child dies but it's "/proc/PID" dir doesn't dissappear until the parent also dies.
(See code below).
What is the right way to let the parent check if the child is dead ?
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
void testprocdir(pid_t pid) {
struct stat sb;
char path[1024];
sprintf(path,"/proc/%d",pid);
if(stat(path, &sb)==-1 && errno == ENOENT) {
printf("%s does not exist\n", path);
} else {
printf("%s exists\n", path);
}
}
int main(int argc,char **argv) {
pid_t parent,child;
parent=getpid();
printf("I am %d\n",parent);
child=fork();
switch(child) {
case -1:
printf("Forking failed\n");
return 2;
case 0:
parent=getppid();
child=getpid();
printf("I am the child (%d) and my parent is %d\n", child, parent);
while(1) { sleep(1); printf("I am the child and I have slept 1s\n");}
printf("This line should not be visible\n");
}
sleep(1); //make sure kid is in the while loop
printf("I am the parent (%d) and my kid is %d\n", parent, child);
kill(child,SIGHUP);
testprocdir(parent);
printf("Waiting 5s before testing if the procdir of the child (/proc/%d) is removed\n",child);
sleep(5);
testprocdir(child);
return 0;
}
You could use the wait family of system-calls.
fork returns the PID of the child process in the parent process, and 0 in the child process.
man waitpid should provide more than enough direction beyond that to call waitpid in the parent, allowing you to check that child process or all child processes ― including the ability to allow the parent to continue executing if the child is still alive or stop all execution in the parent until the child is dead.
I will start with some concepts:
The OS will keep a child process' entry in the process table (including exit status) around until the parent calls waitpid (or another wait-family function) or until the parent exits (at which point the status is collected by the init process). This is what a "zombie" process is: a process that has exited by is still resident in the process table for exactly this purpose. The process' entry in the table should go away after the first call to waitpid.
Also, from the man page :
A child that terminates, but has not been waited for becomes a "zombie". The kernel maintains a minimal set of information about the zombie process (PID, termination status, resource usage information) in order to allow the parent to later perform a wait to obtain information about the child.
So, by using the wait family of functions you can examine the status of child process.
There are some macros also that can be used with with wait family of functions to examine the status of child process like WEXITSTATUS, WIFSIGNALED, WIFEXITED etc .
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.)