Fork and Exec

TheKernel

UNIX-likesystems have a kernel, which contains device drivers, file systems, networkingstacks, memory managment, and CPU scheduling code. The kernel also providessupport for user-level processes, the purpose of which is to run an arbitraryprogram.

Processes

A processis a single, active, invocation of a program. There are usually dozens ofprocesses on a UNIX machine, each with a specific purpose. For example, login(1) isresponsible for authenticating users (by checking a username/passwordcombination) and starting a shell. A single kernel may be running severaldifferent processes with the same program (e.g. we are all running emacs(1) onspinlock).

Whatdata makes up a process?

A processconsists of all the state needed to run a UNIX program. Some of this state isstored in the CPU registers and the allocated memory. The kernel also maintainsa Process Control Block (PCB) for each process (Linux calls thisa task_struct; it's defined in sched.h).The PCB contains all the internal state that the kernel uses to provide theprocess abstraction, including a list of open files, the user ID that theprocess is running as, and scheduling information. The PCB is not directlyaccessible to user programs. We might draw this all as: 
    



    

The text segment is the UNIX term for theprogram's code in machine language. Note that, while I've included arrows forthe SP (stack pointer) and PC (program counter) on the memory drawing, the SPand PC are actually stored in CPU registers.

Howdoes a process get created?

Anyprocess can create a new process by making a request to the kernel. Forexample, I might request that my shell  create a new mail(1) process by typing the text 'mail\n' at aprompt. The shell translates this command into a series of system calls, which,as we'll see, winds up creating a new process running the mail program. (The kernel creates init(8), the first process, at boot. The kernel neverspontaneously creates any other processes.)

UnderUNIX, two main system calls are used by the shell to execute my command: first,the shell clones itself using the fork(2) syscall;then, this clone replaces itself with the mail program using the exec(3) syscall.Kind of strange, but it turns out to be quite useful.

fork(2)

Thefunction of fork(2) is to create an almost exact duplicateof the process that calls it. So, if we had the above diagram before theprocess invoked the fork syscall, then the diagram afterward would look like:





        

      

Mostfields of the PCB are copied from the original to the newly created PCB; theyare starred and shown in blue above (we'll call the new processthe child and the oldthe parent from now on).The parent's memory is also copied .Note that the PC of both processes is exactly the same.

Eventually,the kernel will get around to running these two processes. (Which one first?That's undefined; either could run first.) When this happens, the process (beit child or parent) will continue from it's saved PC. Since the process justexecuted the fork syscall,the PC will point to whatever instruction is immediatly following it.

Readersmay have noticed a problem at this point. The processes are both exactly thesame. When the kernel runs one, it'll do some stuff (whatever the instructionsafter the fork tellit to do). Then, when the other one runs, it'll do...the exact same thing.That's not too useful.

Toget around this, the processes differ in a very slight but important way. Thereturn value of the fork syscall will be 0 in the child process, and will begreater than 0 in the parent. We can use this to have the parent take onecode-path while sending the (almost identical) child down a different path.

Tosummarize, immediatly after executing a fork:

  • There are 2 processes that are exactly the same, except for the differences described in the fork(2) man page.
  • Both processes are at the same line of code (the line immediatly after the fork).
  • In the child process, the return value of the fork is 0.
  • In the parent process, the return value of the fork is greater than 0.

exec(3)

So we nowhave two copies of the shell. But they are still both running the shellprogram; we want the child to run the mail(1) program. The child uses anothersyscall, exec(3), to replace itself with the mail program. exec does not create a new process; it justchanges the program file that an existing process is running.

exec firstwipes out the memory state of the calling process. It then goes to thefilesystem to find the program file requested. exec copiesthis file into the program's memory and initializes register state, includingthe PC.

exec doesn'talter most of the other fields in the PCB - this is important, because it meansthe process calling exec(3) (thechild copy of the shell, in this case) can set things up if it wants to, forexample changing the open files or other user ID.

Atthis point we've still got two processes, but now one (the parent) is theshell, and the other (the child) is mail(1).The kernel will run both of them, making sure each gets a bit of processingtime. It is likely the case that the shell wants to wait for the mail (child)process to finish before doing anything else; it can tell the kernel this usingthe wait(2) syscall.

Fork and ExecExample

#include <unistd.h>
#include <sys/wait.h>
#include <iostream>
using namespace std;
int main(){
  pid_t pid;
  int status, died;
     switch(pid=fork()){
     case -1: cout << "can't fork\n";
              exit(-1);
     case 0 : sleep(2); // this is the code the child runs
              exit(3); 
     default: died= wait(&status); // this is the code the parent runs 
     }
}

The fork systemcall in Unix creates a new process. The new process inherits various propertiesfrom its parent (Environmental variables, File descriptors, etc - see themanual page for details). After a successful fork call,two copies of the original code will be running. In the original process (theparent) the return value of fork willbe the process ID of the child. In the new child process the return value of fork willbe 0. Here's a simple example where the child sleeps for 2 seconds while theparent waits for the child process to exit. Note how the return value of fork isused to control which code is run by the parent and which by the child.

#include <unistd.h>
#include <sys/wait.h>
#include <iostream>
using namespace std;
 
int main(){
   pid_t pid;
   int status, died;
   switch(pid=fork()){
   case -1: cout << "can't fork\n";
            exit(-1);
   case 0 : execl("/usr/bin/date","date",0); // this is the code the child runs 
   default: died= wait(&status); // this is the code the parent runs
   }
}

In the examples above,Often however, you want the new process torun a new program. When, for example, you type "date" on the unixcommand line, the command line interpreter (the so-called "shell")forks so that momentarily 2 shells are running, then the code in the childprocess is replaced by the code of the "date" program by using one ofthe family of exec systemcalls. Here's a simple example of how it's done.

Inall these examples the parent process waits for the child to exit. If theparent doesn't wait, but exits before the child process does, then the child isadopted by another process (usually the one with PID 1). After the child exits(but before it's waited for) it becomes a "zombie". If it'snever waited for (because the parent process is hung, for example) it remains azombie.In more recent Unix versions, the kernel releases these processes, butsometimes they can only be removed from the list of processes by rebooting themachine. Though in small numbers they're harmless enough, avoiding them is avery good idea. Particularly if a process has many children, it's worth using waitpid() ratherthan wait(), so that the code waits for the right process. Some versionsof Unix have wait2(), wait3() and wait4() variantswhich may be useful.



        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值