Вы находитесь на странице: 1из 67

Marathwada Mitra Mandal’s

COLLEGE OF ENGINEERING
S. No. 18, Plot No. 5/3, Karvenagar, PUNE – 411 052
Tel: 020-25473160 Fax: 020-25470909
Website: www.mmcoe.edu.in

DEPARTMENT OF INFORMATION TECHNOLOGY

LABORATORY MANUAL

TE (INFORMATION TECHNOLOGY) (SEMESTER – I)

SOFTWARE LABORATORY II
2015 course

Teaching Scheme: Examination Scheme:


Practical: 4 hrs/week Term Work: 25 Marks
Practical: 50 Marks

Prepared By
Ms. Shital Kakad
Marathwada Mitra Mandal’s
COLLEGE OF ENGINEERING
S. No.18, Plot No.5/3, Karvenagar, PUNE - 411 052
---------------------------------------------------------------------------------------------------------------------
DEPARTMENT OF INFORMATION TECHNOLOGY
List of Assignments
Subject: Software Laboratory II Class: TE (IT)
Teaching Scheme: Examination Scheme:
Practical: 4 Hours/Week Practical: 50 Marks
Term Work: 25 Marks
Experiment No. Name of Assignment

Shell programming
Write a program to implement an address book with options given below: a) Create
1
address book. b) View address book. c) Insert a record. d) Delete a record. e) Modify a
record. f) Exit.

Process control system calls: The demonstration of FORK, EXECVE and WAIT system
calls along with zombie and orphan states.a. 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 sorting
algorithm and waits for child process using WAIT system call to sort the integers using
any sorting algorithm. Also demonstrate zombie and orphan states.b. Implement the C
2
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.
3 Implement multithreading for Matrix Multiplication using pthreads.

Thread synchronization using counting semaphores. Application to demonstrate: producer-


4
consumer problem with counting semaphores and mutex.

Thread synchronization and mutual exclusion using mutex. Application to demonstrate:


5
Reader-Writer problem with reader priority.

Deadlock Avoidance Using Semaphores: Implement the deadlock-free solution to Dining


6 Philosophers problem to illustrate the problem of deadlock and/or starvation that can
occur when many synchronized threads are competing for limited resources.
7 Inter process communication in Linux using following.
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.

Inter-process Communication using Shared Memory using System V. Application to


demonstrate: Client and Server Programs in which server process creates a shared memory
8
segment and writes the message to the shared memory segment. Client process reads the
message from the shared memory segment and displays it to the screen.

Implement an assignment using File Handling System Calls (Low level system calls like
9
open, read, write, etc).

Implement a new system call in the kernel space, add this new system call in the Linux
kernel by the compilation of this kernel (any kernel source, any architecture and any Linux
10
kernel distribution) and demonstrate the use of this embedded system call using C
program in user space.

Content beyond syllabus assignment


11
Study of Boot process for Linux(Ubuntu) & Windows Operating System

Virtual Lab Assignment


12
Write a program to obtain System Information
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:

After studying this subject student should be able to


11. 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.
CO - PO Mapping:
SUBJE PO1 PO2 PO3 PO4 PO5 PO6 PO7 PO8 PO9 PO10 PO11 PO12 PO13
CT

CO1 X X X X X X X X

CO2 X X X X X X X X
OOPL
CO3 X X X X X X X X

CO4 X X X

CO5 X X X

CO6 X X X

Assignment & CO Mapping:


Assignment / CO's CO1 CO2 CO3 CO4 CO5 CO6

Assignment No. 1 X

Assignment No. 2 X X X

Assignment No. 3 X X X

Assignment No. 4 X X

Assignment No. 5 X X

Assignment No. 6 X X

Assignment No. 7 X X X

Assignment No. 8 X X

Assignment No. 9 X

Assignment No. 10 X X X

Assignment No. 11 X X

Assignment No. 12 X X
Experiment No. 1
Title of Experiment: Basics of Shell programming
Write a program to implement an address book with options given below:
a) Create address book.
b) View address book.
c) Insert a record.
d) Delete a record.
e) Modify a record.
f) Exit.

Course Objectives:
To learn shell programming concepts and applications.

Course Outcomes Achieved:


To understand the basics of Linux commands and program the shell of Linux.

Theory:
Shell:
Computer understand the language of 0's and 1's called binary language. In early days of
computing, instruction are provided using binary language, which is difficult for all of us, to
read and write. So in Os there is special program called Shell. Shell accepts your instruction or
commands in English (mostly) and if its a valid command, it is pass to kernel. Shell is a user
program or it's environment provided for user interaction. Shell is an command language
interpreter that executes commands read from the standard input device (keyboard) or from a
file. Shell is not part of system kernel, but uses the system kernel to execute programs, create
files etc.

Types of Shells in Unix:


[1] sh
The Bourne shell, called "sh," is one of the original shells, developed for Unix computers by
Stephen Bourne at AT&T's Bell Labs in 1977. Its long history of use means many software
developers are familiar with it. It offers features such as input and output redirection, shell
scripting with string and integer variables, and condition testing and looping.

TE IT (Course 2015) Software Laboratory II


Figure 1.1 Operating system components
[2] bash
The popularity of sh motivated programmers to develop a shell that was compatible with it, but
with several enhancements. Linux systems still offer the sh shell, but "bash" -- the "Bourne-again
Shell," based on sh -- has become the new default standard. One attractive feature of bash is its
ability to run sh shell scripts unchanged. Shell scripts are complex sets of commands that automate
programming and maintenance chores; being able to reuse these scripts saves programmers time.
Conveniences not present with the original Bourne shell include command completion and a
command history.

[3] csh and tcsh


Developers have written large parts of the Linux operating system in the C and C++ languages.
Using C syntax as a model, Bill Joy at Berkeley University developed the "C-shell," csh, in 1978.
Ken Greer, working at Carnegie-Mellon University, took csh concepts a step forward with a new
shell, tcsh, which Linux systems now offer. Tcsh fixed problems in csh and added command
completion, in which the shell makes educated "guesses" as you type, based on your system's
directory structure and files. Tcsh does not run bash scripts, as the two have substantial
differences.

[4] ksh
David Korn developed the Korn shell, or ksh, about the time tcsh was introduced. Ksh is
compatible with sh and bash. Ksh improves on the Bourne shell by adding floating-point
arithmetic, job control, command aliasing and command completion. AT&T held proprietary
rights to ksh until 2000, when it became open source.

TE IT (Course 2015) Software Laboratory II


Shell Name Developed by Where Remark

BASH Brian Fox and Free Software


Most common shell in Linux. It's
(Bourne- Again Chet Ramey Foundation Freeware shell.
SHell )

University of The C shell's syntax and usage


California
CSH (C SHell) Bill Joy are very similar to the C
(For BSD) programming language.

KSH (Korn SHell) David Korn AT & T Bell Labs --

Table 1.1 Types of Shells

To find all available shells in your system type following command:


$ cat /etc/shells
Note that each shell does the same job, but each understand a different command syntax and
provides different built-in functions. Any of the above shell reads command from user (via
Keyboard or Mouse) and tells Linux Os what users want. If we are giving commands from
keyboard it is called command line interface ( Usually in-front of $ prompt, This prompt is
depend upon your shell and Environment that you set or by your System Administrator,
therefore you may get different prompt ).
Tip: To find your current shell type following command $

echo $SHELL

Shell Scripting She-bang


The sign #! is called she-bang and is written at top of the script. It passes instruction to
program /bin/sh. To run your script in a certain shell (shell should be supported by your system),
start your script with #! followed by the shell name.
Example:
#!/bin/bash
echo Hello World

TE IT (Course 2015) Software Laboratory II


Linux Commands :

[1] The Manual (terminal mode) man


This command brings up the online UNIX manual. Use it on each of the commands below.
Usage: man [command name]
eg: man pwd You will see the manual for the pwd command.

[2] File Handling Commands

[2.1] mkdir – make directories


create directory[ies] if they are not already exists.
Usage: mkdir <DIREECTORY NAME>
Eg. mkdir hiren

[2.2] ls – list directory contents


Usage: ls [OPTION]... [FILE]...
Eg. ls Listing Directory contents including only file names
ls l Long listing of directory contents including user permissions, number of links, size of file or
directory, day and time of last modification and file or directory name.
ls –a Listing all files including hidden files

[2.3] cd – changes directories


Usage: cd [DIRECTORY NAME]
Eg. cd hiren

[2.4] pwd – print working directory


Shows what directory (folder) you are in. In Linux, your home directory is
/home/username.
Usage: pwd
Eg. pwd show present working directory /home/username

[2.5] rmdir- Remove directories


remove directory[ies] if they are empty. Usage: rmdir [DIRECTORY NAME] Eg. rmdir
hiren remove hiren directory if this is empty.

[2.6] rm-remove files or directories


remove file[s] or directory[ies]
Usage:rm –[option] [DIRECTORY NAME OR FILE NAME ] Eg.
rm –i [FILENAME] remove file interactively. This will ask before removing file. rm –f
[FILENAME] removefile forcefully.
rm –r [FILENAME] recusively remove non empty directory.

TE IT (Course 2015) Software Laboratory II


[2.7] mv-move :
move or rename files or directories
Usage: mv [SOURCE DIRECTORY] [DESTINATION DIRECTORY] mv [OLD FILENAME]
[NEW FILENAME]
Eg. mv linixdir hiren renaming or moving directory linixdir as hiren. After execution of
command, the destination files are only available.

[2.8] cp – copy
copy files and directories
Usage: cp [OPTION] SOURCE FILE] [DESTINATION FILE]
eg. cp sample.txt sample_copy.txt After execution of command, the both source and destination
files are available.

[3] Text Processing

[3.1] cat – concatenate files and print on the standard output


Usage: cat [OPTION] [FILE]...
eg. cat file1.txt file2.txt
cat when supplied with more than one file will concatenate the files without any header
information.

[3.2] cat - used to display the contents of a small file on terminal usage:
cat [file name]

[3.3] cat - To create file


Usage: cat > [file name]
NOTE: Press and hold CTRL key and press D to stop or to end file (CTRL+D)

[3.4] cat – to apend text at the end of file


Usage: cat >> [file name]
NOTE: Press and hold CTRL key and press D to stop or to end file (CTRL+D)

[3.5] echo – display a line of text


Usage: echo [OPTION] [string] ...
eg. echo I love India
echo $HOME

[3.6] wc - command is used to count lines, words and characters, depending on the option
used.
Usage: wc [options] [file name]
You can just print number of lines, number of words or number of charcters by using
following
options:
l : Number of lines
w : Number of words
c : Number of characters
TE IT (Course 2015) Software Laboratory II
Eg. wc file.txt
count number of lines, words and character (including whitespaces , newline etc) in a file.

