Linux
Linux
An operating system is the program that controls all the other parts of a computer system - both the hardware and the software. An operating system provides orderly and controlled allocation and use (i.e., sharing) of the resources by the users (jobs) that compete for them. One major function of an operating system is to hide the complexity of the underlying hardware and give the user a better view (an abstraction) of the computer.
OS architecture
Operating System
Operating system interacts with user in two ways Operating system commands Enables user to interact directly with the operating system.
Operating system calls Provides an interface to a running program and the operating system. System calls in UNIX are written in C.
Bootstrapping
The process of initializing the computer and loading the operating system is known as bootstrapping. This usually occurs when the computer is powered up or reset. The initial loading is done by a small program that usually resides in non-volatile memory (e.g., EPROM). This in turn loads the OS from an external device. Once loaded, how does the operating system know what to do next? It waits for some event to occur: e.g., the user typing a command on the keyboard.
Advantages of Linux OS
Linux source code is freely distributed Linux is Multi-user. Linux runs on a wide range of hardware Linux is exceptionally stable Linux has the tools and applications you need Linux is one of the most secure operating systems.
Features of UNIX
Multi-user, multitasking, timesharing
Portability
Modularity
File structure Security Strong networking support & advanced graphics
Accessing OS services
The mechanism used to provide access to OS services (i.e., enter the operating system and perform a privileged operation ) is commonly known as a system call. The (only) difference between a procedure call and a system call is that a system call changes the execution mode of the CPU (to supervisor mode) whereas a procedure call does not. System call interface: A set of functions that are called by (user) programs to perform specific tasks.
Address of the addr block Address of the addr block Address of the addr block
At the time a new file is created, it gets a free inode. The following information in that inode :
Owner and group owner of the file. File type (regular, directory, ...) Permissions on the file Date and time of creation, last read and change. Date and time this information has been changed in the inode. Number of links to this file File size An address defining the actual location of the file data.
The Shell
Whenever you login to a Unix system you are placed in a program called the shell. On most Linux systems a program called bash acts as the shell program. bash stands for Bourne Again SHell, an enhanced version of the original Bourne shell program, sh, written by Steve Bourne There are several additional shell programs available on a typical Linux system. These include: ksh, tcsh and zsh.
PROCESS
A process is a running instance of a program. A process is said to be born when starts executing and remains alive as long as the program is active. As files, processes do have attributes,
PID: Process ID PPID: Parent process ID TTY: Terminal on which the process is connected
The Shell
The Shell is the program that directly executes your commands. The shell acts as a command interpreter. Activities The shell issues a prompt and waits for command. The shell scans for meta-character and expands them. It then passes command to kernel. Waits till the command is executed by the kernel and reappears again.
Simple Commands
pwd
(print [current] working directory)
date
Displays the current date and time Date has various options, which can be used as
+%d to display day of month (01..31)
to display date in the format (mm/dd/yy) to display day of month, blank padded to display hours component (00 .. 23) of current date
Simple Commands
who
Displays the names of all the users who have currently logged in
who am i
Displays the name of the current user.
Getting Help
The command man gives you access to an on-line manual containing a complete description of every command available on this system. man can also provide you with one line descriptions of commands specified by name; or for all commands whose description contains any of a set of keywords.
Meta Characters
Meta Characters * ? [] Purpose Example $ ls l *.c file* $ ls l file? $ ls l file[abc] $ cat file1; cat file2 $ cat abc | wc $ (echo ==== x.c ====; cat x.c) > out count=`expr $count + 1` assuming count has value3, this increments the value of count echo expr $count + 1 displays expr $count + 1 echo expr $count + 1 displays expr 3 + 1 assuming the variable count has value 3 Match with one or more characters or none Match with any single character Match with any single character within the brackets ; Command separator | Pipe two commands () Group commands Useful when the output of thecommand group has to be redirected `command` Execute the command enclosed within back quotes. Useful when the output of a command into a variable in a shell script
string
string
Quote all characters with no substitution (ex. no special meaning for $ ) Quote all characters with substitution. The characters $,\ (back slash) and back quote have special meaning.
$ chmod 744 xyz this sets read, write and execute permissions for owner, read permission for group and others
Directory Creation
Command Syntax mkdir [OPTION] DIRECTORY $ mkdir <path>/<directory> $ mkdir m <directory> $ mkdir p <directory1>/<directory2>/<directory3>
Example: $ mkdir project1 This creates a directory project1 under current directory Note: Write and execute permissions are needed for the directory in which user wants to create a directory
Directory Removal
rmdir command removes directory Syntax rmdir <directory name> Example Removes project1 directory in the current directory rmdir project1 Remove multiple directories rmdir pos1 pos2 Remove the directory recursively rmdir p dir1/dir2/dir3 rmdir removes a directory if it is empty and is not the current
Command - cp
Used to copy files across directories Syntax cp <source file> <new file name> Options to cp -p Copies the file and preserves the following attributes -r recursive copy; copy subdirectories under the directory if any -i interactive; prompts for confirmation before overwriting the target file, if it already exists
Command - mv
Used to move a file, or rename a file
Preserves the following details owner id group id permissions Last modification time
-f
-i
Command - rm
Used to remove a file Syntax : rm file(s)
-f suppresses all prompting
-i
-r will recursively remove the file from a directory (can be used to delete a directory along with the content )
cat
cat command takes the input from the keyboard, and sends the output to the monitor
We can redirect the input and output using the redirection operators
$ cat > file1 Type the content here press <ctrl d> $ cat file1 Displays the content of the file $cat >> file1 This will append standard input to the content of file1
touch
touch is used to change the time stamp of the file
touch <file> will change the time of change of the file if the file exists
If the file does not exist, it will create a file of zero byte size.
wc & sort
wc A filter used to count the number of lines, words, and characters in a disk file or from the standard input. -l - displays the number of lines -w - displays the number of words -c - displays the number of characters sort
sort
Sorts the contents of the given file based on the first char of each line. -n numeric sort (comparison made according to strings numeric value) reverse sort specify delimiter for fields specify sorting field numbers
-r -t
+num
grep
grep -Global Regular Expression Printer is used for searching
regular expressions Syntax grep <options> <pattern> <filename(s)> -c displays count of the number of occurrences -n displays line numbers along with the lines -v displays all lines except lines matching pattern -i Ignores case for matching
tail
Displays the last n lines of a file $ tail -3 file1 Can also specify the line number from which the data has to be displayed till the end of file $ tail +5 file1
Filter command - tr
tr - translate filter used to translate a given set of characters Example : tr [a-z] [A-Z] < filename This converts standard input read from lower case to upper case. -s char
Squeeze multiple contiguous occurrences of the character into single char
-d char
Remove the character
Command Piping
Allows the output (only the standard output) of a command to be sent as input to another command. Multiple pipes may appear in one command line. Example: $ cat * | wc $ cat fil1 | head | wc -l
find
Lets user to search set of files and directories based on various criteria Syntax: find [path...] [expression] [path] where to search [expression] What type of file to search (specified with type option) What action to be applied (exec, print, etc.) Name of the files (specified as part of name option, enclosed in ) Example find . name *.c -print
lists all files with .c extension from the current dir & its subdirectories
find
Finding files on the basis of file size
size [+ ]n[bc]
n represents size in bytes (c) or blocks (b) of 512 bytes find . size 1000c lists all files that are exactly 1000 bytes in size
find . size +1000c lists all files that are more than 1000 bytes in size find . size 1000c lists all files that are less than 1000 bytes in size
Finding files on the basis of access time (atime) or modified time (mtime)
n represents number of days ( actually 24 * n hours) lists files accessed exactly 2 days ago
find . atime 2
find . atime +2
find / mtime 2
Compression Utilities
gzip, Usage is very similar to compress and pack utilities in Unix: gzip [-vc] filename where -v displays the compression ratio. -c sends the compressed output to standard output and leaves the original file intact. gunzip gunzip can uncompress files originally compressed with compress.
Linking files
Rather than having multiple copies of a file, Linux uses linking to one file to save disk space and administrative headaches trying to keep multiple copies up to date and synchronized. Linux supports two types of links,
hard links and symbolic links.
DIRECTORY AND FILE HANDLING COMMANDS FILE COMPRESSION compress, gzip $ compress filename or $ gzip filename $ uncompress filename or $ gunzip filename
VI Editor
Introduction
The VI editor is a screen-based editor used by many Unix users. The VI editor has powerful features to aid programmers. The VI editor lets a user create new files or edit existing files. The command to start the VI editor is vi, followed by the filename.
VI Editor
The Two Modes of VI The VI editor has two modes:
command and insert.
The command mode allows the entry of commands to manipulate text. These commands are usually one or two characters long, and can be entered with few keystrokes. The insert mode puts anything typed on the keyboard into the current file.
VI Editor
VI starts out in command mode. The commonly used commands to get into insert mode are a and i. Once you are in insert mode, you get out of it by hitting the escape key. You can hit escape two times in a row and VI would definitely be in command mode. Hitting escape while you are already in command mode doesn't take the editor out of command mode. It may beep to tell you that you are already in that mode.
VI Editor
Some Simple VI Commands
a
enter insert mode, the characters typed in will be inserted after the current cursor position. If you specify a count, all the text that had been inserted will be repeated that many times.
h
move the cursor to the left one character position.
j
move the cursor down one line.
k
move the cursor up one line.
l
move the cursor to the right one character position.
VI Editor
i
enter insert mode, the characters typed in will be inserted before the current cursor position. If you specify a count, all the text that had been inserted will be repeated that many times.
r
replace one character under the cursor. Specify count to replace a number of characters
u
undo the last change to the file. Typing u again will redo the change.
x
delete character under the cursor. Count specifies how many characters to delete. The characters will be deleted after the cursor.
VI Editor
Deleting words and lines Press the ESC key to enter command mode before using these commands. To delete
current word previous word entire line to end of line to start of line next n lines dw db dd d$ d0 (zero) ndd
VI Editor
Copy and Paste
The command y yanks the text into the temporary buffer. The command p pastes the contents of the temporary buffer back into the file immediately after the cursor.
VI Editor
Saving files and exiting vi Press the ESC key to enter command mode before using these commands. To quit vi
and save the contents of the buffer to the file
without saving the contents of the buffer to the file:
:wq
:q! To quit the vi editor: :q If you have edited (changed) the file you will be prompted with the message: No write since last change You can either save the changes or quit without saving any of the changes you have made.
Shell Programming
Shell is a command line interpreter and also provides a variety of useful programming features to make your scripts truly powerful. A Shell script is collections of commands that are stored in a executable plain file. The shell can read this file and act on the commands as if they were typed at the keyboard. ADVANTAGE A wide range of tasks can be automated.
Shell Programming
EXAMPLE: $vi shell_ex echo "This is a very simple shell procedure " echo "created with the basic echo command " "and three other very basic commands echo ps echo who echo ls
echo
Shell Programming
$sh shell_ex This is a very simple shell procedure created with the very basic echo command and three other very basic commands PID TTY TIME COMMAND 10443 rt02120 0:01 sh 10427 rt02120 0:04 ksh sgavlick rt021e0 Sep 7 13:26 teacher rt021b0 Sep 7 14:39 memo class_notes
Shell Programming
VARIABLES
The Shell has no true numeric variables. It uses string variables to represent numbers, as well as text. Naming a variable has few rules.
It must start with a letter. It must not contain embedded spaces. Use underscores instead. Don't use a name that is already a word understood by bash. These are called reserved words and should not be used as variable names.
Shell Programming
There are three types of variables in the Shell. They are
user variables, Shell variables, and Read-only Shell variables.
You can declare, initialize, read, and modify user variables from a Shell script or from the command line. The Shell also initializes the read-only shell variables, and you can read but not modify them.
Shell Programming
User Variables It is legal to assign any sequence of non-blank characters as the name of a variable.
Shell Programming
Shell Variables The Shell declares and initializes variables that determine such things as
home directory, what directories the shell will look in when you give commands, your prompt, and many other things. internal-field separator
You can assign new values to these variables from the command line.
Shell Programming
Read-Only User Variables The value of read-only variables can not be changed. The variable must be initialized to some value; and then, by entering the following command, it can be made read only. Command format: readonly variable_name variable_name name of the variable to be made read only
Shell Programming
Sample Session: $person=Kathleen $readonly person $echo $person Kathleen $person=Richard person: is read only $
Shell Programming
The readonly command given without any arguments will display a list of all the read-only variables. Sample Session:
$person=Kathleen $readonly person $example=Richard $readonly example $readonly readonly person readonly example $
Shell Programming
Read-Only Shell Variables
The read-only shell variables are similar to the read-only user variables; except the value of these variables is assigned by the shell, and the user CANNOT modify them. Name of the Calling Program The shell will store the name of the command you used to call a program in the variable named $0. It has the number zero because it appears before the first argument on the command line.
Shell Programming
Sample Session: $vi name_ex echo 'The name of the command used' echo 'to execute this script was' $0 $sh name_ex The name of the command used to execute this was name_ex $
script
Shell Programming
Command Line Arguments The Shell will store the first nine command line arguments in the variables named $1, $2, ..., $9. These variables appear in this section because you cannot change them using the equal sign. It is possible to modify them using the set command.
Shell Programming
Sample Session:
$vi arg_ex echo 'The first five command line' echo 'arguments are' $1 $2 $3 $4 $5 $sh arg_ex Richard Kathleen Douglas The first five command line arguments are Richard Kathleen Douglas $
Shell Programming
The script arg_ex will display the first five command-line arguments. The variables representing $4 and $5 have a null value. The Shell variable $* represents all of the command-line arguments as shown in the following example.
Sample Session:
$sh display_all echo $* $sh display_all Richard Kathleen Douglas Richard Kathleen Douglas $
Shell Programming
The Shell variable $# contains the number of arguments on the command line. This is a string variable that represents a decimal number. You can use the expr utility to perform calculations with that number and test to perform logical tests on it.
Sample Session: $vi num_args echo 'This script was called with' echo $# 'arguments' $sh num_args Richard Kathleen Douglas This script was called with 3 arguments $
Shell Programming
Set When set is called with arguments, it sets the value of the command-line arguments ($1-$n) to the arguments. The example sets the first three arguments. Sample Session: $vi set_ex set who really cares echo $#: $* $sh set_ex 3: who really cares $
Shell Programming
Shift
The shift command promotes each of the command-line arguments. The second argument, represented by $2, is now the first argument, represented by $1. The third becomes the second and so on until the last argument becomes the next to last. You can access only the first nine command-line arguments (as $1 through $9). The shift command gives you access to the tenth, and the first becomes unavailable. There is no "unshift" command that will return the arguments that are no longer available.
Shell Programming
Sample Session: $vi demo_shift echo 'arg1='$1 ' shift echo 'arg1='$1 ' shift echo 'arg1='$1 ' shift echo 'arg1='$1 '
$sh demo_shift Richard Kathleen Douglas arg1=Richard arg2=Kathleen arg3=Douglas arg1=Kathleen arg2=Douglas arg3= arg1=Douglas arg2= arg3=
Shell Programming
IFS This is the internal-field separator Shell variable. One can assign the IFS variable to another character and use this character as the field separator. Example:
. $num_args a:b:c:d . This example shows only one argument, namely a:b:c:d. . $IFS=: . $num_args a:b:c:d
This example now shows four different arguments; each being separated by the new IFS, (:).
Shell Programming
PS1
This is the Shell prompt which lets you know that the shell is waiting for you to give it a command. The shell stores the prompt as a string variable in PS1. When you change the value of this variable, the appearance of the prompt will change. When you are working on several different machines, it might be useful to have the prompt be the name of the machine you are working on. Example
$PS1='domax0: ' domax0: Notice that prompt is now domax0:
Shell Programming
SHELL METACHARACTERS
Character Meaning
`
" | & ? * ; ;; > file >> file
Command substitution
Quotes Pipe character Run program in background Match one character Match any number of characters Command separator End of Case statement Standard output Append to standard output
Shell Programming
SHELL METACHARACTERS
$# $* $? $0-$9 $! && || [ 1-9] [] \ Number of arguments to script Arguments to script Status of previous command Command line arguments PID of last background job Short-circuit AND Short-circuit OR Match range of characters Test Avoid Expansion of Metacharecter
Shell Programming
expr The expr command will perform arithmetic operations in the Shell. Command format: `expr expression`
`back quotes
The arguments are taken as an expression. After the evaluation has taken place, the result is written to standard output. The terms of the expression must be separated by blanks. Special characters to the shell must be escaped. Strings containing blanks or other special characters must be quoted.
83
Shell Programming
Sample Session: $expr 7 + 8 + 10 25 $expr 10 - 8 2 expr will also work with user defined variables
`back quotes
84
Shell Programming
POSITIONAL PARAMETERS
A Shell script can also read in command-line arguments. The first argument is referred to as $1, the second is $2, and so on. Command-line arguments are referred to as positional parameters. Sample Session: If we type the name of the Shell script $vi neat_shell with no arguments, we get the following results. echo $1 $2 $3 echo $0 is the name Sample Session: of the shell script $sh neat_shell echo "There were neat_shell is the name $# arguments." of the shell script echo $* 85 There were 0 arguments.
Shell Programming
Reading Input Into a Shell Variable
The Shell script can read user input from standard input. The read command will read one line from standard input and assign the line to one or more variables. The following example shows how this works.
Sample Session: $vi read_script echo "Please enter a string of your choice" read a echo $a $
86
Shell Programming
Command Substitution
You can execute a command by enclosing it within two grave accent marks [these are sometimes called backquotes (`)]. The Shell will replace the command and the grave marks with the output from the command.
Sample Session: $vi dir dir=`pwd` echo 'You are using the' $dir 'directory' $
87
Shell Programming
Comments in Shell Scripts
Comments can be inserted into the Shell script by beginning each comment line with the pound symbol (#) or a colon (:). All characters after the comment character will be ignored by the shell.
Sample Session: $vi list # This program displays all the files and directories in long # listing format present in current working directory. echo ls
88
Shell Programming
Shell Environment - Exporting Variables
Within a process, you can declare, initialize, read, and modify variables. The variable is local to that process. When a process forks a child process, the parent process does not automatically pass the value of the variable to the child process.
Here is an example of the variables not being exported.
89
Shell Programming
Sample Session: $vi no_export car=mercedes echo $0 $car $$
sh inner echo $0 $car $$ $vi inner echo $0 $car $$ $ $sh no_export no_export mercedes 4790 inner 4792 no_export mercedes 4790 $
# set the variable # $0 = name of file executed # $car =value of variable car # $$ = PID number (process id) # execute another BourneShell script # display same as above
# display variables for this process
90
Shell Programming
Sample Session:
OUTPUT
$vi export_it car=mercedes export car echo $0 $car $$ sh inner1.sh echo $0 $car $$ $vi inner1.sh echo $0 $car $$ car=chevy echo $0 $car $$ $sh export_it export_it mercedes 4798 inner1 mercedes 4800 inner1 chevy 4800 export_it mercedes 4798
91
Shell Programming
In the export_it Shell script, the variable car was initialized to mercedes; and then it was exported. This means that the value of car is now available to a child process. When inner1 prints out the value of car it has the value of mercedes. The next line of inner1 changes the value of car to chevy. The last line of the session shows the return to the parent process and the value is still mercedes. Exporting variables is only valid from the parent to the child process.
92
Shell Programming
CONTROL CONSTRUCTS
The Shell control constructs can alter the flow of control within the script.
The Shell provides simple
two-way branch if statements, multiple-branch case statements, for, while, and until statements.
93
Shell Programming
test utility:
The test utility evaluates expressions and returns a condition indicating whether or not the expression is true (equal to zero) or false (not equal to zero). There are no options with this utility. The format for this utility is as follows:
# test expression expression - composed of constants, variables, and operators
94
Shell Programming
Test on Numeric Values Test expressions can be in many different forms. The expressions can appear as a set of evaluation criteria. The general form for testing numeric values is: int1 op int2 This criterion is true if the integer int1 has the specified algebraic relationship to integer int2. The valid operators (op) are: -eq equal -ne not equal -gt greater than -lt less than -ge greater than or equal -le less than or equal
95
Shell Programming
Test on Character Strings
The evaluation criterion for character strings is similar to numeric comparisons. The general form is:
string1 op string2 The operators (op) are: string1 = string2 string1 != string2 string1 true if string1 and string 2 are equal true if string1 and string2 are not equal true if string1 is not the null 96 string
Shell Programming
if then The format for this construct is: if expression then commands fi The if statement evaluates the expression and then returns control based on this status. The fi statement marks the end of the if, notice that fi is if spelled backward.
97
Shell Programming
The if statement executes the statements immediately following it if the expression returns a true status. If the return status is false, control will transfer to the statement following the fi. Sample Session:
$cat check_args if (test $# = 0) then echo 'Please supply at least 1 argument' exit fi echo 'Program is running' $
98
Shell Programming
if then else The format for this construct is: Command Format: if expression then commands else commands fi The else part of this structure makes the single-branch if statement into a two-way branch. If the expression returns a true status, the commands between the then and the else statement will be executed. After these have been executed, control will start again at the statement after the fi.
99
Shell Programming
Sample Session: $cat test_string number=1 numero=0001 if test $number = $numero then echo "String values of $number and $numero are equal" else echo "String values of $number and $numero not equal" fi if test $number -eq $numero then echo "Numeric values of $number and $numero are equal" else echo "Numeric values of $number and $numero not equal" fi
100
Shell Programming
if then elif The format for this construct is: Command Format: if expression then commands elif expression then commands else commands fi The elif construct combines the else and if statements and allows you to construct a nested set of if then else structures.
101
Shell Programming
case The format for this construct is:
case test-string in pattern-1 ) commands-1 ;; pattern-2 ) commands-2 ;; pattern-3 ) commands-3 ;; . . *) commands ;; esac
The case structure allows a multiple-branch decision mechanism. The path that is taken depends on a match between the test-string and one of the patterns.
102
Shell Programming
while The format for this construct is:
Shell Programming
until The format for this construct is:
Shell Programming
for The format for this construct is: for loop-index in argument-list do commands done This structure will assign the value of the first item in the argument list to the loop index and executes the commands between the do and done statements. The do and done statements indicate the beginning and end of the for loop.
105
Shell Programming
After the structure passes control to the done statement, it assigns the value of the second item in the argument list to the loop index and repeats the commands. The structure will repeat the commands between the do and done statements once for each argument in the argument list. When the argument list has been exhausted, control passes to the statement following the done.
106
System Calls
The mechanism used to provide access to OS services (i.e., enter the operating system and perform a privileged operation ) is commonly known as a system call. The (only) difference between a procedure call and a system call is that a system call changes the execution mode of the CPU (to supervisor mode) whereas a procedure call does not. System call interface: A set of functions that are called by (user) programs to perform specific tasks.
System Calls
The system call code is physically located in the kernel. The kernel itself is stored in a separate area of memory - which is normally not accessible to the process. Therefore, the first thing that is required to execute a system call is to change to Kernel Mode - so that the kernel memory can be accessed. A system call is implemented by a ``software interrupt'' that transfers control to kernel code.
System Calls
- creat / open - read, write - lseek - close, unlink - dup/dup2 - fcntl - stat - select - sync
Opening a file
int creat (char *file_name, mode_t mode)
int open (char *file_name, int flags); int open (char *file_name, int flags, mode_t mode); Ex: fd - open("temp", O_RDWR|O_CREAT, 0744);
Random Access
int lseek (int fd, long int offset, int whence); whence: SEEK_SET - from the beginning SEEK_CUR - from the current position SEEK_END - from the end of file
Close a file
int close (fd); int unlink("file_name"); equivalent to $rm file_name;
int unlink("file_name");
equivalent to $rm file_name;
Process Management
Introduction
Process is a program in execution. Processes carry out tasks in a system A process includes program counter (PC), CPU registers and process stacks, which contains temporary data. Unix is a multiprocessing system The UNIX kernel is reentrant
115
Processes : Introduction
A process uses many resources like memory space, CPU, files, etc., during its lifetime. Kernel should keep track of the processes and the usage of system resources. Kernel should distribute resources among processes fairly. Most important resource is CPU. In a multiprocessing environment, to attain an ideal performance of a system, the CPU utilization should be maximum.
116
Context Switch
Execution control is changing from one process to another. When a current process either completes its execution or is waiting for a certain event to occur, the kernel saves the process context and removes the process from the running state. Kernel loads next runnable process's registers with pointers for execution. Kernel space: a fixed part of virtual address space of each process. It maps the kernel text and data structures.
118
Process structure
Every process is represented by a task_struct data structure. This structure is quite large and complex. When ever a new process is created a new taskstruct structure is created by the kernel and the complete process information is maintained by the structure. When a process is terminated, the corresponding structure is removed. Uses doubly linked list data structure.
119
Identifiers
Every process in the system has a process identifier.
The process identifier is not an index into the task vector, it is simply a number. Each process also has User and Group identifiers, these are used to control the process access to the files and devices in the system eg: ppid, pid, uid, gid, euid, egid
120
example of fork()
#include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { printf("Hello \n"); fork(); printf("bye\n"); return 0; }
123
example of fork()
Hello - is printed once by parent process bye - is printed twice, once by the parent and once by the child If the fork system call is successful a child process is produced that continues execution at the point where it was called by the parent process. After the fork system call, both the parent and child processes are running and continue their execution at the next statement in the parent process.
A summary of fork() return values follows: if (fork()==-1) then fork() failed and there is no child if (fork()>0) then this is the parent if (fork()==0) then this is the child
124
The wait() system call accepts a single argument, which is a pointer to an integer and returns a value defined as type pid_t. If the calling process does not have any child associated with it, wait will return immediately with a value of -1. If any child processes are still active, the calling process will suspend its activity until a child process terminates. 126
Example of wait()
int status; int pid; pid = fork(); if (pid == 0) /* child process */ { printf("\n I'm the child!"); exit(0); } else /* parent process */ { wait(&status); printf("\n I'm the parent!") printf("\n Child returned: %d\n", status) }
127
'l' indicates a list arrangement (a series of null terminated arguments) 'v' indicate the array or vector arrangement (like the argv structure). 'e' indicates the programmer will construct (in the array/vector format) and pass their own environment variable list 'p' indicates the current PATH string should be used when the system searches for executable files. NOTE: In the four system calls where the PATH string is not used (execl, execv, execle, and execve) the path to the program to be executed must be fully specified.
130
Signals
132
Signals
Signals are various notifications sent to a process in order to notify it of various "important" events. Signals are one of the oldest inter-process communication methods used by Unix systems. By their nature, they interrupt whatever the process is doing at that moment, and forces to handle them immediately. Each signal has an integer number that represents it (1, 2 and so on), as well as a symbolic name that is usually defined in the file /usr/include/signal.h A signal could be generated by a keyboard interrupt or an error condition such as the process attempting to access a non-existent location in its virtual 133 memory.
Signals
There are a set of defined signals that the kernel can generate or that can be generated by other processes in the system
134
Signals
Each signal in linux has a default action. If a signal's handler is set to the default action then the kernel will handle it. The SIGSTOP signal's default handler will change the current process's state to Stopped and then run the scheduler to select a new process to run. Alternatively, the process can specify its own signal handler.
Processes can block all the signals, with the exception of SIGSTOP and SIGKILL. If a blocked signal is generated, it remains pending until it is unblocked.
135
Signals
Signals have no relative priorities. The number of supported signals is limited to the word size of the processor. Processes with a word size of 32 bits can have 32 signals whereas 64 bit processors like the Alpha AXP may have up to 64 signals. Not every process in the system can send signals to every other process, the kernel can and super users can. Signals are generated by setting the appropriate bit in the task_struct's signal field. 136
A process can send itself a signal with the raise function. This function is declared in signal.h.
140
Example
#include <stdio.h> #include <sys/signal.h> void main() { signal (SIGALRM, times_up); alarm (10); while(1); /* endless loop. */ } int times_up(int sig) { printf("Caught signal #< %d >n", sig); exit(sig); /* return the signal number */ }
141
142
void (*sa_handler)(int)
This is used in the same way as the action argument to the signal function. The value can be SIG_DFL, SIG_IGN, or a function pointer.
143
SA_ONESHOT or SA_RESETHAND
Restore the signal action to the default state once the signal handler has been called.
SA_ONSTACK
Call the signal handler on an alternate signal stack provided by sigaltstack. If an alternate stack is not available, the default stack will be used.
SA_SIGINFO
The signal handler takes 3 arguments, not one. In this case, sa_sigaction should be set instead of sa_handler.
144
void (*sa_restorer)(void) The sa_restorer element is obsolete and should not be used. POSIX does not specify a sa_restorer element.
149
#include <signal.h> int sigsuspend(const sigset_t *set) This function replaces the process's signal mask with set and then suspends the process until a signal is delivered whose action is either to terminate the process or invoke a signal handling function. In other words, the program is effectively suspended until one of the signals that is not a member of set arrives. If the process is woken up by delivery of a signal that invokes a handler function, and the handler function returns, then sigsuspend also returns. The mask remains set only as long as sigsuspend is waiting. The function sigsuspend always restores the previous signal 151 mask when it returns.
The UNIX operating system provides a rich set of features that allows processes to communicate with each other.
IPC Mechanisms
Primitive - Unnamed pipe - Named pipe (FIFO) System V IPC - Message queues - Shared memory - Semaphores POSIX 1.b - Message queues - Shared memory - Semaphores Socket Programming
Unnamed Pipes
There is no form of IPC that is simpler than pipes. In Unix, a pipe is a unidirectional, stream communication that allows related-processes to communicate. One process writes to the ``write end'' of the pipe, and a second process reads from the ``read end'' of the pipe. The order in which data is written to the pipe, is the same order as that in which data is read from the pipe. pipe is a data structure in the kernel.
Unnamed Pipes
A pipe is created by using the pipe system call int pipe(int* filedes); A pipe consists of
two descriptors, one for reading, one for writing. reading from the pipe advances the read pointer writing to the pipe advances the write pointer operating system buffers data in the pipe (Unix pipe 4096 bytes (4K)) operating system blocks reads of empty pipe operating system blocks writes to full pipe pipe data consists of unstructured character stream
EXAMPLE
#define DATA "hello world" #define BUFFSIZE 1024 int rgfd[2]; /* file descriptors of streams */ main() { char sbBuf[BUFFSIZE]; pipe(rgfd);
if (fork()) { /* parent, read from pipe */ close(rgfd[1]); /* close write end */ read(rgfd[0], sbBuf, BUFFSIZE); printf("-->%s\n", sbBuf); close(rgfd[0]); } else { /* child, write data to pipe */ close(rgfd[0]); /* close read end */ write(rgfd[1], DATA, sizeof(DATA)); close(rgfd[1]); exit(0); } }
Pipes (Bi-directional)
If one wants to use pipes for bidirectional communication. The algorithm looks like
parent creates pipe 1, pipe 2 fork parent closes read end of pipe1 parent closes write end of pipe2 child closes write end of pipe1 child closes read end of pipe2
prw-rw-r-- 1 choo choo 0 Nov 7 01:59 prog_pipe The 'p' on the first column denotes this is a named pipe. Just like any file in the system, it has access permissions, that define which users may open the named pipe, and whether for reading, writing or both.
EXAMPLE
#include<stdio.h> #include<fcntl.h> #include<sys/stat.h> int main() { int x,y,z; mknod("./fif",S_IFIFO|0666,0); x=open("fif",O_RDONLY); write(x,"hello",5); close(x); unlink("fif"); } #include<stdio.h> #include<fcntl.h> int main() { int x,y,z; char a[10]; x=open("fif",O_WRONLY); read(x,a,5); write(1,a,5); close(x); }
System V IPC
Linux supports three types of interprocess communication mechanisms which first appeared in Unix System V. These are message queues, semaphores and shared memory. These System V IPC mechanisms all share common authentication methods. Each IPC object has a unique IPC identifier associated with it. When we say ``IPC object'', we are speaking of a single message queue, semaphore set, or shared memory segment. This identifier is used within the kernel to uniquely identify an IPC object.
unique reference identifier to the kernel via system calls. Access to these System V IPC objects is checked using access permissions, much like accesses to files are checked. The access rights to the System V IPC object is set by the creator of the object via system calls. All Linux data structures representing System V IPC objects in the system include an ipc_perm structure which contains the owner and creator processes user and group identifiers, the access mode for this object (owner, group and other) and the IPC object's key.
System V IPC
To obtain a unique ID, a key must be used. The key must be mutually agreed upon by both client and server processes. Two sets of key are supported: public and private. Private means that it may be accessed only by the process that created it, or by child processes of this process. Public means that it may be potentially accessed by any process in the system, except when access permission modes state otherwise.
Command
ipcs ipcs ipcs ipcs ipcs
Option
-q: -s: -m: --help:
Action
to check usage of all SysV IPCs Show only message queues Show only semaphores Show only shared memory Additional arguments
The 'ipcrm' command accepts a resource type ('shm', 'msg' or 'sem') and a resource ID, and removes the given resource from the system. ipcrm <msg | sem | shm> <IPC ID> We need to have the proper permissions in order to delete a resource.
Message Queues
One of the problems with pipes is that it is up to you, as a programmer, to establish the protocol. With a stream taken from a pipe, it means you have to somehow parse the bytes, and separate them to packets. Other problem is that data sent via pipes arrives in FIFO order. A message queue is a queue onto which messages can be placed. Message queues allow one or more processes to write messages which will be read by one or more reading processes. Each message queue (of course) is uniquely identified by an IPC identifier. Linux maintains a list of message queues, the msgque vector; each element of which points to msqid_ds data structure which fully describes the message queue.
When message queues are created a new msqid_ds data structure is allocate from system memory and inserted into the vector. Each msqid_ds data structure contains an ipc_perm data structure and pointers to the messages entered onto this queue. A message is composed of a message type (which is a number), and message data. A message queue can be either private, or public. If it is private, it can be accessed only by its creating process or child processes of that creator. If it's public, it can be accessed by any process that knows the queue's key. Several processes may write messages onto a message queue, or read messages from the queue. Messages may be read by type, and thus not have to be read in a FIFO order as is the case with pipes.
Message Queues
The second parameter contains flags that control how the system call is to be processed. IPC_CREAT
Create the queue if it doesn't already exist in the kernel.
IPC_EXCL
When used with IPC_CREAT, fail if queue already exists.
If IPC_CREAT is used alone, msgget() either returns the message queue identifier for a newly created message queue, or returns the identifier for a queue which exists with the same key value. If IPC_EXCL is used along with IPC_CREAT, then either a new queue is created, or if the queue exists, the call fails with 1. An optional octal mode may be OR'd into the mask, since each IPC object has permissions that are similar in functionality to file permissions on a UNIX file system!
The msgsz argument contains the size of the message in bytes, excluding the length of the message type (4 byte long). The msgflg argument can be set to 0 (ignored), or: IPC_NOWAIT
If the message queue is full, then the message is not written to the queue, and control is returned to the calling process. If not specified, then the calling process will suspend (block) until the message can be written.
Reading A Message From The Queue msgrcv() int msgrcv ( int msqid, struct msgbuf *msgp, int
msgsz, long mtype, int msgflg ); This system call accepts the following list of parameters:
int msqid - id of the queue, as returned from msgget(). struct msgbuf* msg - a pointer to a pre-allocated msgbuf structure. It should generally be large enough to contain a message with some arbitrary data (see more below). int msgsz - size of largest message text we wish to receive. Must NOT be larger than the amount of space we allocated for the message text in 'msg'. int msgtyp - Type of message we wish to read. may be one of:
0 - The first message on the queue will be returned.
Reading A Message From The Queue msgrcv() a positive integer - the first message on the queue whose
type (mtype) equals this integer (unless a certain flag is set in msgflg, see below). a negative integer - the first message on the queue whose type is less than or equal to the absolute value of this integer.
msgctl()
To perform control operations on a message queue, you use the msgctl() system call.
int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
Parameters:
int msqid - id of the queue, as returned from msgget(). Int cmd - process the following command
IPC_STAT Retrieves the msqid_ds structure for a queue, and stores it in the address of the buf argument. IPC_RMID Removes the queue from the kernel.
Semaphores
One of the problems when writing multi-process application is the need to synchronize various operations between the processes. Communicating requests using pipes, sockets and message queues is one way to do it. However, sometimes we need to synchronize operations amongst more than two processes, or to synchronize access to data resources that might be accessed by several processes in parallel. Semaphores are a means supplied with SysV IPC that allow us to synchronize such operations. A semaphore is a resource that contains an integer value, and allows processes to synchronize by testing and setting this value in a single atomic operation. As message queues, the kernel maintains special internal data structure for each semaphore set which exists within its addressing space. This structure is of type semid_ds.
Semaphores
Two types of operations can be carried on a semaphore: wait and signal. A set operation first checks if the semaphore's value equals some number. If it does, it decreases its value and returns. If it does not, the operation blocks the calling process until the semaphore's value reaches the desired value. A signal operation increments the value of the semaphore, possibly awakening one or more processes that are waiting on the semaphore. A semaphore set is a structure that stores a group of semaphores together, and possibly allows the process to commit a transaction on part or all of the semaphores in the set together.
Creating A Semaphore Set semget() the semget() system call. Creation of a semaphore set is done using
#include <sys/sem.h> int semget(key_t key, int nsems, int semflg);
/* ID of the semaphore set. */ int sem_id_1; int sem_id_2; /* create a private semaphore set with one semaphore in it, with access only to the owner. */ sem_id_1 = semget (IPC_PRIVATE, 1, IPC_CREAT | 0600);
/* create a semaphore set with ID 250, three semaphores */ /* in the set, with access only to the owner. */ sem_id_2 = semget (250, 3, IPC_CREAT | 0600);
/* initialize the second semaphore in our set to '6'. */ rc = semctl(sem_id_2, 1, SETVAL, 6);
/* initialize the third semaphore in our set to '0'. */ rc = semctl(sem_id_2, 2, SETVAL, 0);
#include <sys/sem.h> int semop(int semid, struct sembuf *sops, size_t nsops);
Each semaphore operation specified by the sops array is performed on the semaphore set specified by semid. The entire array of operations is performed atomically; no other thread will operate on semaphore set until all of the operations are done or it is determined that they cant be done. The members of the sembuf structure are as follows:
unsigned short sem_num The semaphore number in semaphore set. short sem_op The operation to perform on the semaphore. short sem_flg The operation flags.
nsops
(Input) Number of sembuf structures in sops array.
Return Value
0 semop() was successful. -1 semop() was not successful.
The semctl() function allows the caller to control the semaphore set specified by the semid parameter.
#include <sys/sem.h> int semctl(int semid, int semnum, int cmd, ...);
A semaphore set is controlled by setting the cmd parameter to one of the following values: SETVAL
Set the value of semaphore semnum to the integer value of type int specified in the fourth parameter and clear the associated per-thread semaphore adjustment value.
SETALL
Set the values of each semaphore in the semaphore set to the values contained in the array pointed to by the fourth parameter, which is a pointer to an array of type unsigned short.
IPC_STAT
Store the current value of each member of the semid_ds data structure into the structure pointed to by the fourth parameter. The IPC_STAT command requires read permission to the semaphore set.
GETNCNT
Return the number of threads waiting for the value of semaphore semnum to increase. This value is the semncnt value in the semaphore_t data structure associated with the specified semaphore.
GETVAL
Return the current value of semaphore semnum.
GETALL
Return the values of each semaphore in the semaphore set into the array pointed to by the fourth parameter, which is a pointer to an array of type unsigned short.
GETZCNT
Return the number of threads waiting for the value of semaphore semnum to reach zero.
EXAMPLE
#include<stdio.h> #include<sys/sem.h> #include<sys/ipc.h> #include<stdlib.h> union semun { int x; struct semid_ds *y; unsigned short *array; }; int id; struct sembuf op; union semun s; unsigned short a[ ]={1,1}; int main() { s.array=a; id=semget(0x11,2,IPC_CREAT); semctl(id,0,SETALL,s); op.sem_num=0; op.sem_op=-1; op.sem_flg=0; semop(id,&op,1); sleep(10); printf("HELLO\n"); op.sem_num=0; op.sem_op=1; op.sem_flg=0; semop(id,&op,1); }
EXAMPLE (Cont..)
int main() { int id; struct sembuf op; id=semget(0x11,1,IPC_CREAT); op.sem_num=0; op.sem_op=-1; op.sem_flg=0; semop(id,&op,1); printf("WORLD\n"); op.sem_num=0; op.sem_op=1; op.sem_flg=0; semop(id,&op,1); }
In order to create a new memory segment, or access an existing segment, the shmget() system call is used. PROTOTYPE: int shmget ( key_t key, int size, int shmflg); The first argument to shmget() is the key value. This key value is then compared to existing key values that exist within the kernel for other shared memory segments. At that point, the open or access operation is dependent upon the contents of the shmflg argument. IPC_CREAT
Create the segment if it doesn't already exist in the kernel.
IPC_EXCL
When used with IPC_CREAT, fail if segment already exists.
RETURNS:
shared memory segment identifier on success -1 on error: errno = EINVAL (Invalid segment size specified)
Once a process has a valid IPC identifier for a given segment, the next step is for the process to attach or map the segment into its own addressing space. int shmat ( int shmid, char *shmaddr, int shmflg); RETURNS:
address at which segment was attached to the process or -1 on error.
If the addr argument is zero (0), the kernel tries to find an unmapped region. This is the recommended method. An address can be specified to resolve conflicts with other apps. The SHM_RND flag can be OR'd into the flag argument to force a passed address to be page aligned (rounds down to the nearest page size).
This particular call is modeled directly after the msgctl call for message queues. In light of this fact, it won't be discussed in too much detail. Valid command values are: IPC_STAT
Retrieves the shmid_ds structure for a segment, and stores it in the address of the buf argument
IPC_RMID
Marks a segment for removal.
The IPC_RMID command doesn't actually remove a segment from the kernel. Rather, it marks the segment for removal. The actual removal itself occurs when the last process currently attached to the segment has properly detached it. Of course, if no processes are currently attached to the segment, the removal seems immediate.
To properly detach a shared memory segment, a process calls the shmdt system call. int shmdt ( char *shmaddr ); RETURNS:
0 on success -1 on error
EXAMPLE
#include<stdio.h> #include<sys/shm.h> #include<string.h> #include<stdio.h> #include<sys/shm.h> #include<string.h> int main() int main() { { int x,y,z; int x,y,z; char *a; x=shmget(0x30,1024, char *a; IPC_CREAT|0666); x=shmget(0x30,1024,IPC_CREAT|0 666); a=shmat(x,NULL,0); strcpy(a,"hello"); a=shmat(x,NULL,0); sleep(5); sleep(5); write(1,8,a); printf("\n%s\n",a); strcpy(a+6,"bye"); shmdt(a); shmctl(x,IPC_RMID); shmdt(a); } }
Clock Functions
The supported time-of-day clock is the CLOCK_REALTIME clock, defined in the time.h header file. The CLOCK_REALTIME clock is a systemwide clock, visible to all processes running on the system. The CLOCK_REALTIME clock measures the amount of time that has elapsed since 00:00:00 January 1, 1970 Greenwich Mean Time (GMT). The CLOCK_REALTIME clock measures time in nanoseconds; clock resolution does not reflect fractions of nanoseconds.
Function
clock_settime clock_gettime clock_getres
Description
Set clock to a specified value Get value of clock Get resolution of clock
Clock Functions
#include <time.h> int clock_getres(clockid_t clk_id, struct timespec *res); int clock_gettime(clockid_t clk_id, struct timespec *tp); int clock_settime(clockid_t clk_id, const struct timespec *tp); DESCRIPTION The function clock_getres() finds the resolution (precision) of the specified clock clk_id, The functions clock_gettime() and clock_settime() retrieve and set the time of the specified clock clk_id. struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; All implementations support the system-wide realtime clock, which is identified by CLOCK_REALTIME.
EXAMPLE
#include<stdio.h> #include<time.h> int main() { struct timespec s1,s2; int x,y; clock_gettime(CLOCK_REALTIME,&s1); printf("%d\n",s1.tv_sec); printf("%d\n",s1.tv_nsec); clock_getres(CLOCK_REALTIME,&s2); printf("%d\n",s2.tv_sec); printf("%d\n",s2.tv_nsec); }
Timers
Two types of timers are provided to support realtime timing facilities: one-shot timers and periodic timers. Timers can be set up to expire only once (one-shot) or on a repetitive (periodic) schedule. A one-shot timer is armed with an initial expiration time, expires only once, and then is disarmed. A timer becomes a periodic timer with the addition of a repetition value. The timer expires, then loads the repetition interval, rearming the timer to expire after the repetition interval has elapsed. The initial expiration value can be relative to the current time or an absolute time value. A relative timer has an initial expiration time based on the amount of time elapsed, such as 30 seconds from the start of the application or 0.5 seconds from the last timer expiration. An absolute timer expires at a calendar date and time.
Timers
Function timer_create timer_delete timer_settime timer_gettime timer_getoverrun Description Create a timer Delete a timer Set and arm or disarm a timer Get remaining interval for an active timer Get current overrun count for a timer
One create a timer with the timer_create function, which is associated with a sigevent structure. To use signals with timers, include the following steps in your application: Create and declare a signal handler. Set the sigevent structure to specify the signal you want sent on timer expiration. Establish a signal handler with the sigaction function. Create the timer. Set the timer.
Timers
The timespec and itimerspec data structures are used in many realtime clock and timer functions. The timespec data structure contains members for both second and nanosecond values. The itimerspec data structure contains two timespec data structures. This data structure sets up an initial timer and repetition value . struct itimerspec { struct timespec it_interval; /* Timer interval */ struct timespec it_value; /* Initial expiration */ };
Timers
#include <time.h> #include <signal.h> int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid); The timer_create() function creates a per-process timer using the specified clock, clock_id, as the timing base.
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue); The timer_settime() function sets the time until the next expiration of the timer specified by timerid from the it_value member of the value argument and arm the timer if the it_value member of value is non-zero. If the specified timer was already armed when timer_settime() is called, this call resets the time until next expiration to the value specified. If the it_value member of value is zero, the timer is disarmed.
Timers
If the flag TIMER_ABSTIME is not set in the argument flags, timer_settime() behaves as if the time until next expiration is set to be equal to the interval specified by the it_value member of value. That is, the timer expires in it_value nanoseconds from when the call is made. If the flag TIMER_ABSTIME is set in the argument flags, timer_settime() behaves as if the time until next expiration is set to be equal to the difference between the absolute time specified by the it_value member of value and the current value of the clock associated with timerid. That is, the timer expires when the clock reaches the value specified by the it_value member of value. If the argument ovalue is not NULL, the function timer_settime() stores, in the location referenced by ovalue, a value representing the previous amount of time before the timer would have expired or zero if the timer was disarmed.
Timers
int timer_gettime(timer_t timerid, struct itimerspec *value); int timer_getoverrun(timer_t timerid); The timer_gettime() function stores the amount of time until the specified timer, timerid, expires and the reload value of the timer into the space pointed to by the value argument. The it_value member of this structure contains the amount of time before the timer expires, or zero if the timer is disarmed. The timer_getoverrun() function returns the timer expiration overrun count for the specified timer.
Timers
int timer_delete(timer_t timerid); The timer_delete() function deletes the specified timer, timerid, previously created by the timer_create() function. If the timer is armed when timer_delete() is called, the behavior shall be as if the timer is automatically disarmed before removal.
EXAMPLE
int main() { sigemptyset(&s); sigaddset(&s,SIGINT); event.sigev_notify=SIGEV_SIGNAL; timer_t t; event.sigev_signo=SIGINT; int x,y; signal(SIGINT,handler); struct itimerspec s1,s2; timer_create(CLOCK_REALTIME, struct sigevent event; &event, &t); sigset_t s; s1.it_value.tv_sec=1; s1.it_interval.tv_sec=1; void handler(int p) timer_settime(t,0,&s1,NULL); { sigprocmask(SIG_BLOCK,&s,NULL); int q; printf("GOT THE SIGNAL\n"); sleep(6); sigprocmask(SIG_UNBLOCK,&s, y=timer_getoverrun(t); NULL); printf("%d\n",q); } } #include<stdio.h> #include<time.h> #include<signal.h>
int aio_fildes; /* File descriptor */ off_t aio_offset; /* File offset */ volatile void *aio_buf; /* Pointer to buffer */ size_t aio_nbytes; /* No. of bytes to transfer */ int aio_reqprio; /* Request priority offset */ struct sigevent aio_sigevent; /* Signal structure */ int aio_lio_opcode; /* Specifies type of I/O operation */
Why POSIX? POSIX is an industry-standard operating system specification that enables applications to be easily ported across different hardware and operating systems implementations. POSIX.1b, Real-time extensions POSIX.1c, POSIX Threads
Semaphores
Semaphores are used to control access to shared resources by processes. There are named and unnamed semaphores. Named semaphores provide access to a resource between multiple processes. Unnamed semaphores provide multiple accesses to a resource within a single process or between related processes. Some semaphore functions are specifically designed to perform operations on named or unnamed semaphores. Semaphores are global entities and are not associated with any particular process.
The O_CREAT flag requires two additional arguments: mode of type mode_t, and value of type unsigned int. The value argument specifies the initial value assigned to the newly created semaphore
The sem_post() function unlocks the specified semaphore by performing a semaphore unlock operation on that semaphore.
Closing a Semaphore
When an application is finished using an unnamed semaphore, it should destroy the semaphore with a call to the sem_destroy function. For named semaphores, the application should deallocate the semaphore with a call to the sem_close function. The semaphore name is disassociated from the process. A named semaphore is removed using the sem_unlink function, which takes effect once all processes using the semaphore have deallocated the semaphore with calls to sem_close. If needed, the semaphore can be reopened for use through a call to the sem_open function. Since semaphores are persistent, the state of the semaphore is preserved, even though the semaphore is closed. When you reopen the semaphore, it will be in the state it was when it was closed, unless altered by another process.
Closing a Semaphore
int sem_destroy(sem_t *sem); sem_destroy() destroys the unnamed semaphore at the address pointed to by sem. Only a semaphore that has been initialised by sem_init(3) should be destroyed using sem_destroy(). int sem_close(sem_t *sem); The sem_close() function closes the semaphore specified by the sem argument when the calling process is finished with it. When a semaphore is closed, sem_close() frees up any system resources allocated to be used by that semaphore. These resources are then available for use by a subsequent invocation of the sem_open() function. int sem_unlink(const char *name); The sem_unlink() function removes the specified named semaphore.
EXAMPLE
#include<stdio.h> #include<semaphore.h> #include<unistd.h> #include<sys/types.h> #include<fcntl.h> #include<stdio.h> #include<semaphore.h> #include<unistd.h> #include<sys/types.h> #include<fcntl.h>
int main() { int x,z; sem_t *s; s=sem_open("/xyz",O_CREAT,0666,1); sem_wait(s); sleep(6); write(1,"hello",5); sem_post(s); sem_getvalue(s,&z); printf(value: %d\n",z); sem_close(s); sem_unlink("xyz"); }
int main() { sem_t *s; int z; s=sem_open("xyz",O_CREAT); sem_wait(s); write(1,"world",5); sem_getvalue(s,&z); printf("z5%d\n",z); sleep(5); sem_post(s); sem_close(s); }
Message queues
Message queues work by exchanging data in buffers. Any number of processes can communicate through message queues, regardless of whether they are related. If a process has adequate access permission, it can send or receive messages through the queue. If your application involves heavy message traffic, you can prioritize the order in which processes receive messages by assigning a priority to the message or controlling the priority of the receiving process. Asynchronous notification of the availability of a message on a queue allows a process to do useful work while waiting to receive a message. Realtime message passing is designed to work with shared memory in order to accommodate the needs of realtime applications with an efficient, deterministic mechanism to pass arbitrary amounts of data between cooperating processes.
Message queues
General usage for message queues is as follows:
Get a message queue descriptor with a call to the mq_open function. Send and receive messages with calls to the mq_send and mq_receive functions. Close the message queue with a call to the mq_close function. Remove the message queue with a call to the mq_unlink function
One can identify message queue attributes with a call to the mq_getattr function. One can specify whether the message operation is blocking or non-blocking by calling the mq_setattr function.
Any combination of the remaining flags may be specified in the value of oflag:
O_CREAT O_EXCL O_NONBLOCK Determines whether an mq_send() or mq_receive() waits for resources or messages that are not currently available.
Create a message queue. It requires two additional arguments: mode, which shall be of type mode_t, and attr, which shall be a pointer to an mq_attr structure.
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned * msg_prio, const struct timespec * abs_timeout); The mq_receive() function shall receive the oldest of the highest priority message(s) from the message queue specified by mqdes. If the argument msg_prio is not NULL, the priority of the selected message shall be stored in the location referenced by msg_prio. The mq_timedreceive() function shall receive the oldest of the highest priority messages and if no message exists on the queue to satisfy the receive, the wait for such a message shall be terminated when the specified timeout expires.
The last option is a good choice for a realtime application. The mq_notify function is used to register a request for asynchronous notification by a signal when a message becomes available on a previously empty queue. The process can then do useful work until a message arrives, at which time a signal is sent according to the signal information specified in the notification argument of the mq_notify function. After notification, the process can call mq_receive to receive the message.
int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, struct mq_attr *omqstat); The mq_setattr() function sets the attributes associated with the open message queue description associated with specified the message queue descriptor, mqdes.
EXAMPLE
int main() { int z; sigset_t set; struct sigevent event; struct mq_attr attr; unsigned int y; attr.mq_maxmsg=5; mqd_t x; attr.mq_msgsize=256; char a[256]={0}; event.sigev_notify = SIGEV_SIGNAL; event.sigev_signo=SIGINT; void handler(int q) signal(SIGINT,handler); { sigemptyset(&set); int s; printf("GOT THE SIGNAL\n"); x=mq_open("/sammq",O_CREAT|O_RDWR| O_NONBLOCK,0666,&attr); s=mq_receive(x,a,256,&y); z=mq_notify(x,&event); write(1,a,5); printf("waiting\n"); } sigsuspend(&set); mq_close(x); } #include<stdio.h> #include<fcntl.h> #include<mqueue.h> #include<signal.h>
EXAMPLE
#include<stdio.h> #include<mqueue.h> #include<fcntl.h>
int main() { mqd_t x; struct mq_attr m; unsigned int y=4; char a[256]="hello"; char b[256]="bye"; m.mq_maxmsg=20; m.mq_msgsize=256; m.mq_flags=0; x=mq_open("/xyz",O_RDWR|O_CREAT,0666,&m); mq_send(x,a,256,y); mq_send(x,b,256,8); mq_close(x); }
Files, however, may need to be saved and reused each time the application is run. The unlink and shm_unlink functions remove (delete) the file and its contents. Therefore, if you need to save a shared file, close the file but do not unlink it.
O_RDWR
Open for read or write access.
Any combination of the remaining flags may be specified in the value of oflag:
O_CREAT O_EXCL
Pthreads Overview
What is a Thread? Technically, a thread is defined as an independent stream of instructions that can be scheduled to run as such by the operating system. To the software developer, the concept of a "procedure" that runs independently from its main program may best describe a thread. To go one step further, imagine a main program (a.out) that contains a number of procedures. Then imagine all of these procedures being able to be scheduled to run simultaneously and/or independently by the operating system. That would describe a "multi-threaded" program.
Pthreads Overview
Why threads? As process is created by the operating system, it requires a fair amount of information about program resources and program execution state, including: Process ID, process group ID, user ID, and group ID Working directory. Program instructions Registers Stack Heap File descriptors Signal actions Shared libraries Inter-process communication tools (such as message queues, pipes, semaphores, or shared memory).
Pthreads Overview
Threads use and exist within these process resources, yet are able to be scheduled by the operating system and run as independent entities. Is "lightweight" because most of the overhead has already been accomplished through the creation of its process. This independent flow of control is accomplished because a thread maintains its own:
Stack pointer Registers Scheduling properties (such as policy or priority) Set of pending and blocked signals Thread specific data.
Dies if the parent process dies. Reading and writing to the same memory locations is possible, and therefore requires explicit synchronization by the programmer.
Creating Threads
Initially, your main() program comprises a single, default thread. All other threads must be explicitly created by the programmer. #include <pthread.h>; int pthread_create (pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) ; Description pthread_create creates a new thread and makes it executable. Typically, threads are first created from within main() inside a single process. Once created, threads are peers, and may create other threads. The new thread inherits its creating thread's signal mask; but any pending signal of the creating thread will be cleared for the new thread. The maximum number of threads that may be created by a process is implementation dependent.
Creating Threads
pthread_create arguments: thread: An opaque, unique identifier for the new thread returned by the subroutine. attr: An opaque attribute object that may be used to set thread attributes. You can specify a thread attributes object, or NULL for the default values. start_routine: the C routine that the thread will execute once it is created. arg: A single argument that may be passed to start_routine. It must be passed by reference as a pointer cast of type void. NULL may be used if no argument is to be passed.
Return Values If successful, the pthread_create function returns zero. Otherwise, an error number is returned to indicate the error.
Thread Termination
void pthread_exit (void *retval) pthread_exit terminates the execution of the calling thread. The retval argument is the return value of the thread. It can be retrieved from another thread using pthread_join. The pthread_exit function never returns. int pthread_cancel (pthread_t thread) pthread_cancel sends a cancellation request to the thread denoted by the thread argument. If there is no such thread, pthread_cancel fails and returns ESRCH. Otherwise it returns 0.
pthread_t pthread_self (void) pthread_self returns the thread identifier for the calling thread.
Thread Attributes
By default, a thread is created with certain attributes. Some of these attributes can be changed by the programmer via the thread attribute object. pthread_attr_init and pthread_attr_destroy are used to initialize/destroy the thread attribute object. After thread creation, the thread attributes object can be reused to create another thread
Thread Attributes
Threads have a number of attributes that may be set at creation time. This is done by filling a thread attribute object attr of type pthread_attr_t, then passing it as second argument to pthread_create. The same attribute object can be used for creating several threads once created.
Thread Attributes
int pthread_attr_setattr (pthread_attr_t *obj, int value) Set attribute attr to value in the attribute object pointed to by obj. The following thread attributes are supported: detachstate
Choose whether the thread is created in the joinable state (value PTHREAD_CREATE_JOINABLE) or in the detached state (PTHREAD_CREATE_DETACHED). The default is PTHREAD_CREATE_JOINABLE. In the joinable state, another thread can synchronize on the thread termination and recover its termination code using pthread_join, but some of the thread resources are kept allocated after the thread terminates, and reclaimed only when another thread performs pthread_join on that thread. In the detached state, the thread resources are immediately freed when it terminates, but pthread_join cannot be used to synchronize on the thread termination. A thread created in the joinable state can later be put in the detached thread using pthread_detach.
Thread Attributes
schedpolicy
Select the scheduling policy for the thread: one of SCHED_OTHER (regular, non-realtime scheduling), SCHED_RR (realtime, round-robin) or SCHED_FIFO (realtime, first-in first-out). The default is SCHED_OTHER. Desktop OS only supports SCHED_OTHER - attempting to set one of the other policies will return an error
schedparam
Change the scheduling parameter (the scheduling priority) for the thread. The default is 0. This attribute is not significant if the scheduling policy is SCHED_OTHER; it only matters for the realtime policies SCHED_RR and SCHED_FIFO.
Thread Attributes
inheritsched
Whether the scheduling policy and scheduling parameter for the newly created thread are determined by the values of the schedpolicy and schedparam attributes (value PTHREAD_EXPLICIT_SCHED) or are inherited from the parent thread (value PTHREAD_INHERIT_SCHED). The default is PTHREAD_EXPLICIT_SCHED.
scope
Choose the scheduling contention scope for the created thread. The default is PTHREAD_SCOPE_SYSTEM, meaning that threads contend for CPU time with all processes running on the machine. In particular, thread priorities are interpreted relative to the priorities of all other processes on the machine. The other possibility, PTHREAD_SCOPE_PROCESS, means that scheduling contention occurs only between the threads of the running process: thread priorities are interpreted relative to the priorities of the other threads of the process, regardless of the priorities of other processes.
Mutexes
A mutex is a MUTual EXclusion device is useful for protecting shared data structures from concurrent modifications. A mutex has two possible states:
unlocked (not owned by any thread), and locked (owned by one thread).
A mutex can never be owned by two different threads simultaneously. A thread attempting to lock a mutex that is already locked by another thread is suspended until the owning thread unlocks the mutex first. None of the mutex functions is a cancellation point, not even pthread_mutex_lock, in spite of the fact that it can suspend a thread for arbitrary durations.
Mutexes
The PTHREAD_MUTEX_INITIALIZER macro initializes the mutex to a variable of type pthread_mutex_t.
Mutexes
int pthread_mutex_timedlock (pthread_mutex_t *mutex, const struct timespec *abstime) The pthread_mutex_timedlock is similar to the pthread_mutex_lock function but instead of blocking for in indefinite time if the mutex is locked by another thread, it returns when the time specified in abstime is reached. If the mutex is successfully locked, the function returns zero. If the time specified in abstime is reached without the mutex being locked, ETIMEDOUT is returned. int pthread_mutex_unlock (pthread_mutex_t *mutex) pthread_mutex_unlock unlocks the given mutex. int pthread_mutex_destroy (pthread_mutex_t *mutex) pthread_mutex_destroy destroys a mutex object, freeing the resources it might hold. If the mutex is locked by some thread, pthread_mutex_destroy returns EBUSY. Otherwise it returns 0.
Condition Variables
A condition (short for "condition variable") is a synchronization device that allows threads to suspend execution until some predicate on shared data is satisfied. The basic operations on conditions are:
signal the condition (when the predicate becomes true), and wait for the condition, suspending the thread execution until another thread signals the condition.
A condition variable must always be associated with a mutex, to avoid the race condition where a thread prepares to wait on a condition variable and another thread signals the condition just before the first thread actually waits on it. The PTHREAD_COND_INITIALIZER macro initializes the conditional variable to a variable of type pthread_cond_t. For example: pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
[
Condition Variables
int pthread_cond_signal (pthread_cond_t *cond) pthread_cond_signal restarts one of the threads that are waiting on the condition variable cond. If no threads are waiting on cond, nothing happens. If several threads are waiting on cond, exactly one is restarted, but it is not specified which. This function always returns 0. int pthread_cond_broadcast (pthread_cond_t *cond) pthread_cond_broadcast restarts all the threads that are waiting on the condition variable cond. Nothing happens if no threads are waiting on cond. This function always returns 0.
Condition Variables
int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) pthread_cond_wait atomically unlocks the mutex (as per pthread_unlock_mutex) and waits for the condition variable cond to be signaled. The thread execution is suspended and does not consume any CPU time until the condition variable is signaled. The mutex must be locked by the calling thread on entrance to pthread_cond_wait. Before returning to the calling thread, pthread_cond_wait re-acquires mutex (as per pthread_lock_mutex). Thus, if all threads always acquire the mutex before signaling the condition, this guarantees that the condition cannot be signaled between the time a thread locks the mutex and the time it waits on the condition variable. This function always returns 0.
Condition Variables
int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) pthread_cond_timedwait atomically unlocks mutex and waits on cond, as pthread_cond_wait does, but it also bounds the duration of the wait. If cond has not been signaled before time abstime, the mutex mutex is re-acquired and pthread_cond_timedwait returns the error code ETIMEDOUT. int pthread_cond_destroy (pthread_cond_t *cond) pthread_cond_destroy destroys the condition variable cond, freeing the resources it might hold. If any threads are waiting on the condition variable, pthread_cond_destroy leaves cond untouched and returns EBUSY. Otherwise it returns 0, and cond must not be used again until it is reinitialized.
[
Signal masks are set on per-thread basis, but signal actions and signal handlers, as set with sigaction, are shared between all threads. int pthread_kill (pthread_t thread, int signo) pthread_kill sends signal number signo to the thread thread. pthread_kill returns 0 on success.
A simple compilation
Compiling a small C program requires at least a single .c file, with .h files as appropriate. There are 3 steps to obtain the final executable program, as shown:
Compiler stage: Assembler stage: Linker stage:
Separate compilation
The steps taken in creating the executable program can be divided up in to two compiler/assembler steps circled in red, and one final linker step circled in yellow. The two .o files may be created separately, but both are required at the last step to create the executable program. The three different tasks required to produce the executable program are as follows: Compile green.o: cc -c green.c Compile blue.o: cc -c blue.c Link the parts together: cc green.o blue.o
Dependencies
The principle by which make operates was described to you in the last section. It creates programs according to the file dependencies. For example, we now know that in order to create an object file, program.o, we require at least the file program.c. (There may be other dependencies, such as a .h file.) This section involves drawing what are called "dependency graphs", which are very similar to the diagrams given in the previous section. As you become proficient using make, you probably will not need to draw these diagrams, but it is important to get a feel for what you are doing.
Dependency graphs
The lines which radiate downwards from a file are the other files which it depends on. For example, to create main.o, the three files data.h, io.h, and main.c are needed.
Each dependency shown in the graph is circled with a correspondingly in the Makefile. Comments can be placed in a Makefile by placing a pound s ign (#) in front of it.
Macros in make
Macros in make work similarly to macros used in C programming. The make program allows you to use macros, which are similar to variables, to store names of files. The format is as follows: OBJECTS = data.o io.o main.o Whenever you want to have make expand these macros out when it runs, type the following corresponding string $(OBJECTS). Here is our sample Makefile again, using a macro. OBJECTS = data.o main.o io.o project1: $(OBJECTS) cc $(OBJECTS) -o project1 data.o: data.c data.h cc -c data.c main.o: data.h io.h main.c cc -c main.c io.o: io.h io.c cc -c io.c
Special macros
In addition to those macros which you can create yourself, there are a few macros which are used internally by the make program. Here are some of those, listed below: CC
Contains the current C compiler. Defaults to cc.
CFLAGS
Special options which are added to the built-in C rule.
$@
Full name of the current target.
$?
A list of files for current dependency which are out-of-date.
$<
The source file of the current (single) dependency.
Predefined rules
By itself, make knows already that in order to create a .o file, it must use cc -c on the corresponding .c file. These rules are built into make, and you can take advantage of this to shorten your Makefile. If you just indicate just the .h files in the dependency line of the Makefile that the current target is dependent on, make will know that the corresponding .c file is already required. You don't even need to include the command for the compiler. This reduces our Makefile further, as shown: OBJECTS = data.o main.o io.o project1: $(OBJECTS) cc $(OBJECTS) -o project1 data.o: data.h main.o: data.h io.h io.o: io.h
Special dependencies
Usually, make uses the same command to create or update a target, regardless of which file changes. Some other files, such as libraries allow users to replace a portion of its code. For this kind of different behavior, make allows a special form of the dependency, where the action specified can differ, depending on which file has changed. Here is an example for this rule: target :: source1
command1
target :: source2
command2
As we have described, if source1 changes, target is created or updated using command1; command2 is used if source2 is modified instead.