Creating Processes
Creating Processes
Assignment#1
Android might decide to shut down a process at some point, when memory is low and
required by other processes that are more immediately serving the user. Application
components running in the process that's killed are consequently destroyed. A process
is started again for those components when there's again work for them to do.
When deciding which processes to kill, the Android system weighs their relative
importance to the user. For example, it more readily shuts down a process hosting
activities that are no longer visible on screen, compared to a process hosting visible
activities. The decision whether to terminate a process, therefore, depends on the state
of the components running in that process.
approach of separating these steps into two distinct functions: fork() and exec()8. The first, fork(), creates
a child process that is a copy of the current task. It differs from the parent only in its PID (which is
unique), its PPID (parent's PID, which is set to the original process), and certain resources and statistics,
such as pending signals, which are not inherited. The second function, exec(), loads a new executable into
the address space and begins executing it. The combination of fork() followed by exec() is similar to the
single function most operating systems provide.
Copy-on-Write
Traditionally, upon fork() all resources owned by the parent are duplicated and the copy is given to the
child. This approach is significantly naïve and inefficient in that it copies much data that might otherwise
be shared. Worse still, if the new process were to immediately execute a new image, all that copying
would go to waste. In Linux, fork() is implemented through the use of copy-on-write pages. Copy-on-
write (or COW) is a technique to delay or altogether prevent copying of the data. Rather than duplicate
the process address space, the parent and the child can share a single copy. The data, however, is
marked in such a way that if it is written to, a duplicate is made and each process receives a unique
copy. Consequently, the duplication of resources occurs only when they are written; until then, they are
shared read-only. This technique delays the copying of each page in the address space until it is actually
written to. In the case that the pages are never written—for example, if exec() is called immediately
after fork()—they never need to be copied. The only overhead incurred by fork() is the duplication of the
parent's page tables and the creation of a unique process descriptor for the child. In the common case
that a process executes a new executable image immediately after forking, this optimization prevents
the wasted copying of large amounts of data (with the address space, easily tens of megabytes). This is
an important optimization because the Unix philosophy encourages quick process execution.
fork()
Linux implements fork() via the clone() system call. This call takes a series of flags that specify which
resources, if any, the parent and child process should share (see the section on "The Linux
Implementation of Threads" later in this chapter for more about the flags). The fork(), vfork(), and
__clone() library calls all invoke the clone() system call with the requisite flags. The clone() system call, in
turn, calls do_fork().
The bulk of the work in forking is handled by do_fork(), which is defined in kernel/fork.c. This function
calls copy_process(), and then starts the process running. The interesting work is done
bycopy_process():
Back in do_fork(), if copy_process() returns successfully, the new child is woken up and run.
Deliberately, the kernel runs the child process first 9. In the common case of the child simply
callingexec() immediately, this eliminates any copy-on-write overhead that would occur if the parent ran
first and began writing to the address space.