[3.7] grep - print lines matching a pattern Usage: grep


[OPTION] PATTERN [FILE]...
eg. grep i apple sample.txt Options:
-i case-insensitive search
-n show the line# along with the matched line
-v invert match, e.g. find all lines that do NOT match -w match entire words,
rather than substrings

How to write shell script ?


Following steps are required to write shell script:
(1) Use any editor to write shell script.
(2) After writing shell script set execute permission for your script as follows
syntax: chmod permission your-script-name
Examples: $ chmod +x your-script-name $ chmod
755 your-script-name
Note: This will set read write execute(7) permission for owner, for group and other permission is
read and execute only(5).
ls -l command earlier presented a long of listing file with a line like the following for each file:
-rw-r--r-- 1 root user 0 2009-04-28 08:26 newfile.txt
Here the first character in the first column (-) indicates that the file is a normal file. The next 9
characters indicate the access permissions for the file. The next set of 9 characters is divided
into3 groups of 3 cha- racters. Purpose of these characters is as under:
(-) represents no permission
(r) represents 'read' permission
(w)represents 'write' permission
(x)represents 'execute' permission

Permission Octal number Equivalent Symbol

Read 4 r--

Write 2 -w-

Execute 1 --x

TE IT (Course 2015) Software Laboratory II


The three group represents user (owner of the file), group(to which the owner belongs) and
others (any other user of the system) respectively. Three characters in each group are for 'read',
'write' and 'execute' permission respectively.
In the example, the owner has 'read' and 'write' permission for the file and everyone else has only
read permission. For a normal file, read, write and execute permissions are obvious. For a
directory, read and write permissions mean that to read the contents of the directory and create
new entries in the directory. Execute permission means that one can search in the directory but
not read from or write to the directory.
You can use the chmod command to change the access permissions of a file or a directory. To
specify permissions for a file with chmod, any of the following two methods can be used.

Symbol Meaning

u User

g Group

o Other

a a : All (equals to ugo)

+ Add Permission

- Remove a permission

r Read Permission

w Write permission

x Execute permission

Syntax: chmod u+x filename

(3) Execute your script as


syntax: bash your-script-name sh your-script-name
./your-script-name
Examples:
$ bash bar
$ sh bar
$ ./bar

TE IT (Course 2015) Software Laboratory II


To evaluate Arithmetic Expressions:
expr - expr writes the result of the expression on the standard output. This command is primarily
intended for arithmetic and string manipulation.
Expression Meaning

expr1 | expr2 results in the value expr1 if expr1 is true;


otherwise it results in the value of expr2.

results in the value of expr1 if both


expr1 & expr2 expressions are true; otherwise it results
in 0

expr1 <= expr2 If both expr1 and expr2 are numeric, expr
expr1 < expr2
compares them as numbers; otherwise it
expr1 = expr2
compares them as strings. If the
expr1!= expr2
comparison is true, the expression results
expr1 >= expr2 in 1; otherwise it results in 0.
expr1 > expr2

expr1 + expr2 performs addition or subtraction on the


two expressions. If either expression is
expr1 - expr2
not a number, expr exits with an error.

expr1 \* expr2 performs multiplication, division, or


modulus on the two expressions. If either
expr1 / expr2
expression is not a number, expr exits
expr1 % expr2
with an error.

Implementation Details:

Input:

1. Inserting Record

Inserting the sample data for e.g Name and Contact Number 2.

Searching into the file

Enter a word to search for e.g Jhon

TE IT (Course 2015) Software Laboratory II


3. Delete a Record

Give any line number, for e.g 3.

4. Modify a record

Enter record to modify eg. Suppose xyz / 9919238382

Enter New Record eg. Abc/ 9090909090


5. Display file

Algorithm

1. Create target file to store data.

2. Display menu of operations.

3. perform the operations as per the Input (1. Insert / 2. Search / 3. Delete a record / 4. Modify
a Record/ 5. Display file).

3.1 Insertion

i. Ask user to Enter required data (In this case, “Name” and “Contact Number”)

ii. Extract the input into the target file.

iii. Display Respective completion or Error message to the user. 3.2

Searching

i. Take the word to be searched from the user.

ii. Compare that word with each record in the target file. (Use grep command)

iii. Display Respective completion or Error message to the user. 3.3

Delete a Record

i. Ask user to input a Line no.

ii. Delete a record there on the line no in the target file. (use sed command)

iii. Display Respective completion or Error message to the user. 3.4

Modify a Record

TE IT (Course 2015) Software Laboratory II


i. Get the actual record to be modified from the user. (Refer Input Section)

ii. Enter the new Record.

iii. Search and replace the actual record with new record. (Use sed Command).

iv. Display Respective completion or Error message to the user.

3.5 Display File


i. Use “cat” command to display the target file.
4. Exit

Output:

Completion or error messages shall be displayed for respective operations mentioned above.

For eg. “Insertion is Successful.” likewise.

For Display operation, Entire database to be displayed.

Conclusion:
In this experinment we have done basic shell scripts programs using different shell commands.
With same we implemented address book operation with options of Create address book, View
address book, Insert a record,Delete a record, Modify a record & Exit. For these operations cat,
grep and other basic commands were used.

FAQ’s
Q1. Explain Chmod, Grep, Cat & Sort Command with example.
Q2. Explain sed command with example.
Q3. Write shell script for sorting a given list of numbers.

TE IT (Course 2015) Software Laboratory II


Experiment No. 2

Title of Experiment:
Process control system calls: The demonstration of fork, execve and wait system calls
along with zombie and orphan states.

Part A: 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.
Part B: 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.

Course Objectives:
1. To demonstrate the process, memory, file and directory management issues under the
UNIX/LINUX operating system
2. To introduce LINUX basic commands
3. To make students how to make simple programs in LINUX and administrative task of
LINUX

Course Outcomes Achieved:


1. Describe OS support for processes and threads

Theory:
Processes carry out tasks within the operating system. A program is a set of machine code
instructions and data stored in an executable image on disk and is, as such, a passive entity; a
process can be thought of as a computer program in action. It is a dynamic entity, constantly
changing as the machine code instructions are executed by the processor. As well as the
program's instructions and data, the process also includes the program counter and all of the
CPU's registers as well as the process stacks
TE IT (Course 2015) Software Laboratory II
containing temporary data such as routine parameters, return addresses and saved variables. The
current executing program, or process, includes all of the current activity in the microprocessor.
Linux is a multiprocessing operating system. Processes are separate tasks each with their own rights
and responsibilities. If one process crashes it will not cause another process in the system to crash.
Each individual process runs in its own virtual address space and is not capable of interacting with
another process except through secure, kernel managed mechanisms. During the lifetime of a process
it will use many system resources. It will use the CPUs in the system to run its instructions and the
system's physical memory to hold it and its data. Linux must keep track of the process itself and of
the system resources that it has so that it can manage it and the other processes in the system fairly. It
would not be fair to the other processes in the system if one process monopolized most of the
system's physical memory or its CPUs. Linux is a multiprocessing operating system, its objective is
to have a process running on each CPU in the system at all times, to maximize CPU utilization. If
there are more processes than CPUs (and there usually are), the rest of the processes must wait
before a CPU becomes free until they can be run. Multiprocessing is a simple idea; a process is
executed until it must wait, usually for some system resource; when it has this resource, it may run
again. In a uniprocessing system, for example DOS, the CPU would simply sit idle and the waiting
time would be wasted. In a multiprocessing system many processes are kept in memory at the same
time. Whenever a process has to wait the operating system takes the CPU away from that process
and gives it to another, more deserving process. It is the scheduler which chooses which is the most
appropriate process to run next and Linux uses a number of scheduling strategies to ensure fairness.
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. Every process also has a parent process. Thus, you can think of the processes on a Linux
system as arranged in a tree, with the init process at its root. The parent process ID, or ppid, is
simply the process ID of the process’s parent. When referring to process IDs in a C or C++
program, always use the pid_t typedef, 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.
The ps command displays the processes that are running on your system. The GNU/Linux
version of ps has lots of options because it tries to be compatible with versions of ps on several
other UNIX variants.These options control which processes are listed and what information
about each is shown. By default, invoking ps displays the processes controlled by the terminal or
terminal window in which ps is invoked. You can kill a running process with the kill command.
Simply specify on the command line the process ID of the process to be killed. The kill
command works by sending the process a SIGTERM, or termination, signal. This causes the
process to terminate, unless the executing program explicitly handles or masks the SIGTERM
signal.

Creating Processes using fork & execve:


The DOS and Windows API contains the spawn family of functions. These functions take as an
argument the name of a program to run and create a new process instance of that program. Linux
doesn’t contain a single function that does all this in one step.
TE IT (Course 2015) Software Laboratory II
Instead, Linux provides one function, fork, which makes a child process that is an exact copy of
its parent process. Linux provides another set of functions, the exec family, which causes a
particular process to cease being an instance of one program and to instead become an instance
of another program. To spawn a new process, you first use fork to make a copy of the current
process. Then you use exec to transform one of these processes into an instance of the program
you want to spawn.

Calling fork
When a program calls fork, a duplicate process, called the child process, is created. The parent
process continues executing the program from the point that fork was called. The child process, too,
executes the same program from the same place. So how do the two processes differ? First, the child
process is a new process and therefore has a new process ID, distinct from its parent’s process ID.
One way for a program to distinguish whether it’s in the parent process or the child process is to call
getpid. However, the fork function provides different return values to the parent and child processes
— one
process ―goes in‖ to the fork call, and two processes ―come out,‖ with different return

values. The return value in the parent process is the process ID of the child. The return value in
the child process is zero. Because no process ever has a process ID of zero, this makes it easy for
the program whether it is now running as the parent or the child process.

Using the exec Family


The exec functions replace the program running in a process with another program. When a
program calls an exec function, that process immediately ceases executing that program and
begins executing a new program from the beginning, assuming that the exec call doesn’t
encounter an error. Within the exec family, there are functions that vary slightly in their
capabilities and how they are called.
● Functions that contain the letter p in their names (execvp and execlp) accept a
program name and search for a program by that name in the current execution path; functions
that don’t contain the p must be given the full path of the program to be executed.
● Functions that contain the letter v in their names (execv, execvp, and execve) accept
the argument list for the new program as a NULL-terminated array of pointers to strings.
Functions that contain the letter l (execl, execlp, and execle) accept the argument list using the
C language’s varargs mechanism.
● Functions that contain the letter e in their names (execve and execle) accept an
additional argument, an array of environment variables.The argument should be a NULL-
terminated array of pointers to character strings. Each character string should be of the form
―VARIABLE=value‖ Because exec replaces the calling program with another one, it never
returns unless an error occurs.

