Parent process not running when child process aborted via ctrl-\ - c

I'm doing this assignment in which a number of child processes are generated and if they're aborted (ctrl-\ or ctrl-4) the parent process should run instead. This is the code:
int main(int argc, char *argv[])
{
/* The rest of the code is omitted. "times", "arg1"
and "cmd1" are parameters passed when running the program */
for(cont = 0; cont < times; cont++)
{
pid = fork();
if(pid == 0)
execvp(arg1,cmd1);
else if (pid >0) {
wait(&status);
if (WCOREDUMP(status) !=0)
printf("Core dump generado\n");
}
}
return 0;
}
The program runs a Linux command a number of times, with argv[1] being the number of times, and arg[2], argv[3] and so on the Linux command itself.
It runs fine if it's not cancelled. But when I try to abort it (for instance using a sleep command and then typing ctrl-4):
./ntimes 2 sleep 10
^\Quit (Core dumped)
it generates a dump. I want the parent process to print a message instead. I've tried with signal handling functions and many other things, but I can't seem to make it work.

SIGQUIT targets the whole foreground process group of your terminal—it kills the parent along with the child.
To prevent it from killing the parent, you need to either:
ignore it or block it in the parent
catch it in the parent
With approach 1., you'll need to unblock it/unignore it in the child before execing.
With approach 2., the signal disposition will be automatically defaulted upon execing so it won't affect the child, however, having at least one signal handler will open your application up to the possibility of EINTR errors on long-blocking syscalls such as wait, so you'll need to account for that.
Here's an example of approach 2. You can try it on e.g., ./a.out sleep 10 #press Ctrl+\ shortly after this.
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
void h(int Sig){}
int main(int argc, char *argv[])
{
sigaction(SIGQUIT, &(struct sigaction){.sa_handler=h}, 0);
int cont, status, times=1;
pid_t pid;
for(cont = 0; cont < times; cont++)
{
pid = fork();
if(pid == 0)
execvp(argv[1],argv+1);
else if (pid >0) {
// retry on EINTR
int rc; do{ rc=wait(&status); }while(0>rc && EINTR==errno);
if (0>rc) return perror("wait"),1;
if (WIFSIGNALED(status)){
if (WCOREDUMP(status))
printf("Core dump generado\n");
}
}
}
return 0;
}

Related

How to correctly terminate processes created with fork() without waiting for their result

According to the task, the program must create a copy of itself using fork () to process the task. Parent process must not be blocked while the child is running.
However, I encountered strange behavior of the test program. According to the result of pgrep, child processes are not terminated until the parent program has completed its execution.
Each time fork() is called, pgrep shows that the number of processes is increasing, but they are not terminating.
I wrote a simple program that runs for nearly 35 seconds and spawns a child process 3 times, which should exit after 5 seconds:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
for (int i = 1; i < 36; i++) {
if (i % 10 == 0) {
pid_t forkrank = fork();
if (forkrank == 0) {
printf("forked process starts\n");
sleep(5);
return 1;
}
}
sleep(1);
}
return 0;
}
But during its execution, pgrep shows that running processes are not terminated until parent ends:
But if I add a blocking code to wait for the result of the child process execution, then additional processes are terminated correctly:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char* argv[])
{
for (int i = 1; i < 36; i++) {
if (i % 10 == 0) {
pid_t forkrank = fork();
if (forkrank == 0) {
printf("forked process starts\n");
sleep(5);
return 1;
}
int wstatus;
waitpid(forkrank, &wstatus, 0);
int retValue = WEXITSTATUS(wstatus);
}
sleep(1);
}
return 0;
}
pgrep:
Why is this happening? How to terminate child processes without blocking the main one?
You could do a non-blocking wait. This is done by passing the WNOHANG flag as the third parameter to waitpid. You can also pass -1 for the first argument to allow the parent to wait for any pid. You would then put the call to waitpid after the call to sleep in the main loop.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
for (int i = 1; i < 36; i++) {
if (i % 10 == 0) {
pid_t forkrank = fork();
if (forkrank == 0) {
printf("forked process starts\n");
sleep(5);
return 1;
}
}
sleep(1);
waitpid(-1, NULL, WNOHANG);
}
return 0;
}
The proper term is reaping the children.
This is done using wait/waitpid. The earlier answer shows how to do this using polling, which requires changing your program to periodically call waitpid. But it's also possible to do without making such changes.
To automatically reap the children as they exit, use sigaction to install a SIGCHLD handler which calls waitpid(-1, NULL, WNOHANG) until it returns a 0 or -1.
This has a side-effect. Blocking system calls can now fail with error EINTR unless you have them automatically restarted.
You could also daemonize the child, making it completely independent of the parent process. The process causes them to become children of init (pid 1), and it automatically reaps terminated children.
You can do that by passing flag WNOHANG as option to wpaitpid
See (2): waitpid
WNOHANG return immediately if no child has exited.
waitpid(-1, NULL, WNOHANG);

