You are on page 1of 16

Term Paper

Submitted TO:

Lect. Avinsh Bhagat

Submitted BY:

Sagar Singh

RTb805A24

MCA-5th

10810076
Services for Process management & Job Control in Linux
Contents:

• Process Management

 Multitasking

 Types of Processes

 Tools for working with processes

• Process Scheduling
 Linux processes have the following characteristics:

• Inter-Process Communication

 The types of inter process communication are:

 Linux Signals

• Linux Kernel Process Management

 Process Descriptor and the Task Structure

 Allocating the Process Descriptor

 Storing the Process Descriptor

• Process State
 Manipulating the Current Process State

 Process Context

 The Process Family Tree

• Job Control

 To look at the entire job table, we simply enter the command jobs:
Process Management
Any application that runs on a Linux system is assigned a process ID or PID. This is a numerical
representation of the instance of the application on the system. In most situations this
information is only relevant to the system administrator who may have to debug or terminate
processes by referencing the PID. Process Management is the series of tasks a System
Administrator completes to monitor, manage, and maintain instances of running applications.

Multitasking

Process Management beings with an understanding concept of Multitasking. Linux is what is


referred to as a preemptive multitasking operating system. Preemptive multitasking systems rely
on a scheduler. The function of the scheduler is to control the process that is currently using the
CPU. In contrast, symmetric multitasking systems such as Windows 3.1 relied on each running
process to voluntary relinquish control of the processor. If an application in this system hung or
stalled, the entire computer system stalled. By making use of an additional component to pre-
empt each process when its “turn” is up, stalled programs do not affect the overall flow of the
operating system.

Each “turn” is called a time slice, and each time slice is only a fraction of a second long. It is this
rapid switching from process to process that allows a computer to “appear’ to be doing two
things at once, in much the same way a movie “appears” to be a continuous picture.

Types of Processes

There are generally two types of processes that run on Linux. Interactive processes are those
processes that are invoked by a user and can interact with the user. VI is an example of an
interactive process. Interactive processes can be classified into foreground and background
processes. The foreground process is the process that you are currently interacting with, and is
using the terminal as its stdin (standard input) and stdout (standard output). A background
process is not interacting with the user and can be in one of two states – paused or running.

The following exercise will illustrate foreground and background processes.

1. Logon as root.
2. Run [cd \]

3. Run [vi]

4. Press [ctrl + z]. This will pause vi

5. Type [jobs]

6. Notice vi is running in the background

7. Type [fg %1]. This will bring the first background process to the foreground.

8. Close vi.

The second general type of process that runs on Linux is a system process or Daemon. Daemon
is the term used to refer to process’ that are running on the computer and provide services but do
not interact with the console. Most server software is implemented as a daemon. Apache, Samba,
and inn are all examples of daemons.

Any process can become a daemon as long as it is run in the background, and does not interact
with the user. A simple example of this can be achieved using the [ls –R] command. This will list
all subdirectories on the computer, and is similar to the [dir /s] command on Windows. This
command can be set to run in the background by typing [ls –R &], and although technically you
have control over the shell prompt, you will be able to do little work as the screen displays the
output of the process that you have running in the background. You will also notice that the
standard pause (ctrl+z) and kill (ctrl+c) commands do little to help you.

Tools for working with processes

• accton - Turns process accounting on and off. Uses the file /var/log/pacct. To turn it on
type "accton /var/log/pacct". Use the command with no arguments to turn it off.

• kill - Kill a process by number

• killall - Send a signal to a process by name

• lastcomm (1) - Display information about previous commands in reverse order. Works
only if process accounting is on.

• nice - Set process priority of new processes.

• ps(1) - Used to report the status of one or more processes.

• pstree(1) - Display the tree of running processes.

• renice(8) - Can be used to change the process priority of a currently running process.
• sa(8) - Generates a summary of information about users' processes that are stored in the
/var/log/pacct file.

• skill - Report process status.

• snice - Report process status.

• top - Displays the processes that are using the most CPU resources.

Process Scheduling
Computer time on Linux systems is allocated in jiffies. A jiffie is a microprocessor time slice. On
most Linux systems it is 1/100 of a second. On some systems it is 1/1024 of a second. The Linux
kernel controls process scheduling. There are three types of scheduling:

• normal - Referred to as other, this is the scheduling type set for normal programs

• FIFO - This is a real time scheduling priority. The FIFO term means the first process
started (first in) will be the first done (first out). The only time this type of process exits is
if it sleeps, is rescheduled, or if it must wait on other kernel priorities to be done.