TE IT (Course 2015) Software Laboratory II


Using fork and exec Together
A common pattern to run a subprogram within a program is first to fork the process and then
exec the subprogram. This allows the calling program to continue execution in the parent
process while the calling program is replaced by the subprogram in the child process.

Process States
As a process executes, it changes state. The state of a process is defined as the current activity of
the process.
Process can have one of the following five states at a time.

Table 2.1 States of a Process

S.N. State & Description

1 New
The process is being created.

Ready
2 The process is waiting to be assigned to a processor. Ready processes are waiting to
have theprocessor allocated to them by the operating system so that they can run.

Running
3 Process instructions are being executed (i.e. The process that is currently being
executed).

Waiting
4
5 The process is waiting for some event to occur (such as the completion of an I/O
operation).

Terminated
5
The process has finished execution.

TE IT (Course 2015) Software Laboratory II


Fig 2.1 State Transition Diagram

Zombie Process:
If a child process terminates while its parent is calling a wait function, the child process vanishes
and its termination status is passed to its parent via the wait call. But what happens when a child
process terminates and the parent is not calling wait? Does it simply vanish? No, because then
information about its termination—such as whether it exited normally and, if so, what its exit
status is—would be lost. Instead, when a child process terminates, is becomes a zombie
process.A zombie process is a process that has terminated but has not been cleaned up yet. It is
the responsibility of the parent process to clean up its zombie children. The wait functions do
this,too, so it’s not necessary to track whether your child process is still executing before waiting
for it. Suppose, for instance, that a program forks a child process, performs some other
computations, and then calls wait. If the child process has not terminated at that point, the parent
process will block in the wait call until the child process finishes. If the child process finishes
before the parent process calls wait, the child process becomes a zombie. When the parent
process calls wait, the zombie child’s termination status is extracted, the child process is deleted,
and the wait call returns immediately. What happens if the parent does not clean up its children?
They stay around in the system, as zombie processes.

Orphan Process:
An orphan process is a computer process whose parent process has finished or terminated,
though it remains running itself. In a Unix-like operating system any orphaned process will be
immediately adopted by the special init system process. This operation is called re- parenting
and occurs automatically. Even though technically the process has the "init" process as its
parent, it is still called an orphan process since the

TE IT (Course 2015) Software Laboratory II


process that originally created it no longer exists. A process can be orphaned unintentionally,
such as when the parent process terminates or crashes. A process may also be intentionally
orphaned so that it becomes detached from the user's session and left running in the background;
usually to allow a long-running job to complete without further user attention, or to start an
indefinitely running service. Under UNIX, the latter kinds of processes are typically called
daemon processes.

Implementation Details:
PART A:
Algorithm:
1: START
2: Accept array of integers from the user
3: Call Merge_sort() to sort the array of integers
4: Call fork() to create child
5: Using wait system call, wait for child to finish execution
6: if (pid == 0) i.e. if child process, then call quick_sort() to sort array using quick sort.
7: END

Input:
1: Array of Integers
Steps to execute:

Step 1: Compile file using ―gcc program_name.c –o program_name.out‖

Step 2: Execute the program using ./program_name.out


Step 3: Open 2 new terminals and using command ps –e –o pid, ppid, stat, command show
various states of the processes that are created & terminated from the program (use sleep and
wait functions as per the requirement to show zombie and orphan states).
PART B:
Algorithm:
File1.c
1: START
2: Accept array of integers from the user
3: Sort the array of integers using any sorting technique
4: Convert the integer numbers into string using sprintf(char * str, const char * format, ...)
5: Call the fork() function to create child process.
6: Using wait system call, wait for child to finish execution.
7: Call the execve() system call, and pass the second program name and the string converted
array as parameters to the execve system call.
8: END

TE IT (Course 2015) Software Laboratory II


File2.c
1: START
2: Convert the received string into integer array using atoi() function.
3: Ask the user which number to find
4: Call binary search function to search the particular number.
5: Return the location of the number if found, else print error.
6: END

Input:
1: Array of Integers
2: Number to find

Steps to execute:

Step 1: Compile file1 & file2 using ―gcc program_name.c –o program_name.out‖

Step 2: Execute the program using ―./file1.out file2.out

Conclusion:
Using the fork() and wait () system calls we created new processes and by manipulating the
sleep time for both the processes, we demonstrated the zombie and orphan states for the
processes that we created. For orphan state we kept the child process in sleep and allowed the
parent to terminate, whereas for the zombie state we did not allow the parent process to wait
until the child finished its execution and let the child finish before parent, which made the child
zombie.Using execve function call we executed the second program from the child process and
passedthe sorted array to the second program which further performed binary search over it.

FAQ’s
Q1. What is a system call? Explain briefly execve system call.
Q2. List and explain different states of a process.
Q3. What is a process? Explain how Process is represented internally.

TE IT (Course 2015) Software Laboratory II


Experiment No. 3

Title of Experiment: Thread management using pthread library.


Implement matrix multiplication using multithreading. Application should have
pthread_create,pthread_join, pthread_exit. In the program, every thread must return the value
and must becollected in pthread_join in the main function. Final sum of row column
multiplication must be done by main thread (main function).

Course Objectives:
1. To familiarize the students with the Operating System.
2. To demonstrate the process, memory, file and directory management issues under the
UNIX/LINUX operating system
3. To make students how to make simple programs in LINUX and administrative task of
LINUX

Course Outcomes Achieved:


1. Describe OS support for processes and threads

Theory:
What is a Thread? Technically, a thread is defined as an independent stream of instructions that
can be scheduled to run as such by the operating system. But what does this mean? To the
software developer, the concept of a "procedure" that runs independently from its main program
may best describe a thread. To go one step further, imagine a main program (a.out) that contains a
number of procedures. Then imagine all of these procedures being able to be scheduled to run
simultaneously and/or independently by the operating system. That would describe a "multi-
threaded" program.

Thread Basics:
Thread operations include thread creation, termination, synchronization (joins,
blocking),scheduling, data management and process interaction. A thread does not maintain a list
of created threads, nor does it know the thread that created it. All threads within a process share
the same address space. 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:


◆ Thread ID
◆ set of registers, stack pointer

TE IT (Course 2015) Software Laboratory II


◆ stack for local variables, return addresses
◆ signal mask
◆ priority
◆ Return value: errno
◆ pthread functions return "0" if OK

POSIX thread (pthread) libraries:


The POSIX thread libraries are a standards based thread API for C/C++. It allows one to spawn
anew concurrent process flow. It is most effective on multi-processor or multi-core systems
where the process flow can be scheduled to run on another processor thus gaining speed through
parallel or distributed processing. Threads require less overhead than "forking" or spawning a
new process because the system does not initialize a new system virtual memory space and
environment for the process. While most effective on a multiprocessor system, gains are
alsofound on uniprocessor systems which exploit latency in I/O and other system functions
which may halt process execution. (One thread may execute while another is waiting for I/O or
some other system latency). All threads within a process share the same address space. A thread
is spawned by defining a function and it's arguments which will be processed in the thread. The
purpose of using the POSIX thread library in your software is to execute software faster.

Following are the pthread functions that we are going to use:


int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void * (*start_routine)(void *), void *arg);
Arguments:
Thread - returns the thread id. (unsigned long int defined in bits/pthreadtypes.h) attr -
Set to NULL if default thread attributes are used.
Attributes include:
◆ detached state (Default: PTHREAD_CREATE_JOINABLE.)
◆ scheduling policy (PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED,
SCHED_OTHER)
◆ scheduling parameter
◆ Inherit sched attribute (Default: PTHREAD_EXPLICIT_SCHED)
◆ scope (Kernel threads: PTHREAD_SCOPE_SYSTEM User threads: PTHREAD_SCOPE_PROCESS
Pick one or the other not both.)
◆ guard size
◆ stack address (See unistd.h and bits/posix_opt.h _POSIX_THREAD_ATTR_STACKADDR)
◆ stack size (default minimum PTHREAD_STACK_SIZE set in pthread.h),
◆ void * (*start_routine) - pointer to the function to be threaded. Function has a single argument: pointer
to void.
◆ *arg - pointer to argument of function. To pass multiple arguments, send a pointer to a structure.
void pthread_exit(void *retval);

TE IT (Course 2015) Software Laboratory II


Arguments:
retval - Return value of thread.
This routine kills the thread. The pthread_exit function never returns. If the thread is not
detached, the thread id and return value may be examined from another thread by using
pthread_join.
Note: the return pointer *retval, must not be of local scope otherwise it would cease to exist once
the thread terminates
int pthread_join(pthread_t th, void **thread_return);

Arguments:

◆ th - thread suspended until the thread identified by th terminates, either by calling


pthread_exit() or by being cancelled.

◆ thread_return - If thread_return is not NULL, the return value of th is stored in the location
pointed to by thread_return.

Implementation Details:
Algorithm:
1: START
2: Accept 2 matrices from user
3: If number of columns in matrix1 equals number of rows in matrix2, goto step 5
4: Else goto step 15
5: Initialize result matrix to all 0’s.
6: Loop for each row in matrix1.
7: Loop for each columns in matrix2 and initialize output matrix to 0. This loop will run for each
rows of matrix1.
8: Loop for each columns in matrix1.
9: Create a new thread using pthread_create() function, call the multiply function with created
thread and pass the mat1[i,k] & mat2[k,j] for multiplication
10: Return the multiplication value from the function using pthread_exit() function
11: Using pthread_join() function, receive the multiplication of two numbers
12: Add this value to result matrix[i,j];
13: End all the loops.
14: Print the resultant matrix
15: END

Input:
1: Accept 2-matrices from user

Steps to execute:
Step 1: Compile file using gcc program_name.c –o program_name.out -lpthread
Step 2: Execute the program using ./program_name.out

TE IT (Course 2015) Software Laboratory II


Conclusion:
We implemented a matrix multiplication program using multithreading, where each thread is
responsible for multiplication of two values of the matrices, and the main thread is responsible to
add the values after multiplication that must be stored into the result matrix. For this we used
pthread_create() function which creates a new thread and calls the function pointed to by its
third parameter, and takes as argument for that function which is the fourth parameter of the
function. The pthread_join() function is used to block the program until the thread returns from
the function & also it collects the result of the multiplication function which is returned by the
function pthread_exit().

FAQ’s
1. Write the use of -pthread flag with gcc.
2. What is difference between -pthread and -lpthread gcc flags?
3. What is difference between mutex and semaphores in Linux multithreading?

TE IT (Course 2015) Software Laboratory II


Experiment No. 4
Title of Experiment:
Thread synchronization using counting semaphores and mutual exclusion using mutex.
Application to demonstrate: producer-consumer problem with counting semaphores and mutex.
Course Objectives:

