Does the C execv() function terminate the child proccess? - c

Heres a breakdown of my code.
I have a program that forks a child (and registers the child's pid in a file) and then does its own thing. The child becomes any program the programmer has dignified with argv. When the child is finished executing, it sends a signal (using SIGUSR1) back to the parent processes so the parent knows to remove the child from the file. The parent should stop a second, acknowledge the deleted entry by updating its table, and continue where it left off.
pid = fork();
switch(pid){
case -1:{
exit(1);
}
case 0 :{
(*table[numP-1]).pid = getpid(); //Global that stores pids
add(); //saves table into a text file
freeT(table); //Frees table
execv(argv[3], &argv[4]); //Executes new program with argv
printf("finished execution\n");
del(getpid()); //Erases pid from file
refreshReq(); //Sends SIGUSR1 to parent
return 0;
}
default:{
... //Does its own thing
}
}
The problem is that the after execv successfully starts and finishes (A printf statement before the return 0 lets me know), I do not see the rest of the commands in the switch statement being executed. I am wondering if the execv has like a ^C command in it which kills the child when it finishes and thus never finishes the rest of the commands. I looked into the man pages but did not find anything useful on the subject.
Thanks!

execv replaces the currently executing program with a different one. It doesn't restore the old program once that new program is done, hence it's documented "on success, execv does not return".
So, you should see your message "finished execution" if and only if execv fails.

execv replaces the current process with a new one. In order to spawn a new process, you can use e.g. system(), popen(), or a combination of fork() and exec()

Other people have already explained what execv and similar functions do, and why the next line of code is never executed. The logical next question is, so how should the parent detect that the child is done?
In the simple cases where the parent should do absolutely nothing while the child is running, just use system instead of fork and exec.
Or if the parent will do something else before the child exits, these are the key points:
When the child exits, the parent will get SIGCHLD. The default handler for SIGCHLD is ignore. If you want to catch that signal, install a handler before calling fork.
After a child has exited, the parent should call waitpid to clean up the child and find out what its exit status was.
The parent can also call wait or waitpid in a blocking mode to wait until a child exits.
The parent can also call waitpid in a non-blocking mode to find out whether the child has exited yet.

What did you expect to happen? This is what execv does. Please read the documentation which says:
The exec() family of functions replaces the current process image with a new process image.
Perhaps you were after system or something, to ask the environment to spawn a new process in addition to the current one. Or.. isn't that what you already achieved through fork? It's hard to see what you want to accomplish here.

Related

How to wait for system() completion before advancing into next cycle

I'm using system("./foo 1 2 3") within C to call an external application. I use it inside a for cycle and I want to wait for the foo execution to complete (each execution takes 20/30 seconds) before going into the next cycle iteration. This is a MUST.
The returned system() value only tells me if the process was successfully started or not. So how can I do this?
I looked into fork() and wait() already but didn't manage to do what I want.
Edit:Here's my fork and wait code:
for(i=0;i<64;i++){
if((pid=fork()==-1)){
perror("fork error");
return -1;
}
else if(pid==0){
status=system("./foo 1 2 3"); //THESE 1 2 3 PARAMETERS CHANGE WITHIN EACH ITERATION
}
else{ /* start of parent process */
printf("Parent process started.n");
if ((pid = wait(&status)) == -1)/* Wait for child process. */
printf("wait error");
else { /* Check status. */
if (WIFSIGNALED(status) != 0)
printf("Child process ended because of signal %d.n",
WTERMSIG(status));
else if (WIFEXITED(status) != 0)
printf("Child process ended normally; status = %d.n",
WEXITSTATUS(status));
else
printf("Child process did not end normally.n");
}
}
}
What happens when I do this is that the PC gets extremely slow to the point I need to manually reboot. So What I guess this is doing is starting 64 simultaneous child processes, causing the computer to become really slow.
On a POSIX system, the system function should already be waiting for the command to finish.
http://pubs.opengroup.org/onlinepubs/009695399/functions/system.html
If command is not a null pointer, system() shall return the termination status of the command language interpreter in the format specified by waitpid(). The termination status shall be as defined for the sh utility; otherwise, the termination status is unspecified. If some error prevents the command language interpreter from executing after the child process is created, the return value from system() shall be as if the command language interpreter had terminated using exit(127) or _exit(127). If a child process cannot be created, or if the termination status for the command language interpreter cannot be obtained, system() shall return -1 and set errno to indicate the error.
The one thing to watch out for is if you're starting the program in the background within the command (i.e. if you're doing "./foo &") - the obvious answer is just don't do that.
After you call fork, the child calls system which starts another child that foo runs in. Once it completes, the child continues the next iteration of the for loop. So after the first loop iteration you have 2 processes, then 4 after the next, and so forth. You're spawning off processes at an exponential rate which causes the system to grind to a halt.
There are a few ways to address this:
After the call to system, you have to call exit so the forked off child quits.
Use exec instead of system. This will start foo in the same process as the child. A successful call to exec does not return, however if it fails you still want to print an error and call exit after exec.
Don't bother with fork or wait at all and just call system in a loop, since system doesn't return until the command is completed.
EDIT:
This loop is exhibiting some strange behavior. Here is the culprit:
if((pid=fork()==-1)){
You've got some misplaces parenthesis here. The innermost expression is pid=fork()==-1. Because == has higher precedence than =, it first evaluates fork()==-1. If fork was successful, this evaluates to false, i.e. 0. So then it evaluates pid=0. So after this conditional, both the parent and the child have pid==0.
After applying one of the above changes, put the parenthesis in the right place:
if((pid=fork())==-1){
And everything should work fine.
wait(2)
All of these system calls are used to wait for state changes in a
child of the calling process, and obtain information about the child
whose state has changed. A state change is considered to be: the
child terminated; the child was stopped by a signal; or the child was
resumed by a signal. In the case of a terminated child, performing a
wait allows the system to release the resources associated with the
child; if a wait is not performed, then the terminated child remains
in a "zombie" state (see NOTES below).
If a child has already changed state, then these calls return
immediately. Otherwise, they block until either a child changes
state or a signal handler interrupts the call (assuming that system
calls are not automatically restarted using the SA_RESTART flag of
sigaction(2)). In the remainder of this page, a child whose state
has changed and which has not yet been waited upon by one of these
system calls is termed waitable.
I found out what the problem was.
I saw here: https://askubuntu.com/questions/420981/how-do-i-save-terminal-output-to-a-file
that in order to save the stderr to file I needed to do &>output.txt.
So I was doing "./foo 1 2 3 &>output.txt" but that & causes the system process to go into background.
+1 to #Random832 for guessing it (even though I never said I was using &> -sorry guys, my bad ).
Btw, if you want the stderr to be exported to a file you can use 2>output.txt

Do I need to wait() in the parent process after a fork?

I'm wondering if I have to wait() for all child process to finish in the parent program?
I have read the manuals and some online resources about fork(), but none of them mentioned that a wait() in the parent is enforced.
However, if I do not wait in the parent program, the process does not terminate but just does nothing until I press enter, and than terminates.
If the parent process doesn't use a system call of the wait()-family for its children processes, it could simply die first.
When a parent process dies, all of its children processes are moved under process init with PID == 1 in the process tree. So the process init is their new parent process.
I guess the reason you observe this behaviour with <Enter> (just a guess, it could be wrong) is because the parent process is terminated earlier than its children, so your terminal's prompt is printed before some of the children's output. When you press <Enter>, the prompt is printed again. Check again the output carefully.

A program to fork off children/grandchild

I have an assignment to write a program to use fork off a children. That child will the fork off its own child (grandchild of the original parent). The grandchild should exec() to do a ps -ef (for example). The child should wait for its child (grandchild of original parent) to finish successfully. If it didn't finish successfully (I assume that the status return code is 0), it should spawn off another grandchild until it is successful. Once this is complete, it should send the SIGINT signal to its parent.
This is what I was doing, the second time I fork the grandchild, I exec as specified. Here, I set up a signal handler too. In the child, I wait (wait(&status)) and loop while (status != 0). That was the idea.
But, still, I couldn't get the program works. I guess I have the problem with signal handling (?) Can you give me a hint?
The return value of things that don't succeed is -1. It is never a positive value in C.
The exec() functions only return if an error has occurred. The return value is -1, and errno is set to indicate the error.
Basically what you want to do is wait on your grandchild in your child. If your grandchild calls exec, and it fails, you will return from exec and continue execution in that program(grandchild). If the grandchild succeeds, you won't get an indication except your child process will start again. In your gradchild process, if you fail, you want to throw a signal up to your parent. and when the parent starts, you can check to see if this signal was received. If not, you know it completed successfully.

What does wait() do on Unix?

I was reading about the wait() function in a Unix systems book. The book contains a program which has wait(NULL) in it. I don't understand what that means. In other program there was
while(wait(NULL)>0)
...which also made me scratch my head.
Can anybody explain what the function above is doing?
man wait(2)
All of these system calls are used to wait for state changes in
a child of the calling process, and obtain information about the child
whose state has changed. A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by a signal
So wait() allows a process to wait until one of its child processes change its state, exists for example. If waitpid() is called with a process id it waits for that specific child process to change its state, if a pid is not specified, then it's equivalent to calling wait() and it waits for any child process to change its state.
The wait() function returns child pid on success, so when it's is called in a loop like this:
while(wait(NULL)>0)
It means wait until all child processes exit (or change state) and no more child processes are unwaited-for (or until an error occurs)
a quick google suggests, wait(NULL) waits for any of the child processes to complete
wait(NULL) which should be equivalent to waitpid(-1, NULL, 0)
wait(NULL) waits for all the child processes to complete

How to exit a child process and return its status from execvp()?

In my simple custom shell I'm reading commands from the standard input and execute them with execvp(). Before this, I create a fork of the current process and I call the execvp() in that child process, right after that, I call exit(0).
Something like this:
pid = fork();
if(pid == -1) {
perror("fork");
exit(1);
}
if(pid == 0) {
// CHILD PROCESS CODE GOES HERE...
execvp(pArgs[0], pArgs);
exit(0);
} else {
// PARENT PROCESS CODE GOES HERE...
}
Now, the commands run with execvp() can return errors right? I want to handle that properly and right now, I'm always calling exit(0), which will mean the child process will always have an "OK" state.
How can I return the proper status from the execvp() call and put it in the exit() call? Should I just get the int value that execvp() returns and pass it as an exit() argument instead of 0. Is that enough and correct?
You need to use waitpid(3) or wait(1) in the parent code to wait for the child to exit and get the error message.
The syntax is:
pid_t waitpid(pid_t pid, int *status, int options);
or
pid_t wait(int *status);
status contains the exit status. Look at the man pages to see you how to parse it.
Note that you can't do this from the child process. Once you call execvp the child process dies (for all practical purposes) and is replaced by the exec'd process. The only way you can reach exit(0) there is if execvp itself fails, but then the failure isn't because the new program ended. It's because it never ran to begin with.
Edit: the child process doesn't really die. The PID and environment remain unchanged, but the entire code and data are replaced with the exec'd process. You can count on not returning to the original child process, unless exec fails.
From your question it's a little hard to figure out what you're asking. So I'll try to cover a couple of the related issues:
execvp() either does not return (on success), or it returns an error. Meaning your child code only need handle error conditions. Your child code should capture the result of execvp() and use that value in exit() as you suggested. Your child code should never return 0, since the only success means that the execvp worked and that processs will return 0 (or not).
The parent can obtain child info from waitpid() about it's exit status. There are several macros defined to pull info from the returned status parameter. Notable for your purpose are WIFEXITED to tell you if the child exited "normally", and WEXITSTATUS to get the child's status as passed to exit(). See the waitpid man page for other macros.
Use wait() or waitpid() in the parent process. An example here: Return code when OS kills your process.
Also, when a child dies the SIGCHLD signal is sent to the parent process.

Resources