Académique Documents
Professionnel Documents
Culture Documents
Kuwait University
College
for Women
Information Technology
Solutions
LAB MANUAL
ISC 357
OPERATING SYSTEMS AND FILE SYSTEM ORGANIZATION
LABORATORY
Version 1.0
PREPARED BY
DR. KALIM QURESHI
PROF. DR. MOHAMMAD SARFRAZ
MARYAM AL-OTAIBI
TABLE OF CONTENTS
Lab Hardware, Softwares / Tools Requirements ............................................................................................... 6
LAB SYLLABUS for ISC357 ............................................................................................................................................ 7
Operating Systems and File System Organization .............................................................................................. 7
Course catalog ............................................................................................................................................................... 7
Lab Objective ................................................................................................................................................................. 7
Prerequisite.................................................................................................................................................................... 7
Lab Schedule .................................................................................................................................................................. 8
Evaluation Policy ......................................................................................................................................................... 9
Project .............................................................................................................................................................................. 9
Laboratory Policy ........................................................................................................................................................ 9
Laboratory 0: How install UBUNTU 8.10 DESK TOP EDITION ........................................................... 10
Laboratory1: Introduction to the UNIX Operating System .......................................................................... 18
What is UNIX?............................................................................................................................................................. 18
Types of UNIX ............................................................................................................................................................. 18
Unix Major Components ........................................................................................................................................ 18
Files and processes .................................................................................................................................................. 19
The Directory Structure ......................................................................................................................................... 20
Accessing UNIX System .......................................................................................................................................... 21
SIGHUP .................................................................................................................................................... 90
SIGINT ...................................................................................................................................................... 90
SIGTSTP ................................................................................................................................................... 90
SIGQUIT ................................................................................................................................................... 90
SIGILL ....................................................................................................................................................... 91
SIGTRAP .................................................................................................................................................. 91
SIGIOT ...................................................................................................................................................... 91
SIGEMT .................................................................................................................................................... 91
SIGFPE ..................................................................................................................................................... 91
SIGKILL .................................................................................................................................................... 91
SIGBUS ..................................................................................................................................................... 91
SIGSEGV................................................................................................................................................... 91
SIGPIPE .................................................................................................................................................... 91
SIGALARM .............................................................................................................................................. 92
4|Page
SIGTERM ................................................................................................................................................. 92
SIGUSR1 .................................................................................................................................................. 92
SIGUSR2 .................................................................................................................................................. 92
SIGPWR ................................................................................................................................................... 92
Requesting An Alarm Signal: alarm( ) ......................................................................................... 93
pause System Call:............................................................................................................................... 95
kill System Call ..................................................................................................................................... 96
Excersices..................................................................................................................................................................... 97
laboratory12: Inter Process Communication (IPC)-Using Shared Memory........................................ 100
What is Shared Memory?..................................................................................................................................... 101
Asking for a Shared Memory Segment - shmget( ) ................................................................................... 102
Attaching a Shared Memory Segment to an Address Space - shmat( )............................................. 104
Detaching and Removing a Shared Memory Segment - shmdt( ) and shmctl( )........................... 106
Excersices................................................................................................................................................................... 111
laboratory13: Threads Creation and Execution ............................................................................................. 111
Introduction .............................................................................................................................................................. 112
5|Page
Softwares
Currently used software
1. Redhat server
2. Putty which is a client program for the ssh, telnet and rlogin network protocols.
3. Ubuntu for DESKTOP
Hardware
Hardware required is standard PC having Mouse, Keyboard, Monitor, and Networking Support.
6|Page
COURSE CATALOG
The main aim of this course is to acquire a systematic knowledge of operating systems and to
develop a critical understanding of their purpose, the main concepts, techniques and methods.
Topics covered include processes and threads, scheduling, memory management, file systems,
and storage file organizations and access methods from the operating system, programming
language, and information systems design perspectives are also introduced.
LAB OBJECTIVE
The objective of the operating systems labs is to practically implement the major operating
system issues discussed in the lectures. All the process, memory, file and directory management
issues will be demonstrated under the UNIX/LINUX operating system. Also the UNIX commands
and shell programming will be discussed in detail.
PREREQUISITE
It is very essential for students to have strong knowledge of the C language for this course, as all
experiments will be written in C language.
7|Page
LAB SCHEDULE
Lab
Lab-1
Lab-2
Lab-3
Topic
Introduction
get familiar with UNIX,
Connect to UNIX machines remotely via Putty,
Starting UNIX terminal (UBUNTU),
Getting user accounts, and How to Login Redhat server,
Getting familiar with UNIX environment.
Unix Directory Management Commands (pwd, cd, ls, mkdir, rmdir, cp, mv, find, Determine
file type: file, Linking files and directories)
Lab-11
Lab-12
Lab-13
Lab-4
Lab-5, 6
Lab-7
Lab-8
Lab-9,10
8|Page
EVALUATION POLICY
Activity
Lab Work (12 x 0.5%)
Lab Quizzes + HWs
Lab Project
Total
Weight
6%
9%
6%
21%
PROJECT
There will be two mini projects covering the two major topics Shell Programming and Processes/
Threads. Project summary/outline will be provided on the blackboard. Honor the project
submission dead line. After that dead line, a reduction of 20 points for up to 24 hours late
submission and -50 points for up to next 24 hours late submission and after that, submissions
will not be accepted. The projects will be graded only after the student gives a demonstration of
it. Without the demo, the project will not be graded. The projects should be submitted in both
soft copy (through blackboard) and hard copy (printed) in the form of a report with the source
code, along with the detailed explanation of the approach followed.
Notes:
To pass this course, the student must pass the lab-component of the course.
Cheating in whatever form will result in F grade.
Attendance will be checked at the beginning of each Lab.
Absence for three (03) or more unexcused labs will result in a F grade in the Course. An
official excuse must be shown in one week following return to classes.
Every unexcused absence leads to a loss of 0.5 % marks.
Cheating in Lab Work or Lab Project will result F grade in Lab.
Late Submission of Home Works & Projects will not be accepted.
There will be no make-up for any Quiz/Exam/Lab.
Hard work and dedication are necessary ingredients for success in this course.
LABORATORY POLICY
To pass this course, the student must pass the lab-component of the course.
Absence for three (03) or more unexcused labs will result in a F grade in the Course. An
official excuse must be shown in one week following return to classes.
9|Page
The installation of the base system is easy as 1-2-3 because the Ubuntu installer doesn't offer
a lot of options to choose from, so you cannot go wrong.
1. Download the Ubuntu 8.10 desktop edition iso image from
http://www.ubuntu.com/getubuntu/download (or take the CD from your instructor), burn
it onto a CD, and boot your computer from it.
2.
3. Select the second option "Install Ubuntu," and hit the Enter key...
10 | P a g e
5. When the installer appears, you are able to select your native language for the installation
process. Click the Forward button to continue...
11 | P a g e
12 | P a g e
13 | P a g e
14 | P a g e
15 | P a g e
11. The CD will be ejected, remove it and press the Enter key to reboot...
The computer will be restarted and, in a few seconds, you will see the Ubuntu login screen.
Input your username and password...
16 | P a g e
17 | P a g e
WHAT IS UNIX?
An operating system is the program that controls all the other parts of a computer system - both
the hardware and the software. Most importantly, it allows you to make use of the facilities
provided by the system. Example of operating system are Windows XP, Windows NT, UNIX,
Linux, ..etc.
UNIX is an operating system which was first developed in the 1960s, and has been under
constant development ever since. By operating system, we mean the suite of programs which
make the computer work. It is a stable, multi-user, multi-tasking system for servers, desktops
and laptops [1].
UNIX systems also have a graphical user interface (GUI) similar to Microsoft Windows which
provides an easy to use environment. However, knowledge of UNIX is required for operations
which aren't covered by a graphical program, or for when there is no windows interface
available, for example, in a telnet session [1].
TYPES OF UNIX
There are many different versions of UNIX, although they share common similarities.
The most popular varieties of UNIX are:
Sun Solaris,
GNU/Linux, and
MacOS X.
resources from that point forward. The kernel knows what hardware resources are
available (like the processor(s), memory, the disk drives, network interfaces, etc.), and it has
the necessary programs to talk to all the devices connected to it. It also prevents anyone
from accessing the hardware directly, forcing everyone to use the tools it provides. This way
the kernel provides some protection for users from each other.
2. The Shell
The shell acts as an interface between the user and the kernel. It is a command line
interpreter. It interprets the commands the user types in and arranges for them to be
carried out. It takes each command and passes it to the operating system kernel to be acted
upon. It then displays the results of this operation on your screen. There are several
different shells available for Unix; the most popular are:
/.../sh
/.../csh
C shell (csh)
/.../tcsh
TC shell (tcsh)
/.../ksh
/.../bash
You can identify which shell you are presently using from the last part of the pathname.
Information about which shell you are using is held in the SHELL environment variable.
Example:
Display the value of the variable SHELL.
Note that the name of environment variable must be given in UPPERCASE
>echo $SHELL
/bin/bash
To switch to another shell enter the shell command name at the system prompt.
Example:
This switches you from your current shell to the Bourne shell.
>sh
sh-2.05b>
19 | P a g e
2. Directories. A directory is a file that holds other files and other directories. You
can set access permissions to directory which you create. i.e. you own them.
3. Special files. This type of file is used to represent a real physical device such as a
printer, tape drive or terminal.
4. Pipes. UNIX allows you to link commands together using a pipe. The pipe acts as
a temporary file, which only exists to hold data from one command until it is
read by another.
The /bin directory contains the commands and utilities that you use day to day. These
are executable binary files - hence the directory name bin.
The /dev directory contains special files used to represent real physical devices such as
printers and terminals.
The /etc directory contains various commands and files which are used for system
administration
The /home directory contains a home directory for each user of the system.
The /lib directory contains libraries that are used by various programs and languages.
The /tmp directory acts as a "scratch" area in which any user can store files on a
temporary basis.
The /usr directory contains system files and directories that you share with other users.
Application programs, on-line manual pages, and language dictionaries typically reside
here.
20 | P a g e
21 | P a g e
The regular PUTTY terminal window will pop up, as it is shown in the figure below. Enter your
username and password. Note that while you are typing your password cursor will not be
moved.
An UNIX Terminal window will then appear with a % prompt, waiting for you to start entering
commands.
22 | P a g e
23 | P a g e
command [options][arguments]
Commands are case sensitive.
An option: modifies the command, changing the way it performs. Options are generally
preceded by a hyphen (-) and for most commands, more than one option can be used
together.
argument: indicates on what the command is to perform its action, usually a file or
series of files.
Any options or arguments enclosed in [ ] square brackets are optional.
Anything not enclosed in [ ] square brackets must be entered.
Boldface words are considered to be literals and must be typed exactly as they appear.
This usually applies to the command name and command options.
Arguments shown in italics must be replaced by whatever it is that they represent. This
is usually the name of a file or directory.
Ellipses '...' mean that the previous argument can be repeated any number of times.
24 | P a g e
bin
home
tmp
usr
dev
ISC35
7
091
Myfolder
082
maryam2
test.c
Abrar
report.doc
CHANGING DIRECTORY- cd
cd [directory]
Changing working directory
Examples:
This moves you up to the root
> cd /
This moves you down to your HOME directory.
cd ~
>cd /usr
>pwd
25 | P a g e
/usr
This moves you down one level from current directory to subdirectory bin
>cd bin
>pwd
/usr/bin
>cd usr/etc
>pwd
usr/etc
Move up the directory tree without entering the pathname
>cd ..
>pwd
/usr
This moves you down one level from current directory to subdirectory start with letter e,
for example usr/etc.
>cd e*
This moves you up one level in the directory tree and then moves you into usr/bin the
subdirectory bin
>cd ../bin
(.) means the current directory, in other word stay where you are. Note: there is a space
between cd and the dot. This may not seem very useful at first, but using (.) as the name of
the current directory will save a lot of typing, as we shall see later in the tutorial.
>cd .
26 | P a g e
LIST -ls
ls [-aAcCdfFgilLqrRstu1] [filename]
List the contents of a directory
options
Description
-a
List all entries; even the hidden file that begin with .(dot)
-A
-c
Use time of last edit (or last mode change) for sorting or printing
-C
Force multiple-column output, with entries sorted down the columns .This is the default when
output is to terminal.
-d
If argument is a directory, lidt only its name(not its contents);often used with l to get the
status of a diectory.
-F
Mark directories with a trailing slash (/), executable files with a trailing asterisk(*), symbolic
links with a trailing at-sign (@), and AF_UNIX address family sockets with a trailing equals
sign(=)
-l
list in long format, access permissions, number of links, owner, size in bytes, and time of last
modification for each file. If the file is a special file the size field will instead contain the major
and minor device numbers. If the time of last modification is greater than six months ago, it is
shown in the format month date year; files modified within six months show month date
time .If the file is a symbolic link the path name of the linked-to file is printed preceded by ->.
-L
If argument is a symbolic link, list the file or directory the link references rather than the link
itself.
-q
Display non-graphic characters in filenames as the character ?; for ls, this is the default when
output is to a terminal.
-r
Reverse the order of sort to get reverse alphabetic or oldest first as a appropriate.
-R
-s
Give size of each file, including any indirect blocks used to map the file, in kilobytes.
-t
-u
Use time of last access instead of last modification for sorting (with t option) and/or printing
(with the l option)
-1
Force one entry per line output format; this is the default when output is not to terminal
27 | P a g e
Examples:
>ls
bin dev home lib misc usr boot
>ls -F
/bin /dev /home /lib /misc /usr /boot
>ls a
. .. .autofsck bin dev home lib misc usr boot
etc
>ls Fa
. .. .autofsck /bin /dev /home /lib /misc /usr /boot
/etc
>ls m*
myfile maryam.tar
>ls l
rwxr-xr-x 1
ahmed staff
In the example above, this first item -rwxr-xr-x represents the access permissions on
this file.
The next items represent the number of links (1) to it; the username (ahmed) of the
person owning it; the name of the group (staff) which owns it; its size in bytes (3649);
MAKE A DIRECTORY-mkdir
mkdir directory Name
Make a directory or directories
Examples:
This creates the directory maryam in the current directory
>mkdir maryam
This creates the directory presentations in the parent
directory of the current working directory
>mkdir . . / presentations
>mkdir ~/test/test1
28 | P a g e
REMOVE DIRECTORY
rmdir directory
Remove an empty directories
Example :
The directory must be empty before you can delete it
>rmdir maryam
rm -r directory
Deletes all the contents of the directory including any subdirectories.
Examples:
Current working
directory
command
home/isc357>
mkdir lab1
home/isc357>
home/isc357>
29 | P a g e
Result
COPY -cp
cp [ -ip ] filename1 filename2
Copy the content of filename1 into filename2.
cp rR [ ip ] directory1 directory2
Recursively copies directory1, along with its contents and subdirectories to
directory2
Directory2 is created if it does not exist, and directory1 is created as a
subdirectory within it.
cp [-iprR] filename directory
Copy the filename/s in the indicated directory
Options
Description
-i
Interactive, Prompt for confirmation whenever the copy would overwrite an existing file. A
y answer confirms that the copy should proceed. Any other answer confirms cp from
overwriting the file.
-p
Preserve. Duplicate not only the contents of the original file or directory, but also the
modification time and permission modes.
-r/R
Recursive. If any of the source files are directory, copy the directory along with its
files(including any subdirectories and their files); the destination must be directory.
...
Examples:
>ls ~/maryam/programs
Prog1 prog1.p
> cp ~/maryam/programs/prog1.p ~/maryam/programs/prog1.old
>ls ~/maryam/programs
Prog1 prog1.old prog1.p
>cp r ~/maryam/programs ~/maryam/oldprograms
>ls ~/maryam/oldprograms
Prog1 prog1.old prog1.p
>mkdir ~/tmp
>cp ~/oldprograms/*.* ~/tmp
>Ls ~/tmp
prog1 prog1.old prog1.p
ls R ~/maryam
30 | P a g e
MOVE-mv
mv[ -fi ] filename1 filename2
Rename filename1 as filename2
mv [ fi ] directory1 directory2
mv[-fi] filename directory
Options
Description
-f
Force. Override any mode restrictions and the I option. The f option
also suppresses any warning messages about modes which would
potentially restrict overwriting.
-I
Description
-b
-e
-n
-s
Example:
>cat n testfile
1 Hello
2 i this is my first file text file
3
4
5 bye
>cat testfile -sn
1 Hello
2 i this is my first file text file
3
31 | P a g e
4 bye
more filename
Display the contents of a file on the screen.
less filename
Display the contents of a file on the screen. Press the [space-bar] if you
want to see another page, and type [q] if you want to quit reading. As you
can see, less is used in preference to cat for long files.
Keyboard
Description
space bar
<RETURN>
head -n filename
Display the first n number of lines of a text file use the command.
Displays only first 10 lines if the option of n is not specified
tail -n filename
display the last n number of lines of a text file use the command. Displays
only last and last 10 lines respectively if the option of n is not specified
Example:
Display the first three lines
>head -3 testfile
Displays last and last 10 lines
>tail testfile
Meaning
ignore case
report only a count of the number of lines containing matches, not the
matches themselves
invert the search, displaying only lines that do not match
display the line number along with the line on which a match was found
work silently, reporting only the final status:
0, for matche(s) found
1, for no matches
2, for error
list filenames, but not lines, in which matches were found.
-v
-n
-s
-l
Searches for the string copying in the file help and displays the lines related.
>grep copying help
Find and display each line in the file tasks that contains the pattern don't or Don't. The line
number for each line is also displayed.
>grep -n '[dD]on\'t' tasks
List only those students currently login to your server.
>who | grep st*
Count number of directories in your current work directory.
33 | P a g e
>ls F | grep c /$
Description
command <file
To redirect the standard output from a command. If the file that you
redirect standard output to does not already exists it will be created.
command >>file
command1 | command2
Example:
This redirects the standard output from the man command so that it goes to the file
hlp1.txt
man mkdir > hlp1.txt.
34 | P a g e
This creates a file called list1,then you can start type the content you desire till you type
^D it will save the content and exit.
cat > list1
Then start to type in file, ex: .
pear
banana
apple
you can use [Return] ,
^ D {this means press [Ctrl] and [d] to stop}
This creates a file called chapt2 with the same contents as part1.
cat part1 > chapt2
It then reads the contents of part2 and appends them to the file chapt2. The file chapt2
now contains the data from part1 followed by the data from part2.
cat part2 >> chapt2
Concatenate file1 and file2 to file0
cat file1 file2 > file0
ACCESS PERMISSIONS
UNDERSTANDING ACCESS PERMISSIONS
There are three types of permissions:
r
read the file or directory
w
write to the file or directory
x
execute the file or search the directory
Each of these permissions can be set for any one of three types of user:
u
the user who owns the file (usually you)
g
members of the group to which the owner belongs
all
other users
The access permissions for all three types of user can be given as a string of nine characters:
user group others
rwx rwx rwx
filename or directory
Example:
The owner of the file has read and write permissions and no permissions to others.
>ls l file1
-rw------- 2
35 | P a g e
The owner has read and write permissions. Everyone else - the group and all other users
- can read the file.
>ls -l testfile
-rw-r--r-- 2
execute only
write only
read only
Examples:
36 | P a g e
To remove permissions
Symbolic mode
who
Description
Users permissions
Group permissions
Others
ALL
It may be a system'' program (e.g login, update, csh) or program initiated by the user
(pico, a.out or a user written one).
When you login to the system a process is started to run your shell program init , and
its PID is 1. Any processes that are started from within your shell - such as entering a
command - are the children of this process. A process can have many children, but only
one parent.
You can have multiple processes executing the same program, but each process has its
own copy of the program within its own address space and executes it independently of
the other copies.
Each process created on the system has a unique number (a process ID), known as its
PID, associated with it.
UNIX command ps will list all current processes running on your machine and will
list the pid.
MONITORING PROCESSES
ps [-option]
Without options to list all the processes owned by you and associated
with your terminal.
The information displayed by the ps command varies according to which command option(s)
you use and the type of UNIX that you are using.
These are some of the column headings displayed by the different versions of this command.
PID SZ(size in Kb) TTY(controlling terminal) TIME(used by CPU) COMMAND
Example:
Display information about all your processes.
> ps -u
37 | P a g e
PID
PPID C
root
02
root
root
root
240
daemon
The first three columns are important. The first lists the user the process is running as, the
second lists the ID of the process, and the third lists the ID of the parent of the process. The final
column is a description of the process, usually the name of the binary that was launched.
The presence of a parent PID (PPID) implies that one process is created by another process.
The original process that kicks this off is called init, and it is always given a PID of 1. init is the
first real process to be started by the kernel on bootup. It is the job of init to start up the rest of
the system. init and other processes with a PPID of 0 belong to the kernel.
sleep time
Waits a amount of seconds. time The amount of seconds to wait..
The shell forks a child process to run the command and displays the job number ([n]) and
the PID (Process ID) number. The shell prompt returns and you can enter further
commands.
Redirect the standard output for a command that is being run in the background to a file.
This prevents the output from the command appearing on your screen and interrupting
your current work. If the command is likely to produce error messages you will need to
redirect standard error. Otherwise all error messages are sent to your screen.
Do not run an interactive command that requires you to type something at the terminal as a
background job. If you do the job will stop and wait for user input. You will then have to kill
its process.
39 | P a g e
MISCELLANEOUS COMMAND
man command
It show a brief manual in a clear text format for most of the commands
available on your Unix OS and provides cross-references to other similar
manuals. Press the [space-bar] if you want to see another page, and type
[q] if you want to quit reading
All the manuals for UNIX commands are split into clearly marked sections:
SYNOPSIS - syntax for running a command all the possible command line options
Example:
Press the [space-bar] if you want to see another page, and type [q] if you want to quit
reading
>man cd
history
It displays a numbered list of commands in the order in which you have
used them.
command
Description
!!
!n
!string
!?string
40 | P a g e
Description
who
finger [userid]
echo <message>
tty
Id
Clear
Hostname
cal
Displays the calendar of current month and year
cal <year>
Displays the calendar of the specified year
cal <month> <year> Displays the calendar of the specified months number and year
wc c <filename>
wc w <filename>
wc l <filename>
EXCERSICES
1. Write a command to store a list of your files names in the home directory and the
subdirectories in a text file called myfilesList.txt. (Hint: use redirect standard
output).
2. Create a file named as lab2.txt by using vi editor. Write at least 5 sentences in this file.
3. Write a command to count number of characters, words and lines in lab2.txt , you have
created in problem#2.
4. Write a command to count number of words in the first line of lab2.txt, you have created
in problem#2. (Hint: use pipe).
5. Write a command to count number of words in the last line of lab2.txt. , you have created
in problem#2. (Hint: use pipe).
41 | P a g e
OVERVIEW OF SHELL
Shell
The shell acts as an interface between the user and the kernel. It is a command line interpreter.
It interprets the commands the user types in and arranges for them to be carried out. It takes
each command and passes it to the operating system kernel to be acted upon. It then displays
the results of this operation on your screen. There are several different shells available for Unix;
Three most widely used shells in UNIX are Bourne shell, C shell, and Korn shell.
Shell Scripts and Uses
A shell script or a shell program is a series of commands put in a file and executed by the
Shell. Bourne Again shell will be used to create shell scripts.
Since the user cannot interact with the kernel directly, Shell Programming skills are a must to be
able to exploit the power of UNIX to the fullest extent. A shell script can be used for variety of
tasks, such as:
Customizing the user work environment. For Example user can write a shell script to see
the current date, a welcome message, and the list of users who have logged on, every
time user login.
Automating your daily tasks. For example, to back up all the programs at the end of the
day.
Automating repetitive tasks.
Executing important system procedures, like shutting down the system, formatting a
disk, creating a file system etc.
Performing some operations on many files.
42 | P a g e
Shell Variables
The variables in the Bourne Shell are classified as:
User defined variables: defined by the user for his use (e.g age=32).
Environmental variables: defined by shell for its own operations (PATH, HOME, TERM,
LOGNAME, PS1, SHELL e.t.c).
Predefined variables: reserved variables used by the shell and UNIX commands for
specifying the exit status of command, arguments to the shell scripts, the formal parameters
e.t.c.
Example:
It prompts the user for input, assigns this to the variable name and then displays the value
of this variable to standard output.
>cat lab5_0
>echo "Please enter your name:"
>read name
>echo "Welcome to CFW, ISC $name"
Running the file lab4_0?
>chmod u+x lab5_0
> ./lab5_0
Please enter your name:
Dr. Kalim Qureshi
Welcome to CFW, ISC Dr. Kalim Qureshi.
43 | P a g e
If there is more than one word in the input, each word can be assigned to a different variable.
Any words left over are assigned to the last named variable.
Example:
echo "Please enter your surname\n"
echo "followed by your first name: \c"
read name1 name2
echo "Welcome to ICS Dept, KFUPM , $name2 $name1"
Example:
This shell script will accept the name and age from the user and display the same on the
terminal screen.
>cat lab5_1
echo Enter your name : \c
read name
echo Enter your age : \c
read age
echo Hello $name , nice to meet you. You are $age years old
Example:
This script takes two file names and copies the first file into the second one
>cat lab5_2
echo Please Enter source file name :\c
read source
echo Enter the target file name :\c
read target
cp $source $target
echo file $source is copied into the $target
COMMAND SUBSTITUTION:
Format for command substitution is:
var = `command` (where is back quote)
Example:
It will display the output of date command
>echo date
Check the output of this script
>echo there are who | wc l users working on the system
44 | P a g e
# (Where op is operator)
Description
These variables are the positional parameters
The name of the command currently being executed
The number of positional arguments given to this invocation of the shell, Parameter
Count.
The exit status of the last command executed is given as a decimal string. When a
command completes successfully, it returns the exit status of 0 (zero), otherwise it
returns a non-zero exit status.
The process number of this shell - useful for including in filenames, to make them
unique (PID of Current Shell).
The process id of the last command run in the background (It holds PID of last
background process).
The current options supplied to this invocation of the shell.
$*
$@@
A string containing all the arguments to the shell, starting at $1, i.e. All Parameters.
Same as above, except when quoted.
Notes:
$* and $@@ when unquoted are identical and expand into the arguments.
"$*" is a single word, comprising all the arguments to the shell, joined together with spaces. For
example '1 2' 3 becomes "1 2 3".
"$@@" is identical to the arguments received by the shell, the resulting list of words completely
match what was given to the shell. For example '1 2' 3 becomes "1 2" "3"
SHIFT COMMAND
If more than 9 parameters are passed to a script, it is not possible to refer to the parameters
beyond the 9th one. This is because shell accepts a single digit following the dollar sign as a
positional parameter definition.
The shift command is used to shift the parameters one position to the left. On the execution of
shift command the first parameter is overwritten by the second, the second by third and so on.
This implies that the contents of the first parameter are lost once the shift command is executed.
46 | P a g e
Example
Write a script, which will accept different numbers and finds their sum. The number of
parameters can vary.
>cat lab5_4
sum=0
while [ $# -gt 0 ]
do
sum=expr $sum + $1
shift
done
echo sum is $sum
Here, the parameter $1 is added to the variable sum always. After shift, the value of $1 will be
lost and the value of $2 becomes the value of $1 and so on.
The above script can also be written without using the shift command as:
for i in $*
do
sum=expr $sum + $i
done
Usually only nine command line arguments can be accessed using positional parameters. The
shift command gives access to command line arguments greater than nine by shifting each of the
arguments. The second argument ($2) becomes the first ($1), the third ($3) becomes the
second ($2) and so on. This gives you access to the tenth command line argument by making it
the ninth. The first argument is no longer available.
Successive shift commands make additional arguments available. Note that there is no "unshift"
command to bring back arguments that are no longer available!.
Example
To successively shift the argument that is represented by each positional parameter:
> cat shift_demo
echo "arg1=$1 arg2=$2 arg3=$3"
shift
echo "arg1=$1 arg2=$2 arg3=$3"
shift
echo "arg1=$1 arg2=$2 arg3=$3"
shift
echo "arg1=$1 arg2=$2 arg3=$3"
47 | P a g e
||
(Double pipe)
Examples
The above command will remove mydoc.doc if it exits, otherwise, it will do nothing.
%ls | grep mydoc.doc && rm mydoc.doc
The above command will display the contents of mydoc.doc if it exists otherwise file not
found displayed.
%cat mydoc.doc || echo file not found
In case, more than one command is to be executed or more than one condition need to be
checked simultaneously, then this type of conditional execution is not helpful. In such cases the
if-then-elif-else-fi statement is used.
48 | P a g e
CONDITIONAL STATEMENTS
THE IF STATEMENT
The if statement uses the exit status of the given command and conditionally executes the
statements following.
The general syntax is:
if test
then
commands
else
commands
fi
then, else and fi are shell reserved words and as such are only recognized after a new line or ;
(semicolon). Make sure that you end each if construct with a fi statement.
NESTED IF STATEMENT
if (-----)
then ...
else if ...
...
fi
fi
The elif statement can be used as shorthand for an else if statement.
Example:
if (------)
then ...
elif ...
...
fi
TEST COMMAND
The Unix system provides test command, which investigates the exit status of the previous
command, and translates the result in the form of success or failure, i.e either a 0 or 1.
49 | P a g e
The test command does not produce any output, but its exit status can be passed to the if
statement to check whether the test failed or succeeded.
All commands return the exit status to a pre-defined Shell Variable ?. Which can be displayed
using the echo command. Every Unix command returns a value on exit, which the shell can
interrogate. This value is held in the read-only shell variable $?. A value of 0 (zero) signifies
success; anything other than 0 (zero) signifies failure.
echo $?
If output of this is 0 (Zero) it means the previous command was successful and if output is 1
(One) it means previous command failed.
The test command has specific operators to operate on files, numeric values and strings,
which are explained below:
1. Operators on Numeric Variables used with test command:
-eq
: equal to
-ne
: not equals to
-gt
: grater than
-lt
: less than
-ge
-le
Example:
> a=12; b=23
> test $a eq $b
Gives 1 (one) as output.(Indicates exit status false)
>echo $?
: equality of strings
!=
: not equal
-z
: zero length string (i.e string containing zero character i.e null
string).
-n
50 | P a g e
Examples:
$> name=Ahmad
Will return the exit status 1 as the string name is not null.
>test z $name
Will return 0 as the string is not null.
>test n $name
Will return 0 as the variable has not been defined.
>test z $address
Will return 1 as the value of name is not equal to Ali
>test $name = Ali
-f :
-s :
-d :
directory exists.
-r :
-w :
-x :
Examples:
Will check for the file mydoc.doc , if exists, returns 0 else 1.
>test f mydoc.doc
Will check for read permission for mydoc.doc
>test r mydoc.doc
Will check for the existence of the users home directory.
>test d $HOME
Combining more than one condition is done through the logical AND, OR
and NOT operators.
51 | P a g e
-a
: logical AND
-o
: logical OR
: logical NOT
Example:
Will check both the read and write permission for the file mydoc.doc and returns either 0
or 1, Depending on result.
> test r mydoc.doc a w mydoc.doc
Example
To carry out a conditional action:
if who | grep -s rafiq > /dev/null
then
echo rafiq logged in 357 Lab
else
echo rafiq available in 357 Lab
fi
This lists who is currently logged on to the system and pipes the output through grep to search
for the username rafiq. The -s option causes grep to work silently and any error messages are
directed to the file /dev/null instead of the standard output.
If the command is successful i.e. the username rafiq found in the list of users currently logged in
then the message rafiq logged in 357 Lab is displayed, otherwise the second message is
displayed.
case WORD in
PATTERN1 )
COMMAND(S ) ;;
PATTERN2 )
COMMAND(S ) ;;
--------------------------------------------PATTERNN )
*) default
esac
52 | P a g e
COMMAND(S)
command ;;
;;
A command can be associated with more than one pattern. Patterns can be separated from each
other by a | symbol. For example:
case WORD in
PATTERN1|PATTERN2 ) COMMAND
...
;;
Patterns are checked for a match in the order in which they appear. A command is always
carried out after the first instance of a pattern. The * character can be used to specify a default
pattern as the * character is the shell wildcard character.
Examples:
> cat Lab4_5
# Display a menu of options and depending upon the user's choice,
#Execute associated command
#Display the options to the users
clear
echo "1. Date and time"
echo
echo "2. Directory listing"
echo
echo "3. Users information "
echo
echo "4. Current Directory"
echo
echo "Enter choice (1,2,3 or 4 ) :\n"
read choice
case $choice in
1)
date;;
2)
ls -l;;
3)
who ;;
4)
*)
pwd ;;
echo wrong choice;;
esac
53 | P a g e
54 | P a g e
The commands in COMMAND-LIST1 are executed; and if the exit status of the last command in
that list is 0 (zero), the commands in COMMAND-LIST2 are executed.
The sequence is repeated as long as the exit status of COMMAND-LIST1 is 0 (zero).
This is identical in function to the while command, except that the loop is executed as long as the
exit status of COMMAND-LIST1 is non-zero. The exit status of a while/until command is the exit
status of the last command executed in COMMAND-LIST2. If no such command list is executed, a
while/until has an exit status of 0 (zero).
The continue command causes execution to resume at the while, until or for statement which
begins the loop containing the continue command. You can also specify an argument N|FR to
continue which will cause execution to continue at the N|FRth enclosing loop up.
Example s
To prompt for commands to run
while echo "Please enter command"
55 | P a g e
read response
do
case "$response" in
'done') break
# no more commands
;;
"")
continue
# null command
;;
*)
esac
done
This prompts the user to enter a command. While they enter a command or null string the script
continues to run. To stop the command the user enters done at the prompt.
Examples:
To show use of case statement
>cat Lab5_6
echo What kind of tree bears acorns\ ?
read responce
case $responce in
[Oo][Aa][Kk]) echo $responce is correct ;;
*) echo Sorry, response is wrong
esac
To show use of while statement
>cat Lab5_7
clear
echo What is the Capital of Saudi Arabia \?
read answer
while test $answer != Riyadh
do
echo No, Wrong please try again.
read answer
done
echo This is correct.
56 | P a g e
57 | P a g e
EXCERSICES
1. Run all the programs given above and observe the output of each program one by one.
2. Modify above shell script #File name lab5_2 to check the presence of target file before
copying.
Input and output for this program must be in the following format:
>./lab5_solution2
Please Enter Source filename: m1
Enter target file name: m2
Dear! Target file m2 already present
>./lab5_solution2
Please Enter Source filename: m2
Enter target file name: m21
m2 file copied into m21 file
3. Write a shell script that takes a command line argument and reports on whether it is a
directory, a file, or something else.
Input and output in this program must be in the following format:
>./ lab5_solution3 Math 376_042
I don't Know what is Math 376_042 is
>lab5_solution3 dir
dir is a directory
>lab5_solution3 t1
t1 is a ordinary file
4. Write a shell script to find the number of files in a directory.
Inputs and outputs in this program must be in following format:
>lab5_solution4
Enter the directory path: /a/general/export/homedir/facics/rafiq/Math 376_042
There are 33 numbers of files in the directory
58 | P a g e
A SIMPLE C PROGRAM
Example#1:
Assume we have the following program stored in a file called file.c.
>vi file.c
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char* argv[])
{
printf (Hello World!\n);
exit (0);
}
#include <stdio.h> is a directive to the c preprocessor to include the file stdio.h that will
tell the program how to input and output data.
#include <stdlib.h> is to be able to use the exit function that tells us if the program ran
successfully or not.
The gcc is assumed to be your GNU c compiler. Otherwise you would compile the
program using the standard cc compiler.
The -o option indicates that the compile file should be stored in a file called FileName
instead of the default a.out.
Cont Example#1:
Compile the program in example #1 without using o option
> gcc file.c
Run the program
59 | P a g e
> ./a.out
Or you can compile the program in example #1 using o option
gcc -o file file.c
Run the program
> ./file
C FUNCTIONS
C uses function to package blocks of code. A function has a name, a list of arguments and the
block of code it executes.
Example #4:
The following example shows a function that converts speed from miles/hour to
kilometers/hour.
#include <stdio.h>
float mphtokph(float speed)
{
return(speed*1.60934);
}
int main()
{
float mph, kph;
printf("Enter speed (miles/hour): ");
scanf("%f", &mph);
kph = mphtokph(mph);
61 | P a g e
63 | P a g e
2. control processes,
3. and to provide interprocess communication.
Major System Calls:
1. The UNIX system interface consists of about 80 system calls (as UNIX evolves
this number will increase). The table below lists about 40 of the most important
system call.
2. The file structure related system calls in UNIX let you create, open, and close
files, read and write files, randomly access files, alias and remove files, get
information about files, check the accessibility of files, change protections,
owner, and group of files, and control devices.
64 | P a g e
Input/Output
SYSTEM
CALL
creat( )
SYSTEM CALL
exec( )
open( )
fork( )
close( )
wait( )
read( )
exit( )
write( )
getuid( )
Random Access
lseek( )
getgid( )
Channel Duplication
dup( )
getegid( )
link( )
unlink( )
File Status
stat( )
Access Control
Process Identity
getpid( )
getppid( )
Process Control
signal( )
fstat( )
kill( )
access( )
alarm( )
chmod( )
chdir( )
chown( )
Device Control
umask( )
ioctl( )
SPECIFIC CLASS
SYSTEM CALL
Pipelines
pipe( )
Messages
msgget( )
msgsnd( )
msgrcv( )
msgctl( )
Semaphores
semget( )
semop( )
Shared Memory
shmget( )
shmat( )
shmdt( )
65 | P a g e
System call operations either use a character string that defines the absolute or relative
path name of a file, or a small integer called a file descriptor that identifies the I/O
channel.
A channel is a connection between a process and a file that appears to the process as an
unformatted stream of bytes.
File descriptors 0, 1, and 2 refer to standard input, standard output, and standard error
files respectively.
File descriptor 0 is a channel to your terminal's keyboard and file descriptors 1 and 2
are channels to your terminal's
66 | P a g e
Example#2:
After running the example check whether, the file datafile.dat exist in your current folder
or not.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main( )
{
int fd;
fd = creat("datafile.dat", S_IREAD | S_IWRITE);
if (fd == -1)
printf("Error in creating datafile.dat\n");
else
{
printf("datafile.dat created for read/write access\n");
printf("datafile.dat is currently empty\n");
}
close(fd);
exit (0); /*exit() terminates the calling process , exit(0) for successful &
exit(1) for error, exit() is defined in #include<stdlib.h> */
}
mode defines the file's access permissions if the file is being created. It is only
used with the O_CREAT option_flag and it is concerned with the security
permissions.
Header files needed for this system call in which the actual prototype appears, & in
which useful constants are defined are:
67 | P a g e
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
Description
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
If file doesnt exist , create the file, set the owner ID to the processs
effective UID, and set the group id to the group id of the directory in
which the file is created.
Exclusive open(i. e If O_CREAT is set and the file exists, then open() fails.
Multiple values are combined using the | operator (i.e. bitwise OR). Note: some
combinations are mutually exclusive such as: O_RDONLY | O_WRONLY and will cause open(
) to fail. If the O_CREAT flag is used, then a mode argument is required. The mode argument
may be specified in the same manner as in the creat( ) system call.
Example#3:
This causes the file data, in the current working directory, to be opened as read only for the
use by the program.
fd = open(data, O_RDONLY);
Example#4:
The open call can also be used to create a file from scratch, as follows:
fd = open (/tmp/newfile, O_WRONLY | O_CREAT, 0644);
Example#5:
means, if file lock does not exist, then create it with permission 0644. If it does exist, then
fail open call, returning 1 in fd.
fd = open(lock, O_WRONLY | O_CREAT | O_EXCL, 0644);
Example#6:
O_TRUNC when used with O_CREAT it will force a file to be truncated to zero bytes if it
exists and its access permissions allow.
fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
Example#7:
#include<stdlib.h>
68 | P a g e
#include<fcntl.h>
#define PERMS 0644 /* Permission for open with O_CREAT */
char *filename = "newfile";
main()
{
int fd;
if((fd=open(filename, O_RDWR | O_CREAT, PERMS)) == -1)
{
printf("Couldn't create %s\n",filename);
exit(1);
/*error, so exit */
}
printf(The file is opened and ready for read and write operation)
/* rest of program follows */
exit(0); /*normal successful exit */
}
file_descriptor is a file descriptor which has been obtained from a previous call to either
open or creat,
buffer_pointer points to the area in memory (or is a pointer to an array or structure into
which data will be copied, in many cases it is simply name of array itself ) where the data
is stored for a read( ) or where the data is taken for a write( ), and
transfer_size defines the maximum number of characters transferred between the file
and the buffer (or number of bytes) . There is no limit on transfer_size, but you must
make sure it's safe to copy transfer_size bytes to or from the memory pointed to by
buffer_pointer. A transfer_size of 1 is used to transfer a byte at a time for so-called
69 | P a g e
"unbuffered" input/output. The most efficient value for transfer_size is the size of the
largest physical record the I/O channel is likely to have to handle.
Example#8:
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFSIZE 512
int main()
{ char buffer[BUFSIZE];
int fd;
int nread;
long total = 0;
if(( fd = open("myfile", O_RDONLY)) == -1) /* open "myfile" read only */
{ printf("Error in opening myfile\n");
exit(1);
}
while( (nread = read(fd,buffer, BUFSIZE)) >0) /* loops until EOL, shown by
return value of 0 */
{ total += nread;
/* increment total */
}
printf("Total chars in my file : %ld\n",total);
exit(0);
}
new position
0 (SEEK_SET)
offset bytes into the file (The offset is measured from the beginning of the file;
usual actual integer value=0)
1 (SEEK_CUR)
current position in the file plus offset (The offset is measured from the
current position of the file pointer; usual value =1 )
2 (SEEK_END)
current end-of-file position plus offset (The offset is measured from the end
of the file; usual value =2)
70 | P a g e
The UNIX system file system treats an ordinary file as a sequence of bytes. No internal
structure is imposed on a file by the operating system. Generally, a file is read or written
sequentially -- that is, from beginning to the end of the file. Sometimes sequential reading
and writing is not appropriate. It may be inefficient, for instance, to read an entire file just
to move to the end of the file to add characters. Fortunately, the UNIX system lets you read
and write anywhere in the file. Known as "random access", this capability is made possible
with the lseek( ) system call. During file I/O, the UNIX system uses a long integer, also called
a File Pointer, to keep track of the next byte to read or write. This long integer represents
the number of bytes from the beginning of the file to that next character. Random access
I/O is achieved by changing the value of this file pointer using the lseek( ) system call.
lseek() enables random access into
a file. To use it we need header files
#include<sys/types.h> and #include<unistd.h> .
Example#9:
a program fragment gives a position 16 bytes before the end of the file. From this example
it is clear that offset can be negative i.e it is possible to move backwards from the starting
point indicated by whence.
newpos = lseek(fd, -16, SEEK_END)
Example#10:
a program fragment that will append to the end of an existing file by opening the file,
moving to the end with lseek, and starting to write.
fd = open(filename, O_RDWR);
lseek(fd, 0, SEEK_END);
write(fd, outbuf, OBSIZE);
Example#11:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
{
printf("datafile.dat opened for read/write access\n");
write(fd, message, sizeof(message));
lseek(fd, 0, 0);
EXCERSICES
1. Update the code in example#8 to get the file name from the command line argument.
72 | P a g e
Child inherits open file descriptors from the parent. (Parent and child file
descriptors point to a common entry in the system open file table.) Subsequently
changing attributes of the file descriptors in the parent process won't affect the
file descriptors in the child, and vice versa. However, both processes share the
file position associated with each descriptor.
The child process may execute a different program in its context with a separate exec()
system call.
printf("Hello World!\n");
fork( );
printf("I am after forking\n");
printf("\tI am process %d.\n", getpid( ));
}
Sample output after running the program
>gcc frk1.c
>./a.out
Hello World!
I am after forking
I am process 23848.
I am after forking
I am process 23847.
When a fork is executed, everything in the parent process is copied to the child process.
This includes variable values, code, and file descriptors.
Following the fork, the child and parent processes are completely independent.
The child process begins execution at the statement immediately after the fork, not at
the beginning of the program.
A parent process can be distinguished from the child process by examining the return
value of the fork call. Fork returns a zero to the child process and the process id of the
child process to the parent.
A process can execute as many forks as desired. However, be wary of infinite loops of
forks (there is a maximum number of processes allowed for a single user).
Example#2:
>cat frk2.c
#include <unistd.h>
#include <stdio.h>
int main (void) {
pid_t p; /* fork returns type pid_t */
p = fork();
printf("fork returned %d\n", p);
}
Output after running the program
> gcc frk2.c -o frk2
> ./frk2
74 | P a g e
fork returned 0
pid = fork( );
if (pid == -1) /* check for error in fork */
{
perror("bad fork");
exit(1);
}
if (pid == 0)
printf(" I am the child process.\n");
else
{
}
}
Sample output after running the program
>gcc frk2.c
>./a.out
Hello World!
I am the child process.
I am the parent process.
int i;
if (fork()) {
sleep(2);
76 | P a g e
/* Parent */
_exit(0);
}
for (i=0; i < 5; i++)
{ printf("My parent is %d\n", getppid());
sleep(1);
}
}
>gcc die1.c -o die1
>./die1
My parent is 2920
My parent is 2920
My parent is 1
My parent is 1 My parent is 1
77 | P a g e
die2 runs in the background using the & operator, and then a process listing is displayed,
showing only the running process and its children. PID 2934 is the parent process, and PID
2935 is the one that is forked off and terminated immediately. Despite its untimely exit, the
child process is still in the process table as a defunct process, otherwise known as a zombie.
When the parent dies 60 seconds later, both processes are gone.
When a child process dies, its parent is notified with a signal called SIGCHLD. From the time the
child dies until the time the parent acknowledges the signal, the child sits in a zombie state. The
zombie is not running or consuming CPU cycles; it is merely taking up process table space.
When the parent dies, the kernel is finally able to reap the unacknowledged children along with
the parent. This means that the only way you can get rid of zombie processes is by killing the
parent. The best way to deal with zombies is to make sure they don't happen in the first place.
EXCERSICES
1. Use the template below to write a C-program that creates two processes: Process A (the
process with which the program starts execution) and Process B (As child). The Process
A forks Process B. To identify which process writes the output, each process puts its id at
the beginning of each line (see the sample output). Each process performs the following
operations.
Process A: After spawning Process B, it waits for Process B to terminate. After Process Bs
termination, it will write your home directory (use HOME environment variable) and its
process ID, and terminates.
Process B: After being spawned from Process A, it sleeps for three seconds (using sleep()).
Then, it writes process IDs of itself and Process A immediately. After that, executes ps
command (using system library) and terminates.
Sample output:
./a.out
TIME CMD
Template:
78 | P a g e
#include <stdio.h>
#include <unistd.h> /* contains fork prototype */
main(void)
{
int pid,status;
//printf the first message
//fork B process
if (pid == 0)
//child process
{
// print message
// sleep 3seconds
// print message
// print message
// executes ps command
printf("\n");
}
else
{
// parent waits for child to finish
// print message
system("echo $HOME");
printf("\n");
}
}
79 | P a g e
2. Write a program that creates four processes. The original process creates two
children processes and then prints out "parent". The children processes print
"child1" and child2" respectively. The second child process creates a child
process that prints out "grandchild" see the Figure below. Each process should
also print out its process id and the process id of its parent. Output might look
like:
name
pid
-----------parent
12345
grandchild 12389
child1
12350
child2
12355
parent pid
------------12000
12355
12345
12345
Note that the word might is used, since there is no guarantee of the order of the four
process which will execute the output statement first.
Parent
child1
Child2
Grandchild
3. Use the template below to write a C-program that creates two processes: Process A (the
process with which the program starts execution) and Process B (As child). Process A
forks Process B. The two processes read and write from the same files. To identify which
process writes the output, each process puts its id at the beginning of each line. Study the
output and write your observations.
80 | P a g e
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void) {
int fd_in, fd_out;
char buf[1024];
memset(buf, 0, 1024); /* clear buffer*/
fd_in = //open infile for readonly
/* Open outfile write only if it is not exist creat it with 0644 permission */
fd_out =
/* create new process It doesn't matter about child vs parent */
while (read(fd_in, buf, 2) > 0) { /* Loop through the infile */
printf("%d: %s", getpid(), buf);
/* Write a line */
sprintf(buf, "%d Hello, world!\n\r", getpid());
write(fd_out, buf, strlen(buf));
// sleep 1 second
memset(buf, 0, 1024); /* clear buffer*/
}
sleep(10);
}
> gcc frk.c -o frk
> ./frk
81 | P a g e
pipe
dup / dup2
alarm
kill
signal
PIPES
Pipes are familiar to most UNIX users as a shell facility. For instance, to print a sorted list of who
is logged on, you can enter this command line:
who | sort | lpr
There are three processes here, connected with two pipes. Data flows in one direction only,
from who to sort to lpr. It is also possible to set up bidirectional pipelines (from process A to B,
and from B back to A) and pipelines in a ring (from A to B to C to A) using system calls. The shell,
however, provides no notation for these more elaborate arrangements, so they are unknown to
most Unix users.
We'll begin by showing some simple examples of processes connected by a one-directional
pipeline.
82 | P a g e
After fork
83 | P a g e
This gives two read ends and two write ends. The read end of the pipe will not be closed until
both of the read ends are closed, and the write end will not be closed until both the write ends
are closed. Either process can write into the pipe, and either can read from it. Which process
will get what is not known.
For predictable behavior, one of the processes must close its read end, and the other must close
its write end. Then it will become a simple pipeline again.
Suppose the parent wants to write down a pipeline to a child. The parent closes its read end,
and writes into the other end. The child closes its write end and reads from the other end.
When the processes have ceased communication, the parent closes its write end. This means
that the child gets eof on its next read, and it can close its read end.
Pipes use the buffer cache just as ordinary files do. Therefore, the benefits of writing and
reading pipes in units of a block (usually 512 bytes) are just as great. A single write execution is
atomic, so if 512 bytes are written with a single system call, the corresponding read will return
with 512 bytes (if it requests that many). It will not return with less than the full block.
However, if the writer is not writing complete blocks, but the reader is trying to read complete
blocks, the reader may keep getting partial blocks anyhow. This won't happen if the writer is
faster than the reader, since then the writer will be able to fill the pipe with a complete block
before the reader gets around to reading anything. Still, it's best to buffer writes and reads on
pipes, and this is what the Standard I/O Library does automatically.
#include <stdio.h>
#define SIZE 1024
main( )
{
int pfd[2];
int nread;
int pid;
char buf[SIZE];
84 | P a g e
if (pipe(pfd) == -1)
{
perror("pipe failed");
exit(1);
}
if ((pid = fork()) < 0)
{
perror("fork failed");
exit(2);
}
if (pid == 0)
{ /* child */
close(pfd[1]);
while ((nread = read(pfd[0], buf, SIZE)) != 0)
printf("child read %s\n", buf);
close(pfd[0]);
}
else
{ /* parent */
close(pfd[0]);
strcpy(buf, "hello...");
/* include null terminator in write */
write(pfd[1], buf, strlen(buf)+1);
close(pfd[1]);
}
}
Given that we have two processes, how can we connect them so that one can read from a pipe
what the other writes? We can't. Once the processes are created they can't be connected,
because there's no way for the process that makes the pipe to pass a file descriptor to the other
process. It can pass the file descriptor number, of course, but that number won't be valid in the
other process. But if we make a pipe in one process before creating the other process, it will
inherit the pipe file descriptors, and they will be valid in both processes. Thus, two processes
communicating over a pipe can be parent and child, or two children, or grandparent and
grandchild, and so on, but they must be related, and the pipe must be passed on at birth. In
practice, this may be a severe limitation, because if a process dies there's no way to recreate it
and reconnect it to its pipes -- the survivors must be killed too, and then the whole family has to
be recreated.
85 | P a g e
fd [2], bytesread ;
/* child, writer */
{
close ( fd [READ] ) ;
}
else
/* parent, reader */
{
close ( fd [WRITE] ) ;
86 | P a g e
When a process forks, the child inherits a copy of its parents file descriptors. When process
execs, the standard input, output, and error channels remain unaffected. The UNIX shell uses
these two pieces of information to implement redirection.
To perform redirection, the shell performs the following series of actions:
The parent shell forks and then waits for the child shell to terminate.
The child shell opens the file output, creating it or truncating as necessary.
The child shell then duplicates the file descriptor of output to the standard output file
descriptor, number 1, and then closes the original descriptor of output. All standard
output is therefore redirected to output.
The child shell then execs the ls utility. Since the file descriptors are inherited during an
exec ( ), all of standard output of ls goes to output.
When the child shell terminates, the parent resumes. The parents file descriptors are
unaffected by the childs actions, as each process maintains its own private descriptor
table.
#include <fcntl.h>
#include <stdio.h>
#include <sys/file.h>
main (argc, argv)
int argc ;
char *argv[ ] ;
{
int fd ;
87 | P a g e
dup ( ) finds the smallest free file descriptor entry and points it to the same file as oldfd. dup2 ( )
closes newfd if its currently active and then points it to the same file as oldfd. In both cases, the
original and copied file descriptors share the same file pointer and access mode.
They both return the index of the new file descriptor if successful and 1 otherwise.
dup/dup2 duplicates an existing file descriptor, giving a new file descriptor that is open to the
same file or pipe. The two share the same file pointer, just as an inherited file descriptor shares
the file pointer with the corresponding file descriptor in the parent. The call fails if the argument
is bad (not open) or if 20 file descriptors are already open.
A pipeline works because the two processes know the file descriptor of each end of the pipe.
Each process has a stdin (0), a stdout (1) and a stderr (2). The file descriptors will depend on
which other files have been opened, but could be 3 and 4, say.
Suppose one of the processes replaces itself by an "exec''. The new process will have files for
descriptors 0, 1, 2, 3 and 4 open. How will it know which are the ones belonging to the pipe? It
can't.
Example:
To implement "ls | wc'' the shell will have created a pipe and then forked. The parent will exec
to be replaced by "ls'', and the child will exec to be replaced by "wc''.
The UNIX shells use unnamed pipes to build pipelines. They use a trick similar to the redirection
mechanism to connect the standard output of one process to standard input of another. To
illustrate this approach, heres the program that executes two named programs, connecting the
standard output of the first to the standard input of the second.
#include <stdio.h>
#include <string.h>
#define READ 0
#define WRITE 1
main (argc, argv)
int argc ;
char* argv[] ;
{
int
pid, fd [2] ;
if (pipe(fd) == -1)
{
perror("pipe failed");
exit(1);
}
if ((pid = fork( )) < 0)
{
88 | P a g e
perror("fork failed");
exit(2);
}
if ( pid != 0 )
/* parent, writer */
{
close ( fd [READ] ) ;
dup2 ( fd [WRITE], 1) ;
close ( fd [WRITE] ) ;
}
else
/* child, reader */
{
close ( fd [WRITE] ) ;
dup2 ( fd [READ], 0) ;
close ( fd [READ] ) ;
}
}
Run the above program as
./ a.out who wc
SIGNALS
Programs must sometimes deal with unexpected or unpredictable events, such as:
These kinds of events are sometimes called interrupts, as they must interrupt the regular flow of
a program in order to be processed. When UNIX recognizes that such an event has occurred, it
sends the corresponding process a signal.
The kernel isn't the only one that can send a signal; any process can send any other process a
signal, as long as it has permissions.
A programmer may arrange for a particular signal to be ignored or to be processed by a special
piece of code called a signal handler. In the latter case, the process that receives the signal
suspends its current flow of control, executes the signal handler, and then resumes the original
flow of control when the signal handler finishes.
89 | P a g e
Signals inform processes of the occurrence of asynchronous events. Every type of signal has a
HANDLER which is a function. All signals have default handlers which may be replaced with
user-defined handlers. The default signal handlers for each process usually terminate the
process or ignore the signal, but this is not always the case.
Signals may be sent to a process from another process, from the kernel, or from devices such as
terminals. The ^C, ^Z, ^S and ^Q terminal commands all generate signals which are sent to the
foreground process when pressed.
The kernel handles the delivery of signals to a process. Signals are checked for whenever a
process is being rescheduled, put to sleep, or re-executing in user mode after a system call.
Types of Signals:
There are 31 different signals defined in "/usr/include/signal.h". A programmer may choose
for a particular signal to trigger a user-supplied signal handler, trigger the default kernelsupplied handler, or be ignored. The default handler usually performs one of the following
actions:
Some signals are widely used, while others are extremely obscure and used by only one or two
programs. The following list gives a brief explanation of each signal. The default action upon
receipt of a signal is for the process to terminate.
SIGHUP
Hangup. Sent when a terminal is hung up to every process for which it is the control terminal.
Also sent to each process in a process group when the group leader terminates for any reason.
This simulates hanging up on terminals that can't be physically hung up, such as a personal
computer.
SIGINT
Interrupt. Sent to every process associated with a control terminal when the interrupt key
(Control-C) is hit. This action of the interrupt key may be suppressed or the interrupt key may
be changed using the stty command. Note that suppressing the interrupt key is completely
different from ignoring the signal, although the effect (or lack of it) on the process is the same.
SIGTSTP
Interrupt. Sent to every process associated with a control terminal when the interrupt key
(Control-Z) is hit. This action of the interrupt key may be suppressed or the interrupt key may
be changed using the stty command. Note that suppressing the interrupt key is completely
different from ignoring the signal, although the effect (or lack of it) on the process is the same.
SIGQUIT
90 | P a g e
Quit. Similar to SIGINT, but sent when the quit key (normally Control-\) is hit. Commonly sent in
order to get a core dump.
SIGILL
Illegal instruction. Sent when the hardware detects an illegal instruction. Sometimes a process
using floating point aborts with this signal when it is accidentally linked without the -f option on
the cc command. Since C programs are in general unable to modify their instructions, this
signal rarely indicates a genuine program bug.
SIGTRAP
Trace trap. Sent after every instruction when a process is run with tracing turned on with
ptrace.
SIGIOT
I/O trap instruction. Sent when a hardware fault occurs, the exact nature of which is up to the
implementer and is machine-dependent. In practice, this signal is preempted by the standard
subroutine abort, which a process calls to commit suicide in a way that will produce a core
dump.
SIGEMT
Emulator trap instruction. Sent when an implementation-dependent hardware fault occurs.
Extremely rare.
SIGFPE
Floating-point exception. Sent when the hardware detects a floating-point error, such as a
floating point number with an illegal format. Almost always indicates a program bug.
SIGKILL
Kill. The one and only sure way to kill a process, since this signal is always fatal (can't be ignored
or caught). To be used only in emergencies; SIGTERM is preferred.
SIGBUS
Bus error. Sent when an implementation-dependent hardware fault occurs. Usually means that
the process referenced at an odd address data that should have been word-aligned.
SIGSEGV
Segmentation violation. Sent when an implementation-dependent hardware fault occurs.
Usually means that the process referenced data outside its address space. Trying to use NULL
pointers will usually give you a SIGSEGV.
SIGPIPE
91 | P a g e
Write on a pipe not opened for reading. Sent to a process when it writes on a pipe that has no
reader. Usually this means that the reader was another process that terminated abnormally.
This signal acts to terminate all processes in a pipeline: When a process terminates abnormally,
all processes to its right receive an end-of-file and all processes to its left receive this signal.
Note that the standard shell ( sh) makes each process in a pipeline the parent of the process to
its left. Hence, the writer is not the reader's parent (it's the other way around), and would
otherwise not be notified of the reader's death.
SIGALARM
Alarm clock. Sent when a process's alarm clock goes off. The alarm clock is set with the alarm
system call.
SIGTERM
Software termination. The standard termination signal. It's the default signal sent by the kill
command, and is also used during system shutdown to terminate all active processes. A
program should be coded to either let this signal default or else to clean up quickly (e.g., remove
temporary files) and call exit.
SIGUSR1
User defined signal 1. This signal may be used by application programs for interprocess
communication. This is not recommended however, and consequently this signal is rarely used.
SIGUSR2
User defined signal 2. Similar to SIGUSR1.
SIGPWR
Power-fail restart. Exact meaning is implementation-dependent. One possibility is for it to be
sent when power is about to fail (voltage has passed, say, 200 volts and is falling). The process
has a very brief time to execute. It should normally clean up and exit (as with SIGTERM). If the
process wishes to survive the failure (which might only be a momentary voltage drop), it can
clean up and then sleep for a few seconds. If it wakes up it can assume that the disaster was only
a dream and resume processing. If it doesn't wake up, no further action is necessary.
Programs that need to clean up before terminating should arrange to catch signals SIGHUP,
SIGINT, and SIGTERM. Until the program is solid, SIGQUIT should be left alone so there will be a
way to terminate the program (with a core dump) from the keyboard. Arrangements for the
other signals are made much less often; usually they are left to terminate the process. But a
really polished program will want to catch everything it can, to clean up, possibly log the error,
and print a nice error message. Psychologically, a message like ``Internal error 53: contact
customer support'' is more acceptable than the message ``Bus error -- core dumped'' from the
shell. For some signals, the default action of termination is accompanied by a core dump. These
are SIGQUIT, SIGILL, SIGTRAP, SIGIOT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV, and SIGSYS.
92 | P a g e
The declarations here baffle practically everyone at first sight. All they mean is that the second
argument to signal is a pointer to a function, and that a pointer to a function is returned. The
first argument, sig, is a signal number. The second argument, func, can be one of three things:
signal SIGKILL can't be ignored. Generally, only SIGHUP, SIGINT, and SIGQUIT should
ever be permanently ignored. The receipt of other signals should at least be logged,
since they indicate that something exceptional has occurred.
93 | P a g e
A pointer to a function. This arranges to catch the signal; every signal but SIGKILL may be
caught. The function is called when the signal arrives.
The signals SIGKILL and SIGSTP may not be reprogrammed.
A child process inherits a parents action for a signal. Actions SIG_DFL and SIG_IGN are
preserved across an exec, but caught signals are reset to SIG_DFL. This is essential because the
catching function will be overwritten by new code. Of course, the new program can set its own
signal handlers. Arriving signals are not queued. They are either ignored, they terminate the
process, or they are caught. This is the main reason why signals are inappropriate for
interprocess communication -- a message in the form of a signal might be lost if it arrives when
that type of signal is temporarily ignored. Another problem is that arriving signals are rather
rude. They interrupt whatever is currently going on, which is complicated to deal with properly,
as we'll see shortly. signal returns the previous action for the signal. This is used if it's
necessary to restore it to the way it was.
Defaulting and ignoring signals is easy; the hard part is catching them. To catch a signal you
supply a pointer to a function as the second argument to signal. When the signal arrives two
things happen, in this order:
The signal is reset to its default action, which is usually termination. Exceptions are
SIGILL and SIGTRAP, which are not reset because they are signaled too often.
The designated function is called with a single integer argument equal to the number of
the signal that it caught. When and if the function returns, processing resumes from the
point where it was interrupted.
If the signal arrives while the process is waiting for any event at all, and if the signalcatching function returns, the interrupted system call returns with an error return of
EINTR -- it is not restarted automatically. You must distinguish this return from a
legitimate error. Nothing is wrong -- a signal just happened to arrive while the system
call was in progress.
It's extremely difficult to take interrupted system calls into account when programming. You
either has to program to restart every system call that can wait or else temporarily ignore
signals when executing such a system call. Both approaches are awkward, and the second runs
the additional risk of losing a signal during the interval when it's ignored. We therefore offer
this rule: Never return from a signal-catching function. Either terminate processing entirely or
terminate the current operation by executing a global jump (not described here).
If you make it a habit to always print out the value of errno when a system call fails (by calling
perror for example) you won't be mystified for long since the EINTR error code will clarify
what's going on.
Since the first thing that happens when a caught signal arrives is to change its action to the
default (termination), another signal of the same type arriving immediately after the first can
terminate the process before it has a chance to even begin the catching function. This is rare but
possible, especially on a busy system.
This loophole can be tightened, but not eliminated, by setting the signal to be ignored
immediately upon entering the catching function, before doing anything else. Since we're not
using signals as messages, we don't care if an arriving signal is thereby missed. We're concerned
only with processing the first one correctly and with not terminating prematurely.
94 | P a g e
The following program catches and processes the SIGALRM signal efficiently by having user
written signal handler, alarmHandler ( ), by using signal ( ).
#include <stdio.h>
#include <signal.h>
int alarmFlag = 0 ;
void alarmHandler ( ) ;
main ( )
{
signal(SIGALRM, alarmHandler) ; /*Install signal Handler*/
alarm (5) ;
printf ("Looping ...\n") ;
while (!alarmFlag)
{
pause ( ) ; /* wait for a signal */
}
printf ("Loop ends due to alarm signal\n") ;
}
void alarmHandler ( )
{
printf ("An ALARM clock signal was received\n") ;
alarmFlag = 1 ;
}
The output will be as:
Looping ...
An ALARM clock signal was received
Loop ends due to alarm signal
95 | P a g e
If pid is equal to zero, the signal is sent to every process in the same process group as the
sender. This feature is frequently used with the kill command (kill 0) to kill all background
processes without referring to their process-IDs. Processes in other process groups (such as a
DBMS you happened to have started) won't receive the signal.
96 | P a g e
If pid is equal to -1, the signal is sent to all processes whose real user-ID is equal to the effective
user-ID of the sender. This is a handy way to kill all processes you own, regardless of process
group.
In practice, kill is used 99% of the time for one of these purposes:
To terminate one or more processes, usually with SIGTERM, but sometimes with
SIGQUIT so that a core dump will be obtained.
kill is almost never used simply to inform one or more processes of something (i.e., for
EXCERSICES
Execute the C programs given in the following problems. Observe and interpret the results. You
will learn about child and parent processes, and much more about UNIX processes in general by
performing the suggested experiments. UNIX Calls used in the following problems: signal( ),
alarm( ), pipe( ), sigkey( ), and exit( ).
1. Execute the following program and its suggested modifications. Observe and interpret the
results.
#include <signal.h>
void my_routine ( ) ;
main ( )
{
printf ("Process ID is: %d\n", getpid( ) ) ;
signal (SIGINT, my_routine) ;
for ( ; ; ) ;
}
void my_routine ( )
{
printf ("Have a good day !!!!!!\n") ;
}
Modifications:
97 | P a g e
3. In main ( ), replace the signal ( ) statement by: signal (SIGINT, SIG_IGN) ; Observe the
result. Relate the three parts and explain their results. If you get stuck, you can kill the
processes by pressing CTRL \.
4. The signal sent when CTRL \ is pressed is SIGQUIT and the name of the signal service
routine is sigkey ( ); Go back to the original code. Change the name my_routine to sigkey
and observe what happens when you press CTRL \.
2. Run the following program. Design some experiments. Describe your experiments, state
results and explain them.
#include <signal.h>
void my_routine ( ) ;
main ( )
{
signal (SIGINT, my_routine) ;
signal (SIGQUIT, my_routine) ;
for ( ; ; ) ;
}
void my_routine (signo)
int signo ;
{
printf ("The signal number is %d\n", signo) ;
}
3. Observe and explain the behavior of the following program.
#include <signal.h>
void my_routine ( ) ;
int pid ;
main ( )
{
pid = fork ( );
signal (SIGINT, my_routine) ;
for ( ; ; ) ;
}
void my_routine ( )
{
98 | P a g e
4. Suspending and resuming processes. The SIGSTOP and SIGCONT signals suspend and
resume a process, respectively. They are used by the UNIX shells to implement built-in
commands like stop, fg, and bg. Observe and explain the behavior of the following program.
#include <stdio.h>
#include <signal.h>
main ( )
{
int pid1, pid2 ;
pid1 = fork ( ) ;
if (pid1 == 0)
{
99 | P a g e
/*first child */
/*second child */
{
while (1) /* infinite loop */
{
printf ("pid2 is alive\n") ;
sleep (1) ;
}
}
sleep (3) ;
kill (pid1, SIGSTOP) ;
sleep (3) ;
kill (pid1, SIGCONT) ;
sleep (3) ;
kill (pid1, SIGINT) ;
kill (pid2, SIGINT) ;
}
5. Discover the behavior by writing a main program and a signal handling routine to print a
message in case of an illegal instruction (e.g. division by zero). Including a division by zero in
main program. The signal sent for an illegal instruction is SIGILL.
Shared memory is a feature supported by UNIX System V, including Linux, SunOS and Solaris.
One process must explicitly ask for an area, using a key, to be shared by other processes. This
process will be called the server. All other processes, the clients that know the shared area can
access it. However, there is no protection to a shared memory and any process that knows it can
access it freely. To protect a shared memory from being accessed at the same time by several
processes,
a
synchronization
protocol
must
be
setup.
A shared memory segment is identified by a unique integer, the shared memory ID. The shared
memory itself is described by a structure of type shmid_ds in header file sys/shm.h. To use this
file, files sys/types.h and sys/ipc.h must be included. Therefore, your program should start with
the following lines:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
101 | P a g e
6.
Ask for a shared memory with a memory key and memorize the returned shared memory ID.
This is performed by system call shmget( ).
Attach this shared memory to the server's address space with system call shmat( ).
Initialize the shared memory, if necessary.
Do something and wait for all clients' completion.
Detach the shared memory with system call shmdt( ).
Remove the shared memory with system call shmctl( ).
shm_id = shmget (
key_t k,
int
size,
int
flag
);
/* create/use flag */
In the above definition, k is of type key_t or IPC_PRIVATE. It is the numeric key to be assigned to
the returned shared memory segment. size is the size of the requested shared memory. The
purpose of flag is to specify the way that the shared memory will be used. For our purpose, only
the following two values are important:
1. IPC_CREAT | 0666 for a server (i.e., creating and granting read and write access to the
server)
2. 0666 for any client (i.e., granting read and write access to the client)
Note that due to UNIXs tradition, IPC_CREAT is correct and IPC_CREATE is not!!!
If shmget( ) can successfully get the requested shared memory, its function value is a nonnegative integer, the shared memory ID; otherwise, the function value is negative.
102 | P a g e
The following is a server example of requesting a private shared memory of four integers:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
.....
int shm_id; /* shared memory ID */
.....
shm_id = shmget (IPC_PRIVATE, 4*sizeof(int), IPC_CREAT | 0666);
if (shm_id < 0)
{
printf("shmget error\n");
exit(1);
}
/* now the shared memory ID is stored in shm_id */
If a client wants to use a shared memory created with IPC_PRIVATE, it must be a child process of
the server, created after the parent has obtained the shared memory, so that the private key
value can be passed to the child when it is created. For a client, changing IPC_CREAT | 0666 to
0666 works fine. A warning to novice C programmers: don't change 0666 to 666. The leading 0
of an integer indicates that the integer is an octal number. Thus, 0666 is 10110110 in binary. If
the leading zero is removed, the integer becomes six hundred sixty six with a binary
representation 1111011010.
Server and clients can have a parent/client relationship or run as separate and unrelated
processes. In the former case, if a shared memory is requested and attached prior to forking the
child client process, then the server may want to use IPC_PRIVATE since the child receives an
identical copy of the server's address space which includes the attached shared memory.
However, if the server and clients are separate processes, using IPC_PRIVATE is unwise since
the clients will not be able to request the same shared memory segment with a unique and
unknown key.
Keys:
UNIX requires a key of type key_t defined in file sys/types.h for requesting
resources such as shared memory segments, message queues and semaphores. A key
is simply an integer of type key_t; however, you should not use int or long, since the
length of a key is system dependent.
There are three different ways of using keys, namely:
1. a specific integer value (e.g., 123456)
2. a key generated with function ftok( )
3. a uniquely generated key using IPC_PRIVATE (i.e., a private key).
The first way is the easiest one; however, its use may be very risky since a process
can access your resource as long as it uses the same key value to request that
resource. The following example assigns 1234 to a key:
103 | P a g e
key_t SomeKey;
SomeKey = 1234;
104 | P a g e
After a shared memory ID is returned, the next step is to attach it to the address space of a
process. This is done with system call shmat( ). The use of shmat( ) is as follows:
shm_ptr = shmat (
int
shm_id,
char *ptr,
int
flag
);
/* shared memory ID */
/* a character pointer */
/* access flag */
System call shmat( ) accepts a shared memory ID, shm_id, and attaches the indicated shared
memory to the program's address space. The returned value is a pointer of type (void *) to the
attached shared memory. Thus, casting is usually necessary. If this call is unsuccessful, the
return value is -1. Normally, the second parameter is NULL. If the flag is SHM_RDONLY, this
shared memory is attached as a read-only memory; otherwise, it is readable and writable.
In the following server's program, it asks for and attaches a shared memory of four integers.
/* Lab10_1.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int
shm_id;
key_t mem_key;
int
*shm_ptr;
mem_key = ftok(".", 'a');
shm_id = shmget(mem_key, 4*sizeof(int), IPC_CREAT | 0666);
if (shm_id < 0)
{
printf("*** shmget error (server) ***\n");
exit(1);
}
shm_ptr = (int *) shmat(shm_id, NULL, 0); /* attach */
if ((int) shm_ptr == -1)
{
printf("*** shmat error (server) ***\n");
exit(1);
}
The following is the counterpart of a client.
105 | P a g e
/* Lab10_2.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
int
shm_id;
key_t mem_key;
int *shm_ptr;
mem_key = ftok(".", 'a');
shm_id = shmget(mem_key, 4*sizeof(int), 0666);
if (shm_id < 0)
{
printf("*** shmget error (client) ***\n");
exit(1);
}
shm_ptr = (int *) shmat(shm_id, NULL, 0);
if ((int) shm_ptr == -1)
{ /* attach */
printf("*** shmat error (client) ***\n");
exit(1);
}
Note that the above code assumes the server and client programs are in the current directory. In
order for the client to run correctly, the server must be started first and the client can only be
started after the server has successfully obtained the shared memory.
Suppose process 1 and process 2 have successfully attached the shared memory segment. This
shared memory segment will be part of their address space, although the actual address could
be different (i.e., the starting address of this shared memory segment in the address space of
process 1 may be different from the starting address in the address space of
process 2).
106 | P a g e
The only argument to shmdt( ) is the shared memory address returned by shmat( ). Thus, the
following code detaches the shared memory from a program:
shmdt (shm_ptr);
where shm_ptr is the pointer to the shared memory. This pointer is
returned by shmat( ) when the shared memory is attached. If the detach
operation fails, the returned function value is non-zero.
Two different processes communicating via shared memory we develop two programs here
that illustrate the passing of a simple piece of memory (a string) between the processes if running
simultaneously:
/* shm_server.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#define SHMSIZE 27
main()
{
char c;
int shmid;
key_t key;
char *shm, *s;
/* * We'll name our shared memory segment * "5678". */
key = 5678;
/* * create the segment.* */
107 | P a g e
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#define SHMSIZE 27
main()
{
int shmid;
key_t key;
char *shm, *s;
/*
* We need to get the segment named
* "5678", created by the server.
*/
key = 5678;
/*
* Locate the segment.
*/
if ((shmid = shmget(key, SHMSIZE, 0666)) < 0) {
perror("shmget");
exit(1);
}
/*
* Now we attach the segment to our data space.
*/
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
exit(1);
}
/*
* Now read what the server put in the memory.
*/
for (s = shm; *s != NULL; s++)
putchar(*s);
109 | P a g e
putchar('\n');
/*
* Finally, change the first character of the
* segment to '*', indicating we have read
* the segment.
*/
*shm = '*';
printf ("\nIts done from client.\n\n\n");
exit(0);
}
Attaches itself to the created shared memory portion and prints the string.
Parent and Child processes communicating via shared memory
/*parent_child.c */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(void)
{
int shmid;
char *shmPtr;
int n;
if (fork( ) == 0)
{
sleep(5); /* UUPS */
if( (shmid = shmget(2041, 32, 0)) == -1 )
{
exit(1);
}
shmPtr = shmat(shmid, 0, 0);
if (shmPtr == (char *) -1)
exit(2);
printf ("\nChild Reading ....\n\n");
for (n = 0; n < 26; n++)
putchar(shmPtr[n]);
110 | P a g e
putchar('\n'); }
else
{
if( (shmid = shmget(2041, 32, 0666 | IPC_CREAT)) == -1 )
{
exit(1);
}
shmPtr = shmat(shmid, 0, 0);
if (shmPtr == (char *) -1)
exit(2);
for (n = 0; n < 26; n++)
shmPtr[n] = 'a' + n;
printf ("Parent Writing ....\n\n") ;
for (n = 0; n < 26; n++)
putchar(shmPtr[n]);
putchar('\n');
shmdt(NULL);
wait(NULL);
exit(0);
}
One parent places characters in shared memory, and child reads it.
EXCERSICES
Write a program that creates a shared memory segment and waits until two other separate
processes writes something into that shared memory segment after which it prints what is
written in shared memory. For the communication between the processes to take place assume
that the process 1 writes 1 in first position of shared memory and waits; process 2 writes 2 in
first position of shared memory and goes on to write 'hello' and then process 3 writes 3 in first
position of shared memory and goes on to write 'memory' and finally the process 1 prints what
is in shared memory written by two other processes.
1.
2.
3.
4.
5.
6.
Creating Threads
Terminating Thread Execution
Passing Arguments To Threads
Thread Identifiers
Joining Threads
Detaching / Undetaching Threads
INTRODUCTION
WHAT IS THREAD?
A thread is a semi-process, that has its own stack, and executes a given piece of code. Unlike a
real process, the thread normally shares its memory with other threads (where as for processes
we usually have a different memory area for each one of them). A Thread Group is a set of
threads all executing inside the same process. They all share the same memory, and thus can
access the same global variables, same heap memory, same set of file descriptors, etc. All these
threads execute in parallel (i.e. using time slices, or if the system has several processors, then
really in parallel).
112 | P a g e
WHY PTHREADS?
1. The primary motivation for using Pthreads is to realize potential program performance
gains. When compared to the cost of creating and managing a process, a thread can
be created with much less operating system overhead. Managing threads requires fewer
system resources than managing processes.
2. All threads within a process share the same address space. Inter-thread communication is
more efficient and in many cases, easier to use than inter-process communication.
3. Threaded applications offer potential performance gains and practical advantages over nonthreaded applications in several other ways:
1. Overlapping CPU work with I/O: For example, a program may have sections where it
is performing a long I/O operation. While one thread is waiting for an I/O system
call to complete, CPU intensive work can be performed by other threads.
Priority/real-time scheduling: tasks which are more important can be scheduled to
supersede or interrupt lower priority tasks.
113 | P a g e
Naming conventions: All identifiers in the threads library begin with pthread_
pthread_
pthread_attr
pthread_mutex
Mutexes
Condition variables
pthread_condattr
pthread_key
Function:
(
int pthread_create
Function:
void pthread_exit
(
void *retval /* return value passed as a pointer */
);
This Function is used by a thread to terminate. The return value is passed as a
pointer. This pointer value can be anything so long as it does not exceed the size of
(void *). Be careful, this is system dependent. You may wish to return an address of
a structure, if the returned data is very large.
114 | P a g e
Function:
int pthread_join
(
pthread_t threadhandle, /* Pass threadhandle */
void **returnvalue /* Return value is returned by ref. */
);
Return 0 on success, and negative on failure. The returned value is a pointer
returned by reference. If you do not care about the return value, you can pass NULL
for the second argument.
THREAD INITIALIZATION
Initially, threads are created from within a process. Once created, threads are peers, and may
create other threads. Note that an "initial thread" exists by default and is the thread which runs
main.
115 | P a g e
THREAD ATTRIBUTES
Threads have a number of attributes that may be set at creation time. This is done by filling a
thread attribute object attr of type pthread_attr_t, then passing it as second argument to
pthread_create. Passing NULL is equivalent to passing a thread attribute object with all
attributes set to their default values.
Attribute objects are consulted only when creating a new thread. The same attribute object can
be used for creating several threads. Modifying an attribute object after a call to pthread_create
does not change the attributes of the thread previously created.
116 | P a g e
Store the current setting of attr in obj into the variable pointed to by
value. These functions always return 0.
`detachstate'
Choose whether the thread is created in the joinable state (value PTHREAD_CREATE_JOINABLE)
or in the
detached state (PTHREAD_CREATE_DETACHED). The default is
PTHREAD_CREATE_JOINABLE. In the joinable state, another thread can synchronize on the
thread termination and recover its termination code using pthread_join, but some of the thread
resources are kept allocated after the thread terminates, and reclaimed only when another
thread performs pthread_join on that thread. In the detached state, the thread resources are
immediately freed when it terminates, but pthread_join cannot be used to synchronize on the
thread termination. A thread created in the joinable state can later be put in the detached thread
using pthread_detach.
`schedpolicy'
Select the scheduling policy for the thread: one of SCHED_OTHER (regular, non-realtime
scheduling), SCHED_RR (realtime, round-robin) or SCHED_FIFO (realtime, first-in first-out). The
default is SCHED_OTHER. The realtime scheduling policies SCHED_RR and SCHED_FIFO are
available only to processes with superuser privileges. pthread_attr_setschedparam will fail and
return ENOTSUP if you try to set a realtime policy when you are unprivileged. The scheduling
policy of a thread can be changed after creation with pthread_setschedparam.
`schedparam'
Change the scheduling parameter (the scheduling priority) for the thread. The default is 0. This
attribute is not significant if the scheduling policy is SCHED_OTHER; it only matters for the
realtime policies SCHED_RR and SCHED_FIFO. The scheduling priority of a thread can be
changed after creation with pthread_setschedparam.
THREAD IDENTIFIERS:
pthread_self ( )
Returns the unique thread ID of the calling thread. The returned data
object is opaque cannot be easily inspected.
117 | P a g e
//Lab13_2.c
#include <stdio.h>
#include <pthread.h>
int glob_data = 5 ;
void *kidfunc(void *p)
{
printf ("Kid here. Global data was %d.\n", glob_data) ;
glob_data = 15 ;
printf ("Kid Again. Global data was now %d.\n", glob_data) ;
}
main ( )
{
pthread_t kid ;
pthread_create (&kid, NULL, kidfunc, NULL) ;
printf ("Parent here. Global data = %d\n", glob_data) ;
118 | P a g e
glob_data = 10 ;
pthread_join (kid, NULL) ;
printf ("End of program. Global data = %d\n", glob_data) ;
}
Sample output
Do the threads have separate copies of glob_data?
Parent here. Global data = 5
Kid here. Global data was 10.
Kid Again. Global data was now 15.
End of program. Global data = 15
//File lab13_3.c
/* Multithreaed C Program Using the Pthread API */
#include<ptread.h>
#include<stdio.h>
int sum; /*This data is shared by the thread(s) */
void *runner(void *param); /* the thread */
main(int argc, char *argv[])
{
pthread_t tid; /* the thread identifier */
pthread_attr_t attr;
if(argc != 2)
{
fprintf(stderr,"usage: a.out <integer value>\n");
exit();
}
if(atoi(argv[1]) < 0)
{
fprintf(stderr, "%d must be >= 0 \n", atoi(argv[1]));
exit();
}
/* get the default attributes */
pthread_attr_init(&attr);
/*create the thread */
pthread_create(&tid,&attr,runner,argv[1]);
119 | P a g e
Explanation:
Above Program creates a separate thread that determines the summation of a non-negative
integer. In a thread program, separate thread begins execution in a specified function. In above
program it is the runner function. When this program begins, a single thread of control begins in
main. After some initialization, main creates a second thread that begins control in the summer
function.
All Pthread programs must include the pthread.h header file. The statement pthread_t tid
declares the identifier for the thread we will create. Each thread has a set of attributes including
stack size and scheduling information. The pthread_attr_t attr declaration represents the
attributes for the thread. We will set the attributes in the function
call pthread_attr_init(&attr) . Because we did not explicitly set any attributes, we will use the
default attribute provided.
A separate thread is created with the pthread_create function call. In addition to passing the
thread identifier and the attributes for the thread. We also pass the name of the function where
the new thread will execution, in this case runner function. Lastly, we pass the integer
parameter that was provided on the command line, argv[1].
120 | P a g e
At this point , the program has two threads : the initial thread in main and the thread
performing the summation in the runner function. After creating the second thread, the main
thread will wait for the runner thread to complete by calling the pthread_join function. The
runner thread will complete when it calls the function pthread_exit.
Multiple Threads:
The simple example code below creates 5 threads with the pthread_create( ) routine. Each
thread prints a "Hello World!" message, and then terminates with a call to pthread_exit( ).
//Lab13_4.c
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 5
void *PrintHello(void *threadid)
{
printf("\n%d: Hello World!\n", threadid);
pthread_exit(NULL);
}
int main( )
{
pthread_t threads [NUM_THREADS];
int rc, t;
for(t=0; t < NUM_THREADS; t++) {
printf ("Creating thread %d\n", t);
rc = pthread_create (&threads[t], NULL, PrintHello, (void *) t );
if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
pthread_exit(NULL);
}
Sample output
>./ lab11_4
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
0: Hello World!
1: Hello World!
2: Hello World!
3: Hello World!
121 | P a g e
4: Hello World!
122 | P a g e
}
exit(0);
}
void thread_func(void *dummy) {
int local_thread;
printf("Thread %d, pid %d, addresses: &global: %X, &local: %X\n",
pthread_self(),getpid(),&this_is_global, &local_thread);
this_is_global++;
printf("In Thread %d, incremented this_is_global=%d\n", pthread_self(),
this_is_global);
pthread_exit(0);
}
Sample output
> ./lab11_5
First, we create two threads to see better what context they
share...
Set this_is_global=1000
Thread 4, pid 2524, addresses: &global: 20EC8, &local: EF20BD6C
In Thread 4, incremented this_is_global=1001
Thread 5, pid 2524, addresses: &global: 20EC8, &local: EF109D6C
In Thread 5, incremented this_is_global=1002
After threads, this_is_global=1002
Now that the threads are done, let's call fork..
Before fork(), local_main=17, this_is_global=17
In child, pid 2525: &global: 20EC8, &local: EFFFFD34
Child set local main=13, this_is_global=23
In parent, pid 2524: &global: 20EC8, &local: EFFFFD34
In parent, local_main=17, this_is_global=17
//Lab13_6.c
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
int tot_items = 0 ;
struct kidrec {
int data ;
pthread_t id ;
123 | P a g e
};
#define NKIDS 50
void *kidfunc(void *p)
{
int *ip = (int *)p ;
int tmp, n ;
tmp = tot_items ;
for (n = 50000; n--; )
tot_items = tmp + *ip ;
}
main ( )
{
struct kidrec kids[NKIDS] ;
int m ;
for (m=0; m<NKIDS; ++m)
{
kids[m].data = m+1 ;
pthread_create (&kids[m].id, NULL, kidfunc, &kids[m].data) ;
}
for (m=0; m<NKIDS; ++m)
pthread_join (kids[m].id, NULL) ;
printf ("End of Program. Grand Total = %d\n", tot_items) ;
}
Sample output
Run it several times until you see different output. How many times is the line?
tot_items = tmp + *ip ;
executed? What values does *ip have during these executions?
Passing Arguments to Threads:
The pthread_create( ) routine permits the programmer to pass one argument to the thread start
routine. For cases where multiple arguments must be passed, this limitation is easily overcome
by creating a structure which contains all of the arguments, and then passing a pointer to that
structure in the pthread_create( ) routine.
All arguments must be passed by reference and cast to (void *).
Important: threads initially access their data structures in the parent thread's memory space.
That data structure must not be corrupted/modified until the thread has finished accessing it.
The following example passes a simple integer to each thread.
124 | P a g e
sleep(1);
id_ptr = (int *) threadid;
taskid = *id_ptr;
printf("\n %s from thread %d \n\n", messages[taskid], taskid);
pthread_exit(NULL);
int main( )
{
pthread_t threads[NUM_THREADS];
int *taskids[NUM_THREADS];
int rc, t;
messages[0] = "English: Hello World!";
messages[1] = "French: Bonjour, le monde!";
messages[2] = "Spanish: Hola al mundo";
messages[3] = "Klingon: Nuq neH!";
messages[4] = "German: Guten Tag, Welt!";
messages[5] = "Russian: Zdravstvytye, mir!";
messages[6] = "Japan: Sekai e konnichiwa!";
messages[7] = "Latin: Orbis, te saluto!";
for(t=0;t<NUM_THREADS;t++)
{
taskids[t] = (int *) malloc(sizeof(int));
*taskids[t] = t;
printf("Creating thread %d\n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);
if (rc)
{
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
pthread_exit(NULL);
}
125 | P a g e
Sample output
Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
English: Hello World! from thread 0
French: Bonjour, le monde! from thread 1
Spanish: Hola al mundo from thread 2
Klingon: Nuq neH! from thread 3
German: Guten Tag, Welt! from thread 4
Japan: Sekai e konnichiwa! from thread 6
Russian: Zdravstvytye, mir! from thread 5
EXCERSICES
1. The following Box #1 program demonstrates a simple program where the main thread
creates another thread to print out the numbers from 1 to 20. The main thread waits till the
child thread finishes.
Compile and execute the Box #1 program and show the output and explain why is the
output so?
/* Box #1: Simple Child Thread */
#include <pthread.h>
#include <stdio.h>
void ChildThread(void *argument)
{
int i;
for ( i = 1; i <= 20; ++i )
{ printf(" Child Count - %d\n", i);
pthread_exit(0);
}
int main(void)
126 | P a g e
{
pthread_t hThread;
int ret;
2. In the Box #2 modify the above Box #1 program such that the main program passes the
count as argument to the child thread function and the child thread function prints that
many count print statements.
Compile and Execute the Box #2 program and show the output and explain why is the
output so?
/* Box #2 : Passing Thread Arguments */
#include <pthread.h>
#include <stdio.h>
void ChildThread (int argument)
{
int i;
.......................
pthread_exit(0);
}
int main(void)
{
pthread_t hThread;
pthread_create (.............................);
pthread_join (hThread, NULL);
printf ("Parent is continuing....\n");
}
127 | P a g e
return 0;
3. Write a program Box #3 by removing pthread_exit function from child thread function and
check the output? Is it the same as output of Box #2? If so Why? Explain?
/* Box #3: Implicit Thread Exit */
#include <pthread.h>
#include <stdio.h>
void ChildThread (int argument)
{
int i;
...............................
/* No pthread_exit function */
}
int main(void)
{
pthread_t hThread;
pthread_create (...........................................);
pthread_join (hThread, NULL);
printf ("Parent is continuing....\n");
}
128 | P a g e
return 0;
APPENDIX A
Description
^U
^W
^C
^Z
^S
Stops output and running programs; prevents output from running off end
of screen
^Q
^O
^D
^\
^ =ctrl key
Wildcard Characters
Wildcard characters can be used to represent many other characters. Use them whenever you
need to define a string of characters, such as a filename, for use with a command.
Symbol
Description
?
[ ]
129 | P a g e
REFERENCES
[1] UNIX Tutorial for Beginners, http://www.ee.surrey.ac.uk/Teaching/Unix/
[2] http://www.unixtutorial.org/basic-unix-commands/
[3] Delve into UNIX process creation, http://www.ibm.com/developerworks/aix/library/auunixprocess.html
[4] Operating System lab Manual KUFUPM, K.S.A.
[5] Operating System lab Manual- COMSATS ABBOTTA BAD, PAKISTAN.
130 | P a g e
The loud conversations / discussion that disturbing the other users is prohibited.
Audio CDs or applications with audio output may only be used with headphones with
minimum volume that it should not be disturb other users.
All cell phones are to be turned off or set to silent while in the lab. If you receive a phone
call, you should exit the lab before answering your cell phone.
Any file saved on the computer hard drive will be deleted without notice. Students should
save their work onto an external storage device such as USB drive or CD.
Changing hardware and software configurations in the computer labs is prohibited. This
includes modifications of the settings, modification of system software, unplugging
equipment, etc.
Open labs are reserved for academic use only. Use of lab computers for other purposes, such
as personal email, non-academic printing, instant messaging, playing games, and listening to
music is not permitted.
Please leave the computer bench ready for the next patron. Leave the monitor on the login
screen, and do not forget to take goods related to you. While leaving computer bench please
push the chair inside the computer bench.
Users are responsible for their own personal belongings and equipment. Do not leave
anything in the Computer Lab unattended for any length of time. The Computer Labs staffs
are not responsible for lost or stolen items.
Users are not allowed to clear paper jams in the printer by themselves.
After using white-board the user must clean for other user.
131 | P a g e
APPENDIX B: CERTIFICATION
Instructor name
132 | P a g e
Remarks
Signature
Date