1. To demonstrate the process, memory, file and directory management issues under the
UNIX/LINUX operating system
2. To make students how to make simple programs in LINUX and administrative task of

LINUX

Course Outcomes Achieved:

1. Use C / C++ and Unix commands, and develop various system programs under
Linux to make use of OS concepts related to process synchronization, shared memory, file
systems,etc.
2. Recognize CPU Scheduling, synchronization, and deadlock.

Theory:
Thread Synchronization:
Running several threads in most cases requires synchronization. There is a number of ways to
synchronize threads:
● Mutual exclusions – mutexes (Mutex – MUTual EXclusion);

● Conditional variables;

● Semaphores.
Using conditional variables and semaphores in multiple-threaded applications is similar to using
these synchronization tools in multiple-process applications. Using a mutex is a general method of
synchronizing threads. A mutex can be defined as a synchronization object, which is set in a special
signalling state, when it is not engaged by any thread. At any given time a mutex can only belong to
a single thread. Using a mutex guarantees that only one thread at a given moment completes the
critical section of code. A mutex can be also used in a single-thread code. The following operations
with mutexes are available: initialization, deletion, capture or opening, attempt to capture.
Synchronization objects are variables in process memory and have liveness of process objects.
Threads in different processes can communicate via synchronization objects,

TE IT (Course 2015) Software Laboratory II


situated in shared memory of threads, even when threads in different processes are invisible for
each other.
Threads have to be synchronized in the following cases:

1. In case synchronization is an only way to guarantee a sequence of shared


(common) data.When threads in two or more processes can share a single
synchronization object. In this case a synchronization object has to be initiated by only
one of the interacting processes, because repeated initialization of a synchronization
object sets it in the on-state.
2. In case synchronization can guarantee adequacy of changing data.

3. When a process can display a file and there is a thread in this process, which
gets unique access to records. Just as locking is set, any other thread in any process,
displaying a file, which is trying to lock, is blocked until writing in a file is completed.

Mutex:
To synchronize threads via a mutex the following basic functions are used:
● pthread_mutex_init – initializes mutex;
● pthread_mutex_destroy – deletes mutex;

● pthread_mutex_lock – sets locking. In case locking has been set by another thread, the current thread is interrupted until a

lock is released by another process;

● pthread_mutex_unlock – releases a lock

Mutex scope can be either a certain process, or the entire system. In case of the former a mutex
can only be operated by threads, created by a process, within which a mutex is generated. In case
of the latter a mutex exists in shared memory and can be divided among threads of several
processes. By default a mutex is created within process scope and has process liveness.
int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *mattr)

A mutex, which is indicated by the first argument mp, is initiated with a default value, in case the
second argument mattr is equal to NULL, or with certain attributes, which have already been set
via pthread_mutexattr_init. The function pthread_mutex_init returns 0 upon successful
completion or another value, in case of error. When a mutex is initialized, it is in the on-state
(unlocked). Statically certain mutexes can be directly initialized with default values via the
constant PTHREAD_MUTEX_INITIALIZER
int pthread_mutex_lock(pthread_mutex_t *mutex);

The function pthread_mutex_lock is used to lock or capture a mutex. A function argument is an


address of mutex to be locked. In case a mutex is already locked, a calling thread is blocked and
a mutex is placed in the priority queue. When there is return from pthread_mute_lock, a mutex is
locked, and a calling thread becomes its

TE IT (Course 2015) Software Laboratory II


owner. The function pthread_mute_lock returns 0 upon successful completion, or another value,
in case of error.
int pthread_mutex_unlock(pthread_mutex_t *mutex);

To unlock (disengage) a mutex the function pthread_mutex_unlock is used. In this case a mutex
has to be closed, and a calling thread has to be its owner, i.e. a mutex has to be closed by this
thread. While any other threads are waiting to access a mutex, its owner, situated at the
beginning of the queue, is not locked. The function pthread_mutex_unlock returns 0 upon
successful completion or another value, in case of error.
int pthread_mutex_trylock(pthread_mutex_t *mutex);

There is another way of capturing a mutex without locking a thread. The function
pthread_mutex_trylock is trying to lock a mutex. It is a non-locking version of
pthread_mutex_lock. In case a mutex is already locked, calling returns an error, however, the
thread, calling this function, is not blocked. Otherwise, a mutex is closed, and a calling process
becomes its owner. The function pthread_mutex_trylock returns 0 upon successful completion
or another value, in case of error.

Synchronization via semaphore


A semaphore is aimed at thread synchronization according to operations and data, and in general
using a semaphore is similar to using a mutex. A semaphore (S) – is a protected variable whose
values can be requested and changed only via special operations P(S) and V(S) and
initialization. A semaphore acquires an integral non-negative value. While a thread is running
the operation P with the semaphore S, the semaphore value decreases by 1 in case S > 0 or a
thread is locked,
«waiting at the semaphore», in case S = 0. When the operation V(S) is run, one of the threads
waiting at the semaphore S is woken, in case there are no such threads, the semaphore value
increases by 1. In a simple case, when a se maphore is running in the 2-state mode (S > 0 and S
= 0), its algorithm fully coincides with a mutex algorithm.
As follows from the above said, when a thread enters the critical section it should run the
operation P(S), and while it exits the critical section – the operation V(S). Function prototypes
for operating semaphores are described in the file <semaphore.h>. Below function prototypes
together with their syntax description and operations are given:
● int sem_init(sem_t* sem, int pshared, unsigned int value) –
semaphore initialization sem with the value value. As pshared it is always necessary to indicate 0;
● int sem_wait(sem_t* sem) –
«waiting at the semaphore». Thread running is locked until a semaphore value becomes positive. In this case
the semaphore value decreases by 1;

● int sem_post(sem_t* sem) –


increases the semaphore value sem by 1;

TE IT (Course 2015) Software Laboratory II


Synchronization via conditional variable
A conditional variable enables threads to wait for a certain condition (event), related with shared
data. There are two basic operations with conditional variables: informing about an event and
waiting for an event. When «informing» is done one of the threads, waiting for a value of a
conditional variable, resumes its running. Conditional variables are always used with a mutex.
Before the operation of «waiting» a thread has to lock a mutex. During «waiting» the given
mutex is automatically unlocked. Before the waiting thread resumes, a mutex is automatically
locked, which allows a thread to enter the critical section, after the critical section it is
recommended to unlock a mutex. When other threads are sent signals, it is sensible to protect the
function of «signalling» via a mutex as well. Function prototypes for working with conditional
variables are situated in the file pthread.h. Below function prototypes together with their syntax
description and operations are given:
● pthread_cond_init(pthread_cond_t*cond, const pthread_condattr_t* attr) -
It initializes the conditional variable cond with the given attributes attr or default attributes (when 0 is attr).

● int pthread_cond_destroy(pthread_cond_t* cond)-It deletes the

conditional variable cond.

● int pthread_cond_signal(pthread_cond_t* cond)-


It informs about an event of threads, waiting at the conditional variable cond.

● int pthread_cond_broadcast(pthread_cond_t* cond) -


It informs about an event of threads, waiting at the conditional cond. In this case all waiting threads will
be resumed.

● int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex)-It waits for an event at the

conditional variable cond.

Producer-Consumer problem:
The producer–consumer problem (also known as the bounded-buffer problem) is a classic
example of a multi-process synchronization problem. The problem describes two processes, the
producer and the consumer, who share a common, fixed-size buffer used as a queue. The
producer's job is to generate a piece of data, put it into the buffer and start again. At the same
time, the consumer is consuming the data (i.e., removing it from the buffer) one piece at a time.
The problem is to make sure that the producer won't try to add data into the buffer if it's full and
that the consumer won't try to remove data from an empty buffer.
The solution for the producer is to either go to sleep or discard data if the buffer is full. The next
time the consumer removes an item from the buffer, it notifies the producer, who starts to fill the
buffer again. In the same way, the consumer can go to sleep if it finds the buffer to be empty.
The next time the producer puts data into the buffer, it wakes up the sleeping consumer. The
solution can be reached by means of inter-process communication, typically using semaphores.
An inadequate solution could
TE IT (Course 2015) Software Laboratory II
result in a deadlock where both processes are waiting to be awakened. The problem can also be
generalized to have multiple producers and consumers.

Implementation Details:
Algorithm for Producer Consumer problem using Mutex:
1: START
2: Declare a buffer structure containing the character buffer array along with mutex &
condition variables.
3: Initialize the condition variables using pthread_cond_init() and create 2 threads, one for
producer and another for consumer using pthread_create().
4: END

The Producer:
1: START
2: Start Loop
3: The producer thread acquires a lock on mutex using pthread_mutex_lock().
4: Check if the buffer is full
If yes wait for the consumer to consume 1 item from buffer using pthread_cond_wait()
function. After consumer signals, goto step 5.
5: Copy contents into buffer.
6: Signal to consumer that an item has been produced using pthread_cond_signal()
7: Unlock mutex variable using pthread_mutex_unlock().
8: End Loop
9: END

The Consumer:
1: START
2: Start Loop
3: The consumer thread acquires a lock on mutex using pthread_mutex_lock().
4: Check if the buffer is empty
If yes wait for the producer to produce 1 item into buffer using pthread_cond_wait() function.
After producer signals goto step 5.
5: Copy contents from buffer and display on screen.
6: Signal to producer that an item has been consumed using pthread_cond_signal()
7: Unlock mutex variable using pthread_mutex_unlock().
8. End Loop
9: END

Algorithm for Producer Consumer problem using Semaphore:


1: START
2: Declare a buffer structure containing buffer variable along with semaphore variables.
3: Initialize the semaphore variables using sem_init() and create 2 threads, one for producer and
another for consumer using pthread_create().
4: END

TE IT (Course 2015) Software Laboratory II


The Producer:
1: START
2: Start Loop
3: The producer decreases the semaphore value and waits till it becomes positive using
sem_wait()
4: Copy contents into buffer.
5: Using sem_post() increase the value of semaphore.
6: End Loop
7: END

The Consumer:
1: START
2: Start Loop
3: The consumer decreases the semaphore value and waits till it becomes positive using
sem_wait()
4: Copy contents from buffer and print.
5: Using sem_post() increase the value of semaphore.
6: End Loop
7: END

Input: NA

Steps to execute:
Step 1: Compile file using gcc program_name.c –o program_name.out -lpthread
Step 2: Execute the program using ./program_name.out

Conclusion:
We studied and implemented the producer-consumer problem using both mutex and semaphores.
Using mutex gives us locking control over the buffer which is shared by the two threads where
only one of the threads either producer or consumer can access buffer. A signalling mechanism is
available to signal the waiting thread about the status of the buffer. Using semaphores the
producer- consumer problem was handled with semaphore wait & post signals to the semaphore
variables.