If the subprocess does not call the system call, will the signal sent by kill still take effect?

If the subprocess does not call the system call, will the signal sent by kill still take effect?
It worked. But I want to know when did the subprocess enter the kernel mode.
Code as follows.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
int pid;
if ((pid = fork()) == 0)
{
while (1)
continue;
exit(0);
}
sleep(3);
kill(pid, SIGINT);
int wid, status;
while ((wid = wait(&status)) != -1)
{
printf("child %d: exited with ", wid);
if (WIFEXITED(status))
printf("%d\n", WEXITSTATUS(status));
if (WIFSIGNALED(status))
printf("%d\n", WTERMSIG(status));
}
return 0;
}
Yes, processes that do not make system calls can still receive signals. You can easily test this by writing an infinite loop program, running it, then pressing Ctrl-C, or using the kill command from another terminal window.

The initial process creates 5 child processes, and waits for them to finish. Each child process performs 5 repetitions, where in each repetition

I'm a bit confused with the creation of processes with fork(), sleep() and wait() in c. Take the following piece of code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
void childProcess(void);
void childProcess(void)
{
for(int i = 0; i < 5; i++)
{
printf("pid: %d email: myemail\n", getpid());
sleep(1);
}
}
int main(int argc, char *argv[])
{
for (int i = 0; i < 5; i++)
{
pid_t childpid;
if ((childpid = fork()) == 0)
{
childProcess();
exit(0);
}
}
int status;
while (wait(&status) > 0)
{
}
return 0;
}
After this piece of code has been executed,executes processes but does not remove repetitions from 5 processes. I'm a little confused with the process.
The initial process creates 5 child processes, and waits for them to finish.
Each child process performs 5 repetitions, where in each repetition:
Prints the message
pid: PID email: USER_EMAIL
where PID is the child PID of the process, while USER_EMAIL is the email
Suspends its operation for 1 second (at a time) with the sleep call
The parent process prints the children's PIDs when they are finished
P.S I EDIT THE CODE
#mixalispetros, you have multiple things to fix, and they all have to be fixed together for your code to work as intended.
exit(0);
for (int i = 0; i < 5; i++) {
wait(NULL);
}
The process ends on exit(0). wait is never called.
if (fork() == 0) {
// what code runs here? The code in the new process
}
What code runs within the fork() conditional? The new process's code. Which process should run wait()? The original process. So in addition to being after an exit, the wait() is also in the wrong process.
Where to move it? The for loop wait()s for 5 child processes. Why would there be 5 child processes for which to to wait()? Because we had already started all 5 child processes before we went into our loop of 5 wait()s.
The wait()s must happen not just outside the child process conditional block, but also outside the loop around the call to fork().
I'm a bit confused with the creation of processes with fork(), sleep() and wait() in c
It is confusing. Refer to the documentation often to keep it straight.
Remember, fork() returns twice - in the original process (returning the process ID of the new process), and in the new process (returning 0). wait(), will wait for the next child process to exit.
In summary, put the wait loop outside the loop that fork()s child processes. This will also move it ouside the block of code that executes in the child process.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main() {
// this loop creates 5 processes
for (int i = 0; i < 5; i++) {
if (fork() == 0) {
printf("Child %d, PID %d\n", i, getpid());
sleep(i);
exit(0);
}
}
// now, all subprocesses were started
// wait for the same number of child processes to end
for (int i = 0; i < 5; i++) {
wait(NULL);
}
}

