Ilovepdf Merged
Ilovepdf Merged
Process
A process is a program in execution. For example, when we write a program in C or C++ and
compile it, the compiler creates binary code. The original code and binary code are both
programs. When we actually run the binary code, it becomes a process.
A process is an ‘active’ entity instead of a program, which is considered a ‘passive’ entity. A
single program can create many processes when run multiple times; for example, when we open
a .exe or binary file multiple times, multiple instances begin (multiple processes are created).
Process vs Program
The period for the process is limited, but the period for the program is unlimited.
The Text section is made up of the compiled program code, read in from non-volatile storage
Process states in operating system are a way to manage resources efficiently by keeping track
of the current state of each process.There are several process states in operating system, they
are:
New State: When a process is first created or is initialized by the operating system, it is in the
new state. In this state, the process is being prepared to enter the ready state.
Ready State: When a process is ready to execute, it is in the ready state. In this state, the process
is waiting for the CPU to be allocated to it so that it can start executing its instructions. A
process can remain in the ready state for an indeterminate period, waiting for the CPU to
become available.
Running State: When the CPU is allocated to a process, it enters the running state. In this state,
the process executes its instructions and uses system resources such as memory, CPU, and I/O
devices. Only one process can be in the running state at a time, and the operating system
determines which process gets access to the CPU using scheduling algorithms.
Blocked State: Sometimes, a process needs to wait for a particular event, such as user input or
data from a disk drive. In such cases, the process enters the blocked state. In this state, the
process is not using the CPU, but it is not ready to run either. The process remains in the blocked
state until the event it is waiting for occurs.
Terminated State: The terminated state is reached when a process completes its execution or
terminates by the operating system. In this state, the process no longer uses any system
resources, and its memory space is deallocated.
Process State
Throughout its existence, each process goes through various phases. The present state of the
process is defined by the process state.
Process Number
When a new process is created by the user, the operating system assigns a unique ID i.e.
a process-ID to that process. This ID helps the process to be distinguished from other processes
existing in the system.The operating system has a limit on the maximum number of processes
it is capable of dealing with, let's say the OS can handle most N processes at a time.
So, process-ID will get the values from 0 to N-1.
Program Counter
The address of the next instruction to be executed is specified by the program counter. The
address of the program’s first instruction is used to initialize the program counter before it is
executed.
The value of the program counter is incremented automatically to refer to the next instruction
when each instruction is executed. This process continues till the program ends.
Registers
The registers vary in number and type, depending on the computer architecture. They include
accumulators, index registers, stack pointers, and general-purpose registers, plus any condition-
code information. Along with the program counter, this state information must be saved when
an interrupt occurs, to allow the process to be continued correctly afterward.
List of open files
This contains information about those files that are used by that process. This helps the
operating system close all the opened files at the termination state of the process.
Context Switching
A context switching is a mechanism that involves switching of the CPU from one process or
task to another. While peforming switching the operating system saves the state of a running
process so it can be resumed later, and then loads the state of another process. A context
switching allows a single CPU to handle multiple process requests simultaneously without the
need for any additional processors.
Following are the reasons that describe the need for context switching in the Operating system.
1. If a high priority process falls into the ready queue, the currently running process will be shut
down or stopped by a high priority process to complete its tasks in the system.
2. If any running process requires I/O resources in the system, the current process will be switched
by another process to use the CPUs. And when the I/O requirement is met, the old process goes
into a ready state to wait for its execution in the CPU. Context switching stores the state of the
process to resume its tasks in an operating system. Otherwise, the process needs to restart its
execution from the initials level.
3. If any interrupts occur while running a process in the operating system, the process status is
saved as registers using context switching. After resolving the interrupts, the process switches
from a wait state to a ready state to resume its execution at the same point later, where the
operating system interrupted occurs.
.
The following steps describe the basic sequence of events when moving between processes:
1. The CPU executes Process 0.
2. A triggering event occurs, such as an interrupt or system call.
3. The system pauses Process 0 and saves its state (context) to PCB 0, the process control block
that was created for that process.
4. The system selects Process 1 from the queue and loads the process's state from PCB 1.
5. The CPU executes Process 1, picking up from where it left off (if the process had already been
running).
6. When the next triggering event occurs, the system pauses Process 1 and saves its state to PCB
1.
7. The Process 0 state is reloaded, and the CPU executes the process, once again picking up from
where it left off. Process 1 remains in an idle state until it is called again.
Advantage of Context Switching
Context switching is used to achieve multitasking . Multitasking gives an illusion to the users
that more than one process are being executed at the same time. But in reality, only one
task is being executed at a particular instant of time by a processor. Here, the context
switching is so fast that the user feels that the CPU is executing more than one task at the
same time.
The disadvantage of Context Switching
The disadvantage of context switching is that it requires some time for context switching i.e.
the context switching time. Time is required to save the context of one process that is in the
running state and then getting the context of another process that is about to come in the
running state. During that time, there is no useful work done by the CPU from the user
perspective. So, context switching is pure overhead in this condition.
Process Operations
The operations of process carried out by an operating system are primarily of two types:
1. Process creation
2. Process termination
Process creation
Process creation in operating systems (OS) is a fundamental operation that involves the initiation of
a new process. The creating process is called the parent while the created process is called the child.
Each child process may in turn create new child process. Every process in the system is identified
with a process identifier(PID) which is unique for each process. A process uses a System call to create
a child process.
Figure Below illustrates a typical process tree for the Solaris operating system, showing the name of
each process (daemon) and its pid.
In Linux and other Unix-like operating systems, a daemon is a background process that runs
independently of any interactive user session. Daemons typically start at system boot and run
continuously until the system is shut down. They perform various system-level tasks, such as
managing hardware devices, handling network requests, scheduling jobs, or any other kind of service
that needs to run in the background without direct user intervention
Daemon Operation
init The init process is the first process started by the Linux/Unix kernel and holds
the process ID (PID) 1.
pageout The pageout daemon is responsible for managing virtual memory,
fsflush The fsflush process is a background daemon responsible for synchronizing
cached data to disk.
inetd inetd, short for "internet super-server", is a daemon that listens on designated ports
for incoming connections and then launches the appropriate program.
inetd is responsible for networking services such as telnet and ftp
dtlogin Desktop login- dtlogin is the display manager component responsible for
managing user logins to the graphical environment.
In Solaris, the process at the top of the tree is the sched process, with pid of 0. The sched process
creates several children processes-including pageout and fsflush. The sched process also creates the
init process, which serves as the root parent process for all user processes. we see two children of
init- inetd and dtlogin. When a user logs in, dtlogin creates an X-windows session (Xsession), which
in turns creates the sdt_shel process. (sdt_shel, part of the CDE (Common Desktop Environment), is
a graphical user interface tool designed to provide users with an easy way to access various system
functions and applications). Below sdt_shel, a user's command-line shell-the C-shell or csh-is created.
In this command line interface, the user can then invoke various child processes, such as the ls and
cat commands. We also see a csh process with pid of 7778 representing a user who has logged onto
the system using telnet. This user has started the Netscape browser (pid of 7785) and the emacs editor
(pid of 8105). (Emacs is a text editor which provides a robust platform for executing complex editing
tasks, writing code in various programming languages, managing files, reading emails, browsing the
web, and even playing games.)
Process termination
A process terminates when it finishes executing its final statement and asks the operating system
to delete it by using the exit () system call. At that point, the process may return a status value (typically
an integer) to its parent process (via the wait() system call). All the resources of the process-including
physical and virtual memory, open files, and I/0 buffers-are deallocated by the operating system.
Termination can occur in other circumstances as well A process can cause the termination of
another process via an appropriate system call (for example, TerminateProcess () in Win32). Usually,
such a system call can be invoked only by the parent of the process that is to be terminated. Otherwise,
users could arbitrarily kill each other's jobs. Note that a parent needs to know the identities of its children.
Thus, when one process creates a new process, the identity of the newly created process is passed to the
parent.
A parent may terminate the execution of one of its children for a variety of reasons, such as
these:
The child has exceeded its usage of some of the resources that it has been allocated. (To determine
whether this has occurred, the parent must have a mechanism to inspect the state of its children.)
The task assigned to the child is no longer required.
The parent is exiting, and the operating system does not allow a child to continue if its parent
terminates. if a Parent process terminates (either normally or abnormally), then all its children
must also be terminated. This phenomenon, referred to as cascading termination.
Lets depict all the system calls in the form of a process transition diagram.
A process begins its life when it is created. A process goes through different states before it gets
terminated.
The first state that any process goes through is the creation of itself. Process creation happens
through the use of fork() system call, (A system call is a function that a user program uses to ask the
operating system for a particular service .System calls serve as the interface between an operating
system and a process ) which creates a new process(child process) by duplicating an existing
one(parent process). The process that calls fork() is the parent, whereas the new process is the child.
In most cases, we may want to execute a different program in child process than the parent. The
exec() family of function calls creates a new address space and loads a new program into it.
Finally, a process exits or terminates using the exit() system call. This function frees all the
resources held by the process(except for pcb).
A parent process can enquire about the status of a terminated child using wait() system call.
When the parent process uses wait() system call, the parent process is blocked till the child on which
it is waiting terminates.
System call interface for process management
A system call is a function that a user program uses to ask the operating system for a particular
service which cannot be carried out by normal function. System calls provide the interface between
the process and the operating system.
1. System Call Request. The application requests a system call by invoking its corresponding
function. For instance, the program might use the read() function to read data from a file.
2. Context Switch to Kernel Space. A software interrupt or special instruction is used to trigger a
context switch and transition from the user mode to the kernel mode.
3. System Call Identified. The system uses an index to identify the system call and address the
corresponding kernel function.
4. Kernel Function Executed. The kernel function corresponding to the system call is executed. For
example, reading data from a file.
5.System Prepares Return Values. After the kernel function completes its operation, any return
values or results are prepared for the user application.
6. Context Switch to User Space. The execution context is switched back from kernel mode to user
mode.
7. Resume Application. The application resumes its execution from where it left off, now with the
results or effects of the system call.
Example
fork()
It is the primary method of process creation on Unix-like operatingsystems. This function creates a
new copy called the child out of the original process, that is called the parent. When the parent process
closes or crashes for some reason, it also kills the child process.
Syntax:
pid=fork();
The operating system is using a unique id for every process to keep track of all processes.
And for that, fork() doesn’t take any parameter and return an int value as following:
Zero: if it is the child process (the process created). Positive value: if it is the parent process.
The difference is that, in the parent process, fork() returns a value which represents
the process ID of the child process. But in the child process, fork() returns the value 0.
Negative value: if an error occurred.
Sample Program
/*When working with fork(), <sys/types.h> can be used for type pid_t for processes ID’s as pid_t is
defined in <sys/types.h>.
The header file <unistd.h> is where fork() is defined so you have to include it to your program to use
fork().*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
/* fork a process */
pid_t p;
p = fork();
/* the child and parent will execute every line of code after the fork (each separately) */
if(p == 0)
printf("Hello child pid=%d\n", getpid());
else if(p > 0)
printf("Hello parent pid=%d\n", getpid());
else
printf("Error: fork() failed\n");
return 0;
}
The output will be:
Hello parent pid=1601
Hello child pid=1602
vfork()
The vfork() is another system call that is utilized to make a new process. The child process is the new
process formed by the vfork() system call, while the parent process is the process that uses
the vfork() system call. The vfork() system call doesn't create separate address spaces for both parent
and child processes.
Because the child and parent processes share the same address space,the child process halts
the parent process's execution until the child process completes its execution.
. If one of the processes changes the code, it is visual to the other process transferring the same
pages. Assume that the parent process modifies the code; it will be reflected in the child process code.
fork() vfork()
Child process and parent process has separate Child process and parent process shares the
Parent and child process execute Parent process remains suspended till child
If the child process alters any page in the If child process alters any page in the address
address space, it is invisible to the parent space, it is visible to the parent process as they
process as the address space are separate. share the same address space.
Syntax:
pid=vfork();
Sample Program
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
/* fork a process */
pid_t p;
p = vfork();
/* the child and parent will execute every line of code after the fork (each separately) */
if(p == 0)
{
// Child process
printf("This is the child process. PID: %d\n", getpid());
printf("Child process is exiting with exit() \n");
exit(0);
}
else if(p > 0)
{
// Parent process might wait for child process to terminate
printf("This is the parent process. PID: %d\n", getpid());
}
else
printf("Error: fork() failed\n");
return 0;
}
output
This is the child process. PID: 91
Child process is exiting with exit()
This is the parent process. PID: 90
Wait()
This system call is used in processes that have a parent-child relationship. It makes a parent process
stop its execution till the termination of the child process. The program creates a child process via
the fork() system call and then calls the wait() system call to wait for the child process to finish its
execution.
Syntax
Below, we can see the syntax for the wait() system call. To use this in our C programs, we will have
to include the Standard C library sys/wait.h.
pid_t wait(int *status);
Parameters and return value
The system call takes one argument named status. This argument represents a pointer to an integer
that will store the exit status of the terminated child program.we can even replace status with NULL
parameter.
When the system call completes, it can return the following.
Process ID: The process ID of the child process that is terminated, which has the object
type pid_t.
Error value: If there is any error during system call execution, it will return -1, which can be
used for error handling.
Sample program
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid-t p,childpid;
p = fork();
// code for the child process
if(p== 0){
printf("Child: I am running!!\n\n");
printf("Child: I have PID: %d\n\n", getpid());
exit();
}
// code for the parent process
else{
// print parent running message
printf("Parent: I am running and waiting for child to finish!!\n\n");
// call wait system call
childpid = wait(NULL);
// print the details of the child process
printf("Parent: Child finished execution!, It had the PID: %d,\n", childpid);
}
return 0;
}
Output:
Parent: I am running and waiting for child to finish!!
Child: I am running!!
Child: I have PID: 102
Parent: Child finished execution!, It had the PID: 102
waitpid()
This system call waits for a specific process to finish its execution. This system call can be
accessed using our C programs' library sys/wait.h.
Let's understand how the waitpid() function works with the help of a diagram.
The diagram shows that we have a parent process with 3 child processes, namely, Child1, Child2,
and Child3. If we want the parent to wait for a specific child, we could use the waitpid() function.
In the diagram above, we made the parent process wait for Child1 to finish its execution using the
function. When the parent process is called, the waitpid() function gets blocked by the operating
system and can't continue its execution till the specified child process Child1 is terminated.
If we were to replace the process in the waitpid() function with Child3, then the parent would only
continue when Child3 had terminated.
Syntax
pid_t waitpid(pid_t pid, int *status_ptr, int options);
Let's discuss the three arguments that we provide to the system call.
pid: Here, we provide the process ID of the process we want to wait for. If the provided pid is
0, it will wait for any arbitrary child to finish.
status_ptr: This is an integer pointer used to access the child's exit value. If we want to ignore
the exit value, we can use NULL here.
options: Here, we can add additional flags to modify the function's behavior. The various flags
are discussed below:
o WCONTINUED: It is used to report the status of any child process that has been
terminated and those that have resumed their execution after being stopped.
o WNOHANG: It is used when we want to retrieve the status information immediately
when the system call is executed. If the status information is not available, it returns
an error.
o WUNTRACED: It is used to report any child process that has stopped or terminated.
Return value
The system call will return the process ID of the child process that was terminated. If there is any
error while waiting for the child process via the waitpid() system call, it will return -1, which
corresponds to an error.
Sample Program
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pids[2], wpid;
// Fork first child process
pids[0] = fork();
if (pids[0] == 0) {
// First child process
printf("First child process: PID = %d\n", getpid());
printf("First child process exiting\n");
exit(0); // First child exits
}
// Fork second child process
pids[1] = fork();
if (pids[1] == 0)
{
// Second child process
printf("Second child process: PID = %d\n", getpid());
printf("Second child process exiting\n");
exit(0); // Second child exits
}
exec()
The “exec()” system call is used to replace the current process with a new program or executable. It
loads a new program into the current process’s memory, overwriting the existing program’s code, data,
and stack.
When “exec()” is called, the operating system performs the following steps:
a. The current process’s memory is cleared, removing the old program’s instructions and data.
b. The new program is loaded into memory.
c. The program’s entry point is set as the starting point of execution.
d. The new program’s code begins execution.
The “exec()” system call is often used after a “fork()” call in order to replace the child process’s code
with a different program. This allows the child process to execute a different program while preserving
the parent process’s execution.
Exec system call is a collection of functions and in C programming language, the standard names for
these functions are as follows:
(l- represents list, v represents vector)
execl(): Executes a program specified by a pathname, and arguments are passed as a variable-length
list of strings terminated by a NULL pointer.
Syntax: int execl(const char *path, const char *arg, ..., NULL);
Parameters:
path: Specifies the path to the executable file which the process will run.
arg: Represents the list of arguments to be passed to the executable. The first argument
typically is the name of the program being executed. The list of arguments must be
terminated by NULL to indicate the end of the argument list.
Return Value:
On success, execl() does not return; the new program image takes over the process.
If an error occurs, it returns -1, and errno is set to indicate the error.
execv(): Executes a program specified by a pathname, and arguments are passed as an array of
strings terminated by a NULL pointer.
Syntax: int execv(const char *path, char *const argv[]);
Parameters:
path: Specifies the path to the executable file which the process will run.
argv: An array of pointers to null-terminated strings that represent the argument list available
to the new program. The first argument, by convention, should be the name of the executed
program. The array of pointers must be terminated by a NULL pointer.
Return Value:
On success, execv() does not return because the current process image is replaced by the new
program image.
On failure, it returns -1, and errno is set to indicate the error.
execlp(): Similar to execl(), but the program is searched for in the directories specified by the PATH
environment variable.
Syntax : int execlp(const char *file, const char *arg, ..., NULL);
Parameters:
file: The name of the executable file to run. If this name contains a slash (/), then execlp() will
treat it as a path and will not search the PATH environment variable.
arg: Represents the list of arguments to be passed to the executable. The first argument, by
convention, should be the name of the program being executed. The list of arguments must be
terminated by NULL to indicate the end of the argument list.
Return Value:
On success, execlp() does not return; the new program image takes over the process.
On failure, it returns -1, and errno is set to indicate the error.
execvp(): Similar to execv(), but the program is searched for in the directories specified by the
PATH environment variable.
Syntax: int execvp(const char *file, char *const argv[]);
Parameters:
file: The name of the executable file to run. If this name contains a slash (/), then execvp()
will treat it as a path and will not search the PATH environment variable.
argv: An array of pointers to null-terminated strings that represent the argument list available
to the new program. The first argument, by convention, should be the name of the executed
program. The array of pointers must be terminated by a NULL pointer to indicate the end of the
argument list.
Return Value:
On success, execvp() does not return because the current process image is replaced by the
execle(): Similar to execl(), but allows specifying the environment of the executed program as an
array of strings.
Syntax: int execle(const char *path, const char *arg, ..., char *const envp[]);
Parameters:
path: Specifies the path to the executable file which the process will run.
arg: Represents the list of arguments to be passed to the executable. The first argument, by
convention, should be the name of the program being executed. The list of arguments must be
terminated by NULL to indicate the end of the argument list.
...: A variable number of arguments representing the arguments to be passed to the executable,
environment of the new program. The array must be terminated by a NULL pointer.
Return Value:
On success, execle() does not return because the current process image is replaced by the new
program image.
On failure, it returns -1, and errno is set to indicate the error.
execve(): The most general form, which executes a program specified by a pathname, takes an
array of arguments and an array of environment variables.
Syntax: int execve(const char *pathname, char *const argv[], char *const envp[]);
Parameters:
pathname: Specifies the file path of the executable file which the process will run. This file
passed to the new program. The first argument, by convention, should be the name of the
executed program. The array of pointers must be terminated by a NULL pointer.
envp[]: An array of strings, conventionally of the form key=value, which are passed as the
environment of the new program. The array must be terminated by a NULL pointer.
Return Value:
On success, execve() does not return because the current process image is replaced by the new
program image.
On failure, it returns -1, and errno is set to indicate the error.
Sample Program
include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// Child process
printf("Child process (PID %d): executing '/bin/ls'\n", getpid());
// Replace the child process with '/bin/ls'
// execl() takes the path to the program, the name of the program,
// and any command-line arguments, ending with a NULL pointer.
execl("/bin/ls", "ls", NULL);
}
else
{ // Parent process
printf("Parent process (PID %d): waiting for child process\n", getpid());
}
}
output
Parent process (PID 142): waiting for child process
Child process (PID 143): executing '/bin/ls'
a.out exec.c fork.c vfork.c wait.c waitpid waitpid.c
Introduction to Threads
Program, Process and Threads are three basic concepts of the operating systems.
Program is an executable file containing the set of instructions written to perform a specific job on your
computer. Programs are not stored on the primary memory in your computer. They are stored on a disk or a
secondary memory on your computer. They are read into the primary memory and executed by the kernel. A
program is sometimes referred as passive entity as it resides on a secondary memory.
Process is an executing instance of a program. A process is sometimes referred as active entity as it resides
on the primary memory and leaves the memory if the system is rebooted. Several
Thread is the smallest executable unit of a process. A thread is often referred to as a lightweight process
due to its ability to run sequences of instructions independently while sharing the same memory space and
resources of a process. A process can have multiple threads. Each thread will have their own task and own path
of execution in a process. Threads are popularly used to improve the application through parallelism . Actually
only one thread is executed at a time by the CPU, but the CPU switches rapidly between the threads to give an
illusion that the threads are running parallelly.
For example, in a browser, multiple tabs can be different threads or While the movie plays on the
device, various threads control the audio and video in the background.
Another example is a web server - Multiple threads allow for multiple requests to be satisfied
simultaneously, without having to service requests sequentially or to fork off separate processes for
every incoming request.
Components of Threads in Operating System
The Threads in Operating System have the following three components.
Stack Space
Register Set
Program Counter
The given below figure shows the working of a single-threaded and a multithreaded process:
A single-threaded process is a process with a single thread. A multi-threaded process is a process with
multiple threads. As the diagram clearly shows that the multiple threads in it have its own registers,
stack, and counter but they share the code and data segment.
Process simply means any program in execution while the thread is a segment of a process. The main
differences between process and thread are mentioned below:
Process Thread
A Process simply means any program in Thread simply means a segment of a process.
execution.
The process consumes more resources Thread consumes fewer resources.
The benefits of multithreaded programming can be broken down into four major categories:
Resource Sharing
Processes may share resources only through techniques such as-Message Passing,Shared Memory
Such techniques must be explicitly organized by programmer. However, threads share the memory and the
resources of the process to which they belong by default. A single application can have different threads
within the same address space using resource sharing.
Responsiveness
Program responsiveness allows a program to run even if part of it is blocked using multithreading. This can
also be done if the process is performing a lengthy operation. For example - A web browser with
multithreading can use one thread for user contact and another for image loading at the same time.
Utilization of Multiprocessor Architecture
In a multiprocessor architecture, each thread can run on a different processor in parallel using
multithreading. This increases concurrency of the system. This is in direct contrast to a single processor
system, where only one process or thread can run on a processor at a time.
Economy
It is more economical to use threads as they share the process resources. Comparatively, it is more
expensive and time-consuming to create processes as they require more memory and resources. The
overhead for process creation and management is much higher than thread creation and management.
Thread Types
Threads are of two types. These are described below.
User Level Thread
Kernel Level Thread
Threads
Implementation is easy
Implementation is complicated
There are also hybrid models that combine elements of both user-level and kernel-level threads. For example,
some operating systems use a hybrid model called the “two-level model”, where each process has one or more
user-level threads, which are mapped to kernel-level threads by the operating system.
Advanatges
Hybrid models combine the advantages of user-level and kernel-level threads, providing greater flexibility and
control while also improving performance.
Hybrid models can scale to larger numbers of threads and processors, which allows for better use of available
resources.
Disadvantages:
Hybrid models are more complex than either user-level or kernel-level threading, which can make them more
difficult to implement and maintain.
Hybrid models require more resources than either user-level or kernel-level threading, as they require both a
thread library and kernel-level support.
User threads are mapped to kernel threads by the threads library. The way this mapping is done is called
the thread model. Multi threading model are of three types.
Many to Many Model
In this model, we have multiple user threads multiplex to same or lesser number of kernel level threads.
Number of kernel level threads are specific to the machine, advantage of this model is if a user thread is blocked
we can schedule others user thread to other kernel thread. Thus, System doesn’t block if a particular thread is
blocked. It is the best multi threading model.
Threading Issues in OS
In a multithreading environment, there are many threading-related problems. Such as
System Call
Thread Cancellation
Signal Handling
Thread Specific Data
Thread Pool
Schedular Activation
fork() and exec() System Calls
Discussing fork() system call, Let us assume that one of the threads belonging to a multi-
threaded program has instigated a fork() call. Therefore, the new process is a duplication of fork().
Here, the question is as follows; will the new duplicate process made by fork() be multi-threaded like
all threads of the old process or it will be a unique thread?
Now, certain UNIX systems have two variants of fork(). fork can either duplicate all threads of
the parent process to a child process or just those that were invoked by the parent process. The
application will determine which version of fork() to use.
When the next system call, namely exec() system call is issued, it replaces the whole
programming with all its threads by the program specified in the exec() system call’s parameters.
Ordinarily, the exec() system call goes into queue after the fork() system call.
However, this implies that the exec() system call should not be queued immediately after the
fork() system call because duplicating all the threads of the parent process into the child process will
be superfluous. Since the exec() system call will overwrite the whole process with the one given in
the arguments passed to exec().This means that in cases like this; a fork() which only replicates one
invoking thread will do.
Thread Cancellation
Thread Cancellation is the task of terminating a thread before it has completed.
For example, if multiple threads are concurrently searching through a database and one
thread returns the result, the remaining threads might be cancelled.
Another situation might occur when a user presses a button on a Web browser that stops a
Web page from loading any further. Often, a Web page is loaded using several threads-each image
is loaded in a separate thread. When a user presses the stop button on the browser, all threads
loading the page are cancelled.
A thread that is to be cancelled is often referred to as target thread.The Cancellation of a target
thread may occur in two different scenarios:
Asynchronous cancellation: One thread immediately terminates the target thread.
Deferred Cancellation : The target thread periodically checks whether it should terminate, allowing
it an opportunity to terminate itself in an orderly fashion.
The difficulty with cancellation occurs in situations where resources have been allocated to a
cancelled thread or where a thread is cancelled while in the midst of updating data it is sharing with
other threads. This becomes especially troublesome with asynchronous cancellation. Often, the
operating system will reclaim system resources from a cancelled thread but will not reclaim all
resources. Therefore, cancelling a thread asynchronously may not free a necessary system-wide
resource.
With deferred cancellation, in contrast, one thread indicates that a target thread is to be
cancelled, but cancellation occurs only after the target thread has checked a flag to determine whether
or not it should be cancelled. The thread can perform this check at a point at which it can be cancelled
safely known as cancellation points.
Signal Handling
Signal Handling is used in UNIX systems to notify a process that a particular event has occurred. A
signal may be received either synchronously or asynchronously, depending on the source of and the
reason for the event being signalled.
All signals, whether synchronous or asynchronous, follow the same pattern:
A signal is generated by the occurrence of a particular event.
A generated signal is delivered to a process.
Once delivered, the signal must be handled.
Examples of synchronous signals include illegal memory access and division by 0. If a running
program performs either of these actions, a signal is generated. Synchronous signals are delivered to
the same process that performed the operation that caused the signal (that is the reason they are
considered synchronous).
When a signal is generated by an event external to a running process, that process receives the
signal asynchronously. Examples of such signals include terminating a process with specific keystrokes
(such as ) and having a timer expire. Typically, an asynchronous signal is sent to another process.
A signal may be handled by one of two possible handlers:
A default signal handler
A user-defined signal handler
Every signal has a default signal handler that is run by the kernel when handling that
signal. This default action can be overridden by a user defined signal handler that is called to
handle the signal.
Signals are handled in different ways. Some signals (such as changing the size of a window)
are simply ignored; others (such as an illegal memory access) are handled by terminating the
program.
Handling signals in single-threaded programs is straightforward: signals are always
delivered to a process. However, delivering signals is more complicated in multithreaded
programs, where a process may have several threads.
In general the following options exist:
Deliver the signal to the thread to which the signal applies.
Deliver the signal to every thread in the process.
Deliver the signal to certain threads in the process.
Assign a specific thread to receive all signals for the process.
Thread-Specific Data
Threads belonging to a process share the data of the process. Indeed, this sharing of data
provides one of the benefits of multithreaded programming. However, in some circumstances, each
thread need its own copy of certain data. We will call such data thread specific data.
For example, in a transaction-processing system, we might service each transaction in a separate
thread. Furthermore, each transaction might be assigned a unique identifier. To associate each thread
with its unique identifier, we could use thread-specific data.
Thread Pool
The server develops an independent thread every time an individual attempts to access a
page on it. However, the server also has certain challenges. Bear in mind that no limit in the number
of active threads in the system will lead to exhaustion of the available system resources because we
will create a new thread for each request.
The establishment of a fresh thread is another thing that worries us. The creation of a new
thread should not take more than the amount of time used up by the thread in dealing with the
request and quitting after because this will be wasted CPU resources.
Hence, thread pool could be the remedy for this challenge. The notion is that as many fewer
threads as possible are established during the beginning of the process. A group of threads that forms
this collection of threads is referred as a thread pool. There are always threads that stay on the
thread pool waiting for an assigned request to service.
A new thread is spawned from the pool every time an incoming request reaches the server,
which then takes care of the said request. Having performed its duty, it goes back to the pool and
awaits its second order.
Whenever the server receives the request, and fails to identify a specific thread at the ready
thread pool, it may only have to wait until some of the threads are available at the ready thread pool.
It is better than starting a new thread whenever a request arises because this system works well with
machines that cannot support multiple threads at once.
Schedular Activation
A final issue to be considered with multithreaded programs concerns communication between
the kernel and the thread library, which may be required by the many-to-many and two-level models .
Many systems implementing either the many-to-many or the two-level model place an intermediate
data structure between the user and kernel threads. This data structure-typically known as a
lightweight process, or LWP-is shown in Figure.
To the user-thread library, the LWP appears to be a virtual processor on which the application
can schedule a user thread to run. Each LWP is attached to a kernel thread, and it is kernel threads that
the operating system schedules to run on physical processors.
If a kernel thread blocks (such as while waiting for an I/0 operation to complete), the LWP blocks
as well. Up the chain, the user-level thread attached to the LWP also blocks.
An application may require any number of LWPs to run efficiently. Consider a CPU-bound
application running on a single processor. In this scenario, only one thread can run at once, so one LWP
is sufficient. An application that is I/O intensive may require multiple LWPs to execute. Typically, an LWP
is required for each concurrent blocking system call. Suppose, for example, that five different file-read
requests occur simultaneously. Five LWPs are needed, because all could be waiting for I/0 completion
in the kernel. If a process has only four LWPs, then the fifth request must wait for one of the LWPs to
return from the kernel.
Furthermore, the kernel must inform an application about certain events. This procedure
is known as an Upcall. Upcalls are handled by the thread library with an upcall handlers and upcall
handlers must run on a virtual processor. One event that triggers an upcall, occurs when an
application thread is about to block. In this scenario, the kernel makes an upcall to the application
informing it that a thread is about to block and identifying the specific thread.
The kernel then allocates a new virtual processor to the application. The application runs an
upcall handler on this new virtual processor, which saves the state of the blocking thread and
relinquishes the virtual processor on which the blocking thread is running. The upcall handler then
schedules another thread that is eligible to run on the new virtual processor.
When the event that the blocking thread was waiting for occurs, the kernel makes another
upcall to the thread library informing it that the previously blocked thread is now eligible to run. The
up call handler for this event also requires a virtual processor, and the kernel may allocate a new
virtual processor or preempt one of the user threads and run the upcall handler on its virtual
processor.