FAQ’s

1. What is the difference between pthread and lpthread?

2. How binary and counting semaphores are differentiated in Linux functions?

3. How any threads creation is possible in C?

TE IT (Course 2015) Software Laboratory II


Experiment No. 5

Title of Experiment: Thread synchronization and mutual exclusion using mutex.


Application to demonstrate: Reader-Writer problem with reader priority.

Course Objectives:

1. To demonstrate the functioning of OS basic building blocks like processes, threads under
the LINUX.
2. To demonstrate the functioning of OS concepts in user space like concurrency control (process

synchronization, mutual exclusion & deadlock) and file handling in LINUX.

Course Outcomes Achieved:

1. To implement basic building blocks like processes, threads under the Linux.

2. To develop various system programs for the functioning of OS concepts in user space like
concurrency control and file handling in Linux.

Theory:

Reader Writer Problem:


The readers-writers problem is a classical one in computer science. In this type of problem there
are two types of process are used such as Reader process and Writer process. The reader process is
responsible for only reading and the writer process is responsible for writing. We have a resource
(e.g., a database) that can be accessed by readers, who do not modify the resource, and writers,
who can modify the resource. When a writer is modifying the resource, no-one else (reader or
writer) can access it at the same time: another writer could corrupt the resource, and another reader
could read a partially modified (thus possibly inconsistent) value. This problem has been
extensively studied since the 60’s, and is often categorized into three variants:

1. Give readers priority: when there is at least one reader currently accessing the
resource, allow new readers to access it as well. This can cause starvation if there are writers
waiting to modify the resource and new readers arrive all the time, as the writers will never be
granted access as long as there is at least one active reader.
2. Give writers priority: here, readers may starve.

3. Give neither priority: all readers and writers will be granted access to the resource in
their order of arrival. If a writer arrives while readers are accessing the resource, it will wait
until those readers free the resource, and then modify it. New readers arriving in the meantime
will have to wait.

TE IT (Course 2015) Software Laboratory II


There is a data area shared among a number of processes. The data area could be a file, a block of
main memory, or even a bank of processor registers. There are a number of processes that only
read the data area (readers) and a number that only write to the data area (writers).

The conditions that must be satisfied are as follows:

1. Any number of readers may simultaneously read the file.

2. Only one writer at a time may write to the file.

3. If a writer is writing to the file, no reader may read it.

Thus, readers are processes that are not required to exclude one another and writers are processes
that are required to exclude all other processes, readers and writers alike.

The structure of a reader process is as follows:


Wait (mutex);
Read count++;
if (read count == 1)
Wait (wrt);
Signal (mutex);
...........
Reading is performed
...........
Wait (mutex);
Read count --;
if (read count == 0)
Signal (wrt);
Signal (mutex);
The structure of the writer process is as follows:
Wait (wrt);
Writing is performed;
Signal (wrt);

Mutex:
To synchronize threads via a mutex the following basic functions are used:
● pthread_mutex_init – initializes mutex;
● pthread_mutex_destroy – deletes mutex;

● pthread_mutex_lock – sets locking. In case locking has been set by another thread, the current thread is interrupted until a

lock is released by another process;

● pthread_mutex_unlock – releases a lock

TE IT (Course 2015) Software Laboratory II


Mutex scope can be either a certain process, or the entire system. In case of the former a mutex
can only be operated by threads, created by a process, within which a mutex is generated. In case
of the latter a mutex exists in shared memory and can be divided among threads of several
processes. By default a mutex is created within process scope and has process liveness.
int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *mattr)

A mutex, which is indicated by the first argument mp, is initiated with a default value, in case the
second argument mattr is equal to NULL, or with certain attributes, which have already been set
via pthread_mutexattr_init. The function pthread_mutex_init returns 0 upon successful
completion or another value, in case of error. When a mutex is initialized, it is in the on-state
(unlocked). Statically certain mutexes can be directly initialized with default values via the
constant PTHREAD_MUTEX_INITIALIZER
int pthread_mutex_lock(pthread_mutex_t *mutex);

The function pthread_mutex_lock is used to lock or capture a mutex. A function argument is an


address of mutex to be locked. In case a mutex is already locked, a calling thread is blocked and
a mutex is placed in the priority queue. When there is return from pthread_mute_lock, a mutex is
locked, and a calling thread becomes its owner. The function pthread_mute_lock returns 0 upon
successful completion, or another value, in case of error.
int pthread_mutex_unlock(pthread_mutex_t *mutex);

To unlock (disengage) a mutex the function pthread_mutex_unlock is used. In this case a mutex
has to be closed, and a calling thread has to be its owner, i.e. a mutex has to be closed by this
thread. While any other threads are waiting to access a mutex, its owner, situated at the
beginning of the queue, is not locked. The function pthread_mutex_unlock returns 0 upon
successful completion or another value, in case of error.
int pthread_mutex_trylock(pthread_mutex_t *mutex);

There is another way of capturing a mutex without locking a thread. The function
pthread_mutex_trylock is trying to lock a mutex. It is a non-locking version of
pthread_mutex_lock. In case a mutex is already locked, calling returns an error, however, the
thread, calling this function, is not blocked. Otherwise, a mutex is closed, and a calling process
becomes its owner. The function pthread_mutex_trylock returns 0 upon successful completion
or another value, in case of error.

Implementation Details:
Algorithm for Reader-Writer problem using Mutex

1. Define number of reading processes & writing processes who are accessing the data

2. Gain access to reader_count

TE IT (Course 2015) Software Laboratory II


3. Increment the reader_count

4. If this is the first process to read the shared resource

5. A down is executed to prevent access to the shared resource by a writing process

6. allow other processes to access reader_count

7. Read the shared resource

8. Gain access to reader_count

9. Decrement reader_count

10. If there are no more processes reading from the shared resource, allow writing process to
access the data
11. Writer gains access to the shared resource

12. Write information to the shared resource

13. Release exclusive access to the shared resource

Input: NA

Conclusion:

The readers-writers problem is a classical one in computer science. In this experiment we have
demonstrated this problem with two types of process that is Reader process and Writer process.
These process are synchronized with help of Mutex.

FAQ’s

1. Explain reader writer problem with writers priority.

2. Explain different methods of Mutex.

Explain different ways for Thread Synchronization.

TE IT (Course 2015) Software Laboratory II


Experiment No. 6

Title of Experiment:

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 many synchronized threads are competing for
limited resources.

Course Objectives:

To demonstrate the process, memory, file and directory management issues under the
UNIX/LINUX operating system

To make students how to make simple programs in LINUX and administrative task of LINUX.

Course Outcomes Achieved:

Use C / C++ and Unix commands, and develop various system programs under Linux to make use
of OS concepts related to process synchronization, shared memory, file systems, etc.

Recognize CPU Scheduling, synchronization, and deadlock

Theory:

Deadlock:

In a multiprogramming environment, several processes may compete for a finite number of


resources. A process requests resources; and if the resources are not available at that time, the
process enters a waiting state. Sometimes, a waiting process is never again able to change state,
because the resources it has requested are held by other waiting processes. This situation is called
a deadlock.

Starvation:

Starvation is a situation where, a process that needs several popular resources may have to wait
indefinitely, because at least one of the resources that it needs is always allocated to some other
process.

Dining Philosopher Problem:

TE IT (Course 2015) Software Laboratory II


Consider five philosophers who spend their lives thinking and eating. The philosophers share a
circular table surrounded by five chairs, each belonging to one philosopher. In the center of the
table is a bowl of rice, and the table is laid with five single chopsticks (Figure 4.1). When a
philosopher thinks, she does not interact with her colleagues. From time to time, a philosopher
gets hungry and tries to pick up the two chopsticks that are closest to her (the chopsticks that are
between her and her left and right neighbors). A philosopher may pick up only one chopstick at a
time. Obviously, she cannot pick up a chopstick that is already in the hand of a neighbor. When a
hungry philosopher has both her chopsticks at the same time, she eats without releasing her
chopsticks. When she is finished eating, she puts down both of her chopsticks and starts thinking
again.

The dining-philosophers problem is considered a classic synchronization problem neither because


of its practical importance that is an example of a large class of concurrency-control problems. It
is a simple representation of the need to allocate several resources among several processes in a
deadlock- free and starvation-free manner.

One simple solution is to represent each chopstick with a semaphore. A philosopher tries to grab a
chopstick by executing a wait () operation on that semaphore; she releases her chopsticks by
executing the signal() operation on the appropriate semaphores. Although this solution guarantees
that no two neighbors are eating simultaneously, it nevertheless must be rejected because it could
create a deadlock. Suppose that all five philosophers become hungry simultaneously and each
grabs her left chopstick. All the elements of chopstick will now be equal to 0. When each
philosopher tries to grab her right chopstick, she will be delayed forever. A satisfactory solution to
the dining-philosophers problem must guard against the possibility that one of the philosophers
will starve to death. A deadlock-free solution does not necessarily eliminate the possibility of
starvation.

Figure 6.1 Dining Philosopher Problem

TE IT (Course 2015) Software Laboratory II


Dining Philosopher problem solution using Semaphores: Semaphores:

A semaphore is an object that consists of a counter, a waiting list of processes and two methods
(e.g., functions): signal and wait.

Semaphores methods:

int sem_wait(sem_t *sem);

sem_wait() decrements (locks) the semaphore pointed to by sem. If the semaphore's value is
greater than zero, then the decrement proceeds, and the function returns, immediately. If the
semaphore currently has the value zero, then the call blocks until either it becomes possible to
perform the decrement (i.e., the semaphore value rises above zero), or a signal handler interrupts
the call.

int sem_post(sem_t *sem);

sem_post() increments (unlocks) the semaphore pointed to by sem. If the semaphore's value
consequently becomes greater than zero, then another process or thread blocked in a sem_wait()
call will be woken up and proceed to lock the semaphore.

Figure 6.2 Semaphores

Implementation Details:

Algorithm (pseudo code):

Define the number of philosophers

Declare one thread per philosopher

TE IT (Course 2015) Software Laboratory II


Declare one mutex(represent chopsticks) per philosopher

When a philosopher is hungry See if chopsticks on both sides are free acquire chopsticks eat
restore the chopsticks

If chopsticks aren’t free wait till they’re available


void philosopher(int i)
{
while (TRUE) {
think(); /* thinking */
take_forks(i); /* get two forks,
block */ eat(); /* eating */
put_forks(i); /* give up forks */
}
}
void take_forks(int i)
{
wait(mutex); state[i] = HUNGRY;
test(i); /* try getting 2 forks
*/ signal(mutex);
wait(s[i]); /* block if no forks acquired */
}
void test(int i)
{
if ( state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING ) {
signal(s[i]);
}
}