• RR - This is a round robin type of scheduling, where each task gets a certain amount of
time then it must exit, yield control to the next task and get back into the task queue. This
is a real time scheduling priority.

Linux processes have the following characteristics:

• Policy - normal or real time. Real time processes have a higher priority than normal
processes.

• Priority - The process priority. It is a number between -20 and 19. The value of -20 is the
highest and 19 is the lowest priority. Process priority can be set with the nice(1) command
and changed using the renice(8) command.

Inter-Process Communication
• The types of inter process communication are:

• Signals - Sent by other processes or the kernel to a specific process to indicate various
conditions.

• Pipes - Unnamed pipes set up by the shell normally with the "|" character to route output
from one program to the input of another.

• FIFOS - Named pipes operating on the basis of first data in, first data out.

• Message queues - Message queues are a mechanism set up to allow one or more processes
to write messages that can be read by one or more other processes.

• Semaphores - Counters that are used to control access to shared resources. These counters
are used as a locking mechanism to prevent more than one process from using the resource
at a time.

• Shared memory - The mapping of a memory area to be shared by multiple processes.

Message queues, semaphores, and shared memory can be accessed by the processes if they have
access permission to the resource as set up by the object's creator. The process must pass an
identifier to the kernel to be able to get the access.

Signals

Linux Signals are:

Signal Name Number Description

SIGHUP 1 Hangup (POSIX)

SIGINT 2 Terminal interrupt (ANSI)

SIGQUIT 3 Terminal quit (POSIX)

SIGILL 4 Illegal instruction (ANSI)

SIGTRAP 5 Trace trap (POSIX)

SIGIOT 6 IOT Trap (4.2 BSD)

SIGBUS 7 BUS error (4.2 BSD)


SIGFPE 8 Floating point exception (ANSI)

