0

I have this code:

int main(int argc, char *argv[]) {

    for (int i = 1; i < argc; i++) {
        pid_t pid = fork();

        if ( !pid ) {
            char *aux[] = {argv[i], NULL};
            execvp(aux[0], aux);
            printf("\nChild %d executed %s with exit code %d\n", getpid(), aux[0], i); 
            _exit(i);
        }

    }

    for (int i = 1; i < argc; i++) {
        printf("Child dead\n");
        wait(NULL);
    }

    return 0;
}

When running, I noticed printf(...) after execvp on child process never runs. I checked man exec where I found:

"The exec() family of functions replaces the current process image with a new process image."

Does this mean that besides printf, _exit(i) is also not executed? If so, how can I kill child process? Does execvp new process image takes care of that?

EDIT: Assuming I want to do a printf (or any other instruction) after execvp line, as shown in the code, is there any way?

EDIT[2]: To make it more clear. If I have this chunk:

int main(void) {

    for (int i = 0; i < 10; i++) {
        pid_t pid = fork();

        if (!pid) {
            printf("child %d\n", getpid());
            //_exit(1);
        }

        else {
            wait(NULL);
            printf("parent here\n");
        }
    }

    return 0;
}

Things go wild when exit(1) is commented. So, my doubt is: is there any scenario where execvp executes correctly and the origin child process (the one that calls execvp) does not exit like in _exit(n) call, producing results like in the above chunk.

  • 1
    The child process exits when the program you run exits. Also, remember that *all* processes are started by a `fork` call, and *all* programs are "executed" using one of the `exec` functions. That includes your own program, whose process will exit when you return from the `main` function. – Some programmer dude Mar 15 '17 at 17:03
  • Possible duplicate of [How to exit a child process and return its status from execvp()?](http://stackoverflow.com/questions/903864/how-to-exit-a-child-process-and-return-its-status-from-execvp) – hmatar Mar 15 '17 at 17:11

3 Answers3

2

Does this mean that besides printf, _exit(i) is also not executed?

Yes. Except if execvp() (and other exec family functions) failed, then it'll return and in that the printf() and _exit() statements will be executed.

If so, how can I kill child process? Does execvp new process image takes care of that?

execvp() doesn't kill child process - it doesn't even know the process it's replacing is a child process.

In most practical cases, you don't want to "kill" child processes. You'd want to wait for the child process(es) to complete using wait(2) - which you already do. The child process(es) are independent just like your "main" process; they'll exit when their main() returns or by calling exit()/_exit etc or they terminate abnormally (e.g. getting killed by SIGTERM/SIGKILL signals).

P.P
  • 117,907
  • 20
  • 175
  • 238
  • so, even if _exit(i) is not executed, wait will take care of that, right? it wouldn't even be necessary? –  Mar 15 '17 at 18:03
  • `wait()` simply waits for the status change in its child processes. `_exit()` is necessary in case `execvp()` fails. Otherwise, both parent and child processes would continue which you wouldn't want. That's there's usually a call to exit() (or _exit/_Exit) after `exec*()` call. i.e. it'd constitute a programming error. – P.P Mar 15 '17 at 18:15
  • so I have no way of force execvp exit? –  Mar 15 '17 at 18:29
  • You said "it doesn't even know the process it's replacing is a child process". You could alternatively argue "it always knows that the process it is replacing is a child process, but it doesn't care". There's never a time (AFAIK, even at system startup) when a process switch via `execvp()` or friends is not a child process. – Jonathan Leffler Mar 15 '17 at 18:29
  • or is execvp exit success guaranteed by its return value? –  Mar 15 '17 at 18:30
  • @Ricardo: no (there's no way to force `execvp()` to exit). If `execvp()` is successful, it does not return. If it fails, it returns. Your parent process could send a signal to its child, or something similar, but you're into IPC and there's nothing that the code after `execvp()` can do; it is not executed unless `execvp()` fails. – Jonathan Leffler Mar 15 '17 at 18:31
  • @Ricardo "is there any scenario where execvp executes correctly and the origin child process (the one that calls execvp) does not exit like in _exit(n) call" -- if `execvp()` succeeds then the "origin child process" doesn't exist anymore - it's been replaced with a *new* process image. As long as that new program exits properly, there's no problem at all. – P.P Mar 15 '17 at 18:57
2

Does this mean that besides printf, _exit(i) is also not executed?

Correct. execvp only returns when it fails.

If so, how can I kill child process? Does execvp new process image takes care of that?

I think you're actually asking what you need to do to ensure the process will exit since you can't call exit yourself.

You don't need to do anything. The program now executing in the process will call exit when it wants to exit.

If so, how can I kill child process?

If you really are asking how to kill (prematurely terminate) the process, it's done by using kill to send a signal. The pid of the process to which to send the signal was returned by fork.

I want to do a printf (or any other instruction) after execvp line, as shown in the code, is there any way?

No. Your program is no longer running in that process. All your code and variables have been replaced with the new program's. If you want that process to output something, it would have to be done by the program you executed.

The parent can check if the child has successfully called exec by creating a pipe (before the fork) with the FD_CLOEXEC flag set.

int pipefd[2];
pid_t pid;
int status;

if (pipe2(pipefd, O_CLOEXEC) == -1) {
   perror("Can't create pipe");
   exit(1);
}

pid = fork();
if (pid == -1) {
   perror("Can't create pipe");
   exit(1);
}

if (!pid) {
    char* child_argv[] = { argv[i], NULL };

    close(pipefd[0]);

    execvp(child_argv[0], child_argv);
    write(pipefd[1], &errno, sizeof(errno));
    _exit(1);
}

{
   char buf[sizeof(errno)];
   ssize_t bytes_read;

   close(pipefd[1]);

   bytes_read = read(pipefd[0], buf, sizeof(buf));
   if (bytes_read == -1) {
      /* Ignoring read errors */
   }
   else if (bytes_read > 0) {
      /* An error occurred in the child */
      if (bytes_read >= sizeof(errno)) {
         memmove(&errno, buf, sizeof(errno));
         perror("Can't exec");
      } else {
         fprintf(stderr, "Can't exec: Unknown error\n");
      }

      waitpid(pid, &status, 0);
      exit(1);
   }
}

/* The child is now running the executed program */
waitpid(pid, &status, 0);
if      ( status & 0x7F ) { fprintf(stderr, "Child killed by signal %d\n", status & 0x7F); }
else if ( status >> 8   ) { fprintf(stderr, "Child exited with error %d\n", status >> 8); }
else                      { fprintf(stdout, "Child completed successfully\n"); }
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • when you say "You don't need to do anything. The program now executing in the process will call exit when it wants to exit." does that mean that execvp will call its own exit and then child process will call _exit()? Assuming I want to do a printf after execvp line, as shown in the code, is there any way? –  Mar 15 '17 at 18:00
  • Re "*does that mean that execvp will call its own exit and then child process will call _exit()?*", No, `execvp` doesn't do any of that. It doesn't create a child. It doesn't call `_exit`. `execpv` simply changes what program is executed by the current process. /// Re "*I want to do a printf after execvp line*", You can't. Your program is no longer running in that process. All your code and variables have been replaced with the new program's. – ikegami Mar 15 '17 at 18:07
  • so I have no way of force execvp exit? –  Mar 15 '17 at 18:28
  • or is execvp exit success guaranteed by its return value? –  Mar 15 '17 at 18:31
  • Re "*so I have no way of force execvp exit?*", No idea what you mean by this. – ikegami Mar 15 '17 at 18:34
  • Re "*is execvp exit success guaranteed by its return value?*", No idea what you mean by "execvp exit". If you're asking if `execvp`'s success is guaranteed, then no, it' snot. For example, the file to execute might not exist. On error, `execvp` will return, and `errno` will be set. (On success, your program ceases to run, so it doesn't see `execvp` returning, just like it wouldn't see `exit` returning.) – ikegami Mar 15 '17 at 18:41
  • in other words: is there any possible scenario where `execvp` executes correctly but child process (the one that executes execvp on the code above) does not properly exit()? –  Mar 15 '17 at 18:42
  • Yes. In addition to exiting successfully (`exit(0)`) and existing with an error (`exit(1)`), it could run forever, and it could be killed by a signal. – ikegami Mar 15 '17 at 18:44
  • When your program is no longer running, it can't "go wild". `execvp(...); perror("exec"); _exit(1);` is enough. – ikegami Mar 15 '17 at 18:52
0

Provided the execvp call succeeds, you are correct, neither the printf nor the _exit call will be executed. But a good practice would be to check if the execvp call succeeded, to troubleshoot if it doesn't.

As said in a comment, the program will, hopefully, terminate by itself and be reaped by your wait calls properly.

Killing the process will be necessary only if, for any reason, you want to abort it before it terminates. To do so, you will have to use the kill function, using the pid of the process as returned by fork and the signal you want to send it.

Answer to the EDIT

Assuming I want to do a printf (or any other instruction) after execvp line, as shown in the code, is there any way?

No, unless the execvp call fails. After (inside, in fact...) the execvp, the "text" of the new program will be executed, it's main() will run etc... The "text" of the parent program will be discarded (the replace part in your manpage quote).

Answer to the EDIT[2]

is there any scenario where execvp executes correctly and the origin child process (the one that calls execvp) does not exit like in _exit(n) call

I'm not sure I understood completely your new question.
The program executed by execvp can exit in many ways, exit(), _exit(), by returning from main(), by being killed by a signal... But the _exit call, for example, will be in the "text" of the new program, not of the parent's one.
It may stay alive "forever" too, in which case, you'll have to take some precautions to avoid being blocked inside a wait call in the parent. Most of the time, parents will wait for SIGCHLD signals to detect that a child must be wait-ed and maybe kill() them when they want it to terminate.

As for this part of your question:

... producing results like in the above chunk.

The key point is that your second snippet doesn't call execvp.

After the fork() calls, the two processes (the parent and the child) continue to execute the same program, with the same state. So after forking, both will continue to execute the loop. So the parent process will try to fork (10 - i) children processes and the child process will do the same thing. So you'll end up with many more than 11 processes, like I imagine you really intended.

Note: Maybe using gdb to trace your program will help you understanding more precisely how things work. You'll have to use set follow-fork-mode [child|parent] before each fork() call. It's a little bit tedious but doable.

ncarrier
  • 433
  • 3
  • 14