void put_forks(int i)
{
wait(mutex);
state[i] = THINKING;
test(LEFT);
test(RIGHT);
signal(mutex);
}

Steps to execute:

Step 1: Compile file using ―gcc program_name.c –o program_name.out


Step 2: Execute the program using ―./program_name.out

TE IT (Course 2015) Software Laboratory II


Conclusion:

Using semaphores we demonstrated the deadlock and starvation solution of Dining Philosophers
Problem. We also studied the concept of thread synchronization using semaphores.

FAQ’s

1. What is the difference between mutex and binary semaphore?

2. How many number of maximum philosophers will this problem have?

3. Explain the problem of starvation.

TE IT (Course 2015) Software Laboratory II


Experiment No. 7

Title of Experiment: Inter process communication in Linux using following.

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.

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.

Course Objectives:

1. To demonstrate the process, memory, file and directory management issues under the
UNIX/LINUX operating system

2. To introduce LINUX basic commands

3. To make students how to make simple programs in LINUX and administrative task of
LINUX

Course Outcomes Achieved:

1. Use C / C++ and Unix commands, and develop various system programs under Linux
to make use of OS concepts related to process synchronization, shared memory, file systems,
etc.
2. Recognize CPU Scheduling, synchronization, and deadlock

Theory:

PIPES:

A pipe is a communication device that permits unidirectional communication. Data written to the

―write end‖ of the pipe is read back from the ―read end. ‖ Pipes are serial devices;

the data is always read from the pipe in the same order it was written. Typically, a pipe is used to
communicate between two threads in a single process or between parent and child processes. A
pipe’s data capacity is limited. If the writer process writes faster than the reader process consumes
the data, and if the pipe cannot store more data, the writer process blocks until more capacity
becomes available. If the reader tries to read but no

TE IT (Course 2015) Software Laboratory II


data is available, it blocks until data becomes available. Thus, the pipe automatically synchronizes
the two processes.

There are two types of pipes for two-way communication: anonymous pipes and named pipes.
Anonymous pipes enable related processes to transfer information to each other. Typically, an
anonymous pipe is used for redirecting the standard input or output of a child process so that it can
exchange data with its parent process. To exchange data in both directions (duplex operation), you
must create two anonymous pipes. The parent process writes data to one pipe using its write
handle, while the child process reads the data from that pipe using its read handle. Similarly, the
child process writes data to the other pipe and the parent process reads from it. Anonymous pipes
cannot be used over a network, nor can they be used between unrelated processes.

Properties of Pipe:

Pipes do not have a name. For this reason, the processes must share a parent process. This is the
main drawback to pipes. However, pipes are treated as file descriptors, so the pipes remain open
even after fork and exec.

Pipes do not distinguish between messages; they just read a fixed number of bytes. Newline (\n)
can be used to separate messages. A structure with a length field can be used for message
containing binary data.

Pipes can also be used to get the output of a command or to provide input to a command.

Creating Pipes:
To create a pipe, invoke the pipe command. Supply an integer array of size 2.The call to pipe
stores the reading file descriptor in array position 0 and the writing file descriptor in position 1.

For example, consider this code:


int pipe_fds[2];
int read_fd;
int write_fd;
pipe (pipe_fds);
read_fd = pipe_fds[0];
write_fd = pipe_fds[1];

Data written to the file descriptor write_fd can be read back from read_fd.
If the parent wants to receive data from the child, it should close write_fd, and the child should
close read_fd. If the parent wants to send data to the child, it should close read_fd, and the child
should close write_fd. Since descriptors are shared between the parent and child, we should
always be sure to close the end of pipe we aren't concerned with. On a technical note, the EOF will
never be returned if the unnecessary ends of the pipe are not explicitly closed.

TE IT (Course 2015) Software Laboratory II


FIFO:
A first-in, first-out (FIFO) file is a pipe that has a name in the filesystem.Any process can open or
close the FIFO; the processes on either end of the pipe need not be related to each other. FIFOs are
also called named pipes. Named pipes allow two unrelated processes to communicate with each
other. Named pipes are identified by their access point, which is basically in a file kept on the file
system. Because named pipes have the pathname of a file associated with them, it is possible for
unrelated processes to communicate with each other; in other words, two unrelated processes can
open the file associated with the named pipe and begin communication. Unlike anonymous pipes,
which are process- persistent objects, named pipes are file system persistent objects, that is, they
exist beyond the life of the process. They have to be explicitly deleted by one of the processes by
calling "unlink" or else deleted from the file system via the command line. In order to
communicate by means of a named pipe, the processes have to open the file associated with the
named pipe. By opening the file for reading, the process has access to the reading end of the pipe,
and by opening the file for writing, the process has access to the writing end of the pipe. A named
pipe supports blocked read and write operations by default: if a process opens the file for reading,
it is blocked until another process opens the file for writing, and vice versa. However, it is possible
to make named pipes support non-blocking operations by specifying the O_NONBLOCK flag
while opening them. A named pipe must be opened either read- only or write-only. It must not be
opened for read-write because it is half-duplex, that is, a one-way channel.

Creating a Named Pipe within a Program:


The function "mkfifo" can be used to create a named pipe from within a program. The signature of
the function is as follows:

int mkfifo(const char *path, mode_t mode)

The mkfifo function takes the path of the file and the mode (permissions) with which the file
should be created. It creates the new named pipe file as specified by the path.

Opening a Named Pipe:


A named pipe can be opened for reading or writing, and it is handled just like any other normal
file in the system. For example, a named pipe can be opened by using the open() system call, or by
using the fopen() standard C library function. As with normal files, if the call succeeds, you will
get a file descriptor in the case of open(), or a 'FILE' structure pointer in the case of fopen(), which
you may use either for reading or for writing, depending on the parameters passed to open() or to
fopen(). Therefore, from a user's point of view, once you have created the named pipe, you can
treat it as a file so far as the operations for opening, reading, writing, and deleting are concerned.
Reading from and writing to a named pipe are very similar to reading and writing from or to a
normal file. The standard C library function calls read( ) and write( ) can be used for reading from
and writing to a named pipe. These operations are blocking, by default.

TE IT (Course 2015) Software Laboratory II


Full-Duplex Communication Using Named Pipes:
Although named pipes give a half-duplex (one-way) flow of data, you can establish full-duplex
communication by using two different named pipes, so each named pipe provides the flow of data
in one direction. However, you have to be very careful about the order in which these pipes are
opened in the client and server, otherwise a deadlock may occur. For example, let us say you
create the following named pipes: NP1 and NP2.
In order to establish a full-duplex channel, here is how the server and the client should treat these
two named pipes: Let us assume that the server opens the named pipe NP1 for reading and the
second pipe NP2 for writing. Then in order to ensure that this works correctly, the client must
open the first named pipe NP1 for writing and the second named pipe NP2 for reading. This way a
full-duplex channel can be established between the two processes. Failure to observe the above-
mentioned sequence may result in a deadlock situation.

Benefits of Named Pipes:

● Named pipes are very simple to use.

● mkfifo is a thread-safe function.

● No synchronization mechanism is needed when using named pipes.

● Write (using write function call) to a named pipe is guaranteed to be atomic. It is atomic even
if the named pipe is opened in non-blocking mode.

● Named pipes have permissions (read and write) associated with them, unlike anonymous
pipes. These permissions can be used to enforce secure communication.

Limitations of Named Pipes:

● Named pipes can only be used for communication among processes on the same host
machine.

● Named pipes can be created only in the local file system of the host, that is, you cannot create
a named pipe on the NFS file system.

● Due to their basic blocking nature of pipes, careful programming is required for the client and
server, in order to avoid deadlocks.

● Named pipe data is a byte stream, and no record identification exists.

TE IT (Course 2015) Software Laboratory II


Algorithm for Pipes:
1: START
2: Declare 2 pipe file descriptors along with the necessary variables & a path of the file to be read.
3: Create 2 pipes by calling the pipe() function.
4: Call fork() to create a child process.
5: For the parent_process
Close reading end of pipe1 using close() function.
Write the file path to pipe1 using write() function.
Close the writing end of pipe2 using close() function.
Read contents of pipe2 using read() function.
Display contents on the screen
6: For the child_process
Close writing end of pipe1 using close() function.
Read the contents of pipe1 using read() function.
Open the file specified by the path name on pipe1 in read mode using fopoen() function.
Read and store the contents of the file into a buffer.
Close the reading end of pipe2 using close() function.
Write the contents of buffer into pipe2 using write() function.
7: END

Algorithm for FIFO:


Create a header file for the path of the file pipe to be created for communication. Client
1: START
2: Open the input file using fopen() to read the sentences.
3: Read and store the sentences in a buffer.
4: Open pipe1 file in write only mode using the open() function.
5: Write the contents of buffer into the opened pipe1.
6: Open pipe2 file in read only mode using the open() function.
7: Read the contents of pipe2 & display on screen.
8: END

Server
1: START
2: Create two fifos i.e named pipes using mkfifo() function.
3: Open the pipe1 in read only mode.
4: Read and store the pipe1 contents in a buffer.
5: Calculate the number of lines, characters & words in the contents of pipe1.
6: Open pipe2 file in write only mode using the open() function.
7: Write the number of characters, lines & words into the opened pipe2.
8: END

TE IT (Course 2015) Software Laboratory II


Input: Input for Pipe:
Path Name for the text file whose contents are to be displayed

Input for FIFO:


Text file which client must send to server.

Steps to execute:
Steps for executing Pipe & Signal:
Step 1: Compile file using ―gcc program_name.c –o program_name.out Step 2: Execute the
program using ―./program_name.out

Steps for executing FIFO:


Step 1: Compile file using ―gcc server_program_name.c –o server_program_name.out
Step 2: Execute the server program using ―./server_program_name.out
Step 3: Compile file using ―gcc client_program_name.c –o client_program_name.out
Step 4: Execute the client program using ―./client_program_name.out

Conclusion:
Thus we have implemented inter process communication in Linux using Pipes for Full duplex
communication between parent and child processes, FIFOs for Full duplex communication
between two independent processes, Signals for Detecting the termination of multiple child
processes with the help of SIGCHLD signal

FAQ’s

1. Explain the use of | operator with the example of multiple commands. (At least three
examples with practical demonstration is expected).

2. What is difference between pipe and shared memory implementation in Linux IPC?

3. What is difference between pipe and FIFO? Explain at least three points.

4. What are the advantages of FIFO over pipe?

5. Explain the situation where FIFO is appropriate structure used over pipe.

6. State the difference between named and unnamed pipes

TE IT (Course 2015) Software Laboratory II


