Final OSL Labmanual 2017-18
Final OSL Labmanual 2017-18
Final OSL Labmanual 2017-18
To provide excellent Information Technology education by building strong teaching and research environment.
MISSION
1) To transform the students into innovative, competent and high quality IT professionals to meet
the growing global challenges.
2) To achieve and impart quality education with an emphasis on practical skills and social
relevance.
3) To endeavor for continuous up-gradation of technical expertise of students to cater to the
needs of the society.
4) To achieve an effective interaction with industry for mutual benefits.
1) Graduates of the program will possess strong fundamental concepts in mathematics, science,
engineering and Technology to address technological challenges.
2) Possess knowledge and skills in the field of Computer Science & Engineering and Information
Technology for analyzing, designing and implementing complex engineering problems of any
domain with innovative approaches.
3) Possess an attitude and aptitude for research, entrepreneurship and higher studies in the field
of Computer Science & Engineering and Information Technology.
4) Have commitment to ethical practices, societal contributions through communities and life-
long learning.
5) Possess better communication, presentation, time management and team work skills leading
to responsible & competent professionals and will be able to address challenges in the field of
IT at global level.
Reference Code
Version No 4.0
Compliance Status Complete
Revision Date 07 June 2017
Security Classification Department Specific
Document Status Definitive
Review Period Yearly
Document History
Revision
Revision Date Reason For Change
No.
1.0 15 Dec 2014 University Syllabus Modification - Course 2012
Examination Scheme:
Term Work : 25 Marks
Practical : 50 Marks
Prerequisites:
1. C programming.
2. Fundamental of Data Structures.
Course Objectives:
1. To introduce and learn Linux commands required for administration.
2. To learn shell programming concepts and applications.
3. To demonstrate the functioning of OS basic building blocks like processes, threads under the LINUX.
4. To demonstrate the functioning of OS concepts in user space like concurrency control (process
Synchronization, mutual exclusion & deadlock) and file handling in LINUX.
5. To aware Linux kernel source code details.
6. To demonstrate the functioning of OS concepts in kernel space like embedding the system call in any
LINUX kernel.
Course Outcomes:
1. To understand the basics of Linux commands and program the shell of Linux.
2. To develop various system programs for the functioning of operating system.
3. To implement basic building blocks like processes, threads under the Linux.
4. To develop various system programs for the functioning of OS concepts in user space like
Concurrency control and file handling in Linux.
5. To design and implement Linux Kernel Source Code.
6. To develop the system program for the functioning of OS concepts in kernel space like embedding
the system call in any Linux kernel.
Laboratory Assignments
Assignment No. 2: Process control system calls: The demonstration of FORK, EXECVE and WAIT system
calls along with zombie and orphan states.
b. Implement the C program in which main program accepts an integer array. Main program uses
theFORK system call to create a new process called a child process. Parent process sorts an
integer array and passes the sorted array to child process through the command line arguments
ofEXECVE system call. The child process uses EXECVE system call to load new program that uses
this sorted array for performing the binary search to search the particular item in the array.
Assignment No. 5: Thread synchronization and mutual exclusion using mutex. Application to
demonstrate: Reader-Writer problem with reader priority.
Assignment No. 6: Deadlock Avoidance Using Semaphores: Implement the deadlock-free solution to
Dining Philosophers problem to illustrate the problem of deadlock and/or starvation that can occur
when manysynchronized threads are competing for limited resources.
a. Pipes: Full duplex communication between parent and child processes. Parent process writes a
pathname of a file (the contents of the file are desired) on one pipe to be read by child process
and child process writes the contents of the file on second pipe to be read by parent process and
displays on standard output.
b. FIFOs: Full duplex communication between two independent processes. First process accepts
sentences and writes on one pipe to be read by second process and second process counts
number of characters, number of words and number of lines in accepted sentences, writes this
output in a text file and writes the contents of the file on second pipe to be read by first process
and displays on standard output.
Assignment No. 8: Inter-process Communication using Shared Memory using System V. Application to
demonstrate: Client and Server Programs in which server process creates a shared memory segment
and writes the message to the shared memory segment. Client process reads the message from the
sharedmemory segment and displays it to the screen.
Assignment No. 9: Implement an assignment using File Handling System Calls (Low level system calls
like open, read, write, etc).
Assignment No. 10: Implement a new system call in the kernel space, add this new system call in the
Linuxkernel by the compilation of this kernel (any kernel source, any architecture and any Linux kernel
distribution) and demonstrate the use of this embedded system call using C program in user space.
http://homes.cs.washington.edu/~tom/nachos/
http://web.cecs.pdx.edu/~walpole/class/cse513/project/syscall.html
http://web.cecs.pdx.edu/~walpole/class/cse513/project/syscall.html
Shell programming
OBJECTIVES : To study
1. Basic Shell commands
2. Shell script
THEORY :
Shell Script: Normally shells are interactive. It means shell accept command from you (via
keyboard) and execute them. But if you use command one by one (sequence of 'n' number of
commands) , the you can store this sequence of command to text file and tell the shell to
execute this text file instead of entering the commands. This is known as shell script. Shell
Script is series of command written in plain text file. This manual is meant as a brief
introduction to features found in Bash.
Exit Status:
By default in Linux if particular command/shell script is executed, it return two type of
values which is used to see whether command or shell script executed is successful or not.
(1) If return value is zero (0), command is successful.
(2) If return value is nonzero, command is not successful or some sort of error executing
command/shell script.
This value is known as Exit Status.But how to find out exit status of command or shell script?
Simple, to determine this exit Status you can use $? special variable of shell.
For e.g. (This example assumes that unknow1file doest not exist on your hard drive)
$ rm unknow1file
It will show error as follows
rm: cannot remove `unkowm1file': No such file or directory
and after that if you give command
$ echo $?
Single
' 'Single quotes' - Enclosed in single quotes remains unchanged.
quotes
` Back quote
`Back quote` - To execute command
Example:
$ echo "Today is date"
Can't print message with today's date.
$ echo "Today is `date`".
Variables in Shell
To process our data/information, data must be kept in computers RAM memory. RAM
memory is divided into small locations, and each location had unique number called memory
location/address, which is used to hold our data. Programmer can give a unique name to this
memory location/address called memory variable or variable (Its a named storage location
that may take different values, but only one at a time).
In Linux (Shell), there are two types of variable:
(1) System variables - Created and maintained by Linux itself. This type of variable defined in
is not equal
-ne 5 != 6 if test 5 -ne 6 if [ 5 -ne 6 ]
to
is less than
-le 5 <= 6 if test 5 -le 6 if [ 5 -le 6 ]
or equal to
is greater
-gt 5>6 if test 5 -gt 6 if [ 5 -gt 6 ]
than
is greater
-ge than or 5 >= 6 if test 5 -ge 6 if [ 5 -ge 6 ]
equal to
Operator Meaning
string1 =
string1 is equal to string2
string2
Meaning
Logical Operators:
Logical operators are used to combine two or more condition at a time
Operator Meaning
if condition
if condition which is used for decision making in shell script, If given condition is true then
command1 is executed.
Syntax:
if condition
then
command1 if condition is true or if exit status
of condition is 0 (zero)
Each white space-separated word in list is assinged to variable in turn and commands
executed until list is exhausted.
The case Statement
The case statement is good alternative to Multilevel if-then-else-fi statement. It enable you
to match several values against one variable. Its easier to read and write.
Conclusion: Thus in shell script we can write series of commands and execute
as a single program.
AIM : The demonstration of fork, execve and wait system calls along
with zombie and orphan states.
1. Implement the C program in which main program accepts the
integers to be sorted. Main program uses the fork system call
to create a new process called a child process. Parent process
sorts the integers using merge sort and waits for child process
using wait system call to sort the integers using quick sort. Also
demonstrate zombie and orphan states.
2. Implement the C program in which main program accepts an
integer array. Main program uses the fork system call to create
a new process called a child process. Parent process sorts an
integer array and passes the sorted array to child process
through the command line arguments of execve system call.
The child process uses execve system call to load new program
that uses this sorted array for performing the binary search to
search the particular item in the array.
OBJECTIVES : To study
3. Process control
4. Zombie and orphan processes
5. System calls : fork, execv
THEORY :
Process
A process is the basic active entity in most operating-system models. A process is a program in
execution in memory or in other words, an instance of a program in memory. Any program
executed creates a process. A program can be a command, a shell script, or any binary
executable or any application.
Each process in a Linux system is identified by its unique process ID, sometimes referred to as
pid. Process IDs are 16-bit numbers that are assigned sequentially by Linux as new processes
are created.
When referring to process IDs in a C or C++ program, always use the pid_ttypedef, which is
defined in <sys/types.h>.A program can obtain the process ID of the process it’s running in
with the getpid() system call, and it can obtain the process ID of its parent process with the
getppid() system call.
Creating Process
A process can create a new process by calling fork. The calling process becomes the parent,
and the created process is called the child. The fork function copies the parent's memory image
so that the new process receives a copy of the address space of the parent. Both processes
continue at the instruction after the fork statement (executing in their respective memory
images).
Synopsis
#include <unistd.h>
pid_t fork(void);
Status values
The status argument of wait is a pointer to an integer variable. If it is not NULL, this function
stores the return status of the child in this location. The child returns its status by calling exit,
_exit or return from main.
A zero return value indicates EXIT_SUCCESS; any other value indicates EXIT_FAILURE.
POSIX specifies six macros for testing the child's return status. Each takes the status value
returned by a child to wait as a parameter. Following are the two such macros:
Synopsis
#include <sys/wait.h>
WIFEXITED(intstat_val)
WEXITSTATUS(intstat_val)
Used for new program execution within the existing process. The fork function creates a copy
of the calling process, but many applications require the child process to execute code that is
different from that of the parent. The exec family of functions provides a facility for overlaying
the process image of the calling process with a new image. The traditional way to use the fork–
exec combination is for the child to execute (with an exec function) the new program while the
parent continues to execute the original code. The exec system call is used after a fork system
call by one of the two processes to replace the memory space with a new program. The exec
system call loads a binary file into memory (destroying image of the program containing the
exec system call) and go their separate ways. Within the exec family there are functions that
vary slightly in their capabilities.
Synopsis
#include <unistd.h>
extern char **environ;
Exec family
1. intexecl(const char *path, const char *arg0, ... /*, char *(0) */);
2. intexecle (const char *path, const char *arg0, ... /*, char *(0), char *constenvp[] */);
3. intexeclp (const char *file, const char *arg0, ... /*, char *(0) */);
4. intexecv(const char *path, char *constargv[]);
5. intexecve (const char *path, char *constargv[], char *constenvp[]);
6. intexecvp (const char *file, char *constargv[]);
1. Execl() and execlp():
execl()
It permits us to pass a list of command line arguments to the program to be executed.
The list of arguments is terminated by NULL.
e.g. execl("/bin/ls", "ls", "-l", NULL);
execlp()
It does same job 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. It
can also take the fully qualified name as it also resolves explicitly.
e.g. execlp("ls", "ls", "-l", NULL);
execvp()
It does same job expect 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.
e.g. execvp("ls", argv);
3. execve( )
It executes the program pointed to by filename.
intexecve(const char *filename, char *constargv[ ], char *constenvp[ ]);
Filename must be either a binary executable, or a script starting with a line of the form:
argv is an array of argument strings passed to the new program. By convention, the
first of these strings should contain the filename associated with the file being
executed. envp is an array of strings, conventionally of the form key=value, which are
passed as environment to the new program. Both argv and envp must be terminated
by a NULL pointer. The argument vector and environment can be accessed by the
called program's main function, when it is defined as:
int main(intargc, char *argv[ ] , char *envp[ ])]
execve() does not return on success, and the text, data, bss, and stack of the calling
process are overwritten by that of the program loaded.
All exec functions return –1 if unsuccessful. In case of success these functions never return to
the calling function.
Process Termination
Normally, a process terminates in one of two ways. Either the executing program calls the
Orphan process is the process whose parent process is dead. That is, parent process is
terminated, killed or exited but the child process is still alive.
In Linux/Unix like operating systems, as soon as parents of any process are dead, re-parenting
occurs, automatically. Re-parenting means orphaned process is immediately adopted by
special process. Thing to notice here is that even after re-parenting, the process still remains
Orphan as the parent which created the process is dead.
A process can be orphaned either intentionally or unintentionally. Sometimes a parent process
exits/terminates or crashes leaving the child process still running, and then they become
orphans. Also, a process can be intentionally orphaned just to keep it running. For example
This will show you all the orphan processes running in your system. The output from this
command confirms that they are orphan processes but does not mean that they are all
useless.
Killing a Orphan Process
As orphan processes waste server resources, it is not advised to have lots of orphan processes
running into the system. To kill a orphan process is same as killing a normal process.
# kill -15 <PID>
If that does not work then simply use
# kill -9 <PID>
Daemon Process
It is a process that runs in the background, rather than under the direct control of a user. They
are usually initiated as background processes.
vfork
It is alternative of fork. Creates a new process when exec a new program.
Comparison with fork
1. Creates new process without fully copying the address space of the parent.
2. vfork guarantees that the child runs first, until the child calls exec or exit.
3. When child calls either of these two functions (exit, exec), the parent resumes.
Input
FAQS
Is Orphan process different from a Zombie process?
Are Orphan processes harmful for system?
Is it bad to have Zombie processes on your system?
How to find an Orphan Process?
How to find a Zombie Process?
What is common shared data between parent and child process?
What are the contents of Process Control Block?
Practice Assignments
Example 1
Printing the Process ID
#include <stdio.h>
#include <unistd.h>
int main()
{
printf(“The process ID is %d\n”, (int) getpid());
printf(“The parent process ID is %d\n”, (int) getppid());
return 0;
}
Example 2
Using the system call
Example 3
Using fork to duplicate a program’s process
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_tchild_pid;
printf(“The main program process ID is %d\n”, (int) getpid());
child_pid=fork();
if(child_pid!=0)
{
printf(“This is the parent process ID, with id %d\n”, (int)
getpid());
printf(“The child process ID is %d\n”, (int) child_pid);
}
else
printf(“This is the child process ID, with id %d\n”, (int) getpid());
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
void show_return_status(void)
{
pid_tchildpid;
int status;
childpid = wait(&status); if (childpid == -1)
perror("Failed to wait for child");
else if (WIFEXITED(status))
printf("Child %ld terminated with return status %d\n", (long)childpid,
WEXITSTATUS(status));
}
Example 5
A program that creates a child process to run ls -l
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_tchildpid;
childpid = fork();
if (childpid == -1) {
perror("Failed to fork");
return 1;
}
Example 6
Making a zombie process
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_tchild_pid;
//create a child process
child_pid=fork();
if(child_pid>0) {
//This is a parent process. Sleep for a minute
sleep(60)
}
else
{
//This is a child process. Exit immediately.
exit(0);
}
Example 7
Demonstration of fork system call
#include<stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_tpid;
char *msg;
int n;
printf(“Program starts\n”);
pid=fork();
switch(pid)
{
case -1:
printf(“Fork error\n”);
exit(-1);
case 0:
msg=”This is the child process”;
n=5;
break;
default:
msg=”This is the parent process”;
n=3;
break;
}
while(n>0)
Example 8
Demo of multiprocess application using fork()system call
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
void do_child_proc(intpfd[2]);
void do_parent_proc(intpfd[2]);
int main()
{
intpfd[2];
intret_val,nread;
pid_tpid;
ret_val=pipe(pfd);
if(ret_val==-1)
{
perror(“pipe error\n”);
exit(ret_val);
}
pid=fork();
Thread
A thread is a semi-process that has its own stack, and executes a given piece of code. Unlike a
real process, the thread normally shares its memory with other threads (where as for
processes we usually have a different memory area for each one of them). A Thread Group is a
set of threads all executing inside the same process. They all share the same memory, and thus
can access the same global variables, same heap memory, same set of file descriptors, etc. All
these threads execute in parallel (i.e. using time slices, or if the system has several processors,
then really in parallel).
Threads in the same process share:
Process instructions
Most data
open files (descriptors)
signals and signal handlers
current working directory
User and group id
Each thread has a unique:
Threads vs process
The primary motivation for using threads is to realize potential program performance gains.
Compared to the cost of creating and managing a process, a thread can be created with
much less operating system overhead. Managing threads requires fewer system
resources than managing processes.
All threads within a process share the same address space. Inter-thread communication
is more efficient and in many cases, easier to use than inter-process communication.
pthreads API
The subroutines which comprise the Pthreads API can be informally grouped into three major
classes:
1. Thread management: The first class of functions work directly on threads - creating,
detaching, joining, etc. They include functions to set/query thread attributes (joinable,
scheduling etc.)
2. Mutex: The second class of functions deals with a coarse type of synchronization,
called a "mutex", which is an abbreviation for "mutual exclusion". Mutex functions
provide for creating, destroying, locking and unlocking mutex. They are also
supplemented by mutex attribute functions that set or modify attributes associated
with mutex.
3. Condition variables: The third class of functions deal with a finer type of
synchronization - based upon programmer specified conditions. This class includes
Arguments
Thread - returns the thread id. (unsigned long int defined in
bits/pthreadtypes.h)
attr - Set to NULL if default thread attributes are used. (else define members of
pthread_join
Function: intpthread_join
(
pthread_tth, /* Pass threadhandle */
void **returnvalue/* Return value is returned by ref. */
);
Info: Return 0 on success, and negative on failure. The returned value is a pointer returned by
reference. If you do not care about the return value, you can pass NULL for the second
argument.
Arguments:
th - thread suspended until the thread identified by th terminates, either by
calling pthread_exit() or by being cancelled.
returnvalue - If thread_return is not NULL, the return value of th is stored in the
location pointed to by thread_return.
Thread Initialization
Thread Attributes
Threads have a number of attributes that may be set at creation time. This is done by filling a
thread attribute object attr of type pthread_attr_t, then passing it as second argument to
pthread_create. Passing NULL is equivalent to passing a thread attribute object with all
attributes set to their default values. Attribute objects are consulted only when creating a new
thread. The same attribute object can be used for creating several threads. Modifying an
Steps
Accept the matrices in the main program
Create a thread using pthread_create(). Thread function multiplies two matrix
elements.
The result of multiplication is returned to calling thread using pthread_exit().
Use pthread_join() function in main program to block till the thread terminates and
catch the result returned by pthread_exit().
Add the above result to get the elements of resultant matrix.
Display the resultant matrix.
FAQ
1. What is thread? Explain the concept of multithreading.
2. What is the difference between thread & process?
3. How threads are created using pthread?
4. What is the use of pthread_join() and pthread_exit() function?
Example 1
Pthread Creation and Termination
#include <stdio.h>
#include <pthread.h>
void *kidfunc(void *p)
{
printf ("Kid ID is ---> %d\n", getpid( ));
}
main ( )
{
pthread_t kid ;
pthread_create (&kid, NULL, kidfunc, NULL) ;
printf ("Parent ID is ---> %d\n", getpid( )) ;
pthread_join (kid, NULL) ;
printf ("No more kid!\n") ;
}
Example 2
Multithreaded C Program Using the Pthread API
#include<pthread.h>
#include<stdio.h>
int sum; /*This data is shared by the thread(s) */
Above Program creates a separate thread that determines the summation of a non-negative
integer. In a thread program, separate thread begins execution in a specified function. In
above program it is the runner function. When this program begins, a single thread of control
begins in main. After some initialization, main creates a second thread that begins control in
the summer function. All Pthread programs must include the pthread.h header file. The
pthread_attr_tattr declaration represents the attributes for the thread. Because we did not
explicitly set any attributes, we will use the default attribute provided. A separate thread is
created with the pthread_create function call. In addition to passing the thread identifier and
the attributes for the thread, we also pass the name of the function where the new thread will
execution, in this case runner function. Lastly, we pass the integer parameter that was
provided on the command line, argv[1].
At this point, the program has two threads: the initial thread in main and the thread
performing the summation in the runner function. After creating the second thread, the main
thread will wait for the runner thread to complete by calling the pthread_join function. The
runner thread will complete when it calls the function pthread_exit.
Thread Synchronization
THREAD SYNCHRONIZATION
Counting semaphore
Semaphores which allow an arbitrary resource count are called counting semaphores.
A counting semaphore comprises:
An integer variable, initialized to a value K (K>=0). During operation it can assume any
value <= K, a pointer to a process queue. The queue will hold the PCBs of all those
processes, waiting to enter their critical sections. The queue is implemented as a FCFS,
so that the waiting processes are served in a FCFS order.
Figure illustrates the structure of buffer b. The producer can generate items and store them in
the buffer at its own pace. Each time, an index (in) into the buffer is incremented. The
consumer proceeds in a similar fashion but must make sure that it does not attempt to read
from an empty buffer. Hence, the
POSIX Semaphores
POSIX semaphores allow processes and threads to synchronize their actions. A semaphore is
an integer whose value is never allowed to fall below zero. Two operations can be performed
on semaphores: increment the semaphore value by one (sem_post(3)); and decrement the
semaphore value by one (sem_wait(3)). If the value of a semaphore is currently zero, then
a sem_wait(3) operation will block until the value becomes greater than zero.
Mutex
Mutexes are a method used to be sure two threads, including the parent thread, do not
attempt to access shared resource at the same time. A mutex lock allows only one thread
to enter the part that's locked and the lock is not shared with any other processes.
3. pthread_mutex_unlock()
The function shall release the mutex object referenced by mutex. The manner in which a
mutex is released is dependent upon the mutex's type attribute. If there are threads
blocked on the mutex object referenced by mutex when pthread_mutex_unlock() is called,
resulting in the mutex becoming available, the scheduling policy shall determine which
thread shall acquire the mutex.
intpthread_mutex_unlock(pthread_mutex_t * mutex);
4. pthread_mutex_destroy()
The function shall destroy the mutex object referenced by mutex; the mutex object
becomes, in effect, uninitialized. A destroyed mutex object can be reinitialized using
pthread_mutex_init(); the results of otherwise referencing the object after it has been
destroyed are undefined.
intpthread_mutex_destroy(pthread_mutex_t *mutex);
CONCLUSION:
Thus, we have implemented producer-consumer problem using ‘C’ in Linux.
Thread Synchronization
THREAD SYNCHRONIZATION
Counting semaphore
POSIX semaphores allow processes and threads to synchronize their actions. A semaphore is
an integer whose value is never allowed to fall below zero. Two operations can be performed
on semaphores: increment the semaphore value by one (sem_post(3)); and decrement the
semaphore value by one (sem_wait(3)). If the value of a semaphore is currently zero, then
a sem_wait(3) operation will block until the value becomes greater than zero.
Semaphore functions:
4. sem_init()
It initializes the unnamed semaphore at the address pointed to by sem. The value
argument specifies the initial value for the semaphore.
intsem_init(sem_t *sem, intpshared, unsigned int value);
Mutexes are a method used to be sure two threads, including the parent thread, do not
attempt to access shared resource at the same time. A mutex lock allows only one thread
to enter the part that's locked and the lock is not shared with any other processes.
5. pthread_mutex_init()
The function shall initialize the mutex referenced by mutex with attributes specified by
attr. If attr is NULL, the default mutex attributes are used; the effect shall be the same as
passing the address of a default mutex attributes object. Upon successful initialization, the
state of the mutex becomes initialized and unlocked.
7. pthread_mutex_unlock()
The function shall release the mutex object referenced by mutex. The manner in which a
mutex is released is dependent upon the mutex's type attribute. If there are threads
blocked on the mutex object referenced by mutex when pthread_mutex_unlock() is called,
resulting in the mutex becoming available, the scheduling policy shall determine which
thread shall acquire the mutex.
intpthread_mutex_unlock(pthread_mutex_t * mutex);
8. pthread_mutex_destroy()
The function shall destroy the mutex object referenced by mutex; the mutex object
becomes, in effect, uninitialized. A destroyed mutex object can be reinitialized using
pthread_mutex_init(); the results of otherwise referencing the object after it has been
destroyed are undefined.
intpthread_mutex_destroy(pthread_mutex_t *mutex);
CONCLUSION:
Thus, we have implemented producer-consumer problem using ‘C’ in Linux.
FAQ
4. Explain the concept of semaphore?
5. Explain wait and signal functions associated with semaphores.
6. What is meant by binary and counting semaphores?
What is Deadlock?
A set of processes is deadlocked if each process in the set is waiting for an event that only
another process in the set can cause. Because all the processes are waiting, none of them will
ever cause any of the events that could wake up any of the other members of the set, and all
the processes continue to wait forever. For this model, we assume that processes have only a
single thread and that there are no interrupts possible to wake up a blocked process. The no
interrupts condition is needed to prevent an otherwise deadlocked process from being awake.
Whenever a philosopher gets hungry, he tries to pick up 2 chopsticks that are close to him.
Philosopher may pick up one chopstick at a time. He cannot pick up a chopstick that is already
in the hand of neighbor. When a hungry philosopher has both chopsticks at the same time, he
start eating without releasing his chopstick and starts thinking again .The problem could be
raised when all the philosopher try to keep the chopstick at the same time.
This may lead to deadlock situations. To synchronize all philosophers, semaphore chopsticks
[5] are used as a variable where all the elements are first initialized to 1.The structure of
philosophers is shown below;
CONCLUSION:
Thus, we have implemented dining philosopher’s problem using ‘C’ in Linux.
FAQ:
1. What is dead lock?
2. What are the necessary and sufficient conditions to occur deadlock?
3. What is deadlock avoidance and deadlock prevention techniques?
PIPES:
Pipe provides an inter-process communication channel between related processes. The pipe
interface is intended to look like a file interface. Although a pipe may seem like a file, it is not a
file. Each write to the pipe fills as many blocks as are needed to satisfy it, provided that it does
not exceed the maximum pipe size. Filled blocks are conveyed to the read-end of the pipe,
where they are emptied when they are read. These types of pipes are called unnamed pipes
because they do not exist anywhere in the file system.
Creating a pipe:
#include<unistd.h>
intpipe(intfiledes[2])
The system call pipe(fd), given an integer array filedes of size 2, creates a pair of file
descriptors, filedes[0]and filedes[1], pointing to the "read-end" and "write-end" of a pipe
respectively. If it is successful, it returns a 0, otherwise it returns -1.
Data can be written to the file descriptor fildes[1] and read from the file descriptor fildes[0]. A
read on the file descriptor fildes[0] shall access data written to the file descriptor fildes[1] on a
first-in-first-out basis. If the parent wants to receive data from the child, it should close
fildes[1], and the child should close fildes[0]. If the parent wants to send data to the child, it
should close fildes[0], and the child should close fildes[1]. Since descriptors are shared
Writing to pipe
The pipe has a limited size (64K in some systems) -- cannot write to the pipe infinitely.
Writes upto count bytes to file referenced by file descriptor fd from buffer starting at buf.
Reads upto count bytes from file descriptor fd into buffer starting at buf.
The pipe(2) system call returns two file descriptors that form a "pipe", a one-way
communication channel with a "read end" and a "write end".
Using a Pipe to Pass Data between a Parent Process and a Child Process. The following
example demonstrates the use of a pipe to transfer data between a parent process and a child
process. Error handling is excluded, but otherwise this code demonstrates good practice when
using pipes: after the fork() the two processes close the unused ends of the pipe before they
commence transferring data.
#include <stdlib.h>
#include <unistd.h>
...
intfildes[2];
constint BSIZE = 100;
char buf[BSIZE];
ssize_tnbytes;
int status;
status = pipe(fildes);
if (status == -1 ) {
/* an error occurred */
...
}
switch (fork())
CONCLUSION:
Thus, we studied inter process communication using pipes.
FIFOs
A FIFO (First In First Out) is a one-way flow of data. FIFOs have a name, so unrelated processes
can share the FIFO. FIFO is a named pipe. Any process can open or close the FIFO. FIFOs are
also called named pipes.
Properties:
Creating a FIFO
A FIFO is created by the mkfifo function. Specify the path to the FIFO on the command line. For
example, create a FIFO in /tmp/fifo by invoking this:
#include <sys/types.h>
#include <sys/stat.h>
intmkfifo(const char *pathname, mode_t mode);
pathname: a UNIX pathname (path and filename). The name of the FIFO
mode: the file permission bits. It specifies the pipe’s owner, group, and world
permissions, and a pipe must have a reader and a writer, the permissions must include
both read and write permissions.
If the pipe cannot be created (for instance, if a file with that name already exists), mkfifo
returns –1.
Accessing a FIFO
Access a FIFO just like an ordinary file. To communicate through a FIFO, one program must
open it for writing, and another program must open it for reading. Either low-level I/O
functions like open, write, read, close or C library I/O functions (fopen, fprintf, fscanf, fclose,
and soon) may be used.
CONCLUSION:
Thus, we studied inter process communication using FIFOs.
ASSIGNMENT NO: 10
AIM : Implement a new system call in the kernel space, add this new
system call in the Linuxkernel by the compilation of this kernel
(any kernel source, any architecture and any Linux
kerneldistribution) and demonstrate the use of this embedded
system call using C program in user space.
OBJECTIVES : To study
Linux kernel architecture
System call
THEORY :
Steps
3. Create a “hello.c” file in this folder and add the definition of the system call to it as given
below
gedithello.c
#include<linux/kernel.h>
asmlinkage long sys_hello(void)
{
printk(“Hello world\n”);
4. Create a “Makefile” in the hello folder and add the given line to it.
geditMakefile
Add the following line to it:-
obj-y:=hello.o
This is to ensure that the hello.c file is compiled and included in the kernel source code.
5. Add the hello directory to the kernel’s Makefile
Change back into the linux-3.17.7 folder and open Makefile
geditMakefile
Go to line number 842 which says:-
“core-y+=kernel/mm/fs/ipc/security/crypto/block/”
Change this to
“core-y+=kernel/mm/fs/ipc/security/crypto/block/hello/”
This is to tell the compiler that the source files of our new system call (sys_hello()) are
present in the hello directory.
6. Add the new system call (sys_hello()) into the system call table (syscall_32.tblfile)
If your system is a 64 bit system, you will need to alter the syscall_64.tblfile.
cd arch/x86/syscalls
gedit syscall_32.tbl
Add the following line at the end of the file:-
354 i386 hello sys_hello
354 : It is the number of the system call .It should be one plus the number of the last system
call.
(it was 354 in my system). This has to be noted down to make the system call in the user space
program.
#include<stdio.h>
#include<linux/kernel.h>
#include<sys/syscall.h>
#include<unistd.h>
int main()
{
slongintmycall =syscall(354);
printf(“System call sys_hello returned %ld\n”, mycall);
return0;
}