We have no Fork In the early days ( late 1990's ) uClinux was scorned by a few because , without a MMU, it could not do a true fork. The fork system call is the mechanism by which every single UNIX process normally gets started. Every process is, in fact, a clone of the first process started by the booting kernel. A fork produces a parent and child. Each have individual task control structures and each can be scheduled independently by the kernel. When we have an MMU the parent and child eventually occupy different physical memory spaces. The MMU maps different physical memory addresses to identical virtual memory addresses and copies the data from parent to child. Under Linux this happens when either process attepmts to write to a shared physical memory area. On a typical system this cloneing can work because immediately after the fork the child replaces the parent's program with a new executable. Sometimes the parent and child are supposed to be identical at first but operating on different data. uClinux does not have the functionality of a true fork. It does have a close cousin the vfork system call. When a vfork system call is made the parent task is halted (put in a wait queue and suspend it) and a new task control block created using the same text memory, stack, and data memory as the parent. The new child then has to replace its program and data areas with an execve call or it has to exit before the parent is allowed to continue. The execve system call is one of a series of exec system calls that all have different arrangements of arguments and envronment variables. Try this man 2 execl The purpose of this call is to use the same kernel space task control block but run a different program under that control block. The text and data are read from an executable file specified in one of the arguments and the new program starts from the defined start location specified in the new executable. Once the new program memory has been established the parent task is released. Some Examples The uClinux Distribution contains many good examples of using vfork. The simpleinit program ( which is the first user code executed by the kernel after boot) is given the job of running all the initial programs required by the system. Here is an extract where it is booting to single user mode and trying to run a shell program. // part of the "boot to single user "code av[0] = _PATH_BSHELL; // /bin/sh av[1] = NULL; if((pid = vfork()) == 0) { // pid = 0 in the child extern char **environ; /* the child */ execve(_PATH_BSHELL, av, environ); // if this works parent is released err("exec of single user shell failed\n"); _exit(0); // if execve failed exit releases parent } else if(pid > 0) { int i; // this is the parent while(wait(&i) != pid) /* nothing */; } else if(pid < 0) { err("fork of single user shell failed\n"); } //parent continues, the child has execve'd a new program. Another example static int do_command(const char *path, const char *filename, int dowait) { pid_t pid, wpid; int stat, st; if((pid = vfork()) == 0) { /* the child */ char *argv[3]; char *env[3]; close(0); argv[0] = (char *)path; argv[1] = (char *)filename; argv[2] = NULL; env[0] = "PATH=/bin:/usr/bin:/etc:/sbin:/usr/sbin"; env[1] = NULL; execve(path, argv, env); err("exec rc failed\n"); _exit(2); } else if(pid > 0) { // parent if (!dowait) { stat = 0; } else { /* parent, wait till rc process dies before spawning */ while ((wpid = wait(&stat)) != pid) if (wpid == -1 && errno == ECHILD) { /* see wait (2) manpage */ stat = 0; break; } } } else if(pid < 0) { // error in vfork caught here err("fork of rc shell failed\n"); stat = -1; } st = WEXITSTATUS(stat); return st; } The inetd internet daemon is given the task of monitoring a number of internet sockets and waiting for a connection on one of them. When a connection is established the daemon has to fork a new process to handle the connection. The /etc/inetd.conf file contains details of the ports to be monitored and the programs used to handle incoming connections. Example /etc/inetd.conf file Having established a connection attempt on a designated port the daemon is required to start executing the handler task for that connection. The complication here is that the child has to have the incoming socket as its stdin and stdout devices. The incoming port (fd in this case ) is transferred to stdin and stdout of the new task after the vfork. Here is a code example showing this. // here fd is the incoming socket static pid_t start_child(struct stService *p, int fd) { pid_t pid; pid = vfork(); if (pid == 0) { // if we are the child if (fd != 0) dup2(fd, 0); // fd becomes stdin if (fd != 1) dup2(fd, 1); // fd becomes stdout if (fd != 2) dup2(fd, 2); // fd becomes stderr if (fd > 2) close(fd); // now we can close it (the dups stay open) close_all_fds(2); execlp(p->args[0], // run the new program with fd as stdin etc p->args[0], p->args[1], p->args[2], p->args[3], p->args[4], p->args[5], NULL ); _exit(0); } return(pid); // back to the parent here } Webservers A webserver is another example of a process needing to use forks. Each incoming socket is normally passed to a forked clone of the parent. Consider this code from cgi.c ( in directory user/boa/src ). Here the switch to MMUless is quite painless. // either fork or vfork will do here #if HAVE_FORK child_pid = fork(); #else child_pid = vfork(); #endif Consider this code from thttpd.c ( in directory user/thttpd/ ) which shows the modification made to the MMUfull version of this code to make it run as a foreground process , skipping the daemonize fork */ #ifndef EMBED (void) fclose( stdin ); (void) fclose( stdout ); (void) fclose( stderr ); /* Daemonize - make ourselves a subprocess. */ switch ( fork() ) { case 0: break; case -1: syslog( LOG_CRIT, "fork - %m" ); exit( 1 ); default: exit( 0 ); } #endif Stubborn Cases There are a few. You have to fork and you originally ( in MMUfull world) wanted an exact copy of yourself. These are the problem cases. Normally you can save important data in environment variables and then restart yourself in a call to execve using a command line argument to select the child mode of operation. The new child task will detect that it is functioning in child mode and restore the needed data from environment variables and continue.