Experiment No. 8
Title of Experiment:

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 shared
memory segment and displays it to the screen.

Course Objectives:

1. To demonstrate the functioning of OS concepts in user space like concurrency control (process

synchronization, mutual exclusion & deadlock) and file handling in LINUX.

Course Outcomes Achieved:

1. To develop various system programs for the functioning of OS concepts in user space like
concurrency control and file handling in Linux.

Theory:

Shared Memory :

Shared memory is one of the three interprocess communication (IPC) mechanisms available under
Linux and other Unix-like systems. The other two IPC mechanisms are the message queues and
semaphores. In case of shared memory, a shared memory segment is created by the kernel and
mapped to the data segment of the address space of a requesting process. A process can use the
shared memory just like any other global variable in its address space.

Figure 8.1: Shared memory mechanism

TE IT (Course 2015) Software Laboratory II


In the interprocess communication mechanisms like the pipes, fifos and message queues, the work
involved in sending data from one process to another is like this. Process P1 makes a system call to
send data to Process P2. The message is copied from the address space of the first process to the
kernel space during the system call for sending the message. Then, the second process makes a
system call to receive the message. The message is copied from the kernel space to the address
space of the second process. The shared memory mechanism does away with this copying
overhead. The first process simply writes data into the shared memory segment. As soon as it is
written, the data becomes available to the second process. Shared memory is the fastest mechanism
for interprocess communication.

System V and POSIX Shared Memory

Like message queues and semaphores, shared memory also comes in two flavors, the traditional
System V shared memory and the newer POSIX shared memory. In this post, we will look at the
System V shared memory calls. Example programs for a server and client processes
communicating via the System V shared memory are given near the end of the post.

System V IPC key


To use a System V IPC mechanism, we need a System V IPC key. The ftok function, which
does the job, is
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok (const char *pathname, int proj_id);

The pathname is an existing file in the filesystem. The last eight bits of proj_id are used; these
must not be zero. A System V IPC key is returned.

System V Shared Memory Calls


1. shmget
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget (key_t key, size_t size, int shmflg);

As the name suggests, shmget gets you a shared memory segment associated with the given key.
The key is obtained earlier using the ftok function. If there is no existing shared memory segment
corresponding to the given key and IPC_CREAT flag is specified in shmflg, a new shared memory
segment is created. Also, the key value could be IPC_PRIVATE, in which case a new shared
memory segment is created. size specifies the size of the shared memory segment to be created; it
is rounded up to a multiple of PAGE_SIZE. If shmflg has IPC_CREAT | IPC_EXCL specified and
a shared memory segment for the given key exists, shmget fails and returns -1, with errno set to
EEXIST. The last nine bits of shmflg specify the permissions granted to owner, group and others.
The execute permissions are not used. If shmget succeeds, a shared memory identifier is returned.
On error, -1 is returned and errno is set to the relevant error.

TE IT (Course 2015) Software Laboratory II


2. shmat
#include <sys/types.h>
#include <sys/shm.h>
void *shmat (int shmid, const void *shmaddr, int shmflg);

With shmat, the calling process can attach the shared memory segment identified by shmid. The
process can specify the address at which the shared memory segment should be attached with shmaddr.
However, in most cases, we do not care at what address system attaches the shared memory segment
and shmaddr can conveniently be specified as NULL. shmflg specifies the flags for attaching the
shared memory segment. If shmaddr is not null and SHM_RND is specified in shmflg, the shared
memory segment is attached at address rounded down to the nearest multiple of SHMLBA, where
SHMLBA stands for Segment low boundary address. The idea is to attach at an address which is a
multiple of SHMLBA. On most Linux systems, SHMLBA is the same as PAGE_SIZE. Another flag is
SHM_RDONLY, which means the shared memory segment should be attached with read only access.
On success, shmat returns pointer to the attached shared memory segment. On error, (void *) -1 is
returned, with errno set to the cause of the error.

3 shmdt
#include <sys/types.h>
#include <sys/shm.h>
int shmdt (const void *shmaddr);

shmdt detaches a shared memory segment from the address space of the calling process. shmaddr
is the address at which the shared memory segment was attached, being the value returned by an
earlier shmat call. On success, shmdtreturns 0. On error, shmdt returns -1 and errno is set to
indicate the reason of error.

4 shmctl
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl (int shmid, int cmd, struct shmid_ds *buf);

The shmctl call is for control operations of a System V shared memory segment identified by the
shmid identifier, returned by an earlier shmget call. The data structure for shared memory
segments in the kernel is,
struct shmid_ds
{
struct ipc_perm shm_perm; /* Ownership and permissions */ size_t shm_segsz; /*
Size of segment (bytes) */
time_t shm_atime; /* Last attach time */ time_t shm_dtime; /*
Last detach time */ time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */ shmatt_t shm_nattch; /* No. of

current attaches */ ...

TE IT (Course 2015) Software Laboratory II


};
Where. the struct ipc_perm is,
struct ipc_perm
{
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */ gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */ gid_t cgid; /* Effective
GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and SHM_LOCKED flags */ unsigned short __seq;

/* Sequence number */ };

The cmd parameter in shmctl specifies the command. The important commands are IPC_STAT,
IPC_SET and IPC_RMID. The IPC_STAT command copies the data in the kernel data structure
shmid_ds for the shared memory into the location pointed by the parameter buf. With the
IPC_SET command, we can set some of the fields in the shmid_dsstructure in the kernel for the
shared memory segment. The fields that can be modified are shm_perm.uid, shm_perm.gid and
the least significant 9 bits of shm_perm.mode. The command, IPC_RMID, marks a shared
memory segment for removal from the system. The shared memory segment is actually removed
after the last process detaches it from its address space.

Client-Server communication using System V Shared Memory

The example has a server process which prints strings received from clients. The Server is a kind
of consumer process which consumes strings. The server creates a shared memory segment and
attaches it to its address space. The client processes attach the shared memory segment and write
strings in it. The client processes are producers; they produce strings. The server takes strings from
the shared memory segment and writes the strings on the terminal. The spooler prints strings in the
order they are written in the shared memory segment by the clients. So, effectively, there are n
concurrent processes producing strings at random times and the server prints them nicely in the
chronological order.

Implementation Details

1: Server creates shared memory

2: Attach the shared memory segment to the address space of the client process

3: write to the shared memory

4: Client reads the content of the shared memory and write on to the standard output

5: Delete the shared memory

TE IT (Course 2015) Software Laboratory II


Conclusion

Shared memory allows one or more processes to communicate via memory that appears in all of
their virtual address spaces. The pages of the virtual memory is referenced by page table entries in
each of the sharing processes' page tables. It does not have to be at the same address in all of the
processes' virtual memory. As with all System V IPC objects, access to shared memory areas is
controlled via keys and access rights checking. Once the memory is being shared, there are no
checks on how the processes are using it. They must rely on other mechanisms, for example
System V semaphores, to synchronize access to the memory.

FAQ.

1.Explain sharedmemory segment.

2.Give details of shmid_ds structure.

3.Explain Function of shmget().

TE IT (Course 2015) Software Laboratory II


Experiment No. 09
Title of Experiment:
Implement an assignment using File Handling System Calls (Low level system calls like open,
read, write, etc).

Course Objectives:

1. To demonstrate the functioning of OS concepts in user space like file handling in


LINUX.

Course Outcomes Achieved:

1. To develop various system programs for the functioning of OS concepts in user space like
file handling in Linux.

Theory:
The file is the most basic and fundamental abstraction in Linux. Linux follows the everything-is-
a-file philosophy. Consequently, much interaction transpires via filesystem system calls such as
reading of and writing to files, even when the object in question is not what you would consider
your everyday file.
In order to be accessed, a file must first be opened. Files can be opened for reading, writing, or
both. An open file is referenced via a unique descriptor, a mapping from the metadata associated
with the open file back to the specific file itself. Inside the Linux kernel, this descriptor is
handled by an integer (of the C type int) called the file descriptor, abbreviated fd. File
descriptors are shared with user space, and are used directly by user programs to access files. A
large part of Linux system programming consists of opening, manipulating, closing, and
otherwise using file descriptors.

File descriptors:
The operating system assigns internally to each opened file a descriptor or an identifier (usually
this is a positive integer). When opening or creating a new file the system returns a file
descriptor to the process that executed the call. Each application has its own file descriptors.
Byconvention, the first three file descriptors are opened at the beginning of each process. The 0
file descriptor identifies the standard input, 1 identifies the standard output and 2 the standard
output for errors. The rest of the descriptors are used by the processes when opening an ordinary,
pipe or special file, or directories. There are five system calls that generate file descriptors:
create, open, fcntl, dup and pipe. The following section presents the way to use the most
common system calls in order to make input-output operations on files, as well as operations to
handle files and directories in the Linux operating system.

TE IT (Course 2015) Software Laboratory II


System calls when working with files :

(1) System call OPEN


Opening or creating a file can be done using the system call open. The syntax is:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int flags,... /* mode_t mod */);

This function returns the file descriptor or in case of an error -1. The number of arguments that
this function can have is two or three. The third argument is used only when creating a new file.
When we want to open an existing file only two arguments are used. The function returns the
smallest available file descriptor. This can be used in the following system calls: read, write,
lseek and close. The effective UID or the effective GID of the process that executes the call has
to have read/write rights, based on the value of the argument flags. The file pointer is places on
the first byte in the file. The argument flags is formed by a bitwise OR operation made on the
constants defined in the fcntl.h header.
O_RDONLY Opens the file for reading.
O_WRONLY Opens the file for writing.
O_RDWR The file is opened for reading and writing.O_APPEND It writes successively to the end of the file.
O_CREAT The file is created in case it didn't already exist.
O_EXCL
O_NONBLOCK If the file exists and O_CREAT is positioned, calling open will fail.
In the case of pipes and special files, this causes the open system call
O_TRUNC If the file exists all of its content will be deleted.
O_SYNC It forces to write on the disk with function write.

Though it slows down all the system, it can be useful in critical situations. and any other future
I/O operations to never block. The third argument, mod, is a bit-wise OR made between a
combination of two from the following list:
S_IRUSR, S_IWUSR, S_IXUSR
Owner: read, write, execute.
S_IRGRP, S_IWGRP, S_IXGRP
Group: read, write, execute.
S_IROTH, S_IWOTH, S_IXOTH
Others: read, write, execute.

The above define the access rights for a file and they are defined in the sys/stat.h header.

(2) System call CREAT