C program that tells the user which child process finished first

I am working on an assignment that involves using fork. The program runs two separate programs simultaneously and tells the user which one finished first. If a child finishes, the other child still running should be killed immediately.
My code so far is this...
int main(int argc, char **argv) {
if (argc != 2) {
perror("Invalid number of arguments!");
exit(1);
}
pid_t pid;
pid_t wpid;
int status = 0;
for (int i = 0; i < 2; i++) {
if ((pid = fork()) == 0) {
execv("/bin/sh", argv[i+1]);
}
}
while ((wpid = wait(&status)) > 0);
printf("%s finished first!", <Insert winning program here>);
return 0;
}
From my understanding, this runs the programs and will not let the parent process continue until the child processes have finished. Now I'm wondering how I can terminate another child and return the winning process.
But how can I immediately get the pid of the losing process so that I can kill it?
Just as TonyB told: the "parent" saves the pid of the new child. 2) wait will tell you the pid of the winning process. More verbose: Save the PID of both children, wait for any one, compare the return value to (one of) the saved PIDs; the matching one is the winner, the non-matching one is the loser. E. g.:
#define _POSIX_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
int main(int argc, char **argv)
{
if (argc != 3) // with two program arguments, argc is 3
fputs("Invalid number of arguments!\n", stderr), exit(EXIT_FAILURE);
pid_t pid[2]; // to store both child pids
pid_t wpid;
for (int i = 0; i < 2; i++)
if ((pid[i] = fork()) == 0)
execl("/bin/sh", "sh", "-c", argv[i+1], NULL),
perror(argv[i+1]), exit(EXIT_FAILURE);
wpid = wait(NULL); // wait for first
int wi = wpid==pid[0] ? 0 : 1; // get index of winner
kill(pid[!wi], SIGKILL), wait(NULL); // kill and reap loser
printf("%s finished first!\n", argv[wi+1]);
return 0;
}

waitpid() not allowing SIGINT to be sent to child process?

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void sig_handler(int signal);
int pid, forkFlag = 0;
int main( int argc, char **argv, char **envp )
{
sigset(SIGINT, sig_handler); //handle ctrl + c
sigignore(SIGTSTP);
sigignore(SIGSTOP);
int ex, rv, status;
forkFlag = 1; //fork is being called
pid = fork();
if(pid == -1){
perror("fork");
exit(2);
}
else if (pid == 0){ //if child process
ex = access(argv[0], X_OK); //check if file is executable
if(ex){
perror("access");
exit(1);
}
else{
rv = execve(argv[0], argv, envp); //run program in child process
if(rv == -1){
perror("execve");
exit(1);
}
}
exit(0); //end child process
}
else{
rv = waitpid(pid, &status, 0); //wait for child
if(rv == -1){
perror("waitpid");
}
if(WEXITSTATUS(status)){ //check status of child if it did ot return 0
printf("The return status of the child was %d\n", WEXITSTATUS(status));
}
}
forkFlag=0;
}
void sig_handler(int signal)
{
if(signal == SIGINT && (pid && forkFlag)){
kill(pid,signal); //send kill to child
}
}
I'm trying to make my program ignore ctrl + C, except when there is a child process running, then it sends the the SIGINT to the child process. However, when I press ctrl + c when the child process is running, waitpid() returns -1 with the error "Interrupted System Call." This makes the child process stop running, but if I use ps, the child process is still there, but now labeled as defunct. I know from printf statements that kill is being calle din the function sig_handler, and that pid and forkFlag are their correct values. Is waitpid() making my program ignore the kill? How do I fix this? I know this code does next to nothing, but it's a small portion of my code (the only part involving fork)
Thanks for any help.
The problem is that the child processes get the same overridden handler for SIGINT. You probably want to reset the signal handler in the child process after the fork, or you might want to install the signal handler in the parent after you've already forked the child, so it doesn't inherit the overriden handler.

Resources