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;
}
Related
I am now learning about folk, exec etc and I have this piece of code:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
pid_t childpid;
int status;
childpid=fork();
if (childpid== -1){
perror("Failed to fork\n");
exit(1);
}
if (childpid==0) {
printf("I am in child process with id = %lu\n", (long)getpid());
execvp(argv[1], &argv[1]);
perror("exec failure ");
exit(1);
}
else {
printf("I am in parent process with id = %lu\n", (long)getpid());
exit(1);
}
}
The child process works fine but after that for some reason the program continues running without doing anything. It never prints "I am in child process with id = ...." or "I am in parent process with id =... ". It's like it never goes to parent process. Do you have any ideas why? Thanks in advance
From my top comment ...
You are creating a zombie process. This is because the parent process is not waiting for the child to complete.
The parent process will terminate [relatively] quickly. Thus, the child loses its parent and becomes a zombie. A zombie will be reparented by the kernel as a child of process 1 (e.g. systemd or initd).
To fix, add: wait(NULL); after the final printf
UPDATE:
Therefore do I need to always put wait(NULL) in these types of situations?
The TL;DR is ... Yes!
This is what you normally want to do for most programs.
One of the few times you would want to create a zombie is (e.g.) if you're a server program (e.g. inetd).
Servers want to run "detached". That is, as a child of the init process (e.g. systemd, initd, etc.). There is one and only one init process on the system.
All other processes are children of init, even if indirectly. For example, your program's process hierarchy was something like:
init -> window_manager -> xterm -> bash -> your_program
Anyway, most server programs these days are fired up by systemd directly. It examines some config files and starts things based on these config options. So, now, most server programs don't have to do anything special.
But, if you were testing a server of your own, invoked it from the command line, and wanted it to run [detached] in the background, you might do:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
int opt_d;
int
main(int argc, char **argv)
{
char *cp;
pid_t childpid;
int status;
// skip over program name
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'd':
opt_d = 1;
break;
}
}
// detach into background
if (opt_d) {
childpid = fork();
if (childpid == -1) {
perror("Failed to detach\n");
exit(1);
}
// exit the parent -- child is now detached [and a zombie] and a child
// of the init process
if (childpid != 0)
exit(0);
}
childpid = fork();
if (childpid == -1) {
perror("Failed to fork\n");
exit(1);
}
if (childpid == 0) {
printf("I am in child process with id = %lu\n", (long) getpid());
execvp(*argv, argv);
perror("exec failure ");
exit(1);
}
printf("I am in parent process with id = %lu\n", (long) getpid());
wait(&status);
return 0;
}
I don't understand as to where should I use the wait(NULL) or while(pid = wait(&status))>0). I am very confused and I get weird results please help!.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main (int argc, char *argv[]) {
pid_t childpid = 0;
int i, n;
if (argc != 2){ /* check for valid number of command-line arguments */
fprintf(stderr, "Usage: %s processes\n", argv[0]);
return 1;
}
n = atoi(argv[1]);
for (i = 1; i < n; i++)
if ((childpid = fork()) <= 0)
break;
fprintf(stderr, "i:%d process ID:%ld parent ID:%ld child ID:%ld\n",
i, (long)getpid(), (long)getppid(), (long)childpid);
return 0;
}
How would you modify this code so that the original process prints out its information after all children have exited?
How would you modify this code so that a process prints its information after its child process has exited?
The same modification can do both jobs.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
pid_t childpid = 0;
int i, n;
if (argc != 2)
{ /* check for valid number of command-line arguments */
fprintf(stderr, "Usage: %s processes\n", argv[0]);
return 1;
}
n = atoi(argv[1]);
for (i = 1; i < n; i++)
{
if ((childpid = fork()) <= 0)
break;
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
{
fprintf(stderr, "%d: child %d exited with status 0x%.4X\n",
(int)getpid(), corpse, status);
}
fprintf(stderr, "i:%d process ID:%ld parent ID:%ld child ID:%ld\n",
i, (long)getpid(), (long)getppid(), (long)childpid);
return 0;
}
When a child process executes wait(), it immediately fails since there are no grandchildren to be waited for. Therefore, the child prints its output after "its children" (all zero of them) have exited.
When the parent process executes the wait() loop, it reports on each child as the information is made available, and prints its own information afterwards.
You've got more than one child in general, so the second question is a bit ambiguous. However, if you want it to wait for one child (and you don't care which), then remove the loop from around the wait(). If you care which child, use waitpid() instead of just wait().
You can make the output more interesting by returning i or a number calculated from i in the child processes. Note that before the forking loop runs from 1 rather than 0, so if you specify 4 on the command line, you get 3 child processes plus the original parent process.
I have to write an program which will generate a random amount of processes, and then will kill them one after one, after they all were created.
My problem is that I can't stop the child processes after being created.
Also, I try to call the termination-output to stdout from a child process, but don't really know how to solve it (because pid = 0 is for every child process).
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
//int status;
srand(time(NULL));
int amount = (rand())%9+1;
pid_t fatherid = getpid();
printf("Hello I am a parent process, my PID is %d and I will now create %d children.\n",fatherid,amount);
pid_t pid = 1;
pid_t pidarr[amount];
for(int i = 0;i<amount;i++){
if(pid != 0){
pid = fork();
pidarr[i] = pid;
if(pid ==0){
printf("Hello I am a child process, my PID is %d and my parent has the PID %d.\n",getpid(),fatherid);
}
sleep(1);
}
}
if(pid != 0){
wait(NULL);
}
for(int i = (amount-1);i >= 0;i--){
if(pidarr[(i-1)] != 0){
printf("Hello I am a child process %d, I will terminate now.\n",getpid());
}
sleep(rand()%4);
if(pid != 0){
kill(pidarr[i],SIGKILL);
printf("Child Process %d was terminated.\n",pidarr[i]);
}
}
if(pid != 0){
printf("All child processes were terminated. I will terminate myself now.\n");
}
return EXIT_SUCCESS;
}
the following code shows how to handle fork and child processes.
the code compiles cleanly, is tested and works
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
int main( void )
{
//int status;
srand(time(NULL));
int amount = (rand())%9+1;
pid_t fatherid = getpid();
printf("Hello I am a parent process, my PID is %d and I will now create %d children.\n",fatherid,amount);
pid_t pid;
pid_t pidarr[amount];
for(int i = 0;i<amount;i++)
{
pid = fork();
if( -1 == pid )
{ //then, fork() error
perror( "fork() failed" );
exit(1);
}
// implied else, fork() successful
//pidarr[i] = pid;
if(!pid )
{ // then child process
printf("Hello I am a child process, my PID is %d and my parent has the PID %d.\n",getpid(),fatherid);
exit(0); // exit child process
}
// implied else, parent process
pidarr[i] = pid;
sleep(1);
} // end for
for(int i = (amount-1); i >= 0; i--)
{
kill(pidarr[i],SIGKILL);
printf("Child Process %d was terminated.\n",pidarr[i]);
}
printf("All child processes were terminated. I will terminate myself now.\n");
return(0);
} // end function: main
I am not sure about other parts of your logic (e.g. the if clause inside the fork loop), but
if(pid != 0){
wait(NULL);
}
looks suspiciously as of the parent process waits for a child to exit so that it doesn't get to the code which would kill the children at all (unless they exit on their own, but then the killing seems pointless).
Some issues in your code:
1) As #Peter Schneider points out,
parent process waits for a child to exit so that it doesn't get to the code which would kill the children
So first of all, you have to get rid of:
if(pid != 0){
wait(NULL);
}
2) The for loop that kills the children has to be executed only by the parent process, so the if clause embraces the for:
if(pid != 0){
for(int i = (amount-1);i >= 0;i--){
kill(pidarr[i],SIGKILL);
printf("Child Process %d was terminated.\n",pidarr[i]);
}
}
3) The child processes have to wait doing something until parent kills them, so append the following else clause to the above if:
else{
while(1){
printf("I am a child process %d. Will sleep for 2 senconds\n",getpid());
sleep(2);
}
}
4) the following code makes no sense, because when children are killed they simply stop working.
if(pidarr[(i-1)] != 0){
printf("Hello I am a child process %d, I will terminate now.\n",getpid());
}
If you want children to do something when the signal from kill() gets to them, you will have to use signals.
I am working on an assignment where I have to count the number of chars from the command line arguments. The parent is to pass the child one char at a time and the child is to count the number of chars and return the count to the parent so it can print the number of chars. When I run my program it just sits and does nothing. I think my problem is when I get to the stage of passing the count back to the parent and reaping the child. I think my code is fairly solid up until that point and then that is were I get a little fuzzy. Any help would be greatly appreciated.
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
//
// Child process counts number of characters sent through pipe.
//
// Child process returns number of characters counted to parent process.
//
// Parent process prints number of characters counted by child process.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //for fork and pip
#include <sys/wait.h>
#include <string.h>
int main(int argc, char **argv)
{
pid_t pid;
int comm[2];
int status;
char src;
// set up pipe
if (pipe(comm))
{
printf("Pipe Error!\n");
return -1;
}
// call fork()
pid = fork();
//check if fork failed
if (pid < 0)
{
printf("Fork Error! %d\n", pid);
return -1;
}
if (pid == 0)
{
// -- running in child process --
//close output side of pipe
close(comm[1]);
int nChars = 0;
printf("in child\n");
// Receive characters from parent process via pipe
// one at a time, and count them.
while (read(comm[0], &src, 1))
{
++nChars;
printf("testing child loop = %d\n", nChars);
}
//close input side of pipe
close(comm[0]);
// Return number of characters counted to parent process.
return nChars;
}
else
{
// -- running in parent process --
int nChars = 0;
//close input side of pipe
close(comm[0]);
printf("Assignment 3\n");
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
int i;
for (i = 1; i < argc; ++i) //loop through each argument
{
int j;
for (j = 0; j < strlen(argv[i]); ++j) //loop through each character in argument
write(comm[1], &argv[i][j], 1);
}
//closing the write end of the pipe
close(comm[1]);
// Wait for child process to return. Reap child process.
waitpid(pid, &status, 0);
// Receive number of characters counted via the value
// returned when the child process is reaped
printf("child counted %d chars\n", nChars);
return 0;
}
}
Here is basically how you should have done it - unless you were absolutely forced to go the return code route.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)
#define c2p 0
#define p2c 1
#define READEND 0
#define WRITEEND 1
int main(int argc, char **argv)
{
pid_t pid;
int comm[2][2];
char src;
for (int i = 0; i < 2; ++i)
if (pipe(comm[i]))
errExit("pipe");
if ((pid = fork()) == -1)
errExit("fork");
if (! pid)
{
close(comm[p2c][WRITEEND]);
close(comm[c2p][READEND]);
int nChars = 0;
while (read(comm[p2c][READEND], &src, 1))
++nChars;
write(comm[c2p][WRITEEND], &nChars, sizeof(nChars));
close(comm[c2p][WRITEEND]); //sends eof to parent
printf("child counted %d chars\n", nChars);
return 0;
}
int nChars = 0;
close(comm[p2c][READEND]);
close(comm[c2p][WRITEEND]);
printf("Assignment 3\n");
for (int i = 1; i < argc; ++i) //loop through each argument
{
int len = strlen(argv[i]);
for (int j = 0; j < len; ++j)
write(comm[p2c][WRITEEND], &argv[i][j], 1);
}
close(comm[p2c][WRITEEND]); //sends eof to child
read(comm[c2p][READEND], &nChars, sizeof(nChars)); //should really be in a loop - your task
close(comm[c2p][READEND]);
wait(0);
printf("parent reports %d chars\n", nChars);
return 0;
}
I am taking command line arguments to main from parent to child and counting them and printing. My question is that i am not sure that i am reaping the child? dont i just need an exit 0
or do i need to call fork again?
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int length = 0;
int i, n;
int fdest[2]; // for pipe
pid_t pid; //process IDs
char buffer[BUFSIZ];
if ((pid = fork()) < 0) /* attempt to create child / parent process */
{
printf("fork error");
}
if (pipe(fdest) < 0) /* attempt to create pipe */
printf("pipe error");
/* parent process */
else if (pid > 0) {
close(fdest[0]);
for(i = 1; i < argc; i++) /* write to pipe */
{
write(fdest[1], argv[i], strlen(argv[1]));
}
} else {
/* child Process */
close(fdest[1]);
for(i = 1; i < argc; i++)
{
length +=( strlen(argv[i])); /* get length of arguments */
}
n = read(fdest[0], buffer, length);
printf("\nchild: counted %d characters\n", n);
}
exit(0);
}
No, you are not reaping the child correctly. In your case, if the child process finishes before the parent process exits, the child will become a zombie. Then, when the parent process finishes, the child will be reparented to init (whether it has finished and is a zombie, or is still running). init is then reaping the child for you.
To reap the child, add a call to wait() before exit.
By the way, you have another bug - you are creating the pipe after the fork, so the parent and child each create a (different) pipe - they're not connected. Move the if (pipe(... up before the fork().