Question about execl, replacing process's contents

I'm reading Operating Systems in Depth by Thomas W. Doeppner, and I have a question about execl. He says it's called after fork(), and that it replaces the text (code) of the current process and replaces it with the code of the new program. But that doesn't make sense to me.

Does that mean process A forks itself and its copy is process B, but process A's code is gone, and in its place is the code from process B?

OR does that mean process A forks itself and its copy is process B; process B loads a program into its text section, and now process B's text section is gone and in its place is the program it loaded?

Edit: I realize I forgot to mention the language the author uses in his examples is C

Please note: This is simplified! when you read the man page for exec or fork it goes into lots of technical details. This is how I used to teach this to OS students as a high level peek. Start with a simplified model and then build on it.

Call the parent "A"

After a fork() call you now have two processes, which are identical copies (a little over simplified), A the parent and "B" the child.

If B calls execl() it clears everything out of B's memory, loads new memory and then runs.
You now have two threads of execution, A and B. Otherwise B is running the same code as A see #2 below.

The parent A has three choices:

  1. most times it calls wait() to wait until the child ends. It will receive the SIGCHLD signal to let it know the status of the child. Know that the child can write to STDOUT , STDERR , and read from STDIN . The return value of the wait() call tells the parent things went ok or not ok.

  2. Parent can chug along doing something else, checking periodically for termination of the child. Usually in this case the child is running a function that the parent called for it to run. Kind of like sending your kid to the store while you cook dinner. This often involves IPC (interprocess communication calls) somewhat like making your kid take a cell phone with her.

  3. The parent can call exit(). If the child then calls setsid() it takes over being in charge of things like the existing tty it inherited from the parent. And possibly other objects in memory. Often lots of objects.

#1 is mostly what happens when you run a command.

#2 is what happens when the main process wants to run extra concurrent threads of execution, for example in shell scripts. It is very common on database servers. Used for efficiency usually.
When you learn about shared libraries you will understand one of the main efficiencies of this.

#3 is how to start a daemon - another name for a service like ftpd, which has no connected tty. Note the d on ftpd - means daemon. What you see:After you start a service your connection to the new program terminates and you go back to the command line prompt. You actually forked a child ("B"), which then forked the service process (call it process "C") using the same code as the parent and then the parent "B" died. C never calls setsid() id it wants to run as a detached process like a service. And since "B" died you go back to the command line.

There are variations on this but you now have the basics.

1 Like

Alright, thank you for clarifying that :smiley:

---------- Post updated 12-18-16 at 09:20 PM ---------- Previous update was 12-17-16 at 10:28 PM ----------

As I meditated on what you said, I thought of something. In your #3 explanation, would the tty be process "A"? Or would the tty in your example be process "B"? I guess I have a slight and subtle confusion about using "fork" and "forked" in the verb context. When you said "You actually forked a child ("B")", was forking the child a result of calling fork() in process "A", or calling fork() in process "B"? I hope you can see what I'm getting at here. :stuck_out_tongue:

In #3, A calls fork() . At that point you have processes A and B. Then B calls fork() . At that point you have processes A, B, and C all running the same instructions. Then B calls exit() leaving you with processes A and C running. Then C calls execl() (or another function from the exec family) to replace the instructions C was running with the instructions needed to run the daemon. At that point you then have A running the code it was running and you have C running your daemon.

Note that since C was a child of B and B exited, A cannot use wait() to determine whether or not C is still running and cannot retrieve the exit status of C using wait() . (Grandparents do not become the parents of their children's orphaned children when their children die before their grandchildren.)

1 Like

Thanks, Don. That makes sense now.