A process is an "invocation" or "activation" of a program. A program is a list of instructions for execution by a
computer. A program may be stored in any convenient form including handwritten notes on pieces of paper but is most
commmonly stored in binary form in a file. To run the program it needs to be copied (or loaded) into the main
computer memory and the central processing unit told to start reading (and obeying) instructions taken from that area of
memory. The acitvity of executing the program's instructions is called running the process.
It will be seen that a potion of memory needs to be allocated to a process. The actual allocation of such an area of
memory is a task for the loader. The memory area allocated to a program will usually be split into several sub-areas for
particular.
The code area
This is known as the text area in Unix parlance and simply contains the executable code of the program. If there
are several processes running the same program there will still only be one code area as it is identical for all
processes. The current state of a process is determined by its various data areas. The kernel process exchange (or
switch) mechanism will activate a particular process by indicating to the hardware
where the next instruction is to be read from
what mapping is to be used to access data
The data area
This holds the data being processed by the program, it's size is initially determined by the loader from information
in the binary file which specifies the amount of initialised data to be copied form the binary file and the amount of
uninitialised space to be allocated to the process. On some systems the space may be initialised to zero but this is not
universal and such areas may contain "droppings" left over from previous processes, a possible security problem. On
older Unix systems the uninitialised space is known as bss from a PDP/11 assembler mnemonic.
The Unix size command will give the memory requirement
information for a binary file.
bash$ size testhand2
92763 + 7564 + 2320 = 102647
The above example shows that loading the binary file testhand2 will require 102647 bytes of memory with 92763
bytes for code, 7564 bytes for initialised data and 2320 bytes for non-initialised data.
The stack area and the arena
All modern processors expect programs to be built out of functions and procedures. Whenever a function is called a
stack frame is created to store data related to the current invocation of the function such as the return address,
saved state, local copies of parameters and, of course, local data. The stack is normally built within the data area of
the process and the memory allocated to the stack can be expanded as needed.
Dynamic memory allocation as supported by functions such as malloc will normally be in a separate area of
memory known as the arena or the heap. It is also part of the processes data area and also expands as
required.
The stack and the arena need to be kept separate from the initialised and uninitialised data areas, this is normally
achieved by using widely separated parts of a large virtual address space.
System calls
A system call is a function like mechanism whereby a process can request services from the kernel. A system call
includes a special instruction which causes a software interrupt (sometimes called a trap), responding to this
interrupt switches the processer into kernel or priviliged mode whilst maintaining the process identity. The response
also involves switching the stack pointer to the kernel stack
so that any kernel functions that are called in response to the system call will use the kernel stack rather than the
user stack.
It is interesting to note the overheads associated with handling a disc transfer completion interrupt are "charged"
to the process that happens to be running when the interrupt occurs.
Process Information
There will, inevitably, be a significant amount of information about a process that is only relevant to the kernel.
This will include data such as the process user-id (for access rights checks), pointers to open file information blocks,
saved register states for processes not currently running, process "environment" etc., etc. Some of this information is
stored in an entry in the process table
which has an entry for every current process and some is stored in a per-process memory area called the u_area on
Unix systems.
Details of the structure of a process table entry may be found in the file
/usr/include/sys/proc.h and the u_area in the file /usr/include/sys/user.h.
Process states
Processes can only be created by the system call
fork(), apart from process 1 which is "hand-crafted" at
boot time and is the ultimate parent of all other processes. Once a process has been created it can be in various states
as shown in the following diagram (based on Bach).
As shown above, aprocess can be in any one of nine distinct states.
Created
A freshly created process. Whether freshly created processes are entirely resident in memory depends on the
details of the memory management system. This state may also include processes that have not yet been fully created.
Ready to run, in memory
There is no reason why the process should not run apart from the fact that some other process is currently
running.
Running in kernel mode
The process is running in kernel mode. It may be handling a system call or an interrupt or some other process
(also in kernel mode) may have scheduled it to run. The process may determine that it has finished (either normally
via an exit() or via some kernel detected abnormal condition) or that it is blocked awaiting some event such as a
time signal or peripheral activity.
Running in user mode
The normal state.
Pre-empted
The process has been interrupted and is about to resume normal user mode operation. The kernel scheduler may move
a process into this state.
Zombie (or defunct)
The process will not run again but information, such as the exit code, has not been collected by the parent
process so a process table slot is still required to store this information.
Sleeping in memory
The process is blocked awaiting an event. All that can happen is that the process can be woken up (by changing
its status to "ready to run") or swapped out.
Sleeping, swapped out
The process is waiting for an event (details in the process table entry for the process) and has been swapped
out.
Ready to run, swapped out
Before running the process needs to copied back in to memory.
The concept of swapping implies that the entire memory image of a process is moved between backing storage (disc)
and memory. Modern systems still maintain copies of process images in the swapping area but dynamic paged memory
management automatically deals with the issues of moving page images between backing store and main memory resulting in
a blurring of the distinction between "swapped out" and "in memory" processes.
Process Information
The Unix operating system supports a number of standard commands for determining information about the processes
currently running on a computer. The most commonly used is undoubtedly 'ps', unfortunately this is one of the Unix
commands that come in two flavours, one system V derived and one BSD derived. Which flavour you get when you type "ps"
depends on your search path.
Here's some typical output from the BSD variant.
# ps -aguwwx | head -10
USER PID %CPU %MEM SZ RSS TT S START TIME COMMAND
root 12885 8.4 0.8 2152 1960 pts/0 O 15:18:19 0:01 ps -aguwwx
root 3 1.3 0.0 0 0 ? S Aug 12 1191:52 fsflush
ftp 12881 0.5 0.6 1884 1444 ? S 15:18:13 0:00 in.ftpd -l
root 173 0.5 18.04866845928 ? S Aug 12 265:51 /usr/sbin/in.named
root 134 0.4 0.5 5248 1184 ? S Aug 12 611:21 /usr/multicast/mrouted
root 12884 0.2 0.2 744 528 pts/0 S 15:18:19 0:00 head -10
nobody 12751 0.2 0.9 3032 2208 ? S 15:14:47 0:00 /appsy/apache_1.3.3/src/httpd -f /appsy/apache_1.3.3/etc/httpd.conf
nobody 12238 0.2 0.9 3032 2220 ? S 14:59:11 0:01 /appsy/apache_1.3.3/src/httpd -f /appsy/apache_1.3.3/etc/httpd.conf
nobody 12695 0.2 0.9 3032 2224 ? S 15:12:38 0:00 /appsy/apache_1.3.3/src/httpd -f /appsy/apache_1.3.3/etc/httpd.conf
The listing is in CPU usage order and has been trimmed to show the 10 most CPU intensive processes. Most of the
column headings are fairly obvious, "SZ" is the combined size of the data and stack segments (in kbytes), "RSS" is the
real memory used by the process. Some interesting points to note from the listing above.
The large amount of memory used by the /usr/sbin/in.named process, this is the Internet DNS server.
The multiple WWW server processes (/appsy/apache_1.3.3/src/httpd), there are, in fact, more of these
that have been trimmed from this listing.
The most CPU hungry proces is ps, this is normal.
For more information about processes the ps command may be used with the -l flag (meaning long
listing), the processes are no longer listed in CPU usage order.
This shows scheduling information (PRI (priority), NI (niceness) and CP (short-term CPU
utilisation)), the nature of the event the process is waiting for (WCHAN) and process flags (F).
When there are several runnable processes available, the next one to be run will usually be selected on a priority
basis. The prioirty is a function of the process history and a user manipulable parameter known as the niceness
of the process, the higher the niceness, the lower the priority, the highest value is 20.
The command priocntl
can be used to manipulate priorities. The command
nice can be used to launch a process at a specified
priority and
renice can be used to modify the niceness of an existing
process. Unless you are super-user you can only decrease the niceness of a process.
The fork() system call will spawn a new child process which is an identical process to the parent except
that has a new system process ID. The process is copied in memory from the parent and a new process structure is
assigned by the kernel. The return value of the function is which discriminates the two threads of execution. A zero
is returned by the fork function in the child's process.
The environment, resource limits, umask, controlling terminal, current working directory, root directory, signal
masks and other process resources are also duplicated from the parent in the forked child process.
Parent Process: Global variable: 2 Stack variable: 20
Child Process: Global variable: 3 Stack variable: 21
[Potential Pitfall]: Some memory duplicated by a forked process such as file
pointers, will cause intermixed output from both processes. Use the wait() function so that the processes
do not access the file at the same time or open unique file descriptors. Some like stdout or stderr will be shared
unless synchronized using
wait() or some other mechanism. The file close on exit is another gotcha. A terminating process will close
files before exiting. File locks set by the parent process are not inherited by the child process.
[Potential Pitfall]: Race conditions can be created due to the unpredictability of
when the kernel scheduler runs portions or time slices of the process. One can use wait(). the use of
sleep() does not guarentee reliability of execution on a heavily loaded system as the scheduler behavior is not
predictable by the application.
Note on exit() vs _exit(): The C library function exit() calls the kernel
system call _exit()
internally. The kernel system call _exit() will cause the kernel to close descriptors, free memory, and
perform the kernel terminating process clean-up. The C library function exit() call will flush I/O buffers
and perform aditional clean-up before calling _exit()
internally. The function exit(status) causes the executable to return "status" as the return code
for main(). When
exit(status) is called by a child process, it allows the parent process to examine the terminating
status of the child (if it terminates first). Without this call (or a call from main()
to return()) and specifying the status argument, the process will not return a value.
The vfork() function is the same as fork() except that it does not make a copy of the address
space. The memory is shared reducing the overhead of spawning a new process with a unique copy of all the memory.
This is typically used when using fork() to
exec() a process and terminate. The vfork() function also executes the child process first and
resumes the parent process when the child terminates.
Child Process: Global variable: 3 Stack variable: 21
Parent Process: Global variable: 3 Stack variable: 21
Note: The child process executed first, updated the variables which are shared between the processes and NOT
unique, and then the parent process executes using variables which the child has updated.
[Potential Pitfall]: A deadlock condition may occur if the child process does not
terminate, the parent process will not proceed.
The function clone() creates a new child process which shares memory, file descriptors and signal
handlers with the parent. It implements threads and thus launches a function as a child. The child terminates when
the parent terminates.
See the
YoLinux POSIX threads tutorial
The parent process will often want to wait until all child processes have been completed. this can be implemented
with the wait()
function call.
wait(): Blocks calling process until the child process terminates. If child process has already teminated,
the wait() call returns immediately. if the calling process has multiple child processes, the function returns when
one returns.
waitpid(): Options available to block calling process for a particular child process not the first one.
Avoids orphaned process group when parent terminates. When parent dies, this will be a zombie. (No parent
process. Parent=1) Instead, create a new process group for the child. Later process the group is terminated to stop
all spawned processes. Thus all subsequent processes should be of this group if they are to be terminated by the
process group id. Process group leader has the same process id and group process id. If not changed then the process
group is that of the parent. Set the process group id to that of the child process.
This is the real reason to set up a process group. One may kill all the processes in the process group without
having to keep track of how many processes have been forked and all of their process id's.
See /usr/include/bits/signum.h for list of signals.
The system() call will execute an OS shell command as described by a character command string. This
function is implemented using
fork(), exec() and waitpid(). The command string is executed by calling /bin/sh -c
command-string. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be
ignored. The call "blocks" and waits for the task to be performed before continuing.
The statement "Command done!" will not print untill the "ls -l" command has completed.
The popen() call opens a process by creating a pipe, forking, and invoking the shell (bourne shell on
Linux). The advantage to using
popen() is that it will allow one to interrogate the results of the command issued.
This example opens a pipe which executes the shell command "ls -l". The results are read and printed
out.
The exec() family of functions will initiate a program from within a program. They are also various
front-end functions to execve().
The functions return an integer error code. (0=Ok/-1=Fail).
execl() and execlp():
The function call "execl()" initiates a new program in the same environment in which it is operating. An
executable (with fully qualified path. i.e. /bin/ls) and arguments are passed to the function. Note that
"arg0" is the command/file name to execute.
Where all function arguments are null terminated strings. The list of arguments is terminated by NULL.
The routine execlp() will perform the same purpose except that it will use environment variable PATH
to determine which executable to process. Thus a fully qualified path name would not have to be used. The first
argument to the function could instead be "ls". The function execlp() can also take the fully qualified
name as it also resolves explicitly.
This is the same as execl() except that the arguments are passed as null terminated array of pointers to
char. The first element "argv[0]" is the command name.
The routine execvp() will perform the same purpose except that it will use environment variable PATH
to determine which executable to process. Thus a fully qualified path name would not have to be used. The first
argument to the function could instead be "ls". The function
execvp() can also take the fully qualified name as it also resolves explicitly.
When the fork system call is successful, fork returns the process ID of the child process to the parent
process, and it returns a 0 to the child process. So a process can easily determine if it a parent or child process
by checking the return value from fork.
When a fork system call is made, the operating system generates a copy of the parent process, which becomes the
child process. The OS passes to the child process most of the parent's system information (e.g. open file
descriptors, environment information). However, some information is unique to the child process:
The child has its own PID.
The child has a different parent process ID (PPID) than its parent.
System-imposed process limits are reset (amount of CPU time etc).
All record locks on files are reset.
The action to be taken when receiving signals is different.
if (fork( ) == 0) // Generate some processes
cout << "Child process \t PID " << getpid()<< "\t PPID "<< endl;
cout<<"will return 2 times"<<endl;
return 0;
}
It is a straight-forward program and we notice that the PIDs are different and also the program will return 2
times, one is parent's and the other is child's.
File information:
There are a number of system calls that a process can use to obtain file information. The most useful one is
"stat" system call. Its prototype is like this:
int stat(const char *file_name, struct stat *buf)
The stat structure, which contains the following fields: struct stat {
dev_t st_dev; /* device */
ino_t st_ino; /* inode */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device type (if inode device) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last change */
};
Here is a small program which use the stat call:
/*
Using the stat system call
*/
#include <iostream>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;
const int N_BITS = 3;
int main(int argc, char *argv[ ])
{
unsigned int mask = 0700;
struct stat buff;
static char *perm[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
if (argc > 1) {
if ((stat(argv[1], &buff) != -1))
{
cout << "Permissions for " << argv[1] << " ";
for (int i=3; i; --i)
Linux implement a special virtual filesystem called /proc
that stores information about the kernel, kernel data structures, and the state of each process and associated
threads. Here in Linux a thread is implemented as a special type of process.
The /proc filesystem is stored in memory, not on disk. The majority of the information provided is
read-only and can vary greatly from one version of Linux to another. Standard system calls (such as
open, read, etc.) can be used by programs to access /proc
files.
Linux provides a procinfo command that generates a formatted display of /proc information. The below is an
example. We can see that it give us a lot of system information.
One can see that Numerical entries, such as 1 or 3023, are process subdirectories for existing processes and
contain information specific to the process. Nonnumeric entries, excluding the self
entry, has kernel-related information.
A wide array of data on each process is kept by the operating system. This data is found in the /proc directory
in a decimal number subdirectory named for the process's ID. Each process subdirectory includes:
cmdline - A file that contains the command-line argument list that started the process. Each field is separated
by a null character
cpu - when present, this file contains CPU utilization information.
cwd - A pointer (symbolic link) to the current working directory for the process.
exe - A pointer (symbolic link) to the binary file that was the source of the process.
environ -- environment variable.
fd - A subdirectory that contains on decimal number entry for each file the process has open. Each number is a
symbolic link to the device associated with the file.
maps - A file that contains the virtual address maps for the process as well as the access permissions to the
mapped regions. The maps are for various executables and library files associated with the process.
root
stat
...
This is a example:
/proc/12>ls
ls: cannot read symbolic link cwd: Permission denied
ls: cannot read symbolic link root: Permission denied
ls: cannot read symbolic link exe: Permission denied
cmdline cwd environ exe fd maps mem mounts root stat statm status
The Last but not LeastTechnology is dominated by
two types of people: those who understand what they do not manage and those who manage what they do not understand ~Archibald Putt.
Ph.D
FAIR USE NOTICEThis site contains
copyrighted material the use of which has not always been specifically
authorized by the copyright owner. We are making such material available
to advance understanding of computer science, IT technology, economic, scientific, and social
issues. We believe this constitutes a 'fair use' of any such
copyrighted material as provided by section 107 of the US Copyright Law according to which
such material can be distributed without profit exclusively for research and educational purposes.
This is a Spartan WHYFF (We Help You For Free)
site written by people for whom English is not a native language. Grammar and spelling errors should
be expected. The site contain some broken links as it develops like a living tree...
You can use PayPal to to buy a cup of coffee for authors
of this site
Disclaimer:
The statements, views and opinions presented on this web page are those of the author (or
referenced source) and are
not endorsed by, nor do they necessarily reflect, the opinions of the Softpanorama society.We do not warrant the correctness
of the information provided or its fitness for any purpose. The site uses AdSense so you need to be aware of Google privacy policy. You you do not want to be
tracked by Google please disable Javascript for this site. This site is perfectly usable without
Javascript.