SIGKILL 9 Kill(can't be caught or ignored) (POSIX)

SIGUSR1 10 User defined signal 1 (POSIX)

SIGSEGV 11 Invalid memory segment access (ANSI)

SIGUSR2 12 User defined signal 2 (POSIX)

SIGPIPE 13 Write on a pipe with no reader, Broken pipe (POSIX)

SIGALRM 14 Alarm clock (POSIX)

SIGTERM 15 Termination (ANSI)

SIGSTKFLT 16 Stack fault

SIGCHLD 17 Child process has stopped or exited, changed (POSIX)

SIGCONT 18 Continue executing, if stopped (POSIX)

SIGSTOP 19 Stop executing(can't be caught or ignored) (POSIX)

SIGTSTP 20 Terminal stop signal (POSIX)

SIGTTIN 21 Background process trying to read, from TTY (POSIX)

SIGTTOU 22 Background process trying to write, to TTY (POSIX)

SIGURG 23 Urgent condition on socket (4.2 BSD)

SIGXCPU 24 CPU limit exceeded (4.2 BSD)

SIGXFSZ 25 File size limit exceeded (4.2 BSD)

SIGVTALRM 26 Virtual alarm clock (4.2 BSD)

SIGPROF 27 Profiling alarm clock (4.2 BSD)

SIGWINCH 28 Window size change (4.3 BSD, Sun)

SIGIO 29 I/O now possible (4.2 BSD)

SIGPWR 30 Power failures restart (System V)


As noted above, processes can ignore, block, or catch all signals except SIGSTOP and SIGKILL.
If a process catches a signal, it means that it includes code that will take appropriate action when
the signal is received. If the signal is not caught by the process, the kernel will take default action
for the signal.

Linux Kernel Process Management


The process is one of the fundamental abstractions in UNIX operating systems1. A process is a
program (object code stored on some media) in execution. Processes are, however, more than
just the executing program code (often called the text section in UNIX). They also include a set
of resources such as open files and pending signals, internal kernel data, processor state, an
address space, one or more threads of execution, and a data section containing global variables.
Processes, in effect, are the living result of running program code.

Threads of execution, often shortened to threads, are the objects of activity within the process.
Each thread includes a unique program counter, process stack, and set of processor registers. The
kernel schedules individual threads, not processes. In traditional UNIX systems, each process
consists of one thread. In modern systems, however, multithreaded programs—those that consist
of more than one thread—are common. As you will see later, Linux has a unique implementation
of threads: It does not differentiate between threads and processes. To Linux, a thread is just a
special kind of process.

On modern operating systems, processes provide two virtualizations: a virtualized processor and
virtual memory. The virtual processor gives the process the illusion that it alone monopolizes the
system, despite possibly sharing the processor among dozens of other processes. Chapter 4,
"Process Scheduling," discusses this virtualization. Virtual memory lets the process allocate and
manage memory as if it alone owned all the memory in the system. A program itself is not a
process; a process is an active program and related resources. Indeed, two or more processes can
exist that are executing the same program. In fact, two or more processes can exist that share
various resources, such as open files or an address space.

A process begins its life when, not surprisingly, it is created. In Linux, this occurs by means of
the fork() system call, which creates a new process by duplicating an existing one. The process
that calls fork() is the parent, whereas the new process is the child. The parent resumes execution
and the child starts execution at the same place, where the call returns. The fork() system call
returns from the kernel twice: once in the parent process and again in the newborn child.

Often, immediately after a fork it is desirable to execute a new, different, program. The exec*()
family of function calls is used to create a new address space and load a new program into it. In
modern Linux kernels, fork() is actually implemented via the clone() system call, which is
discussed in a following section.
Finally, a program exits via the exit() system call. This function terminates the process and frees
all its resources. A parent process can inquire about the status of a terminated child via the
wait4()2 system call, which enables a process to wait for the termination of a specific process.
When a process exits, it is placed into a special zombie state that is used to represent terminated
processes until the parent calls wait() or waitpid().

Another name for a process is a task. The Linux kernel internally refers to processes as tasks. In
this book, I will use the terms interchangeably, although when I say task I am generally referring
to a process from the kernel's point of view.

Process Descriptor and the Task Structure

The kernel stores the list of processes in a circular doubly linked list called the task list3. Each
element in the task list is a process descriptor of the type struct task_struct, which is defined in
<linux/sched.h>. The process descriptor contains all the information about a specific process.

The task_struct is a relatively large data structure, at around 1.7 kilobytes on a 32-bit machine.
This size, however, is quite small considering that the structure contains all the information that
the kernel has and needs about a process. The process descriptor contains the data that describes
the executing program—open files, the process's address space, pending signals, the process's
state, and much more.

Allocating the Process Descriptor

The task_struct structure is allocated via the slab allocator to provide object reuse and cache
coloring (see Chapter 11, "Memory Management"). Prior to the 2.6 kernel series, struct
task_struct was stored at the end of the kernel stack of each process. This allowed architectures
with few registers, such as x86, to calculate the location of the process descriptor via the stack
pointer without using an extra register to store the location. With the process descriptor now
dynamically created via the slab allocator, a new structure, struct thread_info, was created that
again lives at the bottom of the stack (for stacks that grow down) and at the top of the stack4.

The new structure also makes it rather easy to calculate offsets of its values for use in assembly
code.

The thread_info structure is defined on x86 in <asm/thread_info.h> as

struct thread_info {

struct task_struct *task;

struct exec_domain *exec_domain;


unsigned long flags;

unsigned long status;

__u32 cpu;

__s32 preempt_count;

mm_segment_t addr_limit;

struct restart_block restart_block;

unsigned long previous_esp;

__u8 supervisor_stack[0];

};

Each task's thread_info structure is allocated at the end of its stack. The task element of the
structure is a pointer to the task's actual task_struct.

Storing the Process Descriptor

The system identifies processes by a unique process identification value or PID. The PID is a
numerical value that is represented by the opaque type5 pid_t, which is typically an int. Because
of backward compatibility with earlier Unix and Linux versions, however, the default maximum
value is only 32,768 (that of a short int), although the value can optionally be increased to the
full range afforded the type. The kernel stores this value as pid inside each process descriptor.

This maximum value is important because it is essentially the maximum number of processes
that may exist concurrently on the system. Although 32,768 might be sufficient for a desktop
system, large servers may require many more processes. The lower the value, the sooner the
values will wrap around, destroying the useful notion that higher values indicate later run
processes than lower values. If the system is willing to break compatibility with old applications,
the administrator may increase the maximum value via /proc/sys/kernel/pid_max.

Inside the kernel, tasks are typically referenced directly by a pointer to their task_struct structure.
In fact, most kernel code that deals with processes works directly with struct task_struct.
Consequently, it is very useful to be able to quickly look up the process descriptor of the
currently executing task, which is done via the current macro. This macro must be separately
implemented by architecture. Some architectures save a pointer to the task_struct structure of the
currently running process in a register, allowing for efficient access. Other architectures, such as
x86 (which has few registers to waste), make use of the fact that struct thread_info is stored on
the kernel stack to calculate the location of thread_info and subsequently the task_struct.

On x86, current is calculated by masking out the 13 least significant bits of the stack pointer to
obtain the thread_info structure. This is done by the current_thread_info() function. The
assembly is shown here:

movl $-8192, %eax

andl %esp, %eax

This assumes that the stack size is 8KB. When 4KB stacks are enabled, 4096 is used in lieu of
8192.

Finally, current dereferences the task member of thread_info to return the task_struct:

current_thread_info()->task;

Contrast this approach with that taken by PowerPC (IBM's modern RISC-based microprocessor),
which stores the current task_struct in a register. Thus, current on PPC merely returns the value
stored in the register r2. PPC can take this approach because, unlike x86, it has plenty of
registers. Because accessing the process descriptor is a common and important job, the PPC
kernel developers deem using a register worthy for the task.

Process State

The state field of the process descriptor describes the current condition of the process (see Figure
3.3). Each process on the system is in exactly one of five different states. This value is
represented by one of five flags:

• TASK_RUNNING—The process is runnable; it is either currently running or on a


runqueue waiting to run (runqueues are discussed in Chapter 4, "Scheduling"). This is the
only possible state for a process executing in user-space; it can also apply to a process in
kernel-space that is actively running.

• TASK_INTERRUPTIBLE—The process is sleeping (that is, it is blocked), waiting for


some condition to exist. When this condition exists, the kernel sets the process's state to
TASK_RUNNING. The process also awakes prematurely and becomes runnable if it
receives a signal.
• TASK_UNINTERRUPTIBLE—This state is identical to TASK_INTERRUPTIBLE
except that it does not wake up and become run able if it receives a signal. This is used in
situations where the process must wait without interruption or when the event is expected
to occur quite quickly. Because the task does not respond to signals in this state,
TASK_UNINTERRUPTIBLE is less often used than TASK_INTERRUPTIBLE6.

• TASK_ZOMBIE—The task has terminated, but its parent has not yet issued a wait4()
system call. The task's process descriptor must remain in case the parent wants to access it.
If the parent calls wait4(), the process descriptor is de-allocated.

• TASK_STOPPED—Process execution has stopped; the task is not running nor is it eligible
to run. This occurs if the task receives the SIGSTOP, SIGTSTP, SIGTTIN, or SIGTTOU
signal or if it receives any signal while it is being debugged.

Manipulating the Current Process State


Kernel code often needs to change a process's state. The preferred mechanism is using

set_task_state(task, state); /* set task 'task' to state 'state' */

This function sets the given task to the given state. If applicable, it also provides a memory
barrier to force ordering on other processors (this is only needed on SMP systems). Otherwise, it
is equivalent to

task->state = state;

The method set_current_state(state) is synonymous to set_task_state(current, state).

Process Context

One of the most important parts of a process is the executing program code. This code is read in
from an executable file and executed within the program's address space. Normal program
execution occurs in user-space. When a program executes a system call (see Chapter 5, "System
Calls") or triggers an exception, it enters kernel-space. At this point, the kernel is said to be
"executing on behalf of the process" and is in process context. When in process context, the
current macro is valid7. Upon exiting the kernel, the process resumes execution in user-space,
unless a higher-priority process has become runnable in the interim, in which case the scheduler
is invoked to select the higher priority process.

System calls and exception handlers are well-defined interfaces into the kernel. A process can
begin executing in kernel-space only through one of these interfaces—all access to the kernel is
through these interfaces.
The Process Family Tree

A distinct hierarchy exists between processes in Unix systems, and Linux is no exception. All
processes are descendents of the init process, whose PID is one. The kernel starts init in the last
step of the boot process. The init process, in turn, reads the system initscripts and executes more
programs, eventually completing the boot process.

Every process on the system has exactly one parent. Likewise, every process has zero or more
children. Processes that are all direct children of the same parent are called siblings. The
relationship between processes is stored in the process descriptor. Each task_struct has a pointer
to the parent's task_struct, named parent, and a list of children, named children. Consequently,
given the current process, it is possible to obtain the process descriptor of its parent with the
following code:

struct task_struct *my_parent = current->parent;

Similarly, it is possible to iterate over a process's children with

struct task_struct *task;

struct list_head *list;

list_for_each(list, &current->children) {

task = list_entry(list, struct task_struct, sibling);

/* task now points to one of current's children */

The init task's process descriptor is statically allocated as init_task. A good example of the
relationship between all processes is the fact that this code will always succeed:

struct task_struct *task;

for (task = current; task != &init_task; task = task->parent);

/* task now points to init */

In fact, you can follow the process hierarchy from any one process in the system to any other.
Oftentimes, however, it is desirable simply to iterate over all processes in the system. This is
easy because the task list is a circular doubly linked list. To obtain the next task in the list, given
any valid task, use:

list_entry(task->tasks.next, struct task_struct, tasks)

Obtaining the previous works the same way:


list_entry(task->tasks.prev, struct task_struct, tasks)

These two routines are provided by the macros next_task(task) and prev_task(task), respectively.
Finally, the macro for_each_process(task) is provided, which iterates over the entire task list. On
each iteration, task points to the next task in the list:

struct task_struct *task;

for_each_process(task) {

/* this pointlessly prints the name and PID of each task */

printk("%s[%d]\n", task->comm, task->pid);

Job Control
Job control is the ability to move processes between the foreground and background. This is very
useful when you need to do several things at once, but only have one terminal. For example, let's
say there are several files spread out across the system that we want to edit. Because we don't
know where they are, we can't use full paths. Because they don't have anything common in their
names, we can't use find. So we try ls -R > more.

After a minute or two, we find the first file we want to edit. We can then suspend this job by
pressing Ctrl+Z. We then see something that looks like this:

[1]+ Stopped ls -R | more

This means that the process has been stopped or suspended. One very important thing to note is
that this process is not in the background as if we had put an "&" at the end. When a process is
suspended, it stops doing anything, unlike a process in the background, which keeps on working.

Once the ‘ls’ is in the background, we can run vi. When we are done with vi, we can bring the ls
command back with the fg (foreground) command.

If we wanted to, we could have more than just one job suspended. I have never had the need to
have more than two running like this, but I have gotten more than ten during tests. One thing that
this showed me was the meaning of the plus sign (+). This is the "current" job, or the one we
suspended last.

The number in brackets is the process entry in the job table, which is simply a table containing
all of your jobs. Therefore, if we already had three jobs, the next time we suspended a job, the
entry would look like this:

[4]+ Stopped ls -R >> output


To look at the entire job table, we simply enter the command jobs:

• Stopped ls -R /usr >> output.usr

• Stopped find / -print > output.find

• Stopped ls -R /var >> output.var

• Stopped ls -R >> output.root

The plus sign indicates the job that we suspended last. So this is the one that gets called if we run
fg without a job number. In this case, it was Job 4. Note that there is a minus sign (-) right after
Job 3. This was the second to last job that we suspended. Now, we bring Job 2 in the foreground
with fg 2 and then immediately suspend it again with Ctrl+Z. The table now looks like this:

• Stopped ls -R /usr >> output

• Stopped find / -print > output.find

• Stopped ls -R /var >> output

• Stopped ls -R >> output

Note that Job 2 now has the plus sign following it and Job 4 has the minus sign.

In each of these cases, we suspended a job that was running in the foreground. If we had started a
job and put it in the background from the command line, the table might have an entry that
looked like this:

[3] Running ls -R /var >> output &

This shows us that although we cannot see the process (because it is in the background), it is still
running. We could call it to the foreground if we wanted by running fg 3. And, if we wanted, we
could use the bg command to send one of the stopped jobs to the background. So

bg %1 would send Job 1 to the background just as if we had included & from the command line.
One nice thing is that we don't have to use just the job numbers when we are pulling something
into the foreground. Because we know that we started a process with the find command, we can
get it by using

fg %find

Actually, we could have used %f or anything else that was not ambiguous. In this case, we were
looking for a process that started with the string we input. We could even look for strings
anywhere within the command. To do this, the command might be

fg %?print which would have given us the same command. Or, if we had tried

fg %?usr we would have gotten Job 1 because it contains the string usr.

If we find that there is a job that we want to kill (stop completely), we can use the kill command.
This works the same way, so kill %<nr> kills the job with number <nr>, kill %<string> kills the
job starting with string, and so on.

Keep in mind that process takes up resources whether they are in the foreground or not. That is,
background processes take up resources too.

If you do not remember the process ID of the last process that was placed in the background you
can reference it any time using the $! System variable. You can also use the wait command to
stop processing until the particular process is done. The syntax is simply:

wait PID

Although generally considered part of "job control" you can change the default priority a process
has when it starts, as well as the process of a running process. Details of this can be found in the
section on process scheduling.