Working With Unix
Working With Unix
Maria Fragouli
Dimitris Leventis
Argyris Petropoulos
Alex Delis
AN INTRODUCTION
TO UNIX
CONTENTS
1. INTRODUCTION TO UNIX .................................................................................... 1
2. UNIX SHELLS.......................................................................................................18
3. C PROGRAMMING ...............................................................................................25
4. UNIX TOOLS ........................................................................................................38
5. DEVELOPMENT TOOLS .......................................................................................51
6. C LIBRARIES ........................................................................................................64
7. INTRODUCTION TO KERNEL ..............................................................................80
8. PROCESSES (I) .....................................................................................................93
9. PROCESSES (II) ..................................................................................................122
10. I/O SUBSYSTEM ...............................................................................................143
11. INTERPROCESS COMMUNICATION ................................................................153
12. PROCESS SCHEDULING ...................................................................................169
13. BUFFER CACHE ...............................................................................................180
14. UNIX ADMINISTRATION .................................................................................187
15. UNIX SECURITY ..............................................................................................200
TABLE OF FIGURES
Figure 1. Data Structures for Processes................................................................................ 85
Figure 2. Process States and Transitions .............................................................................. 85
Figure 3. Sample Code Creating Doubly linked List ............................................................ 85
Figure 4. Incorrect Linked List because of Context Switch .................................................. 86
Figure 5.Multiple Processes Sleeping on a Lock .................................................................. 86
Figure 6. Process State ........................................................................................................ 99
Figure 7. Processes and Regions........................................................................................ 100
Figure 8. Mapping Virtual Addresses ................................................................................ 101
Figure 9. Changing Mode from User ................................................................................. 101
Figure 10. Memory Map of u area in the Kernel ................................................................ 101
Figure 11. Components of Context of a Process................................................................. 102
Figure 12. Sample Interrupt Vector.................................................................................... 102
Figure 13. Handling Interrupts........................................................................................... 102
Figure 14. Example of Interrupts ....................................................................................... 103
Figure 15. Algorithm for System Calls Invocations........................................................... 103
Figure 16. Stack configuration for creat system call........................................................... 103
Figure 17. Steps for a Context Switch................................................................................ 103
Figure 18. Pseudo-Code for Context Switch ...................................................................... 103
Figure 19. Process system calls.......................................................................................... 104
Figure 20. Algorithm for fork ............................................................................................ 104
Figure 21. Fork Creating New Process Context.................................................................. 105
Figure 22. Example of Parent and Child Share File A ........................................................ 105
Figure 23. Use of Pipe, Dup and Fork................................................................................ 106
Figure 24. Checking and Handling Signals ........................................................................ 107
Figure 25. Recognizing Signals ......................................................................................... 107
Figure 26. Algorithm for Handling Signals ........................................................................ 108
Figure 27. Driver Entry Points........................................................................................... 143
Figure 28. Block and Character Device Switch Tables ....................................................... 143
Figure 29. Opening a Device ............................................................................................. 144
Figure 30. Closing a Device .............................................................................................. 144
Figure 31. Reading Disk Data - block & raw interface ....................................................... 145
Figure 32. Data Sequence and Data Flow through Line Dicsipline ..................................... 146
Figure 33. Removing characters from a Clist ..................................................................... 147
Figure 34. Placing characters on a Clist ............................................................................. 147
Figure 35. Writing Data to a Terminal ............................................................................... 148
Figure 36. Flooding Standard Output with Data................................................................. 148
Figure 37. Algorithm for Reading a Terminal ........................................................................ 1
Figure 38. Contending for Terminal Input Data ................................................................. 149
Figure 39. Raw Mode - Reading 5 character Bursts ........................................................... 150
Figure 40. Polling a Terminal ............................................................................................ 150
Figure 41. Loggin in.......................................................................................................... 151
Figure 42. A Stream after Open ......................................................................................... 151
Figure 43. Pushing a Module onto a Stream....................................................................... 152
Figure 44. Windowing VT................................................................................................. 152
Figure 45. Pseudo-code for Multiplexing Windows ........................................................... 152
Figure 46. Process Scheduling ........................................................................................... 170
Figure 47. Range of Process Priorities ............................................................................... 171
Figure 48. Process Scheduling Example ............................................................................ 171
Figure 49. Tie breaker rule ................................................................................................ 173
Figure 50. Fair Share Scheduler......................................................................................... 173
Figure 51. Program Using Timer ....................................................................................... 174
Figure 52. Alarm Call........................................................................................................ 174
Figure 53. Clock Handler .................................................................................................. 175
Figure 54. Invoking Profil system call ............................................................................... 175
Figure 55. Output for Profil Program................................................................................. 175
Figure 56. Algorithm for Allocating Space from Maps ...................................................... 176
Figure 57. Mapping Process Space .................................................................................... 177
Figure 58. Swapping a Process into Memory ..................................................................... 177
Figure 59. Adjusting Memory Map for Expansion Swap.................................................... 177
Figure 60. Algorithm for Swapper ..................................................................................... 178
Figure 61. Thrashing due to Swapping............................................................................... 179
Figure 62. Sequence of Swapping Operations .................................................................... 179
Figure 63. Free List of Buffers .......................................................................................... 180
Figure 64. Buffers on the Hash Queues.............................................................................. 181
Figure 65. Algorithm for Buffer Allocation ....................................................................... 181
Figure 66. First Scenario in Finding a Buffer: Buffer on Hash Queue................................. 182
Figure 67. Second Scenario for Buffer Allocation.............................................................. 183
Figure 68. Third Scenario for Buffer Allocation................................................................ 183
Figure 69. Forth Scenario for Buffer Allocation................................................................. 184
Figure 70. Race for Free Buffer ......................................................................................... 184
Figure 71. Fifth Scenario for Buffer Allocation.................................................................. 184
Figure 72. Race for a Locked Buffer.................................................................................. 185
Figure 73. Reading a Disk Block Bach, "bread"................................................................. 185
Figure 74. Algorithm for Block Read Ahead "breada".................................................... 186
Figure 75. Writing a Disk Block Bach, "bwrite". ........................................................... 186
Introduction to Unix
1. INTRODUCTION TO UNIX
Brief History
The Unix system was developed by Brian Kernighan and Dennis Ritchie at AT&T. It is written in the C language
and it entails many simple powerful concepts such as
- simple homogeneous file system
- devices & files treated the same
- pipes - turn programs into building blocks
- powerful shell command language
- on-line manuals
The system is designed as a program development environment, not a commercial data processing operation system
Earlier Versions
of the system include:
- Version 6 Unix -early 70s -very cheap for Universities
- Version 7 Unix -dominated the research world
- Berkeley Unix 4.1, 4.2, 4.3, 4.4 -fast file system, sockets, symbolic links
- AT&T System V 4.2 -file locking for data bases
- Networking & Remote mounted file systems
! File manipulation
cat <file> output file
more <file> scroll file
mv <from-file> <to-file> rename file
rm <file> remove file
cp <from-file> <to-file> copy file
cp <file1> <file2> <to-dir> copy file to directory
! Printing
lp or lpr print files
lpstat or lpq status of line printer queue
! Compilation
cc C compiler
cc -o name name.c compiles the program in "name.c", creates an executable program file in "name"
lint name.c check C programs scrupulously
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UBUNTU: splint
1
Introduction to Unix
! Execution
mame executes program in file "name"
a.out default from cc if -0 not used
Command Shells
command interpreter accepts commands while logged in
programming languages shell scripts
Example:
man -k write ( = apropos -ucb command)
creat(2) create a new file or rewrite an existing one
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UBUNTU, SOLARIS: touch
File Permissions
Example:
ls -1 myfile
0123456789 L User Group Size Last-Updated File-Name
-rwxrwxrwx 1 fred student 2134 Feb 17 14:05 myfile
where:
L: number of links
0: type of file {"-": ordinary, "d": directory}
123: owner (user) rights
456: group rights
789: world (others) rights
More specifically, the access rights when applied to files or directories are:
Files Directories
147 = "r" read read file names (ls)
258 = "w" write create/delete files (cp, rm)
369 = "x" execute access files in sub-directory (called search permission)
2
Introduction to Unix
Example:
drwxr-xr-- 2 fred student 1024 Mar 21 19:35.
0 = "d" its a directory
123 = "rwx", the owner has full permission
456 = "r-x" the student group members can list this directory and access its files
789 = "r--" everyone else can list the directory but not touch its files
or "--x" access directory files but only if file names are known, can't list directory
! In Summary:
To access an existing file, a user needs:
- search "x" permissions to each directory in the path name
- access "r" permission to the file
Example:
cat ../dir1/dir2/prog.c
- need "x" search permissions in:
..
../dir1
../dir1/dir2
- and "r" read permission
../dir1/dir2/prog.c
Warning:
- don't allow others "w" permissions to your directories, as files can then be deleted
- don't allow "w" permissions to your .profile "rm -r *" in your .profile would zap all your files next time you
logged in chown, chgrp & chmod
By the way...
- chown user file
-changes file ownership
-can donate a file to another user
-restricted to root on most systems
- chgrp group file
-changes the group of a file
-only group members need apply
- chmod mode file
-changes file mode (permission bits)
-can modify existing permissions or explicitly set new permissions
- chmod u+rw myfile
-adds user read & write permissions
- chmod og-x myprog
-removes group & others execute permission
! Mode bits:
File permissions are stored in one number as a set of bits called the mode
Example
chmod 644 myfile sets "rw-r--r--" permissions
chmod 751 myprog sets "rwxr-x--x" permissions
! Unmask command:
The "umask" command is used to set the default creation mask for new files and directories (can be set in .profile).
3
Introduction to Unix
It works in the opposite way to "chmod". The mask specifies which permissions should NOT be given when a file
is created.
Examples:
$ umask 000 set no masked bits
$ date> myfile1 creates "myfile1"
$ ls -1 myfile1 show file permissions
I/O Redirection
Standard Input Standard Output
command
(stdin) (stdout)
TTY process TTY
TTY
(stderr)
Standard Error
Pipes
To redirect the output from "ls -l" straight into the command "wc" we use the pipe connection "|".
ls -l | wc
"|" connects stdout of 1st command to "stdin" of 2nd.
4
Introduction to Unix
stdout ! stdin
(stdin) ls -l wc (stdout)
TTY TTY
Examples:
spell < doc > doc.spell.mistakes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UBUNTU: aspell
Background Processes
Normally when you execute a command the shell will wait until it has completed before prompting for the next
command. Often, users don't want to wait before typing and executing the next command. To do this, the command
is placed in the "background" by placing an ampersand "&" after the command.
Example:
While the spell program is checking spelling on doc1 the user can edit doc2
spell -b < doc1 > docs.spell &
vi doc2
Note ^C won't interrupt background processes. You have to use the "ps" command to find out the process
number, and then "kill" the command.
! Command format
command [options] {file}
/bin/who who
ls -ld
cc -g -o object-file c-files
Note the object-file is an argument for the -o option
Filenames
ls -l junk.c junk.o
ls -l *.c *.o
ls -l *
5
Introduction to Unix
Filename Patterns
One exception is the dot "." character which must be explicitly matched if it is the first character.
echo * all files except those starting with a dot character
echo .* all files starting with a dot
Otherwise the special directory files . and .. and many other normally hidden files would match "*".
Unlike MS-DOS, all unix commands can use patterns like these to generate file names, even your own programs.
The reason is that pattern matching is built into the shell, instead of being duplicated in every program.
Argument Quoting
Often we wish to send a program which may contain some of the special characters like "*", ">", "|" or "&". How
they can we prevent the shell from interpreting these as special?
The answer is to use quoting.
There are several ways to quote characters. The first method quotes just one character and is done by preceding it
by a back-slash "\".
Example: echo Now for the \* of the show...
Outputs: Now for the * of the show...
Now the back-slash is a special character and it can be quoted using another back-slash.
Example: echo Slash me back with a \\
Outputs: Slash me back with a \
Another way to quote a string of characters is to place them inside single or double quotes.
Example: echo 'Please enter a letter: [a-z]?'
Outputs: Please enter a letter: [a-z]?
Note : In what it follows we are working at the Bourne Shell ( give the command sh in the prompt ).
Normally spaces and tabs would be separate each argument and the <return> key would indicate the end of a
command. These special characters can also be quoted so that all characters within a quoted string would be treated
as one argument to the program
Example: echo "Line1 Outputs: Line1
Line2 Line2
Line3" Line3
Note that when typing a multi-line quoted argument, shell will prompt you with a ">" instead of "$" to indicate that
the string is not yet complete.
Environment Variables
Use the command "set" with no arguments to display the values of all shell environment variables.
All environment variables are strings. To change value or create a new variable, use a shell command of the form:
<variable-name>=<string>
Example:
EXINIT="set redraw aw ai wm=0 number"
PS1="Please give me a command? "
Note that there is no space beside the equals sign.
If the string does not contain any characters that need quoting, then the quotes are not needed. i.e. no white space,
or pattern characters
Example:
TERM=vtl00 set terminal type
TMP=/tmp/junk
TMP=" set to null string
TMP=
Each process has its own copy of these environment variables. When a command is executed and a new process is
created by the shell, only the variables marked to be exported are copied into the new process.
Example:
export EXINIT TERM
Most variables set up by the system are already marked as exported. These exported variables are accessible within
C programs by using the library function getenv().
Example:
char *termtype;
termtype = getenv("TERM");
Note however that another program cannot modify the environment variables within the original shell. This is
because they exist in a separate process and only copies of these variables are available within the program.
Shell variables can also be used within the shell. Any shell command may contain variable names preceded by
dollar "$" to substitute it's value.
Example:
echo $HOME displays home directory
If a shell variable contains special characters such as white space or pattern characters then these are interpreted
after the variable is substituted.
Example:
LIB="curses"
CCFILES="yesno.o *.c"
CC=cc -o myprog -l$LIB $CCFILES
$CC assl kit Is this the same as?
cc -o myprog -lcurses yesno.o assl.c kit.c
Command Output
echo $CCFILES yesno.o ass1.c kit.c
echo "$CCFILES" yesno.o *.c
echo '$CCFILES' $CCFILES
Vi modes
7
Introduction to Unix
i, a, o, etc
command insert
<esc> key
! Searching ! Yank
for a string: 3 words y3w, line Y, 5 lines 5Y
forward /, back ? copy a line Yp, make 3 copies Yppp
next same direction n
next reverse direction N ! Mark point (labels a-z)
single character "c" on current line: Mark a point with label a ma
forward fc, backward Fc Return to marked point a 'a
next same direction ; Delete to marked point a d'a
next reverse direction , Yank to marked point a y'a
just before a character:
forward tc, backward Tc ! Buffers (buffers "a - "z)
link line into buffer a "aY
! Insert delete line into buffer a "add
until an <escape> key extract from buffer a "ap
8
Introduction to Unix
! Logging In
The init program automatically starts up a getty for each terminal port on a system. getty determines the baud
rate and displays the login: message. Whet someone types their login name getty starts the login program which
checks the password with the entry in /etc/passwd. If successful the users default shell is activated.
Regular Expressions
used by ed, sed, awk, grep, & vi
Advanced Vi
! Abbreviations
:ab fit Faculty of Information Technology
! Macros -set of macro chars { q, v, K^, ^A, ^ D, ^ E, ^X, ^Y }
:map ^A :!cat $HOME/.vihelp^M
! Search & Replace
:g/man /s//person /gc
:g/\(.*\) -\(.*\)/s//\2 -\1/gc
! Customise options for ".exrc' file
:set all
options abbreviation default
autoindent ai noai
ignorecase (search) ic noic
number nu nonu
redraw noredraw
showmatch ) } sm nosm
wrapscan ws ws
wrapmargin wm wm=0
10
Introduction to Unix
UNIX runs on everything from PCs to super-computers. UNIX is a multi-user, multitasking operating system.
There are millions of UNIX systems around the world supporting many users. Some UNIX is free.
! Criticisms
UNIX is not user friendly, uses cryptic commands, and was designed by programmers for programmers. UNIX
uses concepts which are powerful but unfamiliar to many people who have worked with simpler operating systems.
UNIX has more than 300 commands (DOS < 100).
Basic Concepts
! Accounts
An account must be created by the super user known as "root" before you can log on. Each account has the
following fields in the file /etc/passwd.
- login name
- password
- identification number
- group number
- information field
- home directory
- login shell
! Shells
Bourne Shell uses the dollar sign ($) as a prompt
C-Shell uses the percent sign (%) as a prompt
Korn Shell uses the dollar sign ($) as a prompt
Tcsh uses the sign (>) as a prompt
Files
! Ordinary files
A collection of characters (8-bit bytes) which represent documents, source code, program data, and
executables.
Each files has the following attributes:
- filename,
- inode number,
- size in bytes,
- access permissions,
- the owner and group
! Directory Files
A directory contains the names and inodes numbers of the files within it.
Inodes contain the following information:
- file type,
- links to file,
- location c disc,
- size of file,
- file owner,
- group,
- access permissions,
- and time file was modified
11
Introduction to Unix
! Directory Structure
The inverted tree structured
(root)
directory hierarchy
/
/ -------------------- / ------------------------- \ ------------------------- \
bin etc home/users dev
| | / --------------- \ |
chmod passwd john mark tty0l
/ ---- \ |
mail src text
! User Directory
Within the users "home" directory, a user may have other subdirectories that he/she own and control.
! Filenames
A sequence of 1 to 14 (or 256) characters consisting of letters, digits and other special characters. When a
filename contains an initial period, it is hidden.
The following characters should never be used in filenames because they have special meaning to the shell: ?,
*, [, ], ", ' and -.
! Pathnames
A pathname is a sequence of directory names followed by a simple filename, each separated by a slash "/". If
the pathname begins with a slash it specifies a file that can be found by search from "root", otherwise by search
from user's current directory (found by the command "pwd" -path of working directory). All files and
directories except root have parent directories.
. shorthand name of current directory, e.g. ./filename
.. shorthand name of parent directory, e.g. cd..
! Special Characters
* match zero or more characters, e.g. ls chap*
[] matches any character inside brackets, e.g. ls chap[1-9]*
? matches any single character, e.g. ls chap?l
! Notational Convention
^d hold down control key and press the d key
ESC the escape key
! Some Commands
ls display directory contents
lp file print files
cat file display file contents commands are executable programs
! Command Syntax
cmd [option] [arguments] [filename]
options are always preceded by a dash "-".
e.g. ls -1
grep "string of text" filename
! Command Line
The command line can be edited with ^h (erase/backspace) and ^u (kill). You cc also edit the command line
with the Korn shell using vi commands (activated by ESC key).
12
Introduction to Unix
Multiple commands can be entered on a single line, provided they are separated by a ";". To terminate a
command you can type ^c (interrupt).
Note: C shell syntax is: (spssx < cmdfile > outfile) >& errorfile &
Logging In
! Logging In
login: enter your user name.
password: enter your password.
message of today
(hp) enter your terminal name
$ system prompt (Bourne, Korn)
! Logging Out
% logout from C-shell
$ exit from Korn shell
$ ^d short logout if allowed
Remember to logout!!!
! Changing Password
$ passwd
Changing password for user_name
Old password:
New password: e.g.: no01WAY.
Re-enter new password:
! Terminal Type
$ TERM=vtlOO
$ export TERM
13
Introduction to Unix
! Copying files
$ cp filel file2 copies file1 to file2
! Displaying files
$ cat filename display file on screen
$ more filename waits every screen to continue
$ tail filename displays last 10 lines
$ tail -20 file displays last 20 lines
$ head -30 file display first 30 lines
! Deleting Files
$ rm filename
$ rm -i file confirm before deleting
! Searching Files
grep keyword filenames
e.g. $ grep user_name /etc/passwd
! Word Count
$ wc /etc/motd count of lines, words, character
$ who | wc -l returns number of users
! Printing files
$ lp filename
lp-201 request ID Note: directory must be "publicly executable"
14
Introduction to Unix
! Changing Permissions.
Permissions are shown in the first 10 characters of the long 1isting of files. The first character indicates the type of
file and must be one of the following.
- ordinary file
b block special device -hard disk
c character special device -terminal
d directory
m shared data
n name special
p pipe
s semaphore
The next 9 characters are in three sets of three. Each three indicates permissions for owner (user), group, other
users. Permissions have following meaning.
r readable
w writable
x executable
- permission not granted
! File Protection
$ chmod go-rwx filename
user, group and other (all)
read, write and execute
$ chmod go+rx filename
Directories with r-x allow other users to access it.
! Controlling Processes
$ ps -ef list all processes
$ ps -ef | grep user_name
$ kill -9 process_id kill a process
! Status Information
$ who who is on system
$ date date and time information
$ du disk usage
$ file determine file type
$ stty set terminal options
$ tty get terminal name
# Help
$ help for first time users
$ man manual on how to use command
15
Introduction to Unix
! Communications
$ mail send and receive mail
$ write signal other users
VI Editing
Entering vi
$ vi filename
Command Mode
! Help
^A display help screen (: !cat .vihelp^M)
! Moving cursor
h move left a character b back a word
j move down a character ^F forward a screen
k move up a character ^B backward a screen
l move right w forward a word
! Deleting Text
x delete character at cursor dd delete a line
! Replace Text
r replace a character R enter REPLACE MODE
! Inserting Text
i INPUT MODE before cursor o INPUT MODE line below
a INPUT MODE after cursor O INPUT MODE line above
! Control of Changes
u undo last change U restore current line
. repeat last change
! Moving Text
Y yank line into buffer
p put buffer line below P put buffer line above
! Other commands
J join with next line - toggle case
^G line number information
! Searching
/text^M search for next occurrence of text string
?text^M search for previous occurrence of text string
n repeat last search command
N scan in opposite direction
16
Introduction to Unix
SSH
! SSH is a remote login program
usage: ssh hostname -l login_name or
ssh login_name@hostname
17
Unix Shells
2. UNIX SHELLS
Example:
The -l option of grep lists the names of files that contain a pattern.
grep -l 'bug' *.c: outputs names of any *.c files containing the pattern 'bug'.
Say the output is:
file0.c
file3.c
Now using back-quotes '...' we can take this output from "grep" and treat it as a string substituted into the
arguments for the command "vi".
vi 'grep -l 'bug' *.c': edits all *.c files containing the pattern 'bug'
vi file0.c file3.c
Example:
The "tr" command can translate one or more characters into a different set of characters.
PATH=/usr/local/bin:/usr/ucb:/bin:/usr/bin
The following outputs the string $PATH with colons translated into spaces:
echo $PATH | tr : ' ': outputs /usr/local/bin /usr/ucb /bin /usr/bin
We can use "ls" to display all of the system commands in the $PATH directories:
ls ' echo $PATH | tr : ' ' '
Flow Control Commands (make sure that you are working at the Bourne Shell - sh command)
! FOR Statement
As mentioned earlier shell is a complete programming language and provides a set of conditional and looping
commands. Because shell usually dealing with lists the shell for loop terminates over a list of strings, such as a list
of file names.
for variable in string1 string2 ...stringN
do
commands ...
done
Example:
for file in ex1.c ex2.c yesno.c
do
echo "==== $file ===="
cat $file
done
18
Unix Shells
Before giving another example we will introduce the UNIX command "sed". Sed takes as its arguments a list of
editing commands. These commands are applied to text as it flows from stdin to stdout.
Example:
echo BIGONE | sed 's/BIG/small/': outputs: smallONE
Example:
Suppose that we have a set of files which are named:
example1.c example2.c example3.c
And we wish to rename them using "mv" command to:
ex1.c ex2.c ex3.c
! Conditional Expressions
All UNIX programs return a status code indicating it's success or failure when executed. The value 0 indicates
success, any other value (1-255) indicates failure. Shell programs can test this status in several ways:
! IF Statement
if test-command
then
commands ... # when status is zero
else
commands ... # when status is non-zero
fi
Example:
if cp yourfile myfile
then
vi myfile
fi
This executes the command "cp yourfile myfile". If it is successful (i.e. the copy worked), then it will edit myfile
using the command "vi myfile".
The program "/bin/test" is often used in the if statement, to check if files exist or have correct permissions and to
compare strings and numbers. It is also called "/bin/[" (Unix files can contain almost any character).
Example:
if [ "$TERM" != vt100 ]
then
echo "Funny terminal type: $TERM "
else
echo "Congratulations your emulating $TERM "
fi
! WHILE Statement
while test-command
do
commands...
done
19
Unix Shells
! CASE Statement
case test-string in
pattern-1)
commands
; ;
pattern-2)
commands
; ;
:
esac
TRAP Command
trap commands signal-numbers
Example:
trap '' 15 # prevent script exiting
trap 'echo INTERRUPT; exit l' 2
Functions
Functions are either in ".profile" or in scripts that require them.
function-name()
{
commands
}
Example:
wd()
{
cd $1
PS1=" ['pwd'] "
}
Shell Programs
A shell program is simply an ordinary text file containing shell commands. Shell programs are interpreted and
therefore need no compiler. With one minor exception, there is no difference between what can be typed on the
keyboard and what can be written into a shell program.
The exception is that shell programs should start with a comment line: #!/bin/sh
To ensure that if they are executed under a different shell program such as Korn or C Shell, they will be interpreted
by the Bourne Shell.
20
Unix Shells
$0 Program name
$1 $2 ...$9 Program arguments
$# Number of arguments
$* Same as "$1 $2 ...$9"
$@ Like $* except "$@" will put double quotes around each argument.
$@" --> "$1" "$2"..."$n"
$? Exit status of the last command.
$$ Shell process number. Uniquely identifies the process. Often used for temporary
file names which reside in the /tmp directory which is shared by everyone.
e.g. /tmp/lsout$$
Inputs Outputs
Files Files
Stdin Stdout/Stderr
Program name Exit status
Arguments
Exported environment
variables
! MKSHAR
#!/bin/sh
# @(#)mkshar - creates shell archives from list of files
# shell archives use the "here document" facility to
# store the contents of one or more files.
# shell archives can be executed by shell to create
# the original archived files. They are typically
# used to send files by electronic mail or news.
for file
do
echo "cat > $file << \\!Y@Y@Z@Z@Y"
cat "$file"
echo "!Y@Y@Z@Z@Y"
done
------------------------------------
$ mkshar file1 file2 file3
cat > file1 << !Y@Y@Z@Z@ZY
This is what's inside the 1st file
!Y@Y@Z@Z@ZY
two lines
!Y@Y@Z@Z@ZY
! YESNO
#!/bin/sh
# @(#)yesno - prompts yes/no response
# usage: yesno <prompt> [<default>]
# prompts yes/no response and returns
# corresponding exit status of 1 or 0
# e.g.
# yesno "delete $file" n && rm "$file"
case $# in
1) prompt="$1 [y/n] " ;;
2) prompt="$1 [$2] " ;;
*) echo "usage: `basename $0` prompt [default]" >&2
exit 1 ;;
esac
while :
do
ans=
# portable way to keep cursor on same line
# tr -d '\012' >&2
echo "$prompt" | tr -d '\012' >&2
read ans
[ -z "$ans" ] && ans="$2" # get default from command
case "$ans" in
[Yy]|YES|yes) exit 0 ;;
[Nn]|NO|no) exit 1 ;;
*) echo "Please answer y/n" >&2 ;;
esac
done
! MVSED
#!/bin/sh
# @(#)mvsed - rename files using sed-script
# rename a list of files using a sed(1) command
# mvsed [-e] sed-script file ...
# -e just outputs the "mv" commands
# (no files are renamed)
# e.g.
# mvsed -e 's/w/BIGW/' awk passwd wall
# outputs:
# mv awk aBIGWk
# mv passwd passBIGWd
# mv wall BIGWall
if [ $# = 0 ]
then
echo "`basename $0`: [-e] sed-script file ..." >&2
exit 2
fi
ECHO=
case "$1" in
-e) ECHO=echo; shift;;
esac
sed="$1"; shift
for file
do
22
Unix Shells
case $# in
0) echo "$USAGE" >&2; exit 2 ;;
1) pattern="$1"; shift; set ${VIG-*.[ch]} ;;
*) pattern="$1"; shift;;
esac
! EXAMPLE
#!/bin/sh
# "basename" tool removes any directories in
# file path so use `basename $0` as program name
# set defaults
append=false; encrypt=false; output=outfile
while [ $# != 0 ]
do
case "$1" in
-x) encrypt=true ;;
-a) shift; append=true; output="$1" ;;
-*) echo "$USAGE" >&2; exit 1 ;;
*) break ;; # exit loop to process files
esac
shift # shuffle arguments left (forget $1)
done
for file
do
# process each file
if $crypt
then
crypt "$file"
else
cat "$file"
fi |
23
Unix Shells
if $append
then
tee -a "$output"
else
cat
fi
done
: null command
. execute a program or shell script
'pgm' replace with output of pgm command
break exit from for, while, or until loop
cd change working directory
continue start with next iteration of for, while
eval scan and evaluate the command line
exec execute a program in place of current process
exit exit from current shell
export place the value of a variable in calling environment
newgrp change users group
read read a line from standard input
readonly declare a variable to be readonly
set Set shell variables (display all variables)
shift promote each command line argument
times display times for current shell and its children
trap trap a signal
umask File creation mask
wait wait for a background process to terminate
echo display arguments
getopts parse arguments to a shell script
hash remember location of command in search path
pwd print working directory
return exit from a function
test compare arguments
type display how each arg would be interpreted
ulimit limit the size of files written by shell
unset remove a variable or function
! Shell Variables
24
C Programming – Basics
3. C PROGRAMMING
Syntax Notes
C is a high level language similar to Pascal which provides some low level features.
1. begin and end are replaced by '{' and '}'
2. the assignment operator is '=' not ':='
3. the equality test operator is '==' not '='
4. there is no 'then' keyword
5. there is no 'boolean' type
6. comment delimiters are /* and */
7. '%' is modulo division (i.e. mod)
8. there are no procedures only functions
9. there are no local functions
C Example
The following is an example C program to give an overview of the style of the language.
#include <stdio.h>
main()
/* This program computes the sum of the first n integers
where n is input by the user*/
{ int i,n;
long sum;
/* prompt for n */
printf("Enter value > ");
scanf("%d",&n) ;
Simple Types
There are four primitive types in C :
char a single byte, capable of holding one character from the system's
character set.
int an integer, the size of which is dependent on the host machine.
float single precision floating point number
double double precision floating point number
In addition there are three qualifiers which can be applied to the type int short, long and unsigned.
Short, refer to the number of bits used to represent the number
long
unsigned indicates that only positive integers can be stored in that variable
Declarations
Each bracket pair ('{' and '}') in C define a block and each block may have its own local variables. Scope rules in C
are virtually identical to those of Pascal.
25
C Programming – Basics
Program Form
Storage Classes
As well as having a type, C variables have a class which describes how they are stored in memory. There are four
storage classes:
Automatic
This is the default storage class. Memory is allocated for an automatic variable when the block in which it
is declared is entered and this storage is deallocated when the block is exited. This is equivalent to the
normal Pascal local variable.
Register
This is the same as for automatic except that if possible the compiler will attempt to use a hardware register
for storing the variable making access faster. Most compilers find it too hard to do.
Static
Memory is allocated for a static variable at compile time and is never deallocated. Local static variables
retain their values between function calls. Similar to the SAVE facility in FORTRAN.
External
Equivalent to global variables in Pascal. They are not local to any function including main.
Variables declared outside the scope of any function are global variables. A function may access any global
variable declared above it in the source code without any further declarations.
If however the programmer chooses to declare the global variable again within a function as an external variable,
its global declaration may appear below the function in the source code.
e.g.
static int i;
extern float r;
Simple I/O
C does not have any built-in I/O operations. Instead a library of I/O functions must be provided for the
programmer. To help make C portable there is a standard library of I/O functions which is available with every C
compiler. This library is called 'stdio' .
This library will normally be loaded automatically when a program is loaded however some of the definitions used
by stdio may be needed by a program in order for it to compile properly. All the definitions used by stdio are kept
in a header file called stdio.h. Any program which uses any of the stdio functions should include a copy of this file.
This is achieved by placing the line below in the source code.
e.g.
#include <stdio.h>
The two library functions for writing to standard output and reading from standard input are printf and scanf
respectively.
Scanf takes a string containing only format descriptors and a list of addresses of variables to be read in. The address
of a variable is obtained with the '&' operator
e.g.
printf("Enter a and b : ");
scanf("%d %d", &a, &b);
e.g.
If B represents the number of bits in a word and the bits in a word are numbered in this way
========== ==========
|B-1|B-2|B-3| | 3 | 2 | 1 |
========== ==========
then an expression which returns the n bits starting at position pos from the contents of the variable word would
be (word >> (pos + 1 -n)) & ~(~0 << n)
6. Assignment Operators {+=, -=, *=, /=, %=, <<=, >>=, &=, ^=}
a op = b is equivalent to a = a op b
27
C Programming – Basics
e.g.
i += 2 adds 2 to i
Functions
A function in C is very similar to a function in FORTRAN.
e.g.: a max function
int max(a,b)
/* A function to compute the maximum of two
integers */
int a,b ;
{
if a > b
return(a) ;
else
return(b) ;
}
Notes:
- the type of the function appears first.
- there is no semicolon between the function header and the formal
- parameter declarations.
- results are passed back using the return statement.
- all parameter passing is call by value.
The default type for a function is int and so can be omitted in this case. Even if a function has no parameters its
name must be followed by parentheses,
e.g.
void say_hello()
{
printf ("Hello World\n");
}
Control Structures
! Binary Decision
if (expression)
statement1
else
statement2
Notes:
- the expression must be enclosed in parentheses
- the expression is considered false if it evaluates to 0 and true, otherwise
- as in Pascal, elses associate with the nearest ifs
- as the semicolon is a terminator in C they will occur before elses
! General Loops
while (expression) statement: provides a pretested loop.
C also provides a for statement which is a useful shorthand for an often occurring while statement form.
28
C Programming – Basics
| expr3;
| }
exprl, expr2 and expr3 may be as complicated as you like
do
statement
while (expression)
! Multi-way Decision
switch (expression)
{
case optionl : statement list
case option2 : statement list
...
case optionN : statement list
default: statement list
}
Notes:
- similar to 'case' in Pascal
- execution starts at the statement list which has a label corresponding to the value of the expression and
continues through all remaining statement lists
To stop processing in the middle of a switch statement, a break statement can be used to terminate execution of
the current block.
#include <ctype.h>
double atof(char s[]) /* atof: convert string s to double */
{
double val, power;
int i, sign;
for (i=0; isspace(s[i]); i++); /* skip white space */
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-') i++;
for (val=0.0; isdigit(s[i]); i++)
val = 10.0*val + (s[i] - '0');
if (s[i] == '.') i++;
for (power=1.0; isdigit(s[i]); i++) {
val = 10.0*val + (s[i] - '0');
power *= 10.0;
}
return sign*val/power;
}
The calling routine must know what atof returns thus all functions should be explicitly declared.
#include <stdio.h>
#define MAXLlNE 100
main() /* simple calculator */
{
double sum, atof(char []);
char line[MAXLlNE];
int getline{char line[], int max);
sum = 0;
while (getline{line, MAXLlNE) > 0)
printf{"\t%g\n", sum += atof(line));
return 0;
}
29
C Programming – Basics
External Variables
A C program consists of a set of external objects, which are either variables or functions.
Functions are always external (can't define function inside). External variables are globally accessible.
30
C Programming – Basics
}
return 0;
}
Scope Rules
The scope of a name is the part of the program within which the name can be used.
In filel: /* where you require use of sp and val */
extern int sp;
extern double val[];
In file2: /* initial declaration of sp and val */
int sp = 0;
double val [MAXVAL];
Header Files
calc.h
#define NUMBER '0'
void push(double);
double pop(void);
int getop(char []);
31
C Programming – Basics
main.c
#include <stdio.h>
#include <math.h>
#include .calc.h.
#define MAXOP 10
main () {
...}
stack.c
#include <stdio.h>
#include "calc.h"
#define MAXVAL 100
int sp=0;
double val[MAXVAL];
void push(double)
double pop(void){
...}
getop.c
#include <stdio.h>
#include <ctype.h>
#include "calc.h"
int getop(char s[]){
...}
In the absence of explicit initialisation, external and static variables are guaranteed to be initialised to zero,
automatic and register variables have undefined initial values.
int days [] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char pattern[] = "auld"; /* this is equivalent to */
char pattern[] = {'o', 'u','1','d','\0'};
Recursion
/* qsort: quick sort into ascending order */
void qsort(int v[], int left, int right)
{
int i, last;
void swap(int v[], int i, int j); /* you must implement it */
if (left >= right) /* do nothing if array contains */
return; /* fewer than two elements */
swap(v, left, (left+right)/2); /* move partition elem */
last = left;
for (i=left+1; i<=right; i++) /* partition */
if (v[i] < v[left])
swap(v, ++last, i);
swap(v, left, last); /* restore partition elem */
qsort(v, left, last-1);
qsort(v, last+1, right);
}
Command-line Arguments
Command line parameters can be passed to the program via argv.
argv: | .-| ---> | .-| ---> |echo\0|
| .-| ---> |hello\0|
| .-| ---> |world\0|
| 0 |
32
C Programming – Basics
#include <stdio.h>
main(int argc, char *argv[]) /* echo command line arguments */
{
int i;
for (i=1; i<argv; i++)
printf("%s", argv[i]);
printf("\n"); return 0;
}
#include <stdio.h>
main(int argc, char *argv[]) /* alternative echo program */
{
while (--argc > 0)
printf("%s", *++argv);
printf("\n"); return 0;
}
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getline(char *line, int max); /* you must implement it */
A common convention for C programs on UNIX system is that an argument that begins with a minus sign
introduces an optional flag or parameter.
e.g.
-x (for "except for"), and -n (for "line number")
find -x -n pattern or find -xn pattern
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getline(char *line, int max);
33
C Programming – Basics
Pointers to Functions
It is possible to define pointers to functions, which can be assigned, placed in all's, passed to functions, returned by
functions, and so on.
e.g.
void qsort(void *lineptr[J, int left, int right,
int(*comp)(void *, void *));
/* this declaration says that comp is a pointer to a function */
/* that has two void* arguments and returns an int */
Complicated Declarations
char **argv argv: pointer to pointer to char
int (*month)[13] month: pointer to array[13] of int
int *month[13] month: array[13] of pointer to int
void *comp() comp: function returning pointer to void
void (*comp)() comp: pointer to function returning void
char (*(*x())[])() x: function returning pointer to array[ ] of pointer to function returning char
char (*(*x[3])())[5] x: array[3] of pointer to function returning pointer to array[5] of char
Self-referential Structures
Suppose you wanted to handle the problem of counting the occurrences of all words in some input. One solution is
to keep the set of words seen so far sorted at all times, by placing each word into its proper position in the order it
arrives. This can be done with a binary tree.
The tree contains one "node" per distinct word; each node has:
- a pointer to the text of the word
- a count of the number of occurrences
- a pointer to the left child node
- a pointer to the right child node
34
C Programming – Basics
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXWORD 100
root=NULL;
while (getword(word, MAXWORD) != EOF)
if (isalpha(word[0]))
root=addtree(root, word);
treeprint(root);
return 0;
}
35
C Programming – Basics
Unions
A variable that holds, at different times, objects of different types and sizes, i.e. different data in a single area of
storage.
union u_tag {
int ival;
float fval;
char *sval;
}u;
if (utype == INT)
printf("%d\n", u.ival);
else if (utype == FLOAT)
printf("%f\n", u.fval);
else if (utype == STRING)
printf("%s\n", u.sval);
else
printf(" bad type id in utype\n", utype);
A union is a structure in which all members have an offset zero from the base. Can only initialise value first
member.
Bit fields
When storage space is at a premium, it may be necessary to pack several objects into a single machine word.
The usual way this done us to define a set of "masks" corresponding to the relevant positions, as in
#define KEYWORD 01
#define EXTERNAL 02
#define STATIC 04
or
enum { KEYWORD = 01, EXTERNAL = 02; STATIC = 04 );
As an alternative C offers the capability of directly defining and accessing fields within a word.
struct{ /* 3 one bit fields */
unsigned int is_keyword : 1;
unsigned int is_extern : 1;
unsigned int is_static : 1;
}flags;
Table Lookup
Table lookup code is typically found in the symbol table management routines of a macro processor or a compiler.
36
C Programming – Basics
37
Unix Tools
4. UNIX TOOLS
sed
sed command file
sed 's/Unix/UNIX/g' intro > temp
sed -n '1,2p' intro # print first 2 lines only
sed '1,2d' intro > temp # delete first 2 lines
tr
tr from-char to-chars
date | tr ' ' '\12' # translate spaces to newlines
tr '[a-z]' '[A-Z]' < intro # translate to upper case
tr -s ' ' ' ' < intro # squeeze out multiple spaces
grep
grep pattern files
grep '*' intro
grep -i 'unix’ intro # ignore case
grep -v 'UNIX' * # print lines that don‘t contain
grep -1 'Unix' *.c # list file names that contain
grep -n 'unix' intro # precede matches with line numbers
sort
sort names
sort -u names # climate duplicates
sort -r names # reverse
sort names -o names # sort names > names # won't work
sort -n data # arithmetically
sort +1n data # skip first field
sort +2n -t: passwd # sort by user id
uniq
uniq in_file out_file # remove duplicates
sort names | uniq –d # list duplicates
sort names | uniq –c # count line occurrences
test
test expression
if test "$name" = john
38
Unix Tools
-d file # directory
-f file # ordinary file
-r file # readable
-s file # nonzero file
-w file # writable
-x file # executable
-a # AND
-o # OR
Parameter Substitution
$(parameter) $(parameter:?value)
mv $file $(file)x # reduces conflicts if parameter is set, substitute value,
else write value to standard error and exit
$(parameter:-value) $(PHONEBOOK:?"No PHONEBOOK file!")
if parameter is set, substitute value
$ EDITOR=/bin/ed $(parameter:+value)
$ echo $(EDITOR:-/bin/vi) it parameter is set, substitute value,
/bin/vi else substitute nothing
$(parameter:=value)
if parameter is null, value is assigned to parameter
:$(PHONEBOOK:=$HOME/phonebook)
Misc
! eval
eval command-line # scan the command line twice
$ pipe="|"
$ ls $pipe wc –l # |, wc, -1 are not found
$ eval ls $pipe wc –l # first scan substitutes |
# second scan recognises
! More I/O
command 2> file #redirect standard error
command >& 2 # redirect output to std error
command > log 2>>log # both std output & std error
command > log 2>&1 # same effect
39
Unix Tools
Korn shell
! .profile
HISTSIZE=100
export HISTSIZE
set -o vi
! .scripts
#!/bin/ksh
# @(#)fibonacci – number generator
(( fib = 1 ))
(( oldfib = 0 ))
! job control
$ prog &
[1] 886
$ jobs
[1] + running prog
$ kill %1
[1] + terminated prog
$ prog
^Z
[1] + stopped prog
$ bg
[1] prog &
#!/bin/sh
# @(#)rolo - rolodex: look up, add & remove phone book entries
#
# phonebook entry
# e.g.
# name:address:city:phone:
if [ "$reply" != y ]; then
exit 1
fi
40
Unix Tools
case "$choice" in
1 | l) echo "Enter name to look up: \c"
read name
if [ -z "$name" ]; then
echo "Lookup ignored"
else
rololu "$name"
fi;;
2 | a) roloadd ;;
5 | s) roloshowall;;
6 | q) exit 0;;
#!/bin/sh
# @(#)rololu - look up someone in the phone book
41
Unix Tools
if [ ! -s /tmp/matches$$ ]; then
echo "Can't find $name in the phone book"
else
# display each matching entry
while read line; do
./rolodisplay "$line"
done < /tmp/matches$$
fi
rm /tmp/matches$$
#!/bin/sh
# @(#) rolodisplay - display rolo entry from phone book
# ------------------------------------------
# | Joe's Pizza |
# | George Street |
# | Brisbane |
# | 864-3021 |
# | |
# | |
# | o o |
# ------------------------------------------
echo
echo "------------------------------------------"
entry=$1
IFS=":" # field separater
set $entry
#!/bin/sh
# @(#)roloadd - add someone to the phone book
first=
entry=
while true ; do
echo ">> \c"
read line
if [ -n "$line" ]; then
entry="$entry$line:"
if [ -z "$first" ]; then
first=$line
fi
else
break
fi
done
42
Unix Tools
#!/bin/sh
# @(#)roloremove - remove someone from the phone book
name=$1
if [ ! -s /tmp/matches$$ ]; then
echo "Can't find $name in the phone book"
exit 1
fi
if [ "$reply" = y ]; then
break
fi
done
rm /tmp/matches$$
if [ "$reply" = y ]; then
if grep -v "^$line$" $PHONEBOOK > /tmp/phonebook$$ ; then
mv /tmp/phonebook$$ $PHONEBOOK
echo "selected entry has been removed"
else
echo "entry not removed"
fi
fi
#!/bin/sh
# @(#)roloshowall - show all entries in phone book
IFS=':'
echo "--------------------------------------"
while read line ; do
# get first and last fields, names and phone numbers
set $line
# display first and last fields
eval echo "\" \$$#\r$1\""
done < $PHONEBOOK
echo "--------------------------------------"
AWK – Tutorial
convenient & expressive programming language, Example: employee hourly rate hours worked
2 types of data: numbers & strings John 8.00 0
Mark 8.50 10
Sue 9.00 20
43
Unix Tools
Usage: mawk [-W option] [-F value] [-v var=value] [--] 'program text' [filename]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
! Program structure
pattern ( action )
every input line is tested using 'pattern'
! Running AWK
awk 'program' input files
awk -f progfile input files
! Output
{ print $0 } print entire line
{ print $1, $3 } print first and third fields
{ print NF, $1, $NF }print number of fields and first and last fields
{ print NR, $0 } print number of records and line prefix each line with line number
{ printf("pay for", $1, "is", $2 * $3) } place text in output
{ printf("pay for %-8s is $%6.2f\n", $1, $2 * $3) } formatted output
! Selection
$2 >= 5
$1 == "Sue"
/Sue/
$2 >= 4 || $3 >= 20
!($2 < 4 && $3 < 20)
# data validation
NF != 3 {print $0, "number of fields is not equal to 3"}
$2 < 5 {print $0, "rate is below minimum wage"}
$3 < 0 {print $0, "negative hours worked"}
$3 > 60 {print $0, "too many hours worked"}
! Computing
$3 > 15 { emp = emp + 1 }
END { print emp, "employees worked more than 15 hours" }
{ pay = pay + $2 * $3 }
END { print NR, "employees"}
print "average pay is", pay/NR
}
# string concatenation
{ names = names $1" "}
END { print names }
44
Unix Tools
! Flow Control
# compute total & average pay of employees above $6/hour
$2 > 6 { n = n + 1; pay = pay + $2 * $3 }
END {
if (n > 0)
print n, "employees, total pay is", pay, "average is", pay/n
else
print "no employees are paid more than $6/hour"
}
! Arrays
# reverse – print input in reverse order by line
{ line[NR] = $0 } # remember each input line
END
{ for (i = NR; i > 0; i = i – 1)
print line[i]
}
! "One-Liners"
# Print the total number of input lines NF > 4
END { print NR }
# Print all input lines where last field is greater than 4
# Print the tenth input line $NF > 4
NR == 10
# Print the total number of fields in all input lines
# Print the last field of every input line { nf = nf + NF }
{ print $NF } END {print nf}
# Print the total number of lines that contain Mark
# Print the last field of the last input line /Mark/ { nlines = nlines + 1 }
{ field = $NF } END {print nlines}
END { print field }
# Print the largest field first and line that contains it
# Print every input line with more than four fields $1 > max { max = $1; maxline = $0 }
45
Unix Tools
# Print every line that has at least one field # Print the first two fields in opposite order
NF > 0 {print $2, $1}
# Replace first field by the line number
# Print every line longer than 80 characters {$1 = NR; print}
length($0) > 80
# Erase second field
{$2 = ""; print }
# Print the number of fields in every line and the line
AWK – Programming Language
File processing programming language:
- generates reports
- matches patterns
- validates data
- filters data for transmission
! Program Structure
pattern ( action )
pattern ( action )
…
The pattern or action may be omitted, but not both
! Lexical Units
Awk programs are made up of lexical units called tokens:
- numerical constants – decimal or floating e.g. 12.
- string constants – sequence of characters e.g. "ab"
- keywords
- BEGIN END FILENAME FS NF NR OFS ORS OFMT RS
- break close continue exit exp for getline if in length log next
number print printf split sprintf substr while
- comments
46
Unix Tools
- #this is a comment
! Primary Expressions
Patterns and actions are made up of expressions:
- numeric constant numeric value string value
0 0 0
1 1 1
.5 0.5 .5
5e2 50 50
- string constant numeric value string value
"" 0 empty
"a" 0 a
"xyz" 0 xyz
".5" 0.5 .5
- variables
- identifier
- identifier [expression]
- $term
- functions
- arithmetic functions exp(el) int(e1) log(el) sqrt(el)
- string functions
- getline replace current record with next record, returns 1 if there is a
next record or a 0 if no input record
- index(el, e2) returns the first position where e2 occurs as a substring in el.
- length(el) number of characters in string
- split(el) split expression into fields are stored in array[1],... array[n]
returns number of fields found.
- sprintf(f, el, e2, ...) similar to printf
- substr(e1, e2, e3) returns the suffix of a string
e.g. substr("abc", 2, 1) ="b"
! Terms
Operators are applied to primary expr to produce larger syntactic units called terms:
- primary expression
- binary terms – term binop term
- unary terms – unop term
- incremented vars – ++var --var var++ var--
- parenthesized terms – group terms
! Expressions
Awk expression is one of the following:
- term
- term term ...
- concatenation of terms – e.g. 1+2 3+4 ==>37
- var asgnop expression
- assignment expressions – e.g. a += b
Using AWK
! Input and Output
awk '{ print x }' x=5 – # input from std input
awk '{ print x }' file1 # input from file
awk -f awkprog RS=":" file1 # set Record Separator
awk -F: -f awkprog filel # set Field Separator
47
Unix Tools
Let an example input file "countries", contain fields "country", "area", "population", "continent"
! Patterns
Certain words
BEGIN { FS="\t"
printf "Country\t\tArea\tPopulation\tContinent\n\n"
}
{ printf "%-10s\t%6d\t%6d\t\t%-14s\n", $1,$2,$3,$4 }
END { print "The number of records is", NR }
Regular expressions
/xly/ # contains either x or y
/ax+b/ # 1 or more x's between a and b
/ax?b/ # 0 or more x's between a and b
/a.b/ # any character between a and b
/ax*b/ # 0 or more x’s between a and b
Combinations of above
$2 >= 3000 && $3 >= 100 # AND
$4 == "Asia" || $4 == "Africa" # OR
$4 ~ /"Asia|^"Africa/ # matches
! Pattern Ranges
patternl, pattern2 ( action )
# all lines between pattern1 and pattern 2
e.g.
/Canada/,/Brazil/ {…}
NR == 2, NR == 5 {…}
! Actions
Sequence of action statements separated by newlines
# expressions
{ print $1, (1000000 * $3) / ($2 * 1000)}
# variables
/Asia/ { pop += $3; ++n }
END { print "total population of", n, "Asian countries is", pop }
# initialisation of variables
maxpop < $3 {
maxpop = $3
country = $1
}
END { print country, maxpop }
# field variables
48
Unix Tools
BEGIN ( FS="\t" }
{ $4 = 1000 * $3 / $2; print}
# string concatenation
/A/ { s = s" "$1 }
END { print s }
# arrays
{ x[NR] = $0 }
END { ... program ...}
! Special Features
Built-in Functions
# print length of line
{ print length $0 }
Flow of Control
{ if (maxpop < $3)
{
maxpop = $3
country = $1
}
}
END { print country, maxpop }
{ i = 1
while (i <= NF)
{
print $i
++i
}
}
BEGIN { FS="\t"}
{ population[$4] += $3}
END { for (i in population)
print i, population[i]
}
! Report Generation
Smith draw 3 # input
Brown eqn 1
Jones spell 5
Smith draw 6
49
Unix Tools
{ if ($1 != prev)
{
print $1 ":"
prev = $1
}
print" "$2" "$3
}
Brown: # output
eqn 1
Jones:
spell 5
Smith:
draw 9
! Multidimensional Arrays
for (i = 1; i <= 10; i++)
for (j = 1; j <= 10, j++)
multi[i "," j] = …
50
Development Tools
5. DEVELOPMENT TOOLS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UBUNTU: cb is not provided
Indent is not installed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ctags generates "tags" file used by vi editor for quickly finding function definitions
cc c compiler options
cpp c preprocessor – called by cc
ld link loader – called by cc
size bytes for text, data and bss sections
strip remove symbol & line information from common object file
ar archival libraries e.g. /usr/lib/libxxx.a
diff prints lines that differ in two files
sccs toolkit used for managing revisions of programs and group projects
adb/sdb assembler and symbolic debuggers
dbx source code debugger
tar write file tree to tape/disk
compress compact to save space
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UBUNTU: compress provided as “ncompress” but is not installed
sccs is not provided
adb/sdb is not provided
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MAKE
When a program is written as multiple .c and .h files there can be many steps to recompiling and eventually linking
the entire system. Manually keeping track of which files that need to be recompiled can be a difficult and unreliable
process. To automate this Unix provides the make program.
When invoked, make searches for a text file called "Makefile" or "makefile" which defines the rules for rebuilding
any part or sub-part of a system. In the most common case we wish to rebuild (compile) an object file
corresponding to a .c file.
Programs often consist of many source files, each of which may need to pass through preprocessors, assemblers,
compilers, and other utilities before being combined. Forgetting to recompile a module that has been changed – or
that depends on something you've changed – can lead to frustrating bugs.
51
Development Tools
Make looks at the date stamps on your files, then does what is necessary to create an up-to-date version.
Makefile format:
target: prerequisite-list
<TAB> construction-commands
Increases of the modularity of programs means that a project may have to cope with a large number of files:
- file-to-file dependencies
- make creates the finished program by recompiling only those portions directly of indirectly affected by the
change
- find the target in the description file
- ensure that all files on which the target depends, exists and are up to date
- create target file if any of the generators have been modified more recently than the target
Commands in the rule will be performed whenever "main.o" is needed and "main.c" or "win.h" have been updated
since "main.o" was last rebuilt.
/* main.c ---------------------------*/
#include "win.h"
...
/* win.c ----------------------------*/
#include "win.h"
...
/* kit.c ----------------------------*/
#include "kit.h" #include "win.h"
...
# makefile --------------------------
prog: main.o win.a kit.a
cc -o prog main.a win.a kit.a #link
$ make kit.o # recompiles kit.a if any of kit.c, kit.h or win.h has been changed
$ make prog # rebuilds (compiles) any of object files required to build "prog " and then link these files
$ make # same as "make prog" (1st rule)
$ make -n prog # show commands only (not performed)
$ make -ffile.mk # use file.mk instead of Makefile
kit.c kit.o
kit.h
# Makefile ----------------------------------------
BASE = /staff/neville
CC = cc
52
Development Tools
CFLAGS = -Aa -O
INCLUDE = -I$(BASE)/include
LIBS = $(BASE)/lib/glib.a \
$(BASE)/lib/ulib.a
PROG = $(BASE)/bin/compsort
OBJS = main.a compare.o quicksort.o \ rankorder.o
$(PROG): $(OBJS)
@echo "linking ..."
@$(CC) $(CFLAGS) -o $(PROG) $(OBJS) $(LIBS)
@echo "Done"
$(OBJS): compare.h
$(CC) $(CFLAGS) $(INCLUDE) -c $*.c
# --------------------------------------------------
TOUCH
Allows you to change dates on individual files
/* preprocessor directives */
# include <stdio.h> redefine TABSIZE 8
/* prototypes */
int findstop(int col);
main()
{
int c; /* char read from stdin */
int posn=0; /* column position of char */
int inc; /* column increment to tab stop */
53
Development Tools
LINT
A C program checker/verifier. Attempts to detect features that are likely to be:
- execution errors – detects bugs
- non-portable
- wasteful of resources – obscurities
usage:
lint [options] files libraries
options:
-a suppress messages about assignments of long values to variables that are not long
-b suppress messages about break statements that can not be reached
consult "man lint"
INDENT
Indent and format C program source. It reformats the C program in the input-file according to switches:
indent [ input-file [ output-file ] ) [ switches ]
If you only specify an input-file, formatting is written back into the input file (a backup is made in file.BAK)
There are options to place blank lines before or after various blocks of code: -bap -bad,
-bbb, -bc
You may turn off the -bc option with -nbc.
CTAGS
Create a tags file for "vi". Each line of the tags file contains the object name, file in which it is defined, and an
address specification for the object definition
-x causes ctags to print a simple function index: function name, filename, line number, text of line to standard
output (no tags file is created)
C Compiler
Example:
$ cc -c main.c filel.c file2.c
54
Development Tools
cc [options] file.c
-C compile only (suppress link editor)
-g generate code for debugger
-O optimize for speed
-p produce code for profiler
-o name put executable code in name
-M make a makefile
-S generate assembler code .s file
- ld – link editor
Takes one or object files or libraries as input and combines them to produce executable file. It resolves references
to external symbols, and performs "relocation" of addresses.
- cpp -c preprocessor
-Dname=def define name for preprocessor
-Idir include file directory
-E invoke preprocessor only
Example:
# Example makefile
LIB=~/lib
CFLAGS=-I~/include -DDEBUG
.c.o:
cc -c $(CFLAGS) $<
C Preprocessor
cpp - actions before c compiler
used to include:
#defines
externs
typedefs
struct definitions
nested #includes
#if const_expr
#ifdef ident
#ifndef ident
#else
55
Development Tools
#endif
#line constant ident
Profiler
Produces a report on the amount of execution time spent in various portions of the ram times each function called.
#include <stdio. h>
#define N 5000
main()
{
int a[N], i;
void quicksort(int *, int *);
srand(time(NULL));
for (i=0; i<N; ++i)
ati1 = rand() % 1000;
quicksort(a, a+N-1);
for (i=0; i<N-1; ++i)
if (a[i] > a[i+1])
{
printf("SORTING ERROR – bye!\n");
exit(1);
}
}
AR – archive
Used to create libraries of object files
ar key [posname] arfile.a [object_files]...
e.g.
ar rv rst.a *.a # replace, verbose
ar t /lib/libc.a # print table of contents
NM
nm -f rst.a # name list
name value class type size line section
Application Programming
Need for interaction G sharing of information. Developed by a team of programmer. Lifespan of application –
average of 5 years. Different programmer – average every 2 years.
Functions
- operation of each
- number S name of arguments
- arguments are input/output
56
Development Tools
Only the changes are recorded. Each version is identified by its SID (SCCS indent number).
SCCS commands:
admin initialise SCCS files – access
get retrieves versions of SCCS files
delta applies changes to SCCS files
prs prints portions of SCCS files
rmdel remove a delta from SCCS
cdc change comment with delta
what search files for special pattern
sccsdiff show differences between SCCS files
comb combine consecutive deltas into one
val validate an SCCS file
Terminology
A delta is a set of changes made to a file under SCCS custody. To identify and keep track of a delta, it is
assigned as SID (SCCS ID).
57
Development Tools
1.1
5 lines
Creates "lang" for both reading and writing, also creates another file p.lang" needed by "delta". Add two
more languages to the file "lang“.
SNOBOL
ADA
$ help col
col:
"not an SCCS file”
A file that you think is an SCCS file does not begin with the character "s"
Delta Numbering
Think of deltas as nodes of a tree in which the root node is the original version of the file. The root is
named 1.1 and delta nodes are named 1.2, 1.3, etc. release.level.branch.sequence
Debugging
It usually faster and more efficient to place a few well placed print statements within your code and recompile it,
than resort to using adb/sdb. However newer debugging tools are mouse driven and extremely ease to use.
58
Development Tools
Example commands:
r args run program with arguments
S step – execute one line of program
S Step one line
t display runtime stack
q quit
GDB – the GNU debugger
- What statement or expression did the program crash on?
- If an error occurs while executing a function, what line of the program contains the call to that
function, and what are the parameters?
- What are the values of program variables at a particular point during execution of the program?
- What is the result of a particular expression in a program?
$ gdb prog
main:25: x[i] = 0;
GDB commands
When gdb starts, your program is not actually running. It won't run until you tell gdb how to run it.
Whenever the prompt appears, you have all the commands on the quick reference sheet available to you.
• run command-line-arguments
Starts your program as if you had typed
a.out command-line arguments
or you can do the following
a.out < somefile
to pipe a file as standard input to your program
• break place
Creates a breakpoint; the program will halt when it gets there. The most common
breakpoints are at the beginnings of functions, as in
The command break main stops at the beginning of execution. You can also set breakpoints at a
particular line in a source file:
(gdb) break 20
Breakpoint 2 at 0x2290: file main.c, line 20
When you run your program and it hits a breakpoint, you'll get a message and prompt like this.
Breakpoint 1, Traverse(head=0x6110, NumNodes=4)
at main.c:16
(gdb)
59
Development Tools
In Emacs, you may also use C-c C-b to set a breakpoint at the current point in the program ( the
line you have stepped to, for example) or you can move to the line at which you wish to set a
breakpoint, and type C-x SPC (Control-X followed by a space).
• delete N
Removes breakpoint number N. Leave off N to remove all breakpoints. info break gives info
about each breakpoint
• help command
Provides a brief description of a GDB command or topic. Plain help lists the possible topics
• step
Executes the current line of the program and stops on the next statement to be executed
• next
Like step, however, if the current line of the program contains a function call, it executes the
function and stops at the next line.
• finish
Keeps doing nexts, without stepping, until reaching the end of the current function
• Continue
Continues regular execution of the program until a breakpoint is hit or the program stops
• file filename
Reloads the debugging info. You need to do this if you are debugging under emacs, and you
recompile in a different executable. You MUST tell gdb to load the new file, or else you will keep
trying to debug the old program, and this will drive you crazy
• where
Produces a backtrace - the chain of function calls that brought the program to its current place.
The command backtrace is equivalent
• print E
prints the value of E in the current frame in the program, where E is a C expression (usually just a
variable). display is similar, except every time you execute a next or step, it will print out the
expression based on the new variable values
• quit
Leave GDB. If you are running gdb under emacs,
C-x 0
will get you just your code back
The goal of gdb is to give you enough info to pinpoint where your program crashes, and find the bad
pointer that is the cause of the problem. Although the actual error probably occurred much earlier in the
program, figuring out which variable is causing trouble is a big step in the right direction. Before you
seek help from a TA or preceptor, you should try to figure out whereyour error is occurring.
60
Development Tools
TAR
tar cf files.tar dirname # create tar of files
tar tvf files.tar # full view of files in tar
compress files.tar # compress (tree archive)
uuencode files.tar.Z files.tar.Z > files.tar.Z.uu
elm -s "files.tar.Z.uu" neville < files.tar.Z.uu
Other compression tools such as "freeze“, "zoo", "zip", "jpeg" are freely available for UNIX systems.
Program Development
yacc a parser generator – generates a parser from a grammatical description of a language
make controlling the processes by which a complicated program is compiled
lex making lexical analysers
- Input to yacc:
%{
C statements like #include, declarations
%}
yacc declarations: lexical tokens, grammar variables, precedence and
associativity information
%%
grammar rules and actions
%%
more C statements
main() { ...; yyparse(); …}
yylex() { … }
/*
$ yacc hocl.y
$ cc y.tab.c -o hocl
61
Development Tools
*/
%{
#define YYSTYPE double /* data type for yacc stack */
#include <stdio.h>
#include <ctype.h>
char *progname;
int lineno=1;
%}
%token NUMBER
%left '+' '-' /* left associative, same precedence */
%left '*' '/' /* left assoc., higher precedence */
%%
list: /* nothing */
| list '\n'
| list expr '\n' { printf("\t%.8g\n", $2);}
;
expr:
NUMBER
| expr '+' expr {$$ =$1 + $3;}
| expr '-' expr {$$ =$1 - $3;}
| expr '*' expr {$$ =$1 * $3;}
| expr '/' expr {$$ =$1 / $3;}
| '(' expr ')' {$$ =$2;}
;
%% /*end of grammar */
yylex()
{
int c;
while ((c=getchar() == ' ' || c == '\t');
if (c == EOF)
return 0;
if (c == '.' || isdigit(c))
{
ungetc(c, stdin);
scanf("%1f", &&yylval);
return NUMBER;
}
if (c == '\n')
lineno++;
return c;
}
yyerror(char *s)
{
warning(s, (char *) 0);
}
62
Development Tools
Example:
/*
$ lex scan.1 ==> lex.yy.c
*/
D [0-9]
E [DEde][-+]?{D}+
%%
{D}+ printf("integer");
(D)+"."{D}*({E})? | /* at least one digit before . */
(D)*"."{D}+({E})? | /* at least one digit after . */
{D}+{E} printf("real");
%%
63
C Libraries
6. C LIBRARIES
To explicitly direct the system to flush the buffer at any time use the function 'ff lush"
void setbuf(FILE *stream, char *buf)
getc() fgetc(*fp++)
getchar()
putc() fputc(c,*fp++)
putchar()
ferror() NULL is zero
clearerr() EOF is -1
feof()
/* stream_stat.c
if neither flag is set, stat will equal zero
if error is set, but not eof, stat equals 1
if eof is set, but not error, stat equals 2
if both flags are set, stat equals 3
*/
#include <stdio.h>
#define EOF_FLAG 1
#define ERR_FLAG 2
File I/O
FILE *fopen(char *filename, char *type)
type | Description
"r" open existing text file for reading at beginning
"w" create a new text file for writing
"a" open an existing text file in append mode
64
C Libraries
"r+" open an existing text file for reading & writing at beginning
"w+" open a new text file for reading & writing at beginning
"a+" open an existing text file in append mode & allow reading
"b" binary file
int fread(void *buffer, unsigned element size, unsigned count, FILE *stream)
/* read a block of binary data from a stream */
int fwrite(void *buffer, unsigned element size, unsigned count, FILE *stream)
void rewind(FILE *stream)
/* open test.c */
#include <stdio.h>
FILE *open_test(void)
FILE *fp;
fp = fopen("test","r");
if (fp == NULL)
fprintf(stderr, "Error opening file test\n");
return fp;
/* test_copy.c */
#include <stdio.h>
#define FAIL 0
#define SUCCESS 1
while (!feof(fp1))
putc(getc(fp1),fp2);
fclose(fp1);
fclose(fp2);
return SUCCESS;
}
Unbuffered I/O
standard device | file descriptor
stdin 0
stdout 1
stderr 2
flags
O_RDONLY open read only
O_WRONLY open write only
O_RDWR open read & write
perms
0666 all read & write
String Operations
#include <string.h>
/* s and t are char * and c and n are int */
66
C Libraries
Storage Management
#include <stdlib.h>
The functions malloc and calloc obtain blocks of memory dynamically.
void *malloc(size t n); /* pointer to n bytes */
void *calloc(size t n, size t size); /* n objects */
int *ip;
ip = (int *) calloc (n, sizeof(int));
67
C Libraries
Mathematical Functions
#include <math.h>
/* functions returns a double and has double arguments */
68
C Libraries
Random Access
long lseek(int fd, long offset, int origin);
To append to file (>> in UNIX, "a" for fopen), seek end of file before writing: lseek(fd, 0L, 2);
To beginning ("rewind"): lseek(fd, 0L, 0);
#define NULL 0
#define EOF (-1)
#define BUFSIZ 1024
#define OPEN_MAX 20
enum _flags {
_READ = 01, /* file open for reading */
_WRITE= 02, /* file open for writing */
_UNBUF= 04, /* file is unbuffered */
_EOF = 010, /* EOF has occurred on this file */
_ERR= 020 /* error occurred on this file */
};
The getc macro decrements the count, advances the pointer, and returns the character If the count goes negative, getc
calls the function _fillbuf to replenish the buffer and return a character.
#include “syscalls.h”
/* fillbuf.c: – allocate and fill input buffer */
int _fillbuf(FILE *fp)
{
int bufsize;
69
C Libraries
Reading Directories
A directory is a list of filenames and an indication of where they are located. The inode for a file is where all
information about a file except its name is kept.
To show a list of files lets write a fsize program (1s), using three routines "opendir", "readdir" and "closedir" to
provide system independent access to the name and inode number in a directory entry.
/* dirent.h */
#define NAME_MAX 14 /* longest filename component */
typedef struct{ /* portable directory entry */
long ino; /* inode number */
char name[NAME_MAX+1); /* name + '\0' terminator */
}Dirent;
The system call "stat" takes a filename and returns all of the information in the inode for that file, or -1 for an error.
char *name;
struct stat stbuf;
int stat(char *, struct stat *);
stat(name, &stbuf);
Fills the structure "stbuf" with the inode information for the file name. This structure is described in
<sys/stat.h>.
70
C Libraries
The types "dev_t" and "ino_t" are defined in <sys/types.h>. The "st_mode" is a set of flags defined in <sys/stat.h>.
#include <stdio.h>
#include <strings.h>
#include <fcntl.h> /* flags for read and write */
#include <sys/types.h>
#include <sys/stat.h>
#include "dirent.h"
#include "syscalls.h" /* prototypes of syscalls */
The function "fsize" prints the size of the file. If the file is a directory, fsize calls dirwalk to handle all the files.
71
C Libraries
#ifndef DIRSIZ
#define DIRSIZ 14
#endif
struct direct /* directory entry */
{
ino_t d_ino; /* inode number */
char d_name[DIRSIZ]; /* long name does not have ’\0‘ */
};
72
C Libraries
function calls
UNIX kernelreturn values
Pipes
FILE *p; /* pipe stream */
char line[MAXLINE]; /* lines read */
Parallel execution
fork duplicates a process and sets both executing in parallel, where as exec allows process to hand over control to
another program. By combining fork and exec, one program may start a second program and continue executing
itself. The original is then called the parent process, the copy is called the child process and both exe in parallel.
73
C Libraries
switch (pid=fork()){
case 0: /* child exec's shell */
execl("/bin/sh","sh","-c",command,0);
/* fall through if exec fails */
case -1: /* could not fork, print error message*/
perror(myname);
exit(1);
default: /* parent waits for child to finish */
while ((wval=wait(&status)) != pid)
if (wval == -1) return -1;
}
return status;
}
Buffer control
#include <stdio.h>
char outbuf[BUFSIZ];
main()
{
int c; /* for no buffering */
The C Preprocessor
#include for including files of text into a program
#include "filename" from current directory
or
#include <filename> from directory "/usr/include"
#define DEBUG
#ifdef DEBUG
printf("MyProg Version 1.0 (debug)\n"),
#else
printf("Myprog Version 1.0 (production)\n");
#endif
#undef __TURBOC__
#ifndef __TURBOC__
system("grep name * > names");
#endif
74
C Libraries
Storage Allocator
Free list (points to a circular list of free blocks f)
-------------------------------------------------
n|x|f|f|x|x|f|n|n|x|f|f|f|f|n
-------------------------------------------------
x blocks (in use);
n blocks (not owned by malloc)
nunits = (nbyte+sizeof(Header)-l)/sizeof(Header) + 1;
if ((prevp = freep) == NULL) /* no free list yet */
{
base.s.ptr = freep = prevp = &base;
base.s.size = 0;
}
75
C Libraries
What is curses?
- library of routines for screen management
- located in "/usr/lib/libcurses.a"
- link editor "cc file.c -lcurses -o file"
/* example program: */
#include <curses.h>
main()
{
initscr(); /* initialise terminal settings */
move(LINES/2 - 1, COLS/2 - 4);
addstr("Bulls");
refresh(); /* send output to terminal screen */
addstr("Eye");
refresh();
endwin(); /* restore all terminal settings */
}
What is terminfo ?
- routines within curses library, e. g. to program function keys
- database of terminal capabilities
Screen management programs using curses obtain info on terminals at run time from terminfo database.
TERM;vt100
export TERM
tput init
/usr/lib/termintolv/vtl00
Components:
captoinfo(lM) tool to convert termcap to terminfo
curses(3x)
infocmp(1m) tool for printing compiled terminal info
tabs(1) tool for setting non-standard tab stops
terminfo(4)
tic(1M) tool to compile terminal info
tput(1) tool for outputting terminal capability
Output:
int addch(chtype ch) write a character at a time
int addstr(char *str) write a string (calls addch)
int printw(fmt) similar to printf
int move(int y, x) move cursor to row y, column x
int clear() clear screen
Input:
int getch() read character from terminal
int getstr(char *str) read string until <CR>
int scanw(fmt) similar to scan
Output Attributes:
int attron(chtype attrs) turns on attribute in addition
76
C Libraries
Input Options:
int echo()
int noecho()
int cbreak()
int nocbreak()
Output:
Curses assumes stdin and stdout are connected to a terminal.
Once initscr() is called, curses takes over terminal control.
If endwin() is missing, may need to type "stty sane" and terminated with ^J.
#include <curses.h>
#include <signal.h>
main()
{
initscr();
signal (SIGINT, exit);
...
return 0;
}
if (argc != 2)
{
fprintf(stderr, "usage: %s file\n", argv[0]);
exit(1);
77
C Libraries
}
if ((fd=fopen(argv[1], "r")) == NULL)
{
perror(argv[1]);
exit(2);
}
while(1)
{
move(0,0);
for (line=0; line<LINES; line++)
{
if (!fgets(linebuf, sizeof(linebuf), fd))
{
clrtobot();
done ();
}
move(line,0);
printw("%s", linebuf);
}
refresh();
if (getch() == 'q')
done();
}
}
void done()
{
move(LINES-1,0);
clrtoeol();
refresh();
endwin();
exit(0);
}
Terminal capabilities
#include <stdio.h>
if ((name=getenv("TERM")) == NULL)
{
fprintf(stderr, "%s: can't find terminal type\n",
argv[0]); exit(1);
}
cl = tgetstr("cl",&ap);
cm = tgetstr("cm",&ap);
co = tgetnum("co");
li = tgetnum("li");
printf(%s%s%s\n", cl, tgoto(cm, (co/2)-(strlen(msg)/2),li/2), msg);
}
79
Introduction to kernel
7. INTRODUCTION TO KERNEL
! sharing
advantages | disadvantages
cost saving resource allocation
building on work of others simultaneous access to data
sharing data simultaneous execution
removing redundancy protection against corruption
! nondeterminacy
OS must be determinate - same program run today or tomorrow with same data should produce same results.
indeterminate - must respond to unpredictable order of events
80
Introduction to kernel
Desirable Features
! efficiency ! maintainability
response time resource utilization modular in construction
throughput clearly defined interfaces
well documented
! reliability
OS should be error free ! small size
able to handle all contingencies memory space
large systems more prone to error
Architecture of UNIX OS
File System
! Ordinary Files
It is not possible to insert bytes into the middle of a file, or delete bytes from the middle
- editor for example
- just write a completely new file
! Directories
Inconvenient to refer to files by i-numbers, directories provide names to be used
two column table, name & i-number-pair is called a link
usr/ast/data usr ---> i-number to usr directory
relative path OR absolute path begins with /
when link count is zero the kernel discards the file
directory entry: 14 bytes for file, 2 bytes for inode-number
/usr ==> /usr/ast ==> /usr/ast/data
! Special Files
some type of device: tty, disk, FIFO
block & character devices
kernel pool of buffers - are used to cache to speed up I/O
! I-node
When file opens the inode is kept in memory.
81
Introduction to kernel
The UNIX
Kernel user programs
traps libraries
User Level
Kernel Level
system call interface
file subsystem process control subsystem
interprocess communication
buffer cache scheduler
memory management
character block
device drivers
hardware control
Kernel Level
Hardware Level
hardware
System calls interact with the file subsystem and process control system.
The file subsystem manages files, allocating file space, administrating free space, controlling access to files,
retrieving data for users.
The file subsystem accesses file data using a buffering mechanism that regulates data flow between the kernel and
secondary storage devices.
Block I/O devices are random access storage devices, raw devices are called character devices.
The process control subsystem is responsible for process synchronization, interprocess communication, memory
management, and process scheduling.
Processes interact with file subsystem via systems calls:
open, close, read, write, stat, chown, chmod.
The system calls for controlling processes are: fork, exec, exit, wait, brk, signal.
Memory management - swapping and demand paging
Scheduler - allocates the CPU to processes
IPC - asynchronous signaling of events to synchronous transmission of messages between processes
Hardware control is responsible for handling interrupts and communicating with the machine.
82
Introduction to kernel
When a process creates a new file, the kernel assigns it an unused inode. Inodes are stored in the file system, but
the kernel reads them into an in-core inode table.
The file table keeps track of the byte offset in the file where the user's next read or write will start, and the access
rights allowed to the opening process.
The user file descriptor table identifies all open files for a process. The kernel returns a file descriptor for the open
system call, which is an index into the user fd table.
Processes
A process is the execution of a program and consists of bytes that the CPU interprets as machine instructions.
Processes communicate with other processes and with the rest of the world via system calls.
A process on a UNIX system is created by the" fork" system call. Every process except process 0 is created by
"fork". Process 0 is the swapper, process 1, known as init is the parent of all other processes.
Executable File contents:
- set of headers that describe the attributes of the file
- the program text
- machine language representation of data initial values when much memory space for uninitialized data (bss =
block started
- other sections, such as a symbol table
The kernel loads an executable file into memory during an "exec" system call. The three regions are: text, data and
stack.
The stack region is automatically created and its size is dynamically adjusted by t_ kernel at run time.
{
printf("can't create file %s\n", argv[2]);
exit(1);
}
copy (fdold, fdnew);
exit(0);
}
84
Introduction to kernel
The kernel allows a context switch only when a process moves from state "kernel running" to "asleep in memory".
Critical sections of code are executed by at most one process at a time.
struct queue {
…
}*bp, *bpl;
bpl->forp = bp->forp;
bpl->backp = bp;
bp->forp = bpl;
/*consider possible context switch here */
bpl->forp->backp = bpl;
Figure 3. Sample Code Creating Doubly linked List
85
Introduction to kernel
System Administration
Disk formatting, creating new file systems, repair of damaged file system, kernel debugging. The kernel does not
recognize a separate class of administrative process! - superuser privileges
86
Introduction to kernel
Summary
File subsystem controls storage and retrieval of data in user files. Files are organized into file systems, which are
treated as logical devices; a physical de' such as a disk can contain several logical devices.
Each file system has a super block that describes the structure and contents of the file system. Each file in a file
system is described by an inode that gives the attributes of the file.
Processes exist in various states and move between them according to well defined transition rules.
The kernel is non-preemptive - a process executing in kernel mode will continue t execute until it enters sleep state
or until it returns to execute in user mode.
It maintains the consistency of its data structures by enforcing the policy of non-preemption and by blocking
interrupts when executing critical regions of code.
! File allocation
- Files are allocated on a block basis.
- Allocation is dynamic, as needed.
87
Introduction to kernel
UNIX Internals
Kernel Basics - system calls & interrupts
File System - directory, regular, device files
Process management - share CPU & memory
Input/Output - terminal I/O
Interprocess Communication IPC
Kernel Basics
Kernel is part of UNIX OS
- share CPU & memory between competing processes
- processes system calls
- handles peripherals
Talking to Kernel
Processes access kernel facilities via system call interface & peripherals communicate with kernel via hardware
interrupts.
Peripherals ======= KERNEL ======= Processes
Hardware Interrupts System Calls
System Calls
- interface to kernel
- open/close files
- perform I/O read/write
- send signals - kill
- create pipes/sockets
- duplicate process - fork
- overlay a process - exec
- terminate a process - exit
- input/output
- interprocess communication
- process management
e.g.
User Process
user code
...
result = open (" file", O_RDONLY);
88
Introduction to kernel
...
C runtime library
open(char *name, int mode)
{
place parameters in registers.
execute trap instruction, switching to kernel code
return result of system call
}
Kernel
system call vector table
...
address of kernel close()
address of kernel open()
address of kernel write()
...
-----------------------------------
kernel system call code
kernel code for open()
{
manipulate kernel data structures
...
return to user code and user mode
}
The scheduler will not assign the CPU to another process during the execution of a system call. i.e. when a process
performs a system call, it cannot be "preempted".
System calls that make I/O requests from a device, may take time to complete. To avoid leaving the CPU idle, the
kernel puts the process to sleep and wakes it with a hardware interrupt signalling I/O completion.
Interrupts
Interrupt vector table
highest 0 hardware errors
priority
1 clock pointers to kernel
2 disk I/O interrupt handlers
3 keyboard
lowest 4 traps S/W interrupts
priority
current process suspend resume
----------------------------> - - - - - - - - - - - - - - - ------------------------------->
| |
| keyboard interrupt handler |
|----------------------------------------------->|
keyboard interrupt completed
When an interrupt occurs, the current process is suspended and the kernel determines the source of the interrupt. It
then examines the interrupt vector table to find the location of the code to process the interrupt.
If a higher priority interrupt than the current arrives, the lower priority interrupt handler is suspended until the
higher priority interrupt completes.
Critical sections of kernel code protect themselves from interrupts by temporarily disabling interrupts.
<disable all interrupts>
<enter critical section of code>
…
<leave critical section of code>
<re-enable all interrupts>
89
Introduction to kernel
File System
regular files - contain data – standard I/O system calls
directory files - backbone of fs – directory system calls
special files - peripherals – standard I/O system calls
! Disk architecture
cylinders
tracks
sectors
blocks 4K bytes
! Interleaving
1:1 interleave - logically contiguous blocks
3:1 interleave - e.g. 8 sectors, blocks 1 4 7 2 5 8 3 6
! Fragmentation
loss of storage due to under-use of last block
! Scattered
file blocks are rarely contiguous
! Block I/O
To read the first byte of data from a file, using the read system call, the device driver issues an I/O request to the
disk controller to read the first 4K block into a kernel buffer, then copies the first byte to your process.
! Inodes
Index Node to store information about each file.
The Inode of regular or directory file contains the location or its disk blocks, the inode of special file contains
peripheral device information.
type of file
file permissions
user and group ids
hard link count
last modified, last accesses times
location of blocks or major and minor numbers
symbolic link
! Block Map
Only the first 10 blocks of a file are stored directly in the inode. Larger files use indirect addressing schemes.
Superblock
- Total number of blocks in file system
- Number of inodes in inode free list
- Free block bitmap - linear sequence of bits, one per disk block, 1 indicates it is free
- Size of block in bytes
- Number of free blocks
- Number of used blocks
! Bad blocks
mkfs - location of all bad blocks on disk
inode number 1
90
Introduction to kernel
! Directories
Inode number 2 contains the location of blocks containing the root directory. ..parent
. itself
Filenames are not stored in file's inode.
Directory is a list of <filename, inodenumber> pairs Label inode
. 2
.. 2
bin 3
etc 4
usr 5
! Pathname to Inode
open - absolute pathname starts from inode #2
- relative pathname starts from cwd
components of pathname processed from left to right search for matching label to obtain inode number
Process Management
Scheduler - area of kernel that shares CPU
Memory Manager - area of kernel that shares RAM
91
Introduction to kernel
Every process has its own user area created in the kernel's data region and only accessible by the kernel.
- how process should react to each signal
- process's open file descriptors
- how much CPU time used
Process Table created in the kernel's data region and only accessible by the kernel.
- PID and parent PID
- real and effective user id and group id
- state (running, runnable, sleeping, suspended, idle, zombie)
- location of its code, data, stack, user areas
- list of pending signals
The Scheduler
- responsible for sharing CPU time
- maintains a multi-level priority queue
- a linked list of runnable processes
- allocate CPU time in proportion to importance
- CPU time allocated in "time quantums" 1/10 sec.
! Scheduling Rules
Every second, calculate the priorities of all runnable processes and organize them into several priority queues.
Every 1/10 sec, the scheduler selects the highest priority process. If the process is still running at end of time
quantum, it is placed at the end of its priority queue.
92
Processes I
8. PROCESSES (I)
Processes
- Created by "fork" system call (all except process 0)
- Invoking process: parent
- New Process: child
- Every process has one parent, but parent may have many children.
- Kernel identifies files by process ID (PID)
- Process 0 created "by hand" at boot. After "forking" a child process, it becomes the 'swapper". Its child is
called "init".
- "init" is the ancestor of all other processes on the system.
- When a user compiles a source program, an executable file is created which contains:
- Set of headers describing the attributes of the file
- Program text
- Initialized data and an indication of the space needed for uninitialized data
- other sections e.g.: symbol table info.
Executables
- Image, etc. loaded into memory during an 'exec' system call.
- When loaded, consists of 3 "regions":
- Text
- Data
- Stack
Context of a Process
- Process's state:
- Text
- Values of global user variables and data structures
- Values of machine registers
- Values in its process table entry and its u_area
- Contents of its user and kernel stacks
- "Context switch" change of active process.
- Interrupts are handled in the context of the current process, not necessarily the originator.
93
Processes I
Process States
1. Executing in user mode
2. Executing in kernel mode
3. Not executing but ready to run
4. Sleeping (e.g.: I/O wait)
Processes can't be pre-empted while in kernel mode (otherwise mutual exclusion problems)
void forktest()
{
int pid;
printf ("Start of test\n");
pid = fork();
printf ("Returned %d\n",pid);
}
Also, while file descriptor table (parent-process open file table) is copied exactly, the file pointer open file table) is
shared and if the child closes its FD, the parent's is undisturbed.
exec system calls
94
Processes I
Executed in child process to overlay itself with a specified binary program file.
- testenv program
main(argc,argv,envp)
int argc; char * argv[]; char * envp[];
{
int cntr;
printf("%d\n",argc);
for (cntr = 0; cntr < argc; cntr++)
{
95
Processes I
testenv output
0 a.out
-= ./a/out
FCEDIT=/usr/bin/vi
EXINIT=set dir=/tmp
HOME=/staff/tech/greg
PWD=/staff/tech/greg/itb443
SHELL=/bin/ksh
MAIL=/usr/mail/greg
EDITOR=vi
TERMCAP=/etc/termcap
LOGNAME=greg
TERM=vt100
PATH=/usr/bin:/usr/local/bin:/bin:/usr/lib …>
TZ=est10
wait (status)
if *status.lbyte = 0
then *status.rbyte is child's exiting status-code, i.e. as in "exit(n);"
Pipes
- accessed via std i/f (i.e. via file descriptor)
- each pipe associated with an inode (in table)
- size: 10 blocks = 5120 bytes (>4096)
- non-blocking read, blocking write (full)
Must check no. of bytes read in. If it is not blocking it will just return fewer bytes than requested.
Pipe creation
int fd[2]; pipe(fd);
/* fd[0] for reading
96
Processes I
int fd[2];
pipe(fd);
if (fork() != 0) { /*parent*/
close(fd[0]; /* close reading end */
write to fd[l] ...
}else { /* child code */
close fd[l]; /* close write */
exec(whatever)... /* overlay*/
/* reads from fd[0]; */
}
'fork' generates a clone with an exact copy of "per process file table", The fd[0], fd[l] file descriptors (table
subscripts) refer to a clone's local table.
int fd[2];
pipe (fd) ;
if (fork()! = 0) { /* parent */
close (fd[0]); /* reading end */
if (fork() == 0){ /* 2nd child */
exec(foo); /* write to fd[l] */
} else /* first child */
close (fd[l]); /* close writing end */
exec(whatever)... /* overlay */
/* reads from fd[0] */
}
97
Processes I
pipe (pfd);
if (fork()!= 0) { /* parent */
close (pfd [0]); /* close the reading end */
if (fork()!= 0) { /* parent still */
close(pfd[l]); /* parent closes the writing end */
} else { /* 2nd child */
close(l); /* close stdout */
dup2(pfd[l],l); /* copy the writing end over stdout */
close(pfd[l]); /* close the original writing end */
exec(foo); /* execute the utility writing to stdout */
}
} else { /* first child */
close(pfd[l]); /* close the writing end */
close(0); /* close stdin */
dup2(pfd[0], 0); /* copy reading end over stdin */
close(pfd[0]); /* close the original reading end */
exec (utility); /* utility will read from stdin */
}
Bi-directional pipes ?
two results:
- short circuit (Pl will read back from pfd[0] its own data just written to pfd[l])
- possibility of deadlock or looping (both processes):
Processes interact with F.S. by a set of system calls P.C.S system calls
- open - fork
- close - exec
- read - exit
- write - wait
- stat (Query attributes of a file) - brk
- chrnod (change access permissions) - signal
Scheduler Module
- Allocates the CPU to processes
- Processes run till they voluntarily give up the CPU (waiting on a device for example) or until the scheduler
preempts them when time's up.
- Scheduler chooses the highest priority eligible process to run.
Hardware Control
- Responsible for handling interrupts and for communicating with the machine
- Interrupts are handled by special functions in the kernel (as we have discuss recently)
Inter-Process Communication
- Asynchronous signaling of events
- Synchronous transmission of
messages between processes
Process states
1. executing in user mode
2. executing in kernel mode
3. is ready to run, resides in main
memory
4. is sleeping, resides in main memory
5. is ready to run, waiting on swapper
6. is sleeping, waiting on swapper
7. is returning from kernel to user mode,
but kernel preempts it
8. is newly created, process exists, but is
not ready to run, nor is it sleeping
executed the exit system call, is a
zombie, but contains an exit code and
timing statistics
Regions
A Region is a contiguous area of virtual
address space of a process that can be
treated as a distinct object.
Several processes can share a region. e.g.
processes can execute the same program,
share one copy of text region; processes
can share a common shared memory area.
Each process contains a private per
process region table called a pregion.
Assuming a page is lK bytes, want to access virtual memory address 68, 432. Therefore it is in the stack region,
byte offset 2986 in the region, counting from 0, with byte offset 848 of page 2, physical address 986k.
100
Processes I
The u area
A process can access its u area when it executes in kernel mode but
not when executes in user mode.
101
Processes I
102
Processes I
! Context Switch
1. Decide whether to do a context switch, and if (save context()) /* save context of executing process * /
whether a context switch is permissible now. {
2. Save the context of the "old" process. /* pick another process to run */
3. Find the "best" process to schedule for execution, …
using process scheduling algorithm of Figure 46. resume_context (new...process); /* never gets here! */
4. Restore its context.
}
Figure 17. Steps for a Context Switch /* resuming process executes from here */
Figure 18. Pseudo-Code for Context Switch
103
Processes I
Process Control
use and implementation of system calls
Process Creation
pid = fork(); /* parent is returned child’s PID */
- allocates a slot in process table for new process
- assigns unique ID number to child process
- logical copy of the context of parent process
- increment file and inode table counters
- returns 0 value to child, and child PID to parent
algorithm fork
input: none
output: to parent process, child PID number
to child process, 0
{
check for available kernel resources;
get free proc table slot, unique PID number;
check that user not running too many processes;
mark child state "being created;"
copy data from parent proc table slot to new child slot;
increment counts on current directory inode & changed root (if applicable);
increment open file counts in file table;
make copy of parent context (u area, text, data, stack) in memory;
push dummy system level context layer onto child system level context;
dummy context contains data allowing child process to recognize
itself, and start running from here when scheduled;
if (executing process is parent process)
{
change child state to "ready to run;"
return (child ID); /* from system to user */
}
else /* executing process is the child process */
{
initialize u area timing fields;
return (0); /* to user */
}
}
Figure 20. Algorithm for fork
104
Processes I
- the kernel assigns the parent process ID field in the child slot, putting the child in the process tree structure,
initialises scheduling parameters such as priority, CPU usage, timing.
- the kernel increments reference counts for files. Both processes manipulate the same file table entries, the
effect of "fork" is similar to that of dup.
- the kernel allocates memory for the
child process u area, regions and page
tables.
- the kernel create a context layer for the
child containing registers and sets the
program counter. The child state is set
to "ready-to-run".
#include <fcntl.h>
int fdrd, fdwt;
char c;
main(argc, argv)
int argc;
char *argv[];
{
if (argc != 3)
exit(1);
if((fdrd = open(argv[1], O_RDONLY)) == -1)
exit(1);
if((fdwt = creat(argv[2], 0666)) == -1)
exit(1);
fork();
/* both procs execute same code */
rdwrt();
exit(0);
}
rdwrt()
{
for (;;)
{
if (read (fdrd, &c, 1) != 1)
return;
write(fdwt, &c, 1);
}
}
105
Processes I
Although the processes appear to copy the source file twice as fast because they share the work load, the contents of
the target file depends on the order that the kernel scheduled the processes.
#include <string.h>
char string[0] = "hello world";
main ()
{
int count, i;
int to_par[2], to_chil[2]; /* for pipes to parent, child */
char buf[256];
pipe(to_par);
pipe(to_chil);
if (fork() == 0)
{
/* child process executes here * /
close(O); /* close old standard input */
dup(to_chil[O]); /* dup pipe read to standard input */
close(1); /* close old standard output */
dup(to_par[l]); /* dup pipe write to standard out */
close(to_par[1]); /* close unnecessary pipe descriptors */
close(to_chil[O]);
close(to_par[O]);
close(to_chil[1]);
for (;;)
{
if((count == read (0, buf, sizeof(buf))) == 0)
exit();
write(O, buf, count);
}
}
/* parent process executes here * /
close(1); /* rearrange standard in, out */
dup(to_chi1[1]);
close(O);
dup(to_par[O]);
close(to_chil[1]);
close(to_par[O]);
close (to_chil[0]);
close(to_par[1]);
for (i = 0; i < 15; i++)
{
write(l, string, strlen(string));
read(O, buf, sizeof(buf));
}
}
Signals
Signals inform processes of the occurrence of asynchronous events. Processes may send each other signals with the
"kill" system call.
Use of Signals:
! termination of a process
- "exit", "signal" death of child
! process induced exceptions
- access memory outside address space
! unrecoverable conditions
- running out of system resources
! unexpected error condition
- making non existent system call – writing a pipe that has no reader – illegal reference to "1seek"
! originating from process in user mode
106
Processes I
107
Processes I
Handling Signals
- process exists on receipt of signal, or
- it ignores the signal, or
- it executes a user function on receipt of signal
oldfunction = signal (signum, function);
signum - signal number to specify action function - address of user function to invoke
If the signal handling function is set to its default value, the kernel will dump a "core" image of the process for
certain types of signals before exiting.
108
Processes I
Process
An instance of program being executed by operating system. A new process is created by issuing the fork
system call. A program may be executed by many processes at same time.
System Calls
The Unix kernel provides a limited number (60-200) of direct entry points for services from the kernel.
The standard Unix C library provides a C interface to each system call or function.
Most system calls return -1 if an error occurs, or a value >= 0
A global integer variable errno is provided by the C interface. The header file <errno.h> contains the names
and values of these error numbers.
Some system calls return a pointer to a structure of information, e.g. stat and fstat system calls.
kernel
! Argument List
Whenever a program is executed, a variable-length argument process. The argument list is passed to the process.
The argument list is an array of pointers to character strings (maximum size of 5120 bytes).
! Environment List
main (argc, argv, envp)
int argc; char *argv[]; char *envp[];
{
int i;
for (i = 0; envp [i] != (char *) 0; i++)
printf("%s\n", envp[i]);
exit(0) ;
}
HOME=/user1/staff/neville
SHELL=/bin/ksh
TERM=vtl00
109
Processes I
USER=neville
PATH=/userl/staff/joe/bin:/usr/local/bin:/bin:/usr/bin:
main ()
{
char *ptr, *getenv();
if ( (ptr = getenv("HOME")) == (char *) 0)
printf ("HOME is not defined\n ") ;
else
printf("HOME=%s\n", ptr);
exit(0);
}
The argument list, environment pointers and character strings pointed to are in the data space of the process. The
process can modify these but this has no effect on the parent process.
The only value passed by the terminating process to its parent process by the operating system is the 8-bit argument
to the exit function.
The parent and child can exchange information using a disk file or by interprocess communication. A process can
modify its environment to affect any child processes it created.
! Process user context kernel context
stack kernel data
heap
uninitialized data
initialized read-
write data read from program file
initialized read- when program is executed
only data
text
user context portion of address space accessible to the process while it is running in user mode.
text the actual machine instruction that are executed by hardware. Often set read-
only so that process cannot modify its instructions. It is read into memory from disk, unless as
supports shared text and it already is executing.
data contains the program's data
- initialized read-only – ro while program executing. e.g. literal strings; not supported on many
OSs
- initialized read-write – modified during execution uninitialized - set to zero before process
starts, advantages – save disk space & time to read data
heap used to allocate data space dynamically to the process while the process is running.
stack used dynamically while process is running to contain stack frames that are used by the
programming language. Stack frames contain the calling arguments and return addresses.
kernel context is maintained and accessible only to the kernel. It contains information that the kernel needs to
keep track of the process and to stop and restart the process while other processes are allowed to
execute.
110
Processes I
! Example
int debug = 1; /* initialised read-write variable */
char *progname; /* uninitialised read-write variable*/
main (argc, argv)
int argc; char *argv[];
{
int i; /* automatic variable stored on stack */
char *ptr; /* automatic variable stored on stack */
char *malloc(); /* space allocated stored on heap */
progname = argv[0];
printf("argc = %d\n", argc); /* read-only data */
for (i = 1; i < argc; i++)
{
ptr = malloc(strlen(argv[i]) + 1);
strcpy(ptr, argv[i]);
if (debug)
printf("%s\n", ptr); /* read-only data */
}
} /* functions main, printf, strlen, strcpy &
malloc are all in the text segment */
login-name:password:user-ID:group-ID:misc:home:shell
#include <pwd.h>
struct passwd {
char *pw_name; /* login-name */
char *pw-passwd; /* encypted-password */
int pw_uid; /* user-ID */
int pw_gid; /* group-ID */
char *pw_age; /* password age System V */
char *pw_gecos; /* miscellany */
char *pw_dir; /* home directory */
char *pw_shell; /* shell */
};
111
Processes I
! Shadow Password
/etc/shadow set so that only superuser can read.
The encrypted-password field is set to an asterisk.
! Group File
/etc/group
group-name:encypted-password:group-ID:user-list
#include <grp.h>
struct group *getgrgid(int gid);
struct group *getgrnam(char *name);
struct group {
char *gr_name; /* group-name */
char *gr-passwd; /* encrypted-password */
int gr_gid; /* group-ID */
char **gr_mem; /* array of ptrs to user-list */
};
! Shells
/bin/sh Bourne shell
/bin/ksh Korne shell
/bin/csh C shell
/bin/tcsh Enhanced C shell
! Filenames
limit of 14 to 256 characters
NULL ('\0') terminates pathname
slash ('/') separates filenames
characters interpreted by shell are not recommended *, [, ], -
! Pathname
relative – path begins at current directory
absolute – starts with a slash (from root)
! File Descriptor
a small integer used to identify a file that has been opened for I/O
0 standard input
1 standard output
2 standard error
assigned by the kernel by a system call {open, creat, dup, pipe, fcntl}
! Files
#include <sys/types.h>
#include <sys/stat.h>
struct stat {
ushort st_mode; /* file type & access perms */
ino_t st_dev; /* i-node number */
dev_t st_dev; /* ID of device containing directory entry for file */
short st_nlink; /* number of links */
ushort st_uid; /* user ID */
ushort st_gid; /* group ID */
112
Processes I
st_mode
#define S_IFMT 0170000 /* type of file */
#define S_IFREG 0100000 /* regular */
#define S_IFDIR 0040000 /* directory */
#define S_IFCHR 0020000 /* character special */
#define S_IFBLK 0060000 /* block special */
#define S_IFLNK 0120000 /* symbolic link */
#define S_IFSOCK 0140000 /* socket - BSD only */
#define S_IFIFO 0010000 /* fifo - System V only */
113
Processes I
If the "stick bit" is set, the executable program's read-only text is left in swap, so that it will start faster next time.
(This is one of the few system calls that cannot fail and does not have an error return
{exit, getpid, getpgrp, getppid, getuid,
geteuid, getgid, getegid, umask})
The file creation mask is used when a new file or directory is created. The mask specifies which bits in the new
file are to cleared. If the file mode creation mask is octal 022, the group-write bit is off giving an actual mode of
octal 0644.
! Directories
int mkdir(char *pathname, int mode); /* 14 byte name, 2 byte mode */
int system(char *string);
! Process Group ID
Every process is a member of a process group. It is possible to send a signal using the kill system call to all
processes belonging to a specified process group.
The value of the process group ID is obtained by calling getpgrp system call. Under System V a process is only
able to change its process group ID to be equal to its process ID, effectively becoming a process group leader.
int setpgrp();
! Socket Group ID
BSD supports the notion of a process group of sockets. Each socket that is open has a socket group ID.
! Time-of-Day
BSD provides gettimeofday system call
#include <sys/time.h>
int gettimeofday(struct timval *tvalptr, struct timezone *tzoneptr);
struct timeval {
long tv_sec; /* seconds since 00:00:00 GMT, 1 Jan 1970 */
long tv_usec; /* and microseconds */
};
114
Processes I
struct tms (
time_t tms_utime; /* user time */
time_t tms_stime; /* system time */
time_t tms_cutime; /* user time, children */
time_t tms_cstime; /* system time, children */
};
! System Calls
#include <fcntl.h>
int open(char pathname int oflag[, int mode]);
returns a file descriptor if successful, else -1
oflag
O_RDONLY open for reading only
O_WRONLY open for writing only
O_RDWR open for reading and writing
O_NDELAY do no block on open or read or write
O_APPEND append to end of file on each write
O_CREAT create the file is it does not exist
O_TRUNC if file exist, truncate to zero length
O_EXCL error if O_CREAT & file already exist
115
Processes I
! signals
Notification to a process that an event has occurred "software interrupt" usually occur asynchronously.
- by one process to another process
- by the kernel to a process
#include <signal.h>
! Reliable Signals
- Signals handlers remain installed after a signal occurs.
- A process must be able to prevent selected signals from occurring when desired.
- While a signal is being delivered to a process, that signal is blocked (held).
for (; ;) {
sigblock(sigmask(SIGINT));
while (flag == 0)
sigpause(0); /* wait for signal */
/* signal has occurred, process it */
...
}
System V version:
int flag = 0; /* global set when SIGINT occurs */
for (; ;) {
sighold(SIGINT);
while (flag == 0)
sigpause(SIGINT); /* wait for signal */
/* signal has occurred, process it */
...}
! Process Control
Network programming involves the interaction of two or more processes. How are processes created, executed, and
terminated?
int fork(); /* system call */
Creates a copy of the process that was executing. The process that executed the fork is the parent and the new
117
Processes I
main()
{
int childpid;
if ((childpid = fork()) == -1) {
fprintf(stderr, "can't fork\n"); exit(1);
} else if (childpid == 0) { /* child process */
printf("child: childpid=%d, parentpid=%d\n",
getpid(), getppid()); exit(0);
} else { /* parent process */
printf("parent: childpid=%d, parentpid=%d\n",
childpid, getpid()); exit(0);}
}
- Exec process inherits attributes: process ID, parent process ID, process group ID, terminal group ID, time left
until an alarm clock signal, root directory, current working directory, first mode creation mask, file locks, real
user ID, real group ID
- Attributes that can change: effective user ID, effective group ID
- If the set-user-ID bit is set then effective user ID is changed to the user ID of the owner of the program
(init process)
- If process ID, process group ID, terminal group ID are all equal then hangup signal “SIGHUP” is sent to
each process with process group ID equal to terminating process
- To prevent a child process from becoming a zombie
signal (SIGCLD, SIG_IGN)
! Process Relationships
For each terminal to be activated, init process forks a copy of itself and each child process execs the getty program
which sets terminal speed, output greeting message and waits for login name.
getty execs the program login which checks your login name and password in /etc/passwd
If the login is successful the login program sets the current working directory, chdir sets the group ID and user
ID, setgid & setuid execs the shell program /bin/sh
PID=
l init init getty login sh
fork exec exec exec
To execute a command the shell forks a copy of itself and waits for child to terminate, the child execs the program,
and when finished, it calls exit which terminates the child.
! Job Control
- consider process groups with/without job-control
BSD4.3 supports job-control - need to check system
main()
{
printf ("lipid = %d, pgrp = %d\n ", getpid(), getpgrp());
exit(0);
}
a.out Bourne, C & Korn shells
a.out & a.out & twice in background
(a.out & a.out &) from a subshell
e.g.
BSD C shell pid = 2530, pgrp = 2528 BSD Korn shell pid = 2530, pgrp = 2530
pid = 2529, pgrp = 2528 pid = 2529, pgrp = 2529
- process group leader
parent process file table i-node table
- kill with a pid argument of zero sends a
table entry
signal to all processes in the sender's process
group fd0: current file i-node
fd1: position information
! File Sharing fd2: i-node ptr
fdi:
There are 3 kernel tables used to access a file:
- every process has a process table entry
- file pointers in the process table point to child process
entries in the file table (current file position) table entry
- i-node table (every open file has an entry) fd0: current file …
fd1: position
Since the i-node table does not keep the file's fd2: i-node ptr
current position, an i-node entry for a. file can be fdi:
shared by any number of processes.
other process
e.g. When two or more processes are reading the table entry
same file at some point in time - the file position fd0: …
of one process must be independent of the other fd1:
fd2:
fdi:
…
119
Processes I
! Daemon Processes
A daemon is a process that executes in the background waiting for some event to occur, or waiting to perform a
task on a periodic basis.
A standard Unix process named cron performs periodic tasks at given times during
the day from /usr/lib/crontab /* cron table */.
Run in Background
If a daemon is started from a login session without being placed in the background, the daemon will tie up the
terminal while it is executing.
120
Processes I
socket structure
fd0:
fd1: . so_pgrp:
fd2: . set by fcnt1
fdi: … . socket structure (F_SETOWN)
so_pgrp:
control
tty:
! Daemon Termination
Both System V and BSD 4.3 use the SIGTERM signal to notify all processes that the system is going from
multiuser to single-user. If it doesn’t terminate after 20 secs, SIGKILL is sent to the the process.
121
Processes II
9. PROCESSES (II)
FORK
! /* fork.c */
#include <stdio.h>
main()
{
int pid;
! /* orphan.c */
#include <stdio.h>
main()
{
int pid;
A process that terminates cannot leave the system until its parent accept code. If its parent is already dead, it is
adopted by the "init" process
If a process's parent is alive but never executes a wait() the process's will never be accepted and the process will
remain a zombie.
/* zombie.c */
#include <stdio.h>
main()
{
int pid;
pid = fork(); /* duplicate process */
if (pid != 0) /* parent lives */
{
while (1)
sleep(1000); /* child dies */
}
else {
exit(2);
}
}
ps
PID TT STAT TIME COMMAND
160 p1 S 0:00 -ksh
170 p1 S 0:00 zombie # parent process
171 p1 Z 0:00 <defunct>
180 p1 R 0:00 ps
kill 170
[1] Terminated
ps
PID TT STAT TIME COMMAND
160 p1 S 0:00 -ksh
190 p1 R 0:00 ps
/* wait.c */
#include <stdio.h>
main()
{
int pid, status, childpid;
printf("original process with PID %d\n", getpid());
pid = fork(); /* duplicate process */
if (pid != 0)
{ /* parent */
printf("parent process with PID %d and PPID %d\n", getpid(), getppid());
childpid = wait(&status); /* wait for child */
printf("child PID %d terminated with exit code %d\n", childpid, status>>8);
} else { /* child */
printf("child process with PID %d and PPID %d\n", getpid(), getppid());
exit(2);
}
printf("PID %d terminates\n", getpid());
}
original process with PID 190
child process with PID 191 and PPID 190
parent process with PID 190 and PPID 188
child PID 191 terminated with exit code 2
PID 191 terminates
123
Processes II
/* background.c */
#include <stdio.h>
background cc wait.c
ps
PID TT STAT TIME COMMAND
664 p1 S 0:00 -ksh (ksh)
710 p1 R 0:00 ps
715 p1 D 0:00 cc wait.c
/* redirect.c */
#include <stdio.h>
#include <sys/file.h>
redirect ls.out ls -1
cat ls.out
SIGNALS
- terminates process and generates core file (dump)
- terminates process without core (quit)
- ignores and discard signal (ignore)
- suspends process (suspend)
- resumes process
/* alarm.c */
#include <stdio.h>
main()
{
alarm(3); /* schedule an alarm in 3 secs */
printf("looping forever ...\n");
while (1);
fprintf(stderr, "should never execute\n");
}
/* handler.c */
#include <stdio.h>
#include <signal.h>
int alarmflag = 0;
void alarmhandler();
main()
{
signal (SIGALRM, alarmhandler); /* signal handler */
alarm(3); /* schedule an alarm in 3 secs */
printf ("looping. . . \n") ;
124
Processes II
while (!alarmflag)
pause(); /* wait for signal */
printf("loop ends due to alarm signal\n");
}
void alarmhandler()
{
printf("alarm clock signal was received\n");
alarmflag = 1;
}
main()
{
int (*oldHandler)();
int delay;
void childhandler();
limit 5 ls -1
limit 4 sleep 40
main()
{
int pid1, pid2;
if((pid1=fork())== 0){ /* first child */
125
Processes II
while (1){
printf("pid1 is alive\n");
sleep(1);
}
}
if((pid2=fork())== 0){ /* second child */
while (1){
printf("pid2 is alive\n");
sleep(1) ;
}
}
sleep(3);
kill(pid1, SIGSTOP) ; /* suspend first child */
sleep(3);
kill(pid1, SIGCONT) ; /* resume first child */
sleep(3);
kill(pid1, SIGINT) ; /* kill first child */
kill(pid2, SIGINT) ; /* kill second child */
}
pidl is alive
pid2 is alive
pidl is alive
pid2 is alive
pidl is alive
pid2 is alive
pid2 is alive ... just second child runs
pid2 is alive
pid2 is alive ... first child is resumed
pidl is alive
pid2 is alive
pidl is alive
pid2 is alive
pidl is alive
pid2 is alive
Process Groups
Every process is a member of a process group. Several processes can be members of the same process group.
When a process forks, the child inherits its process group from its parent. A process may change its process group
to a new value by using setpgrp().
Every process can have an associated control terminal. A child process inherits its control terminal from its parent.
When a process execs, its control terminal stays the same.
Every terminal can be associated with a single control process. When ^C is detected, the terminal sends the
appropriate signal to all processes in the process group of its control process.
/* proc_groupl.c */
#include <stdio.h>
#include <signal.h>
void sigintHandler();
main ()
{
signal (SIGINT, sigintHandler); /* handle ^C */
if (fork() == 0)
printf("child PID %d PGRP %d waits\n", getpid(), getpgrp(0));
else
printf("parent PID %d PGRP %d waits\n", getpid(), getpgrp(0));
126
Processes II
void sigintHandler()
{
printf("process %d got a SIGINT\n", getpid());
}
/* proc_group2.c */
#include <stdio.h>
#include <signal.h>
void sigintHandler();
main()
{
int i;
signal (SIGINT, sigintHandler); /* handle ^C */
if (fork() == 0)
setpgrp(0, getpid()); /* place child in own process group */
printf("process PID %d PGRP %d waits\n", getpid(), getpgrp(0));
sleep(5); /* time to ^C */
for (i=0; i<3; i++) {
printf("process %d is alive\n", getpid());
sleep(1);
}
}
void sigintHandler() {
printf("process %d got a SIGINT\n", getpid());
exit(1);
}
process PID 591 PGRP 591 waits
process PID 592 PGRP 592 waits
^C
process 591 got a SIGINT
process 592 is alive
process 592 is alive
process 592 is alive
If a process attempts to read from its control terminal and is not a member of the same process group as the
terminal's control process, the process is sent a SIGTTIN (suspend process).
/* proc_group3.c */
#include <stdio.h>
#include <signal.h>
#include <sys/termio.h>
#include <sys/file.h>
void sigintHandler();
main()
{
int status; char str[100];
if (fork()== 0)
{
127
Processes II
/* child */
signal (SIGTTIN, sigintHandler);
setpgrp(0, getpid()); /* place child in new process group */
printf("enter a string: ");
scanf ( "%s", str); /* try to read from control terminal */
printf("you entered %s\n", str);
} else
wait(&status); /* wait for child to terminate */
}
void sigintHandler()
{
printf("attempted inappropriate read from control terminal\n");
exit(1) ;
}
enter a string: attempted inappropriate read from control terminal
PIPES
Interprocess communication mechanism that allow two or more processes to send information to each other.
Used to connect standard output of one utility to standard input of another.
$ who | wc -1
Both the writer process and the reader process of a pipeline execute concurrently, a pipe automatically buffers the
output of the writer and suspends the writer if the pipe gets too full.
UNNAMED PIPES
pipe(fd) - unidirectional communication link
fd[0]----------------------------------------------------
write end | . |--->pipe--->| . | read end
fd[l]-----------------------------------
/* talk.c */
#include <stdio.h>
#define READ 0
#define WRITE 1
main() {
int fd[2], nread;
char str[100];
128
Processes II
connect who wc
If a process tries to open a named pipe for read-only and no process writing, the reader will wait until a process
opens it for writing.
(If O_NDELAY is set then open succeeds immediately).
If a process tries to open a named pipe for write-only and no process reading, the writer will wait until a process
opens it for reading.
(If O_NDELAY is set then open fails immediately).
/* reader.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
/* writer.c */
#include <stdio.h>
#include <sys/file.h>
main()
{
int fd,i;
char str[100];
Changing directories
/* chdir.c */
#include <stdio.h>
130
Processes II
main()
{
chdir ("/") ;
system("pwd");
chdir("/usr/local/bin") ;
system("pwd");
}
/* exchange.c - full duplex communications between processes */
#include <string.h>
#include <stdio.h>
#define IN 0
#define OUT 1
char string [] = "hello world";
main()
{
int count, i;
int pipe_to_parent[2], pipe_to_child[2];
char buffer[256];
pipe(pipe_to_parent);
pipe(pipe_to_child);
if (fork () == 0)
{/* child process */
close(IN); /* close old stdin */
dup(pipe_to_child[IN]); /* dup pipe read to stdin */
close(OUT); /* close old stdout */
dup(pipe_to_parent[OUT]); /* dup pipe write to stdout *
for (;;) {
if ((count = read(IN, buffer, sizeof(buffer))) == 0)
exit(0);
write(OUT, buffer, count);
}
}
/* parent process */
close(OUT); /* close old stdout */
dup(pipe_to_child[OUT]); /* dup pipe write to stdout */
close(IN); /* close old stdin */
dup(pipe_to_parent[IN]); /* dup pipe read to stdin */
131
Processes II
{
char process_name[MAXLEN];
char line[MAXLEN];
sprintf(process_name, "parent");
while (1) {
printf("%s> ",process_name);
fgets(line, MAXLEN, stdin);
if (strcmp(line, "dir") == 0)
directory(process_name);
else if (strcmp(line, "start") == 0)
start(process_name);
else if (strcmp(line, "exit") == 0)
exit(0) ;
else if (strcmp(line, "") == 0)
continue;
else
printf("there is no help yet\n");
}
}
directory(char *pname)
{
int fd, nread,size;
char *dname,*path;
static struct dirent dlink;
getpath(pname, path);
Interprocess Communication
- IPC between two processes on a single system
user user
process process
kernel
kernel kernel
132
Processes II
The problem is that in the time it takes a single process to execute these three steps, another process can perform
the same steps. Chaos results.
kernel
pipe
->flow of data->
133
Processes II
kernel
pipe
->flow of data->
kernel
pipe
->flow of data->
who | sort | lpr
kernel
pipe 1 pipe 2
->flow of data-> ->flow of data->
pipe 2
->flow of data->
134
Processes II
FIFOs
First In, First Out is similar to a pipe (System V). FIFOs are used by the System V line printer.
A FIFO is created by the mknod system call.
int mknod(char *pathname, int mode, int dev); /etc/mknod name p
! Name Spaces
The name is how the client and server "connect" to exchange messages
IPC type Name space Identification
pipe (no name) file descriptor
FIFO pathname file descriptor
message queue key_t key identifier
shared memory key_t key identifier
semaphore key_t key identifier
unix socket pathname file descriptor
key_t key
ftok function converts a pathname to a IPC key
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(char *pathname, char proj);
135
Processes II
System V IPC
- message queues
- semaphores
- shared memory0
/* <sys/ipc.h> */
struct ipc_perm { /* <sys/ipc.h> */
ushort uid; /* owner's user id */
ushort gid; /* owner's group id */
ushort cuid; /* creator's user id */
ushort cgid; /* creator's group id */
ushort mode; /* access modes */
ushort seq; /* slot usage sequence number */
ket_t key; /* key */
};
! Logic flow for opening start here OK create new entry return ID
an IPC channel | | no
yes
key==IPC PRIVATE? yes system table full? errno= ENOSPC
| no | yes
Key already exists? no IPC_CREAT set? no
| yes errno= ENOENT
yes error return
CREAT & EXCL set? errno=EEXIST
| no
access permission? no error return
| yes errno=EACCES
OK return ID
Message queues
There is no requirement that any process be waiting for a message to arrive on queue before some other process is
allowed to write a message to that queue
For every message queue in the system, the kernel maintains the following structure of information:
#include <sys/types.h>
#include <sys/ipc.h> /* defines ipc_perm structure */
struct msqid_ds {
struct ipc_perm msg_perm; /* operation perm struct */
struct msg *msg_first; /* ptr to first msg on q */
struct msg *msg_last; /* ptr to last msg on q */
ushort msg_cbytes; /* current # of bytes on q */
ushort msg_qnuro; /* current # of messages on q */
ushort msg_qbytes; /* max # of bytes allowed on q */
ushort msg_lspid; /* pid of last msgsnd */
ushort msg_lrpid; /* pid of last msgrcv */
time_t msg_stime; /* time of last msgsnd */
136
Processes II
! Message queue
structures in kernel msqid msg-perrn link link link
type=100 type=200 type=300
msg_first length=l length=2 length=3
msg_last
… data_
… data_
msg_ctime data
A new message is created, or an existing message queue is accessed with msgget system call.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, struct msgbuf *ptr, int length, int flag);
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[l]; /* message data */ ;
}
- The data mtext can be binary data or text.
- The kernel does not interpret the contents of the message at all, so cooperating processes could define their own
structure.
- The length is in bytes.
- The flag can be set to IPC_NOWAIT or zero.
int msgrcv(int msqid, struct msgbuf *ptr, int length, long msgtype, int flag);
If MSG_NOERROR bit in flag is set, than data of received message is greater than length.
137
Processes II
Multiplexed Messages
Semaphores
Semaphores are a synchronization primitive. We will use to synchronize access to shared memory segments.
A semaphore is a integer resource counter. If we have one resource, a shared file, then valid values are 0 & 1.
process A process B
kernel
semaphore: 0 or 1
Since our use of semaphores is to provide resource synchronization between different processes, the semaphore
value must be stored in the kernel.
To obtain a resource that is controlled by a semaphore, a process needs to test its current value, and if value> 0,
decrement the value by 1.
If value = 0, the process must wait until value> 0 (wait for some other process to release resource).
To release resource, a process increments the value. System V implementation of semaphores is done in the kernel
- guarantee a group of operations is atomic.
#include <sys/types.h>
#include <sys/ipc.h> /* defines ipc-perm structure */
struct semid_ds {
struct ipc-perm sem-perm; /* operation perm struct */
struct sem *sem_base; /* ptr to 1st semaphore in set */
ushort sem_nsems; /* # of semaphores in set */
time_t sem_otime; /* time of last semop */
time_t sem_ctime; /* time of last change */
};
struct sem {
ushort semval; /* semaphore value, non -ve */
short sempid; /* pid of last operation */
ushort semncnt; /* # awaiting semval > cval */
ushort semzcnt; /* # awaiting semval = 0 */
};
138
Processes II
Kernel data structures for a semaphore set semid sem-perm semval [0]
sempid [0]
sem_base semncnt [0]
sem_nsems semzcnt [0]
sem_otime semval [1]
sem ctime sempid [1]
semncnt [1]
#include <sys/types.h>
semzcnt [1]
#include <sys/ipc.h>
#include <sys/sem.h>
semflag
0400 SEM_R alter by owner
0200 SEM_A read by owner
0040 SEM_R >> 3 alter by group
0020 SEM_A >> 3 alter by group
0004 SEM_R >> 6 read by world
0002 SEM_A >> 6 alter by world
IPC_CREAT
IPC_EXCL
struct sembuf {
ushort sem_num; /* semaphore # */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
Semaphore operations:
- if sem_op > 0, sere_val is added to semaphore value (release of resources)
- if sem_op = 0, caller waits until semaphore value = 0
- if sem_op < 0, caller waits until semaphore value >= absolute of sem_op
int semctl(int semid, int semnum, int cmd, union semnum arg);
union semun {
int val; /* used for SETVAL only */
struct semid_ds *buffi /* used for IPC_STAT & IPC_SET */
ushort *arrarY; /* used for IPC_GETALL & IPC_SETALL */
} arg;
139
Processes II
Shared Memory
Normal steps in client-server file copying:
- The server reads from the input file. Data is read by kernel into its internal block buffers and copied to the
server's buffer.
- The server writes this data in a message (via a pipe, FIFO, or message queue). Data is copied from user's buffer
into the kernel.
- The client reads the data from the IPC channel. Data is copied from kernel's IPC buffer to client's buffer.
- Finally the data is copied from the client's buffer to the output buffer. This might involve just copying the data
into a kernel buffer and returning, with the kernel doing the actual write operation to the device at some later
time.
Most Unix implementations try to speed up these copies as much as possible expensive in time.
Movement of data between client and server
client server
FIFO, pipe
output or message input
file kernel file
The problem with these forms of IPC - pipes, FIFOs and message queues - is that for processes to exchange data, it
has to go through the kernel.
Shared memory provides a way around this by letting two or more processes share a memory segment.
The steps for the client-server examples:
- The server gets access to a shared memory segment using a semaphore.
- The server reads from the input file into the shared memory segment. address to read into points into shared
memory.
- When the read is complete the server notifies the client, again using a semaphore.
- The client writes the data from the shared memory segment to the output file
Movement of data between client and server
output input
file file
kernel
Data is only copied twice. Both of these copies involve the kernel's block buffers. For every shared memory
140
Processes II
shrnflag
0400 SHM_R read by owner
0200 SHM_W write by owner
0040 SHM_R >> 3 read by group
0020 SHM_W >> 3 write by group
0004 SHM_R >> 6 read by world
0002 SHM_W >> 6 write by world
IPC_CREAT
IPC_EXCL
if shrnaddr == 0
system selects address for the caller
else
if value for shrnflag specifies SHM_RND,
shared memory is attached at the address specified by the shrnaddr
argument rounded down by SHMLBA (lower boundary address)
else
shared memory is attached at the address specified by the shrnaddr argument
See shared_memory.c
The two process wait for access to the shared memory by waiting for a semaphore's value to become greater than
zero.
This is the most efficient way to wait for the resource, since it is the kernel that does all semaphore operations and
the kernel puts a process to sleep when it has to wait for a semaphore.
! Busy-waiting - instead of sleeping
- keep trying to obtain resource
/* server loop */
mesgptr->mesg_flag = 0; /* signal client */
while (mesgptr->mesg_flag == 0)
; /* wait for client to process */
141
Processes II
/* client loop */
mesgptr->mesg_flag = 1; /* signal server */
while (mesgptr->mesg_flag == 1)
; /* wait for server to process */
! Multiple Buffers
/* typical program loop */
while ( (n = read(fdin, buff, BUFFSIZE» > 0) /* process the data */
write (fdout, buff, n);
142
I/O Subsystem
DEVICE INTERFACES
- block device
- character "raw" device
143
I/O Subsystem
Drivers frequently sleep, waiting for hardware connections or the arrival of data.
algorithm open /* for device drivers */
input: pathname
openmode
output: file descriptor
{
convert pathname to inode, increment inode reference count,
allocate entry in file table, user file descriptor,
as in open of regular file;
if (block device)
{
use major number as index to block device switch table;
call driver open procedure for index:
pass minor number, open modes;
}
else
{
use major number as index to character device switch table;
call driver open procedure for index:
pass minor number, open modes;
}
144
I/O Subsystem
The kernel can transmit data directly between address space and the device, or device drivers may buffer data
internally e.g. terminal drivers use clist to buffer data.
- memory mapped - status registers
- programmed I/O - execute instructions
- direct memory access (DMA) - used for bulk data transfer in parallel to CPU operations.
- transfer data between device and user's address space faster (one less copy, no kernel buffers).
! Strategy interface
To transmit data between the buffer cache and a device. The process must be locked in memory until the I/O
transfer is complete.
! Interrupt handlers
Many physical devices can be associated with one interrupt vector entry, the driver must be able to resolve which
device caused the interrupt.
Disk Drivers
Partitioning the disk into sections, means that some
sections can be read-only, some read-write, and some #include "fcntl.h" #include <stdio.h>
unmounted (no access) main()
{
char buf1[4096], buf2[4096];
Section Name Start Length int fdl, fd2, i;
Block in Blocks
0 0s0 0 64000 if (((fdl =open("/dev/dsk5", O_RDONLY)) == -1) ||
1 0s1 64000 192000 ((fd2=open("/dev/rdsk5", O_RDONLY)) == -1))
2 0s2 256000 256000 {
printf("failure on open\n");
3 0s3 0 512000 exit();
}
Sections may overlap, but file systems must not. lseek(fdl, 8192L, 0);
lseek(fd2, 8l92L, 0);
$ ls -1 Idev/dsk15 Idev/rdsk15 if ((read(fdl, bufl, sizeof(buf1)) == -1) ||
br------- 2 root root 0,21 Feb 12 15:40 (read(fd2, buf2, sizeof(buf2)) == -1))
/dev/dsk15 {
crw-rw--- 2 root root 7,21 May 7 09:29 printf("failure on read\n");
/dev/rdsk15 exit(0);
}
The kernel loops internally 4 times to read 4096 bytes
for (i = 0; i < sizeof(buf1); i++)
if (buf[I] != buf2[i])
Programs that read and write the disk directly can
{
destroy the consistency of the file system data. printf("different at offset %d\n", i);
Therefore "fsck" should not run on active file system. exit(0);
}
printf("reads match\n");
Figure 31. Reading Disk Data - block & raw interface }
145
I/O Subsystem
Terminal Drivers
- Internally implement a "line discipline" which interprets the users' I/O.
- In "canonical" mode, the line discipline converts the "raw" sequences typed by the user to a canonical form
(what the user meant) before sending them to the user process.
- In "raw" mode, the line discipline passes data between the process and the user without conversion.
Figure 32. Data Sequence and Data Flow through Line Dicsipline
- Variable length linked list of CBlocks with a count of the number of characters on the list.
- CBlock contains:
- Pointer to next CBlock on list
- Character array
- Start & end offsets for data
|7 | 14 | garbage ...
147
I/O Subsystem
Canonical Mode
If number of characters on output clist becomes greater than a high-water mark, the line discipline calls driver
procedures to transmit the data on the output clist to the terminal and puts the writing process to sleep.
When the amount of data on the output clist drops below a low-water mark, the interrupt handler awakens all
processes asleep on the event, the terminal can accept more data.
When multiple processes write out to a terminal, garbled output results but this is normally permitted.
char form[ ] - "this is a sample output string from child ";
main()
{
char output[128];
int i;
for (i = 0; i < 18; i++)
{
switch (fork())
{
case -1: /* error --- hit max procs */
exit();
default: /* parent process */
break;
case 0: /* child process */
/* format output string in variable output */
sprintf(output, "%s%d\n%s%d\n", form, i, form, i);
for (;;)
write(1, output, sizeof(output));
}
}
}
148
I/O Subsystem
When data is entered, the terminal interrupt handler invokes the line discipline interrupt handler, which places the
data on the raw clist for input to reading processes and on the output clist for echoing back to the terminal.
Character processing in input and output directions is asymmetric, two input clists and one output clist.
The use of two input clists means that the interrupt handler can simply dump characters onto the raw clist and
wakeup up reading processes, which properly incur the expense of processing input data.
The interrupt handler puts input characters immediately on the output clist, so that the user sees the typed character
with minimal delay.
Figure 38. Contending for Terminal Input
char input[256];
Data
main()
{
register int i;
for (i = 0; i < 18; i++)
{
switch (fork())
{
case -1: /* error */
printf("error cannot fork\n");
exitO;
default: /* parent process * /
break;
case 0: /* child process */
for (;;) The processes will spend most of their
{ time sleeping in terminal_read,
read(0, input, 256); /* read line */ waiting for input data.
printf("%d read %s\n", i, input);
} Intelligent terminals "cook" their
} input in the peripheral, freeing CPU
} for other work.
}
149
I/O Subsystem
150
I/O Subsystem
Control Terminal
Terminal on which the user logs into the system.
When a user presses DELETE, BREAK, RUBOUT, QUIT keys the interrupt handler invokes the line discipline,
which sends a signal to all processes in the control process group.
Streams
- different drivers tend to duplicate functionality
- a full-duplex connection between a process and a device driver
- a set of linear linked queue pairs, one for input and one for output
151
I/O Subsystem
/* assume file descriptors 0 and 1 already refer to physical tty */ Figure 45. Pseudo-code for
for (;;) /* loop */ Multiplexing Windows
{
select (input); /* wait for some line with input */
read input line;
switch (line with input data)
{
case physical tty: /* input on physical tty line */
if (control command) /* e.g. create new window */
{
open a free pseudo-tty;
fork a new process:
if (parent)
{
push a msg discipline on mpx side;
continue; /* back to for loop */
}
/* child here */
close unnecessary file descriptors;
open other member of pseudo-tty pair, get stdin, stdout, stderr;
push tty line discipline;
exec shell; /* looks like virtual tty */
}
/* "regular" data from tty coming up for virtual tty */
demultiplex data read from physical tty,
strip off headers and write to appropriate pty;
continue; /* back to for loop */
case logical tty: /* a virtual tty is writing a window */
encode header indicating what window data is for;
write header and data to physical tty;
continue; /* back to for loop */
}
}
152
Interprocess Communication
System V IPCs:
- messages
- shared memory
- semaphores
BSD sockets
Process Training
A debugger process, such as sdb, spawns a process to be traced and controls its execution with ptrace system call.
if ((pid = fork()) == 0)
{
/* child -traced process */
ptrace(0, 0, 0, 0);
exec("name of traced process here");
}
! ptrace
cmd = reading data, writing data, resuming execution
pid = process ID of traced process
addr = virtual address to be read/written in child
data = integer value to be written
/* -------------------------------------- */
/* trace */
int data[32];
main()
{
int i;
for (i=0; i<32; i++)
printf("data[%d]=%d\n", i, data[i]);
printf("ptrace data addr 0x%x\n", data);
}
/* -------------------------------------- */
/* debug */
#define TR_SETUP 0
#define TR_WRITE 5
#define TR_RESUME 7
int addr;
main(int argc, char *argv[])
{
int i, pid;
scanf(argv [1], "%x", &addr);
if ((pid = fork ()) == 0)
153
Interprocess Communication
{
ptrace(TR_SETUP, 0, 0, 0);
execl ("trace", "trace", 0);
exit(0) ;
}
for (i=0; i<32; i++)
{
wait ( (int *) 0);
/* write value of i into addr in proc pid */
if (ptrace(TR_WRITE, pid, addr, i) ==-1)
exit(0);
addr += sizeof(int);
}
/* traced process should resume execution */
ptrace(TR_RESUME, pid, 1, 0);
}
Disadvantages:
- kernel must do 4 context switches to transfer a word of data between debugger and traced process
- debugger can only trace child processes
- debugger cannot trace a process that is already executing
- impossible to trace setuid programs
Alternatives:
- users identify processes by their PID and treat them as files in /proc.
- users can examine the process address space by reading the files, and set breakpoints by writing files
System V IPC
Messages - allow processes to send formatted data streams
Shared Memory - allow processes to share parts of their address space
Semaphores - allow processes to synchronize execution
Message Queues
$ ipcs
IPC status from /dev/kroem as of Mon May 3 22:27:34 1993
#include <sys/msg.h>
main ()
{
int msqid;
msqid = msgget((key_t)10, IPC_CREAT);
printf("Message queue created with key %d\n", msqid);
}
$ipcs -q
IPC status from /dev/krnem as of Mon May 3 22:30:31 1993
struct ipc_perm {
ushort uid; /* owners user id */
ushort gid; /* owners group id */
ushort cuid; /* creators user id */
ushort cgid; /* creators group id */
ushort mode; /* access modes */
ushort seq; /* slot usage sequence nice number */
key_t key; /* key; */
};
/* -------------------------------------- */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
main ( )
{
int msqid;
key_t key = 32769;
msqid = msgget(key, IPC_CREAT | IPC_EXCL);
if (msqid < 0)
perror ("msgget failed");
else
printf ("Message queue created with key %d\n", msqid);
}
$ipcrrn -q <id_number>
QUEUE PERMISSIONS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
main()
{
int msqid;
key_t key = 15;
msqid = msgget(key, IPC_CREAT | 0644);
if (msqid < 0)
perror("msgget failed");
else
printf ("Message queue created with key %d\n", msqid);
}
$ipcs -q
IPC status from /dev/krnem as of Mon May 3 22:30:31 1993
155
Interprocess Communication
main()
{
int i, msqid;
key_t key = 100;
for (i=0; i<50; i++)
{
msqid = msgget(key, IPC_CREAT | PERMS);
if (msqid < 0) {
perror("msgget failed");
exit(1);
}
printf("msqid = %d\n", msqid);
if (msgctl(msqid, IPC_RMID, 0) < 0) {
perror("msgctl failed");
exit(1);
}
}
}
Whenever a message queue is created with the same name, the identifier value returned by msgget() is
incremented by the maximum number of table entries that are held by the table, each time the entry is reused.
Messages
msgqid = msgget(key, flag);
156
Interprocess Communication
----- -----
----- -----
----- -----
----- -----
: :
: :
/* Client Process */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSGKEY 75
struct msgform {
long mtype;
char mtext[256];
};
main() {
struct msgform msg;
int msgid, pid, *pint;
/* Server Process */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSGKEY 75
struct msgform {
long mtype;
char mtext[256];
157
Interprocess Communication
};
int msgid;
main()
{
extern cleanup();
int i, pid, *pint;
struct msgform msg;
for (i=0; i<20; i++)
signal(i, cleanup);
msgid = msgget(MSGKEY, 0777 | IPC_CREAT);
for (;;) {
msgrcv(msgid, &msg, 256, 1, 0);
pint = (int*) msg.mtext;
pid = *pint;
printf("server: receive from pid %d\n", pid);
msg.mtype = pid;
*pint = getpid();
msgsnd(msgid, &msg, sizeof(int), 0);
}
}
Shared Memory
Communicate directly by sharing virtual address space
shmget - creates a new region of shared memory or existing one
158
Interprocess Communication
/* ------------------------------------------ */
/* attaching shared memory twice to a process */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHMKEY 75
#define K 1024
int shmid;
main()
{
int i, *pint;
char *addr1, *addr2;
extern cleanup();
159
Interprocess Communication
pause();
}
SEMAPHORES
allow processes to synchronize execution by doing a set of operations atomically. Before semaphores, a process
would create a lock file.
! Dijkstra
two atomic operations P and V
P operation decrements the value of a semaphore
if its value is greater than 0
V operation increments its value
- value of semaphore
- process ID of last process to change semaphore
- number of processes waiting for semaphore value to increase
- number of processes waiting for semaphore value to equal 0
Semaphore Semaphore
Table Arrays
0|1|2|3|4| 5|6
-----
0|1|2
-----
0
-----
0|1|2
-----
:
160
Interprocess Communication
161
Interprocess Communication
/* struct sembuf {
unsigned short sem_nurn;
short sem_op;
short sem_flg;}; */
if (argc == 1) {
for (i=0; i<20; i++)
signal(i, cleanup);
semid = semget(SEMKEY, 2, 0777 | IPC_CREAT);
initarray[0] = initarray[1] = 1;
semctl(semid, 2, SETALL, initarray);
semctl(semid, 2, GETALL, outarray);
printf("sem init vals %d %d\n", outarray[0], outarray[1]);
pause(); /* sleep until awakened by a signal */
}
else
if (argv[1][0] == 'a') {
first = 0;
second = 1;
}
else{
first = 1;
second = 0;
}
semid = semget(SEMKEY, 2, 0777);
psernbuf.sem_op = -1;
psernbuf.sem_flg = SEM_UNDO;
vsernbuf.sem_op = 1;
vsernbuf.sem_flg = SEM_UNDO;
for (count=0; ; count++)
{ psernbuf.sem_nurn = first; semop(semid, &psernbuf, 1);
psernbuf.sem_nurn = second; semop(semid ,&psernbuf, 1);
printf ("proc %d count %d\n", getpid(), count);
vsernbuf.sem_nurn = second; semop(semid, &vsernbuf, 1);
vsernbuf.sem_nurn = first; semop(semid ,&vsernbuf, 1);
}
}
162
Interprocess Communication
When processes A and B run simultaneously, a situation could arise whereby process A has locked semaphore 0
and wants to lock semaphore 1, but process B has locked semaphore 1 and wants to lock semaphore 0.
Both processes sleep, unable to continue. They are deadlocked and exit only on receipt of a signal.
psernbuf[0].sem_nurn = 0;
psernbuf[l].sem_nurn = 1;
psernbuf[0].sem_op = -1;
psernbuf[0].sem_op = -1;
semop(semid, psernbuf, 2);
----- desc
----- num
value
:
-----
BERKELEY Sockets
The Application Program Interface (API) is the interface to a programmer. For UNIX there is Berleley Sockets and
System V Transport Layer Interface (TLI).
Network I/O includes File I/O system calls: open, creat, close, read, write, & lseek
! Network I/O considerations
- client or server?
- connection-oriented or connectionless
- process names are more important in networking
- more parameters for a network connection
- communication protocol record boundaries
- support multiple communication protocols
163
Interprocess Communication
Client
allocate space t_alloc()
create endpoint socket() t_open() open()
bind address bind() t_bind()
connect to server connect() t_connect()
transfer data read() read() read ()
write() write() write() write()
recv() t_rcv()
send() t_snd()
datagrams recvfrom() t_rcvudata()
sendto() t_sndudata()
164
Interprocess Communication
The name space used by unix domain protocols consists of pathnames, for example:
{ unixstr, 0 /tmp/log.1528, 0, /dev/logfile }
unixstr unix stream connection oriented
0 local address
/tmp/log.1528 local process
0 remote address
0 /dev/logfile remote process
Socket Addresses
/* defined in <sys/socket.h> */
struct sockaddr {
u_short sa_family; /* address family: AF_xxx */
char sa_data[14];} /* protocol specific addr */
/* defined in <netinet/in.h> */
struct in_addr {
u_long s_addr; /* 32-bit netid/hostid */
}; /* network byte ordered */
struct sockaddr_in {
short sin_family; /* AF_INET * /
u_short sin-port; /* 16-bit port number */
struct in_addr sin_addr; /* netid/hostid */
char sin_zero[8]; /* unused */
};
/* defined in <sys/un.h> */
struct sockaddr_un {
short sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname */
};
165
Interprocess Communication
…
connect(sockfd,(struct sockaddr*) & serv_addr, sizeof(serv_addr));
Socket System Calls
#include <sys/types.h>
#include <sys/socket.h>
protocol IPPROTO_UDP }
IPPROTO_TCP } AF _INET family
IPPROTO_ICMP}
IPPROTO_RAW }
accept takes the first connection request on the queue and creates another socket with the same properties as
sockfd. If there are no connection requests pending, this call blocks the caller until one arrives.
int sockfd, newsocketfd;
166
Interprocess Communication
OR
Byte Operations
bcopy(char *src, char *dest, int nbytes);
bzero(char *dest, int nbytes); /* write null bytes */
int bcmp(char * ptr1, char *ptr2, int nbytes);
A Simple Example
1. The client reads a line from its standard input and writes the line to the server.
2. The server reads a line from its network input and echoes the line back to the client.
3. The client reads the echoed line and prints it on its standard output.
4. This is known as an echo server. The example shows a concurrent server using connection-oriented
Internet.
Utility Routines
Read or writing n bytes to or from a stream socket.
int readn(int sockfd, char *ptr, int nbytes);
int writen(int sockfd, char *ptr, int nbytes);
int readline(int sockfd, char *ptr, int maxlen);
Note that readline function issues one read system call for every byte of data.
Would like to buffer the data using a read system call to read as much data as it can, and then examine the
buffer one byte at a time.
Read a stream socket one line at a time, and write each line back to the sender.
str_echo(int sockfd);
Read contents of FILE, write each line to stream socket, then read line back from socket and write to standard
out.
str_cli(FILE *fd, int sockfd);
Stream Pipes
int s-pipe(int fd[2]); /* unnamed stream pipe */
int ns-pipe(int fd[2]); /* named stream pipe */
168
Process Scheduling
The Scheduler
The kernel is responsible for sharing CPU time between competing processes.
Multi-Level Priority Queue
- linked list of runnable processes
Processes are allocated CPU time in proportion to their importance. Time is allocated in fixed size units called
"time quantums" (~ 1/10 second).
Process Table
Next PID PPID Stat
MLPQ ---> . 36 12 R . ---> Process 34
| -- free entry
0 .-- | - 18 1 S
1 .-- | free entry
2- | -> - 12 1 R . ---> Process 12
3- | free entry
4- | -> - 48 1 S
----> - 1 - R . ---> Process 1
Scheduling Rules
- Every second, scheduler recalculates priority of all runnable processes - organizes them into priority
queues.
- Every 1/10 sec, the scheduler selects highest priority process in priority queue and allocates it the CPU.
- If process is runnable at end of time quantum, it is placed at end of its priority queue.
- If process sleeps on an event during time quantum, the scheduler selects next runnable process.
- If process returns from system call during time quantum, and higher priority process is ready to run, the
lower priority process is preempted.
- Every hardware clock interrupt (1/100 second), the process's clock tick count is incremented, every 4th
tick, scheduler recalculates priority.
Memory Management
Sharing of RAM between processes (secure, efficient)
! Memory Pages
Allow processes bigger than RAM capacity to execute. RAM (code, data, stack) divided into fixed-size pages,
analogous to, disk divided into fixed-size blocks.
The size of memory page is set to size of disk block. Only pages of process, currently accessed or recently
accessed are stored in RAM pages, the rest are on disk.
169
Process Scheduling
It makes no sense to select a process if it is not loaded in memory, cannot execute until swapped in.
If several processes tie for highest priority, pick the one that has been "ready to run" the longest.
Each process table entry has a priority field. The priority of a process in user mode is a function of its recent CPU
usage (recently used lower priority).
User & Kernel mode priority - The kernel does not change the priority of processes in kernel mode.
170
Process Scheduling
The kernel adjusts the priority of a process that returns from kernel mode to user mode. The kernel recomputes the
priority of all active processes once
a second. At every clock interrupt,
the clock handler increments the
recent CPU usage.
171
Process Scheduling
priority = (CPU_usage / 2) +
60
ptl = times(&pb1);
for (i = 0; i < 10; i++)
if (fork() == 0)
child (i);
for (i = 0; i < 10; i++)
wait((int *) 0);
#include <sys/types.h>
pt2 = times(&pb2);
#include <sys/stat.h>
printf("parent real %u user %u sys
#include <sys/signal.h>
%u cuser %u csys %u\n",
pt2 - ptl, pb2.tms_utime - pb1.tms_utime,
main(argc, argv)
pb2.tms_stime - pb1.tms_stime,
int argc;
pb2.tms_cutime - pb1.tms_cutime,
char *argv[ ];
pb2.tms_cstime - pb1.tms_cstime);
{
}
extern unsigned alarm();
child(n)
extern wakeup();
int n;
struct stat statbuf;
{
time_t axtime;
int i;
struct tms cb1, cb2;
if (argc != 2)
long tl, t2;
{
printf("only 1 arg\n");
tl = times(&cb1);
exit(0);
for (i = 0; i < 10000; i++) stime(p
}
;
axtime = (time_t) 0;
t2 = times(&cb2);
printf("child %d: real %u user %u sys %u\n",
value), time(tloc), times (tbuffer), alarm
for (;;)
{
n, t2 - tl, cb2.tms_utime - cb1.tms_utime,
/* find out file access time */
cb2.tms_stime - cb1.tms_stime);
if (stat (argv[1], &statbuf) == -1)
exit(0);
{
}
printf("file %s not there\n", argv[1]);
exit(0);
Figure 51. Program Using Timer }
if (axtime != statbuf.st_atime)
{
printf("file %s accessed\n", argv[1]);
axtime = statbuf.st_atime;
}
signal(SIGALRM, wakeup); /* reset for alarm */
alarm(60);
pause(); /* sleep until signal */
}
}
wakeup()
{
}
174
Process Scheduling
Clock
- restart clock algorithm clock
- invocation of internal input: none
kernel functions output: none
- execution profiling {
- system & process restart clock; /* so that it will interrupt again */
accounting if (callout table not empty){
- track time adjust callout times;
schedule callout function if time elapsed;
- alarm signals }
- wakeup swapper if (kernel profiling on)
process note program counter at time of interrupt;
- control process if (user profiling on)
scheduling note program counter at time of interrupt;
gather system statistics;
gather statistics per process;
adjust measure of process CPU utilitization;
if (1 second or more since last here and interrupt not in critical region of code){
for (all processes in the system) {
adjust alarm time if active;
adjust measure of CPU utilization;
if (process to execute in user mode)
adjust process priority;
}
wakeup swapper process is necessary;
}
Figure 53. Clock Handler }
175
Process Scheduling
! swapping
- managing space on swap device
- swapping processes out of main memory
- swapping processes into main memory
! Fork Swap
The fork systems call assumes that parent process
found enough memory to create the child context.
The parent places the child in the "ready-to-run"
state and returns to user mode.
! Expansion Swap
Process requires more physical memory than is allocated (user stack growth or brk system call).
! Swapping Processes In
When the swapper wakes up to swap
processes in, it examines all processes
that are in the state "ready to run but
swapped out" and selects one that has
been swapped out. the longest.
177
Process Scheduling
178
Process Scheduling
179
Buffer Cache
Buffer Headers
A buffer consists of two parts:
- the memory array that contains data from the disk and
- the buffer header that identifies the buffer.
The buffer is an in-memory copy of the disk block. A disk block can never map into more than one buffer at a time.
180
Buffer Cache
The kernel takes buffers from the head of the free list, removes them from the list, and returns a buffer to the buffer
pool by attaching the buffer to the tail of the free list.
Hence buffers closer to the head have not been used as recently as those towards the tail.
When the kernel accesses a disk block, it searches for a buffer with appropriate device-block number.
Rather than search entire buffer
pool, it organizes buffers into
separate queues, hashed on
device-block number.
The kernel links the buffers on a
hash queue into a circular,
doubly linked list, similar to the
free list.
Each buffer always exists on a
hash queue. Every disk block in
the buffer pool exists on one and
only one hash queue and only
once on that queue. A buffer may
be on the free list as well if its
status is free.
Figure 64. Buffers on the Hash Queues
5 Scenarios for Retrieval of a Buffer
181
Buffer Cache
182
Buffer Cache
The kernel places the buffer at the end of the free list, unless an I/O error occurred or is marked old, in which case it places the buffer at the beginning of the free list.
The kernel raises the processor execution level to prevent disk interrupts while manipulating the free list, thereby preventing corruption of the buffer pointers.
When the
asynchronous write
completes, the
kernel releases the
buffer and places it
at the head of the
free list.
183
Buffer Cache
184
Buffer Cache
Process B will find the locked block on the hash queue. Process B marks the buffer "in demand" and the sleeps.
Another process C, may have been waiting for the same buffer, if C is scheduled before B, B must check the block
is free.
Process C may allocate the buffer to another block, so when process B executes it must search for the block again.
With contention for a locked buffer need to start search again.
Processes in user mode do not control the allocation of kernel buffers directly, so they cannot purposely "hog"
buffers.
The kernel does not guarantee that a process get a buffer in the order that they requested one.
If the disk block is not in cache, the kernel calls the disk driver to "schedule" a read request and goes to sleep
awaiting the event that the I/O completes.
185
Buffer Cache
algorithm breada /* block read and read ahead */ Figure 74. Algorithm for Block Read Ahead
input: (1) file system block number for immediate read "breada".
(2) file system block number for asynchronous read
output: buffer containing data for immediate read
{
if (first block not in cache)
{
get buffer for first block (algorithm getblk); If the second block is not in buffer cache, the
if (buffer data not valid) kernel instructs the disk driver to read it
initiate disk read; asynchronously.
}
if (second block not in cache) algorithm bwrite /* block write * /
{ input: buffer
get buffer for second block (algorithm getblk); output: none
if (buffer data valid) {
release buffer (algorithm brelse); initiate disk write;
else if (I/O synchronous)
initiate disk read; {
} sleep(event I/O complete);
if (first block was originally in cache) release buffer (algorithm brelse);
{ }
read first block (algorithm bread); else
return buffer; if (buffer marked for delayed write)
} mark buffer to put at head of free list;
sleep(event first buffer contains valid data); }
return buffer;
}
Figure 75. Writing a Disk Block Bach, "bwrite".
If the write as asynchronous, the kernel starts the disk write but does not wait for the write to complete. The kernel
will release the buffer when I/O completes.
The kernel marks the buffer "delayed-write" and releases the buffer using "brelse". The kernel writes the block to
disk before another process can reallocate the buffer.
Summary
The kernel uses least recently used replacement to keep blocks in buffer cache, assuming that blocks that were
recently accessed are likely to be accessed again soon.
The hash function and hash queues enable the kernel to find particular blocks quickly, and use of doubly linked
lists makes it easy to remove buffers from the lists.
The kernel identifies the block it needs by logical device and block number. "getblk" searches buffer cache for a
block, if present and free, locks the buffer and returns it.
If the buffer is locked, the requesting process sleeps until it becomes free.
If the block is not in the cache, the kernel reassigns a free buffer to the block, locks it and returns.
If kernel determines that is not necessary to copy data immediately to disk, it marks the buffer "delayed-write". A
process is not sure when the data is physically on disk.
186
Unix Administration
Administration Topics
1. Day-to-Day Tasks
2. File System
3. Backup
4. Startup & Shutdown
5. Cron
6. Printing
7. Networks
8. Mail
9. News
A. Accounting
B. Performance tuning
C. Epilogue
Day-to-day Administration
Between meetings and user interrupts
187
Unix Administration
Other tasks:
# cleanup
(
find / -type f \( -name core -o -name a.out \
-o name dead.letter \) -atime +1 -exec rm -f {} \;
find /usr/spool/console -type f -mtime +7 -exec rm -f {} \;
find /usr/preserve -type f -mtime +15 -exec rm -f {} \;
find /usr/mail -type -f -atime +28 -exec rm -f {} \;
) > /dev/console 2>&1
User password administration on multiple machines without YP requires the creation of a user-ID data base.
User-ID from 0 to 100 are reserved for non-humans, User-ID from 101 to 999 are system staff
File Systems
Disks are split into partitions
A partition is then mounted as a subtree of the Unix directory structure
Example Configuration
Drive 0 Drive 1
/ root partition / 2n d root partition
/tmp temporary files /usr system libraries
swap space /home user directories
- root partitions are small and near the outer edge of the drive to reduce risk of failure
- 2nd root partition to fix things if drive 0 fails (Harder without swap space)
- Essentials are kept on one drive (drive 0)
- /tmp can fill up and not interfere with /
- /tmp on different drive to user files to reduce disk head seeking when creating temporary files from user files
188
Unix Administration
- Disk configurations also specify the number of blocks and inodes for each filesystem
- partitions may be mounted read only
! Must include:
/bin Frequently used commands and those required to boot, restore, and repair system
(include C compiler and assembler)
/lib Essential C library files
/etc System configuration and accounting management tables and some admin programs
e.g. init, inittab, rc, passwd, group, ...
/dev Home of the device files that are used by the device drivers to interface kernel and hardware
/tmp Temporary files only
/lost+found (This exists on each filesystem) Missing files found during filesystem consistency checking,
see man on fsck
Backups
- security in case of damage to disks, viruses
- restore files accidentally lost/damaged
! Principles of backup security
- Files are worth far more than equipment in terms of man hours and irreplaceable resources
- Full, partial & Incremental backups UNIX dump command provides multilevel backups (increments of
increments).
- Keep multiple versions of full backups. Don't just write over your last version. The system might fail and then
you have nothing !!!
- Keep long term backups. Files may be lost/corrupted but not noticed for a long period. Recent backups are then
useless.
- Keep a full backup in another distant building. Fire insurance may restore the machine but not the files 11
(Your boss will be grateful...).
- Keep dump tapes in a safe cool environment, preferably the same room (i.e. temperature) as the tape drive (1/2
hr to acclimatize tapes).
! Backup considerations
- Nonarchive (No Header File) - copies everything, external label on tape
- Archive (Header File) - writes header first
- Catalog (Online Data Base) - contents, dates, media name, locations
Unmounting a disk for even a short period is expensive day or night, in terms of work hours lost and programs
killed or maimed.
! cpio
Similar to tar, reads a list of file names from stdin to be copied to tape, cannot detect end of tape.
ls /user/bill | cpio -oc > /dev/floppy
find . -print } cpio -ocv > /dev/rmt0
Unix "find" command can search for all files modified since a given date and hence be used with "cpio" for
incremental backups. No rewind is permitted.
find / -depth -print | cpio -odlmv > /dev/nrmt1
find /etc -depth -print | cpio -odlmv > /dev/nrmt1
190
Unix Administration
*)
IN=-mtime -2 -type f; TYPE=incremental;;
esac
for dir in 'awk -F:' $3>100 { print $6 }' /etc/passwd' do
echo "$dir \n"
find $dir -depth $IN -print | cpio -ovdum > /dev/nrmt0
done
echo "\n$TYPE backup complete -- rewind tape"
exit 0
To recover a file:
cpio -ivdum <pathname> < /dev/tape
i -in, v - verbose, d - directory
u - unconditional copy old files over new
m - modification time
! dump
"dump" only writes from device to device Berkeley UNIX - not available on vanilla System V.
dump 0 /dev/rdsk/0s5 # sent to default tape
dump 9udf 6250 /dev/rmt1 /dev/rdsk/Os6
! restore
interactive mode - BSD version
fete/restore -if /dev/rmt1
restore> cd home/bill/bin
restore> ls
ar bart chkdsk
restore> add bart
restore>extract
...
! Backup Strategies
Unix dump has a "level" option for control of incremental backups.
Level 0 is a full backup
Level 1 is a incremental since the last level 0
Level 2 is a incremental since the last level 1 or 0
Level 3 is a incremental since the last level 2, 1 or 0
:
Level N is a incremental since the last level < N
Example Strateqy:
Full backup is done every week.
Incremental backups are done every day.
Mon 0
Tue 1
Wed 2
Thu 3
Fri 4
Another Example:
Week-1 0
Week-2 1
Week-3 2
Week-4 1
Week-5 3
Week-6 1
Week-7 2
Week-8 1
Week-9 0
This permits recovery of files lost anytime over the past 8 weeks.
We can combine these two strategies using levels 4, 5, 6, 7 during Tuesday to Friday and performing levels 0..3
each Monday.
Shutting down
- /etc/shutdown - shuts down the system cleanly
- /etc/sync; /etc/halt - minimum after all users logoff
- /etc/init S - go single user
- /etc/reboot - shutdown and restart as, some versions do not sync
Does the systems administrator have to process more interrupts than an operating system?
The init process has ID = 1, has no parent. It reads the /etc/inittab configuration file.
Look at the /etc/inittab file on "water".
id:runstate:action:process
where "action" is either:
- initdefault - set default run-level
- boot
- bootwait
- wait
- respawn - when process dies run it again
- process
- off
Run-level 2 entries include /etc/rc initialization script and letc/getty for each terminal line.
/etc/rc
- speed up startup
- check filesystem
- start system accounting
- start daemons
- recover files after crash
- start printer spooler
System Shutdown
Shutdown vs Reboot
- users logged on
- how quickly need system down shutdown uses kill -14 on processes
reboot uses kill –9 on processes
sync writes memory out to disk
Phase:
1. Checks Blocks and Sizes
i.e. checks inode types, examines the inode block numbers for bad or duplicate blocks, and checks the inode
format.
2. Checks Pathnames
Removes directory entries pointing to files or directories modified by Phase 1.
3. Checks Connectivity
Cleans up after Phase 2 - making sure that there is at least one directory entry for each inode and that multiple
links make sense.
4. Checks Reference Counts
List errors from unreferenced files, missing or full "lost+found" directories, incorrect link count, bad or
duplicate blocks, or incorrect sum for free inode count.
5. Checks Free List
193
Unix Administration
if (argc != 3 ){
fprintf(stderr,"usage: mklf /path/dirname /dev/special\n");
exit(1);
}
194
Unix Administration
Sync
The superblock exists both in memory and on disk.
The "sync" command flushes memory to disk. This is done by the kernel or /etc/update at regular intervals.
#define TRUE 1
main() /* update.c */
{
while (TRUE) {
sync(); sleep(30);
}
}
Cron
Cron - from the greek "chronos" meaning "time".
/etc/cron executes commands at specified dates and times. Regularly scheduled commands can be specified by
instructions in /etc/crontab. Cron is started by /etc/rc at boot time and from then on wakes up each minute to
determine if any commands are scheduled to be run.
Examples:
Automatically shutdown at 8am each Friday
0 8 * * 1 /etc/shutdown "shutting down for backup"
Order some milk at midnight every Monday - Friday
0 0 * * 1-5 echo "I need more milk" | mail milkman
Run suidcheck every 20 minutes Mon-Fri, 9am - 5pm
0, 20, 40 9-16 * * 1-5 suidcheck
0 17 * * 1-5 suidcheck
Printing
$ lpr filename # BSD
$ lp filename # System V
A "spooler" is a method of buffering data on its way to a specific destination. i.e. hold files to be printed until line
printer is ready to process them.
A "daemon" program wakes up to do the task when required, and then goes back to sleep again. These daemons
reside in /usr/lib or /etc called lpd (BSD) or lpsched (SYS V).
! BSD printing
195
Unix Administration
! Testing devices
cat /etc/motd > /dev/lp
stty < /dev/tty0l # display settings
stty 9600 < /dev/tty0l # change settings
ln /dev/tty0l /dev/epson
lpstart
#!/bin/sh
# lpstart - opposite lpshut
# e.g. /bin/su lp -c "/usr/local/etc/lpstart" &
196
Unix Administration
fi
done
echo $status
Compression
tar cf dirname.tar dirname; compress dirname.tar
(tar cf- dirname ) | compress > dirname.tar.Z
Networks
NFS or RFS Network File System or other TCP/IP Services
TCP Transmission Control Protocol Transport - data packaging
IP Internet Protocol Network Layer - routing
Ethernet Link Layer - Ethernet Address
Physical Hardware
Distributed Filesystems
NFS and RFS provide facilities for distributed filesystems. This means that users can have a host on their desk but
still access shared disks
Backups and software updates can be more reliably administered in one place.
Super User permissions do NOT work across remote mounted filesystems - prevents broke security propagating
over the network.
To: [email protected]
Subject: I need help ...
<text>
Network Domains
Used throughout the Internet:
[email protected]
Australian domain
Education sub-domain
QUT sub-domain
Faculty sub-domain
Host "water"
User name
Domains & sub-domains are used to prevent naming conflicts with other domains. So we can invent our own host
names without worrying about host names at other sites.
Things to Remember
Don't be superuser more than necessary. Always re-read what you type when using commands like:
197
Unix Administration
Optimizing Performance
! Rebuilding the filesystem
Over time, files on the system become fragmented and spread their data over distant parts of the filesystem. To
optimize system performance, the system should ideally be copied onto tape and rebuilt from scratch to enhance
performance. The drives should also be reformatted to enhance reliability. (Only if you know what you're doing!!)
! Super User
- login as "root"
- "su" - preferred
! AT&T System V
sar - system activity reporter (-a all)
crash
! BSD
iostat - number of chars (kbytes) read, written to term, disk, and cpu time as user mode, niced,
in-system mode, idle mode.
uptime - display time, system up time, number of users, number of jobs
vrnstat - virtual memory statistics - procs, memory, page, faults, cpu
pstat - process statistics
! Tunable Parameters
NBUF - number of system buffers 250 (3 x number of ttys)
NHBUF - number of hash buffers 64
NPROC - number of process table entries/slots 250
MAXUP - number of process a user can have 20
MAXPROC - maximum number of system processes
NCLIST - character minibuffers are called clists.
Accounting
Unix provides facilities for monitoring system performance, network traffic etc. The administrator may need to tune
the system by reorganising filesystems or network links.
od -c /etc/utmp | more
198
Unix Administration
New Software
! Purchasing
For major equipment & software purchases ask to see things working before you commit yourself. This includes
hardware AND software.
! Installing
- Backup previous version of system
- Installation may need root privileges. If shareware or network software – use source code from moderated
news groups.
- Keep software packages in separate directories to handle future releases.
- Test basic features.
- Check software works in non-privileged accounts.
- Liaise with customer support from company supplying the software if problems occur.
$ WON'T WORK
Common problems that usually appear when a user complains "the system does not work properly".
e.g. pr .profile | lp # no longer works
The x bit has been removed for the owners home directory
e.g.
System administrator performs chmod 666 recursively from root, thus no traversal privileges - end of system.
$ NO DISK SPACE
df –t # show no disk free
sed -n '$p' /usr/spool/console/May19 # print last line
find / -type f -size +100000c –print # find large files
(fuser -uk /dev/dsk/0s5; umount /dev/dsk/0s5)
fsck /dev/dsk/0s5 # no errors now
The in-core inode table had been corrupted
199
Unix Security
Philosophy of Security
! Computer systems must be accessible
- easy to access ("open")
- able to communicate with other hosts
! Trade off between openness & security
! Depends on attitudes of administrators and users
- an investment by both
! Unix tries to be more open. Full on-line manuals, Unix source available
! Experience has shown that non disclosure of information does not assure security.
! Unix philosophy is to be more open so that security holes are found and fixed!
"root" account used to install software, configure system, backing up, managing accounts etc.
Password Security
- Minimum of 6 chars (Unix allows 8 chars)
- Not personal (e.g. girl/boy-friend name)
- Never dictionary words
- Include non-alphabetic characters or mixed upper/lower case (any printable character)
- Can be remembered without writing down e.g. 1st letters of a sentence
- Different for computer different systems
- Change regularly (but not predictably!)
- Don't reuse old passwords - always invent a new one.
- Don't write it down or store it in the function keys of your terminal.
Examples:
user name:encrypted password:user uid:group gid: name, room, phone comment:home dir: login shell:
root: h6H9fs*k: 0: 1: Super User: /root: /bin/sh
lpstat: : 10: 10: : : /usr/bin/lpstat
accts: f7J8gs6/: 80: 100: Accounts: /tmp: /db/accmenu
fred: Ef5g7sG3: 500: 300: Fred Hill,A501,1900: /user/fred: /bin/sh
200
Unix Security
- Group members can be implied by the default GID in /etc/passwd. All users must therefore be members of
at least one group.
- User "fred" is a member of group "users" because his default GID in /etc/passwd is 300 which is group
"users" in /etc/group.
Yellow Pages
Manages files such as /etc/passwd, etc/group ... across computer networks.
ypcat passwd
ypcat group
ypcat hosts
Privileged Access
Often a non-privileged user needs a system program to be able to access or update files which are otherwise
inaccessible. To be able to access these privileged files there must exist a process with an effective UID and/or GID
which provide the permissions required.
201
Unix Security
UID GID
Process Eff. Real Eff. Real
/bin/sh fred fred student student
[300] [300] [200] [200]
Sendmail Process
- has permissions of the user "mail"
- has permissions of the group "spool"
- creates files owned by "root" and in the group "mail"
- can change back to permissions of user "fred" or group "student" by making effective UID/GID equal to real
UID/GID
Daemons
are alternatives to the setuid/setgid programs for
providing secure access to system files. User's Client Daemon Server
Process Process
(Unprivileged) (Privileged)
>
<
Socket Connection
Daemon run's permanently, waiting to service requests from other non-privileged client processes.
>
<
Socket
Changing UID or GID
! su command
/bin/su [user] creates a new shell with UID & GID set to that of user's /etc/passwd record
Always type the full path "/bin/su" to avoid trojans, especially when changing to super user.
! newgrp command
newgrp group changes effective GID of the current shell "newgrp" is implemented within the shell
File Encryption
$ crypt < exam320 > exam320.encrypted
$ rm exam320
202
Unix Security
Spoof
- Run by "nasty-user"
- "nasty" is still logged in
- Typically tricks unsuspecting user into thinking they're logging on and giving away their password
Trojan Horse
- Program executed by unsuspecting user
- Tricks the unsuspecting user into thinking that the program only performs a safe function
- Usually the same name as a safe program or as a program to perform some other function
- On Unix, it's usually in a $PATH directory
Special Trojans
Viruses
- Modifies other programs to make them into similar Trojans, hence "infecting" other programs
- Can spread throughout a system
Time bombs
- Waits until a given time before it performs the nasty deed e.g. 1st April
Worms
- Virus that can spread across a network
- The Internet Worm
203
Unix Security
Sushi
- first thing a bad person might try once root
# cp /bin/sh /own/bad/sushi
# chmod 4755 /own/bad/sushi
- untraceable access via super-user shell interactive
$ cd /own/bad
$ sushi
#
- never let anyone use root password or login
- no program that is SUID root should be writable
- don't use any SUID shell programs
- checks for SUID programs
- do not use SUID on programs with a shell escape
- use chmod 4755 not chmod +s
- restrict chown to root
Crontab
/usr/lib/crontab
/usr/lib/atrun is started by cron every 10 minutes
User Protection
- Horne directories should not be writable
find 'awk -F: '{print $6}' /etc/passwd' \
-prune -perm -02 -exec ls -ld '{}' \;
Device Files
- Protect memory and swap files: mem, krnem, swap.
- All devices should be in /dev
# find devices outside /dev
find / -hidden -name /dev -prune -o -type b -exec ls -1 {} \;
Network Security
204
Unix Security
Perspective on Security
Access controls and auditing to prevent unauthorized access attempts (reading, modifying, deleting).
Weak Points:
- computers, networks, users, administrators
Security packages:
- repeated login attempts
- monitor files requests
205
Unix Security
pwentry = getpwuid(getuid());
printf("Hello, %s\n", pwentry->pw_name);
/* but storage associated with it will not be removed until the last file descriptor
referring to file is closed */
! Executing commands
/* want to edit first argument from within program */
sprintf(cmdstr, "ed %s", argv[l]);
206
Unix Security
system(cmdstr);
/* or specify path */
system("PATH=/bin:/usr/bin:/etc ed");
$ cp ed bin
$ PATH=: IFS=/ smarter idiot
/* solution */
system("IFS=' \t\n'; export IFS: /bin/ed");
system("IFS=' \t\n'; export IFS; PATH=/bin:/usr/bin:/etc ed");
! Shell Escapes
saveeuid = geteuid();
setuid(getuid());
system("/bin/ed") ;
setuid(saveeuid):
$ cat mkrmdir. c
main ()
{
system("/bin/mkdir foo");
system("/bin/rmdir foo");
}
$ ls -l mkrmdir
-rwsr-xr-x 1 pat ITB100 2048 May 26 17:01 mkrmdir
$ ls -ld
drwxr-xr-x 2 pat ITB100 320 May 26 17:02 .
$ who am i
greg tty08 May 26 17:05
$ mkrmdir
mkdir: cannot access.
rmdir: foo non-existent
$ su pat
Password: XXXX
$ id
207
Unix Security
uid=10(pat) gid=10000(ITB100)
$ mkrmdir
! Programming as root
- some routines can only be called from a process whose effective UID is zero (a root process)
- setuid() & setgid() - behaves differently for root
The "init" program is started when the system is started. It is run as a root process with both its effective and
real UIDs set to zero. init starts "getty" on a terminal which starts "login" once a user begins logging in.
Thus, both getty and login run as root processes. So when login is started, it runs with effective and real UIDs of 0.
After the password is validated, login must be able to set effective and real UIDs to that of the user logging in
before the user's shell is started (i.e. setuid(user's UID)).
$ cat chrt.c
/* chrt must be SUID to root */
main ()
{
chdir("/restrict");
chroot("/restrict");
setuid(getuid));
execl("/bin/sh", "sh", 0);
}
System Administrator
- implements auditing procedures
- inspects and analyzers audit log
- administers group and user accounts
208
Unix Security
Limiting SETUID
- use only when absolutely necessary
- make not writable
- use setgid instead of setuid
- periodically search for new setuid programs
- know what the setuid and setgid programs do
- write setuid programs so that they can be tested on non-critical data, without setuid attributes, only add setuid
after checking security
- if in doubt remove setuid and rebuild program.
209