A new file can be created by:
# include <sys/types.h> #include
<sys/stat.h> #include <fcntl.h>
TE IT (Course 2015) Software Laboratory II
int creat(const char *path, mode_t mod);The function returns the file descriptor or in case of an error it returns the
value -1. This call is equivalent with: open(path, O_WRONLY | O_CREAT | O_TRUNC, mod);
The argument path specifies the name of the file, while mod defines the access rights. If the
created file doesn\92t exist, a new i-node is allocated and a link is made to this file from the
directory it was created in. The owner of the process that executes the call - given by the
effective UID and the effective GUID - must have writing permission in the directory. The open
file will have the access rights that were specified in the second argument (see umask, too). The
call returns the smallest file descriptor available. The file is opened for writing and its initial size
is 0. The access time and the modification time are updated in the i-node. If the file exists
(permission to search the directory is needed), it looses its contents and it will be opened for
writing. The ownership and the access permissions won't be modified. The second argument is
ignored.

(3) System call READ


When we want to read a certain number of bytes starting from the current position in a file, we
use the read call. The syntax is:
#include <unistd.h>
ssize_t read(int fd, void* buf, size_t noct);

The function returns the number of bytes read, 0 for end of file (EOF) and -1 in case an error
occurred. It reads noct bytes from the open file referred by the fd descriptor and it puts it into a
buffer buf. The pointer (current position) is incremented automatically after a reading that certain
amount of bytes. The process that executes a read operation waits until the system puts the data
from the disk into the buffer.

(4) System call WRITE


For writing a certain number of bytes into a file starting from the current position we use the
write call. Its syntax is:
#include <unistd.h>
ssize_t write(int fd, const void* buf, size_t noct);

The function returns the number of bytes written and the value -1 in case of an error. It writes
noct bytes from the buffer buf into the file that has as its descriptor fd. It is interesting to note
that the actual writing onto the disk is delayed. This is done at the initiative of the root, without
informing the user when it is done. If the process that did the call or an other process reads the
data that haven't been written on the disk yet, the system reads all this data out from the cache
buffers. The delayed writing is faster, but it has three disadvantages:
● a disk error or a system error may cause loosing all the data
● a process that had the initiative of a write operation cannot be informed in case a writing error
occurred
● the physical order of the write operations cannot be controlled.

TE IT (Course 2015) Software Laboratory II


To eliminate these disadvantages, in some cases the O_SYNC is used. But as this slows down
the system and considering the reliability of today\92s systems it is better to use the mechanism
which includes using cache buffers.

(5) System call CLOSE


For closing a file and thus eliminating the assigned descriptor we use the system call close.
#include <unistd.h>
int close(int fd);

The function returns 0 in case of success and -1 in case of an error. At the termination of a
process an open file is closed anyway.

System calls when working with Directories


UNIX offers a number of system calls to handle a directory. The following are most commonly
used system calls.

1. opendir()
Syntax : DIR * opendir (const char * dirname );

opendir () takes dirname as the path name and returns a pointer to a DIR structure. On error
returns NULL.

2. readdir()
Syntax: struct dirent * readdir ( DIR *dp) ;

A directory maintains the inode number and filename for every file in its fold. This function
returns a pointer to a dirent structure consisting of inode number and filename.'dirent' structure is
defined in <dirent.h> to provide at least two members – inode number and directory name.
struct dirent
{
ino_t d_ino ; // directory inode number
char d_name[]; // directory name
}

3. closedir()
Syntax: int closedir ( DIR * dp);

Closes a directory pointed by dp. It returns 0 on success and -1 on error.

TE IT (Course 2015) Software Laboratory II


Implementation Details:
1. Start the program.
2. open file or create and open file in write mode.
3. Read data from standard input into a buffer.
4. Write data to file from buffer .
5. Close the file.
6. Open file for Read only.
7. Read data from file into buffer.
8. Display buffer data to standard output
9. Close the file
10. Exit.

Input:
Enter information to be written to the file
HELLO

Output:
Information read from the file
HELLO

Conclusion:
The file is the most basic and fundamental abstraction in Linux. Linux follows the everything-is-
a-file philosophy. Consequently, much interaction transpires via filesystem system calls such as
reading of and writing to files, even when the object in question is not what you would consider
your everyday file. In order to be accessed, a file must first be opened. Files can be opened for
reading, writing, or both.

FAQ:
1. Write a program to implement “cat” Unix command using system calls.
2. Program to get the attribute of a file or directory on linux using system calls.
3. Write a program that will list all files in a current directory and all files in subsequent sub
directories.

TE IT (Course 2015) Software Laboratory II


Experiment No. 10

Title of Experiment:
Implement a new system call, add this new system call in the Linux kernel (any kernel source,
any architecture and any Linux kernel distribution) and demonstrate the use of same.

Course Objectives:

1. To familiarize the students with the Operating System.

2. To demonstrate the process, memory, file and directory management issues under the
UNIX/LINUX operating system

3. To introduce LINUX basic commands

4. To make students how to make simple programs in LINUX and administrative task of
LINUX

Course Outcomes Achieved:


1. Use C / C++ and Unix commands, and develop various system programs under Linux to
make use of OS concepts related to process synchronization, shared memory, file
systems,etc.

Theory:

System Call:
In computing, a system call is how a program requests a service from an operating system's
kernel. This may include hardware-related services (for example, accessing a hard disk drive),
creation and execution of new processes, and communication with integral kernel services such
as process scheduling. System calls provide an essential interface between a process and the
operating system. In most systems, system calls are possible to be made only from user space
processes, while in some systems, OS/360 and successors for example, privileged system code
also issues system calls.

System calls are generally not invoked directly, but rather via wrapper functions in glibc (or
perhaps some other library). Often, but not always, the name of the wrapper function is the same
as the name of the system call that it invokes. For example, glibc contains a function truncate()
which invokes the underlying "truncate" system call.

On Unix, Unix-like and other POSIX-compliant operating systems, popular system calls are
open, read, write, close,wait, exec, fork, exit, and kill. Many modern operating systems have
hundreds of system calls. For example, Linux and OpenBSD each have over 300 different calls,
NetBSD has close to 500, FreeBSD has over 500, while Plan 9 has 51.
TE IT (Course 2015) Software Laboratory II
Tools such as strace and truss allow a process to execute from start and report all system calls the
process invokes, or can attach to an already running process and intercept any system call made
by said process if the operation does not violate the permissions of the user. This special ability
of the program is usually also implemented with a system call, e.g. strace is implemented with
ptrace or system calls on files in procfs.

Implementing system calls requires a control transfer which involves some sort of architecture-
specific feature. A typical way to implement this is to use a software interrupt or trap. Interrupts
transfer control to the operating system kernel so software simply needs to set up some register
with the system call number needed, and execute the software interrupt.

Categories of system calls


System calls can be roughly grouped into five major categories:

● Process Control
A running program needs to be able to stop execution either normally or abnormally. When
execution is stopped abnormally, often a dump of memory is taken and can be examined with a
debugger.
● load

● execute

● create process (for example, fork on Unix-like systems)

● terminate process

● get/set process attributes

● wait for time, wait event, signal event

● allocate, free memory

● File management
Some common system calls are create, delete, read, write, reposition, or close. Also, there is a
need to determine the file attributes – get and set file attribute. Many times the OS provides an
API to make these system calls.
■ create file, delete file

■ open, close

■ read, write, reposition

TE IT (Course 2015) Software Laboratory II


■ get/set file attributes

● Device Management
Process usually require several resources to execute, if these resources are available, they will be
granted and control returned to the user process. These resources are also thought of as devices.
Some are physical, such as a video card, and others are abstract, such as a file. User programs
request the device, and when finished they release the device. Similar to files, we can read, write,
and reposition the device.
● request device, release device

● read, write, reposition

● get/set device attributes

● logically attach or detach devices

● Information Maintenance
Some system calls exist purely for transferring information between the user program and the
operating system. An example of this is time, or date. The OS also keeps information about all its
processes and provides system calls to report this information.
■ get/set time or date

■ get/set system data

■ get/set process, file, or device attributes

● Communication

● create, delete communication connection ●

send, receive messages

● transfer status information

● attach or detach remote devices

The steps for making a system call:

1. push parameters on stack

2. invoke the system call

3. put code for system call on register

TE IT (Course 2015) Software Laboratory II


4. trap to the kernel

5. since a number is associated with each system call, system call interface
invokes/dispatch intended system call in OS kernel and return status of the system call and
any return value
6. increment stack pointer

Steps in making a system call:

Fig 10.1 Steps in a system call

Syscall() function

long syscall(long number, ...);

syscall() is a small library function that invokes the system call whose assembly language
interface has the specified number with the specified arguments. Employing syscall() is useful,
for example, when invoking a system call that has no wrapper function in the C library. syscall()
saves CPU registers before making the system call, restores the registers upon return from the
system call, and stores any error code returned by the system call in errno if an error occurs.
Symbolic constants for system call numbers can be found in the header file <sys/syscall.h>. The
return value is defined by the system call being invoked. In general, a 0 return value indicates
success. A -1 return value indicates an error, and an error code is stored in errno.

TE IT (Course 2015) Software Laboratory II


Implementation Details:
Steps to execute:

1: START
2: Go to directory usr/src/linux-3.18.3
3: Create a new directory named ―addnum and create ―addnum.c file for the system call
4: Add the code for adding 2 numbers
asmlinkage long sys_addnum(int i, int j)
{
printk(KERN_INFO "Addnum is working! Now adding %d and %d", i, j); return i+j;
}

5: Create a Makefile in the same directory containing line


obj-y := addnum.o

6: go back to /usr/src & issue the command


grep -n "kernel/ mm/" /user/src/linux-3.18.3/Makefile
result of this command will give a line number

7: Open Makefile at /user/src/linux-3.18.3/Makefile & modify the Makefile at the line


number returned in step 6 from
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ to core-y += kernel/ mm/ fs/ ipc/

security/ crypto/ block/ addnum/

8: Open the file /usr/src/linux-3.18.3/arch/x86/syscalls/syscall_32.tbl and go to end of the file.

9: Open the file /usr/src/linux-3.18.3/include/linux/syscalls.h and just before the last #endif
statement add the following line
asmlinkage long sys_addnum(int i, int j);

10: Perform kernel compilation using steps


Make –j4
Make modules_install Make install

11: Write a C code and using syscall function and the line number at which we have added
the syscall in step 8

12: Compile and execute this code.


TE IT (Course 2015) Software Laboratory II
Conclusion:

Thus we have implemented and added a new system call in Linux Kernel to add 2 numbers using
the steps defined above and have used the system call to implement addition of 2 numbers.

FAQ’s
Q1. Explain the importance of make file in Linux

Q2. What does the ―asmlinkage‖ do in syscall.h file?

Q3. “Make –j4”: Explain the importance of j4 in the statement

TE IT (Course 2015) Software Laboratory II

Вам также может понравиться