-2

I have a problem where I am trying to run multiple commands through execvp(). To do this, I have a while loop doing through each command, parsing it, then calling a function to use fork + exec.

The issue is, I will run fork + exec, and while I wait for the exec to go through, the parent runs and continues on to the second loop, ie. the second command. But what happens, from my understanding, is the child process from the previous loop takes over and all of a sudden, the child process is the current process.

How do you run a process in the child process, but maintain control in the parent? All the examples I've seen on SO have been for parent processes what wait for the child process to terminate before continuing, but I don't have a choice in this assignment - I'm supposed to keep the child processes running and check later to see if they're still running.

Here's some pseudocode of my thought process:

funct forkprocess() {
   //call fork
   //call execvp within child process, let process run and return control to parent
       //if execvp failed, kill child process
   //return child pid
}

int main() {
   while(not end of file containing list of commands) :
      //parse command for execvp call
      //call forkprocess() to run process
      //if childpid is returned, report it and store pid
      //else, report failure
   }
}

My output has been close to the following:

\\parent PID is printed
\\any code outside the if-else ladder for fork is printed
\\it jumps back to main and prints any statements there

[jumps to next iteration in loop, ie. the next command]

\\child PID is printed
\\parent PID is printed
\\any code outside the if-else ladder for fork is printed
\\it jumps back to main and prints any statements there
\\child PID is printed
B.M. Corwen
  • 205
  • 3
  • 10
  • `How do you run a process in the child process, but maintain control in the parent?` . What does that mean? What do you mean by "control"? Are you trying to ensure that the parent doesn't yield the processor? Are you trying to ensure that the first child does not begin executing until all the children have been created? Are you trying to ensure that all the children run in some particular sequence? – William Pursell Oct 15 '19 at 02:57
  • And what do you mean by "current process"? If you have multiple processors, and multiple children processes all running and writing data to your tty...then you will get interleaved output. It sounds like you're looking for a synchornization mechanism. – William Pursell Oct 15 '19 at 02:58
  • In the `fork()` call, I'm trying call `exec()` in the child process, `exec()` being the command I'm trying to run (ex. `ls a`). However, I only want to run`exec()`, and then go back to the parent to keep running more child processes for more commands that I have. Meanwhile, the child process just keep running in the background. Hopefully that makes sense, I'm having trouble explaining it. – B.M. Corwen Oct 15 '19 at 03:07
  • 1
    After you call fork, both the parent and child will be running concurrently. If the child execs 'ls', then it will write output to its stdout, which is probably the terminal from which you invoked the program. The parent is still running (uinless it has exited for some reason) and spawning more children. Just because the child is writing output to the tty doesn't mean it has "take[n] over". – William Pursell Oct 15 '19 at 03:09
  • Something in what you said made something click, so I went back and fixed something. Now I'm able to see the behaviour that you're referring to. Now the issue seems to be that in the first run of the `while` loop, it only seems to go through the parent code clock. It's not until the second run of the `while` loop (which should be for the second command) that the child process from the first run starts running. Is there some way to fix that lag? – B.M. Corwen Oct 15 '19 at 03:16
  • It's really tough to say, since you've given no code. What makes you think the first child isn't running until the 2nd iteration of the loop? Is it the order of some output coming from the parent and the child? Is the 2nd child writing something before the first child? There are potentially a lot of different issues. – William Pursell Oct 15 '19 at 04:30
  • I had print statements showing the pid of the child and the parent every time I looped in an iteration. In the first iteration of the `while` loop, only print statements from the parent printed (and the child's PID), including the parent's PID, and in the second iteration, the child PID from the first iteration printed. – B.M. Corwen Oct 15 '19 at 04:49
  • It sounds like the parent spawned the first child, printed some data, then spawned the second child and printed some data, and then the first child got scheduled and printed some data. In a multi-tasking system, that's pretty normal. – William Pursell Oct 15 '19 at 05:00
  • The answer to this may be obvious, but how am I supposed to track whether a process failed or not? Would I just run waitpid() for the previous process and see if it returns the child PID or not? And how does that look like for failed exec calls? – B.M. Corwen Oct 15 '19 at 05:17
  • Debugging someone else's pseudocode is essentially impossible. If you can't create an MCVE ([Minimal, Complete, Verifiable Example](https://stackoverflow.com/help/mcve)) (or MRE or whatever name SO now uses; MCVE was good for over five years and it did not need changing) or an SSCCE ([Short, Self-Contained, Correct Example](http://sscce.org/)), then we probably can't help you much more. – Jonathan Leffler Oct 15 '19 at 09:35

2 Answers2

0

You need to make sure that exec is only called in the child, not the parent. When fork() returns, it does so twice (once in the child, once in the parent), and it returns the following values:

  • non-zero (the PID of the child) if we are in the parent. We can use this PID to wait for the child to terminate using waitpid.
  • zero if we are in the child.
  • negative if the fork failed (in which case it only returns once, in the parent process, since a child process could not be created)

You may want to consider using the following structure:

int pid = fork();
if(pid < 0) {
    perror("fork"); // remember to error check!
} else if (pid == 0) {
    // We are in the child
    // ...
    if(execvp(...) < 0) {
        perror("exec");
    }
} else {
    // We are in the parent; wait for the child to terminate using waitpid if desired.
    waitpid(...);
}

I include the waitpid call here for simplicity; since your assignment requires you to keep the child processes running you can check their status later by using either that function, or by handling SIGCHLD.

nanofarad
  • 40,330
  • 4
  • 86
  • 117
  • Thank you for answering! I have done this though in my code - I used `waitpid()` with the `WNOHANG` option, so I wouldn't have to wait for the child process. The child process, as far as I know, won't terminate. How would that work in that case? – B.M. Corwen Oct 15 '19 at 02:33
  • @B.M.Corwen If no child processes have exited and you call `waitpid(0, &status, WNOHANG)`, it will immediately return zero (as opposed to a child PID if a child had indeed terminated): http://man7.org/linux/man-pages/man2/waitpid.2.html#RETURN_VALUE. I think a bit of confusion might stem from trying to combine the fork+exec with the step of waiting for the child; based on your description the issue lies more with the former than the latter. – nanofarad Oct 15 '19 at 02:37
  • I'm sorry, I'm still not quite understanding. In my function that calls fork, I have actually structured my code like how you suggested above, and I'm still running into the issue of the child process taking over. While the control does eventually go back to the parent, there is a lag where the exec doesn't appear to run until the next loop, where I'm already supposed to be testing for the next command. Is there some way to manage this? – B.M. Corwen Oct 15 '19 at 02:45
  • 1
0
int main(int argc, char ** argv) {
    int status;

    if((waitpid(fork(), &status, 0)) == -1) {
       execvp(...);
       /* print error */
       exit(1);
    }
    return 0;
}

Thanks for the question, if fork is passed straight into waitpid the parent will wait, meanwhile the child will execute exec, which will exit the thread on success or return on failure and continue to print the error and exit. This is really helpful for executing multiple functions at once ...

int main(int argc, char ** argv) {
    int *status = malloc(sizeof(int) * (argc - 1));
    int i = 0;

    while(((waitpid(fork(), &status[i++], 0)) == -1) || i < argc) {
       execvp(argv[i], &argv[i + 1]);
       /* print error */
       exit(1);
    }
    return 0;
}