Chapter 6
UNIX InterProcess Communication(IPC)
Course: SYSTEM PROGRAMMING
Instructor: Amsalu T. & Getnet G.
Department of Software Engineering
Addis Ababa Science & Technology University (AASTU)
Jan 14, 2019
Outline
● Signals
● PIPES
● FIFOS
● Message Queues
● Shared Memory
● Memory Mappings
Addis Ababa Science & Technology University(AASTU) 2
Jan 14, 2019
Introduction to UNIX IPC
Addis Ababa Science & Technology University (AASTU) 3
Jan 14, 2019
Application Design
Option 1
One huge monolithic program that does every thing
Option 2
Multi_threaded programs
Option 3
Multiple programs using fork() that communicate with each
other using some form of Inter Process Communication (IPC)
Addis Ababa Science & Technology University (AASTU) 4
Jan 14, 2019
Ways to Share Information b/w UNIX Processes
Shared
Process Process Process Process Process Process
memory
User
area
Kernel
area Processes can access shared
Disk Kernel Buffer memory without involvement
of kernel at all
Addis Ababa Science & Technology University (AASTU) 5
Jan 14, 2019
Taxonomy of IPC
Categories of IPC
Communication Synchronization Signals
Addis Ababa Science & Technology University (AASTU) 6
Jan 14, 2019
Taxonomy of IPC
Communication
Data Transfer Shared Memory
Byte Stream Message Passing
Pipes SysV MQ SysV SM
FIFOS POSIX MQ POSIX SM
Stream Sockets Datagram Sockets Memory Mappings
Anonymous Mapping
Memory Mapped Files
Addis Ababa Science & Technology University (AASTU) 7
Jan 14, 2019
Taxonomy of IPC (cont...)
Synchronization
Semaphores For Threads File Locks
SysV Semaphores Mutex File Locks
POSIX Semaphores Condition Variables Record Locks
Named
Unnamed
Addis Ababa Science & Technology University (AASTU) 8
Jan 14, 2019
Taxonomy of IPC (cont...)
Signals
Standard Signals Real Time Signals
Addis Ababa Science & Technology University (AASTU) 9
Jan 14, 2019
Persistence of IPC objects
●
Exists as long as it is held open by a process
●
Pipes and FIFOs
●
TCP, UDP sockets
Process ●
Mutex, condition variables, read write locks
Persistence ●
POSIX memory based semaphores
●
Exists until kernel reboots or IPC objects is explicitly
Kernel deleted
Persistence
●
Message Queues, semaphores & shared memory are at
least kernel persistent
●
Exists until IPC objects is explicitly deleted, or file
File system system crashes
Persistence ●
Message queues, semaphores & shared memory can be
file system persistent if implemented using mapped
files
Addis Ababa Science & Technology University (AASTU) 10
Jan 14, 2019
Overview of Signals
Addis Ababa Science & Technology University (AASTU) 11
Jan 14, 2019
Introduction to Signals
●
Suppose a program is running in a while(1) loop and you press
Ctrl+C key. The program dies. How does this happens?
●
User presses Ctrl+C
●
The tty driver receives character, which matches intr
●
The tty driver calls signal system
●
The signal system sends SIGINT(2) to the process
●
Process receives SIGINT(2)
●
Process dies
●
Actually by pressing <ctrl+c>, you ask the kernel to send SIGINT
to the currently running foreground process. To change the key
combination you can use stty(1) or tcsetattr(2) to replace
the current intr control character with some other key combination
Addis Ababa Science & Technology University (AASTU) 12
Jan 14, 2019
Introduction to Signals (cont...)
●
Signal is a software interrupt delivered to a process by OS because:
●
The process did something (SIGFPE (8), SIGSEGV (11), SIGILL (4))
●
The user did something (SIGINT (2), SIGQUIT (3), SIGTSTP (20))
●
One process wants to tell another process something (SIGCHILD (17))
●
Signals are usually used by OS to notify processes that some event
has occurred, without these processes needing to poll for the event
●
Whenever a process receives a signal, it is interrupted from whatever it
is doing and forced to execute a piece of code called signal handler.
When the signal handler function returns, the process continues
execution as if this interruption has never occurred
●
Asignal handler is a function that gets called when a process receives
a signal. Every signal may have a specific handler associated with it. A
signal handler is called inasynchronous mode. Failing to handle
various signals, would likely cause our application to terminate, when it
receives such signals
Addis Ababa Science & Technology University (AASTU) 13
Jan 14, 2019
Synchronous & Asynchronous Signals
●
Signals may be generated synchronously or asynchronously
●
Synchronous signals pertains to a specific action in the program
and is delivered (unless blocked) during that action. Examples:
●
Most errors generate signals synchronously
●
Explicit request by a process to generate a signal for the same
process
●
Asynchronous signals are generated by the events outside the
control of the process that receives them. These signals arrive at
unpredictable times during execution. Examples include:
●
External events generate requests asynchronously
●
Explicit request by a process to generate a signal for some
other process
Addis Ababa Science & Technology University (AASTU) 14
Jan 14, 2019
Signal Delivery and Handler Execution
Program Code
Start of program Signal Handler
Flow of execution
oc ess
2 of p
r
alf
beh
Ho
n 3
1 s S.
el call Code of signal
n
Ker
Signal delivery Instruction n handler is
Pro executed
gram
Instruction n+1 re sum
es a
t poin
t of in
terru
5 4 ptio
n return
exit()
Addis Ababa Science & Technology University (AASTU) 15
Jan 14, 2019
Signal Handling on the Shell
Demos
Addis Ababa Science & Technology University (AASTU) 16
Jan 14, 2019
Signal Numbers and Strings
●
Every signal has a symbolic name and an integer value associated with
it, defined in /usr/include/asm-generic/signal.h
●
You can use following shell command to list down the signals on your
system:
$ kill -l
●
Linux supports 32 real time signals from SIGRTMIN (32) to
SIGRTMAX (63). Unlike standard signals, real time signals have no
predefined meanings, are used for application defined purposes. The
default action for an un-handled real time signal is to terminate the
receiving process. See also $ man 7 signal
Addis Ababa Science & Technology University (AASTU) 17
Jan 14, 2019
Sending Signals to Processes
A signal can be issued in one of the following ways:
●
Using Key board
●
<Ctrl+c> gives SIGINT(2)
●
<Ctrl+\> gives SIGQUIT(3)
●
<Ctrl+z> gives SIGTSTP(20)
●
Using Shell command
●
kill -<signal> <PID> OR kill -<signal> %<jobID>
●
If no signal name or number is specified then default is to send
SIGTERM(15) to the process
●
Do visit man pages for jobs, ps, bg and fg commands
●
bg gives SIGTSTP(20) while fg gives SIGCONT(18)
●
Using kill() or raise() system call
●
Implicitly by a program (division by zero, issuing an invalid addr, termination of a child process)
Addis Ababa Science & Technology University (AASTU) 18
Jan 14, 2019
Signal Disposition
Upon delivery of a signal, a process carries out one of the following
default actions, depending on the signal: [$man 7 signal]
1. The signal isignored; that is, it is discarded by the kernel and has no
effect on the process. (The process never even knows that it occurred)
2. The process isterminated (killed). This is sometimes referred to as
abnormal process termination, as opposed to the normal process
termination that occurs when a process terminates using exit()
3. Acore dump file is generated, and the process is terminated. A core
dump file contains an image of the virtual memory of the process,
which can be loaded into a debugger in order to inspect the state of the
process at the time that it terminated
4. The process is stopped—execution of the process is suspended
(SIGSTOP, SIGTSTP)
5. Execution of the process isresumed which was previously stopped
(SIGCONT, SIGCHLD)
Addis Ababa Science & Technology University (AASTU) 19
Jan 14, 2019
Signal Disposition (cont...)
●
Each signal has a current disposition which determines how the
process behave when the OS delivers it the signal
●
If you install no signal handler, the run time environment sets up a set
of default signal handlers for your program. Different default actions
for signals are:
TERM Abnormal termination of the program with _ exit( ) i.e, no
clean up. However, status is made available to wait() &
waitpid() which indicates abnormal termination by the
specified signal
CORE Abnormal termination with additional implementation
dependent actions, such as creation of core file may occur
STOP Suspend/stop the execution of the process
CONT Default action is to continue the process if it is currently stopped
Addis Ababa Science & Technology University (AASTU) 20
Jan 14, 2019
Important Signals (Default Behavior: Term)
SIGHUP(1) Informs the process when the user who run the process logs out. When a
terminal disconnect (hangup) occurs, this signal is sent to the controlling
process of the terminal. A second use of SIGHUP is with daemons. Many
daemons are designed to respond to the receipt of SIGHUP by
reinitializing themselves and rereading their configuration files.
SIGINT(2) When the user types the terminal interrupt character (usually <Control+C>,
the terminal driver sends this signal to the foreground process group. The
default action for this signal is to terminate the process.
SIGKILL(9) This is the sure kill signal. It can’t be blocked, ignored, or caught by a
handler, and thus always terminates a process.
SIGPIPE(13) This signal is generated when a process tries to write to a pipe, a FIFO, or a
socket for which there is no corresponding reader process. This normally
occurs because the reading process has closed its file descriptor for the IPC
channel
SIGALRM(14) The kernel generates this signal upon the expiration of a real-time timer set
by a call to alarm() or setitimer()
SIGTERM(15) Used for terminating a process and is the default signal sent by the kill
command. Users sometimes explicitly send the SIGKILL signal to a
process, however, this is generally a mistake. A well-designed application
will have a handler for SIGTERM that causes the application to exit
gracefully, cleaning up temporary files and releasing other resources
Punjab University College Of Information And Technology(PUCIT) 21
beforehand. Killing a process with SIGKILL bypasses SIGTERM handler.
Jan 14, 2019
Important Signals (Default Behavior: Core)
SIGQUIT(3) When the user types the quit character (Control+\) on the keyboard, this
signal is sent to the foreground process group. Using SIGQUIT in this
manner is useful with a program that is stuck in an infinite loop or is
otherwise not responding. By typing Control-\ and then loading the
resulting core dump with the gdb debugger and using the backtrace
command to obtain a stack trace, we can find out which part of the program
code was executing
SIGILL(4) This signal is sent to a process if it tries to execute an illegal (i.e.,
incorrectly formed) machine-language instruction module
SIGFPE(9) Generate by floating point Arithmetic Exception
SIGSEGV(11) Generated when a program makes an invalid memory reference. A
memory reference may be invalid because the referenced page
doesn’t exist (e.g., it lies in an unmapped area somewhere between
the heap and the stack), the process tried to update a location in read-
only memory (e.g., the program text segment or a region of mapped
memory marked read-only), or the process tried to access a part of
kernel memory while running in user mode. In C, these events often
result from dereferencing a pointer containing a bad address. The
name of this signal derives from the term segmentation violation
Addis Ababa Science & Technology University (AASTU) 22
Jan 14, 2019
Important Signals (cont...)
Default Behavior: Stop
SIGSTOP(19) This is the sure stop signal. It can’t be blocked, ignored, or
caught by a handler; thus, it always stops a process
SIGTSTP(20) This is the job-control stop signal, sent to stop the foreground
process group when the user types the suspend character
(usually <Control+Z>) on the keyboard.. The name of this
signal derives from “terminal stop”
Default Behavior: Cont
SIGCHILD(17) This signal is sent (by the kernel) to a parent process when
one of its children terminates (either by calling exit() or as
a result of being killed by a signal). It may also be sent to a
process when one of its children is stopped or resumed by a
signal
SIGCONT(18) When sent to a stopped process, this signal causes the process
to resume (i.e., to be rescheduled to run at some later time).
When received by a process that is not currently stopped, this
signal is ignored by default. A process may catch this signal,
so that it carries out some action when it resumes
Addis Ababa Science & Technology University (AASTU) 23
Jan 14, 2019
Masking of Signals
●
A signal is generated by some event. Once generated, a signal is later
delivered to a process, which then takes some action in response to the
signal. Between the time it is generated and the time it is delivered, a
signal is said to be pending. Normally, a pending signal is delivered to
a process as soon as it is next scheduled to run, or immediately if the
process is already running (e.g., if the process sent a signal to itself).
There can be at most one pending signal of any particular type, i.e.,
standard signals are not queued
●
Sometimes, however, we need to ensure that a segment of code is not
interrupted by the delivery of a signal. To do this, we can add a signal to
The process’s signal mask—a set of signals whose delivery is currently
blocked. If a signal is generated while it is masked/blocked, it remains
pending until it is later unmasked or unblocked (removed from the
signal mask)
Addis Ababa Science & Technology University (AASTU) 24
Jan 14, 2019
Sending Signals to Processes
Addis Ababa Science & Technology University (AASTU) 25
Jan 14, 2019
kill() System call
int kill(pid_t pid, int sig);
●
One process can send a signal to another process using the
kill() system call
●
The pid argument identifies one or more processes to which the
signal specified by sig is to be sent
●
If sig is zero then normal error checking is performed but no
signal is sent. Used to determine if a specified process still exists.
If it doesn't exist, a -1 is returned & errno is set to ESRCH
●
If no process matches the specified pid, kill() fails and sets
errno to ESRCH
Addis Ababa Science & Technology University (AASTU) 26
Jan 14, 2019
kill() System call (cont...)
int kill(pid_t pid, int sig);
Four different cases determine how pid is interpreted:
●
If pid > 0, the signal is sent to the process with the process ID
specified by first argument
●
If pid == 0, the signal is sent to every process in the same process
group as the calling process, including the calling process itself
●
If pid < –1, the signal is sent to every process in the process group
whose PGID equals the absolute value of pid
●
If pid == –1, the signal is sent to every process for which the calling
process has permission to send a signal, except init and the calling
process. If a privileged process makes this call, then all processes on
the system will be signaled, except for these last two
Addis Ababa Science & Technology University (AASTU) 27
Jan 14, 2019
raise() Library call
int raise(int sig);
●
Sometimes, it is useful for a process to send a signal to itself. The raise()
function performs this task
●
In a single-threaded program, a call to raise() is equivalent to the
following call to kill():
kill(getpid(), sig);
●
When a process sends itself a signal using raise() or kill(), the signal
is delivered immediately (i.e., before raise() returns to the caller)
●
Note that raise() returns a nonzero value (not necessarily –1) on error.
The only error that can occur with raise() is EINVAL, because sig was
invalid
Addis Ababa Science & Technology University (AASTU) 28
Jan 14, 2019
abort() Library call
void abort();
●
The abort() function terminates the calling process by raising a
SIGABRT signal. The default action for SIGABRT is to produce a core
dump file and terminate the process. The core dump file can then be
used within a debugger to examine the state of the program at the time
of the abort() call
●
abort() function never returns
Addis Ababa Science & Technology University (AASTU) 29
Jan 14, 2019
pause() System call
int pause();
●
The pause() system call causes the invoking process/thread to
sleep until a signal is received that either terminates it or causes it
to call a signal catching function
●
The pause() function only returns when a signal was caught
and the signal-catching function returned. In this case pause()
returns -1, and errno is set to EINTR
Addis Ababa Science & Technology University (AASTU) 30
Jan 14, 2019
alarm() System call
unsigned int alarm(unsigned int seconds);
●
The alarm() system call is used to ask the OS to send calling process a
special signal SIGALARM(14) after a given number of seconds. If seconds is
zero no new alarm is scheduled
●
This function returns the previously registered alarm clock for the process that
has not yet expired, i.e., the number of seconds left for that alarm clock is
returned as the value of this function. Previously registered alarm clock is
replaced by new value
●
UNIX like systems do not operate as real-time systems, so your process might
receive this signal after a longer time than requested. Moreover, there is only
one alarm clock per process. Can be used for following purposes:
●
To check timeouts (e.g., wait for user input up to 30 seconds, else exits)
●
To check some conditions on a regular basis (e.g., if a server has not
responded in last 30 seconds, notify the user and exits)
Addis Ababa Science & Technology University (AASTU) 31
Jan 14, 2019
Adding a Delay: using sleep()
int sleep(unsigned int secs);
int usleep(useconds_t usec);
int nanosleep(const struct timespec* req,
struct timespec* rem);
●
These calls causes the calling thread to sleep (suspend execution) either
until the number of specified in seconds specified in the argument have
elapsed or until a signal arrives which is not ignored
●
Returns zero if the requested time has elapsed, or the number of seconds left
to sleep, if the call was interrupted by a signal handler
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
Addis Ababa Science & Technology University (AASTU) 32
Jan 14, 2019
Ignoring Signals and Writing SHs
using
signal()
Addis Ababa Science & Technology University (AASTU) 34
Jan 14, 2019
signal() System call
sighandler_t signal(int signum, void (*sh)(int));
●
To change the disposition of a particular signal a programmer can use the
signal() system call, which installs a new signal handler for the signal
with number signum
●
The second parameter can have three values
i) SIG_IGN: the signal is ignored
ii) SIG_DFL: the default action associated with signal occur
iii) A user specified function address, which is passed an integer argument
and returns nothing
●
The signal() system call returns the previous signal handler, or SIG_ERR
on error
●
The signals SIGKILL and SIGSTOP cannot be caught. Moreover, the
behavior of a process is undefined after it ignores SIGFPE, SIGILL or
SIGSEGV signal that was not generated by kill() or raise() functions
Addis Ababa Science & Technology University (AASTU) 35
Jan 14, 2019
Handling Signals
void (*oldhandler)(int);
oldhandler = signal(SIGINT, newhandler);
---
--- If SIGINT is delivered, newhandler
--- will be used to handle signal
---
if (signal(SIGINT,oldhandler) == SIG_ERR){---}
---
--- If SIGINT is delivered, oldhandler
--- will be used to handle signal
Addis Ababa Science & Technology University (AASTU) 36
Jan 14, 2019
Masking Signals
using
sigprocmask()
Addis Ababa Science & Technology University (AASTU) 38
Jan 14, 2019
Avoiding Race Conditions Using Signal Mask
●
One of the problems that might occur when handling a signal, is the
occurrence of a second signal while the signal handler function is
executing
●
A process can temporarily prevent signals from being delivered, by
blocking/masking it, while it is doing some thing critical, or while it is
executing inside a signal handler
●
Every process has a signal mask that defines the set of signals
currently blocked for that process. One bit for each possible signal. If
a bit is ON, that signal is currently blocked
●
Since it is possible for the number of signals to exceed to number of
bits in an integer, therefore, POSIX.1 defines a data type called
sigset_t that holds a signal set of a process
●
When a process blocks a signal, the OS doesn’t deliver signal until the
process unblocks the signal. However, when a process ignores a
signal, signal is delivered and the process handles it by throwing it
away
●
Remember, after a fork(), child process inherits its parent mask
Addis Ababa Science & Technology University (AASTU) 39
Jan 14, 2019
Functions related to Signal Sets
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);
To create a process signal mask, you need to create a variable of type
sigset_t. The sigemptyset() function initializes a signal set to
contain no members, while the sigfillset() function initializes a set
to contain all signals. After initialization, individual signals can be added
to a set using sigaddset() and removed using sigdelset(). There
are two ways to initialize a signal set:
●
You can initially specify it to be empty with sigemptyset() and
then add specified signals individually using sigaddset()
●
You can initially specify it to be full with sigfillset() and then
delete specified signals individually using sigdelset()
Addis Ababa Science & Technology University (AASTU) 40
Jan 14, 2019
Setting the Process Signal Mask
int sigprocmask(int how,const sigset_t* nset, sigset_t* oset);
●
The sigprocmask() allows us to get the existing signal mask or set a
new signal mask of a process
●
The second argument specifies the new signal mask. It it is NULL, then the
signal mask is unchanged
●
The third argument will store the old mask of the process. This is useful
when we want to restore the previous masking state once we're done with our
critical section
●
The first argument how actually determines how the process signal mask
will be changed. It can have following three values:
SIG_BLOCK The set of blocked signals is the union of nset and the
current signal set oset
SIG_UNBLOCK The signals in thenset are removed from the current set of
blocked signals. It is legal to attempt to unblock a signal
which is not blocked
SIG_SETMASK The set of blocked signals is set to the argument nset
Addis Ababa Science & Technology University (AASTU) 41
Jan 14, 2019
Limitations of signal() System call
●
Using the signal() call, we cannot determine the current
disposition of a signal without changing the disposition.
Example: If we want to determine the current disposition of
SIGINT, we can't do it without changing the current disposition
sighandler_t oldHandler = signal(SIGINT, &newHandler);
●
If we use signal(), to register a handler for a signal, it is
possible that after we entered the signal handler, but before we
managed to mask all the signals using sigprocmask(), we
receive another signal, which WILL be called
●
There are a lot of variations in the behavior of signal() call
across UNIX implementations
Addis Ababa Science & Technology University (AASTU) 44
Jan 14, 2019
sigaction() System call
int sigaction(int signum, const struct sigaction*
newact, struct sigaction* oldact);
●
Although sigaction() is somewhat more complex to use than
signal(), it gives following advantages over signal():
●
sigaction() allows us to retrieve the disposition of a signal
without changing it, and to set various attributes controlling precisely
what happens when a signal handler is invoked
●
sigaction() is more portable than signal()
●
The first argument signum identifies the signal whose disposition we
want to retrieve or change
●
The second argument newact is a pointer to a structure specifying a
new disposition for the signal. If we are interested only in finding the
existing disposition of the signal, then we can specify NULL for this
argument
●
The third argument oldact is used to return information about the
signal’s previous disposition. If we are not interested in this information,
then we can specify NULL for this argument
Addis Ababa Science & Technology University (AASTU) 45
Jan 14, 2019
sigaction() System call (cont...)
●
The structures pointed to by third and fourth argument to sigaction
is of following type:
struct sigaction {
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
};
●
The sa_handler field specifies the pointer to the handler function
●
The sa_mask field specifies the process signal mask to be set while
this signal is being handled
●
The sa_flags field contains flags that effect signal behavior,
normally it is set to zero
Addis Ababa Science & Technology University (AASTU) 46
Jan 19, 2019
UNIX Pipes
On the Shell
Addis Ababa Science & Technology University (AASTU) 3
Jan 19, 2019
Introduction to Pipes
●
History of Pipes: Pipes history goes back to 3 rd edition of UNIX
in 1973. They have no name and can therefore be used only
between related processes. This was corrected in 1982 with th
addition of FIFOs
● Byte stream: When we say that a pipe is a byte stream, we mean
that there is no concept of message boundaries when using a pipe.
Each read operation may read an arbitrary number of bytes
regardless of the size of bytes written by the writer. Furthermore,
the data passes through the pipe sequentially, bytes are read from
a pipe in exactly the order they were written. It is not possible to
randomly access the data in a pipe using lseek()
● Pipes are unidirectional: Data can travel only in one direction.
One end of the pipe is used for writing, and the other end is used
for reading
Addis Ababa Science & Technology University (AASTU) 4
Jan 19, 2019
Introduction to Pipes (cont...)
Reading from a pipe:
● When a process reads bytes from a pipe, those bytes are removed
from the pipe (Destructive read semantics)
● By default, when a process attempts to read from a pipe that is
currently empty, the read call blocks until some bytes are written
into the pipe
● If the write end of a pipe is closed, and a process tries to read, it
will receive and EOF character, i.e., read() returns 0
● If two processes try to read from the same pipe, one process will
get some of the bytes from the pipe, and the other process will get
the other bytes. Unless the two processes use some method to
coordinate their access to the pipe, the data they read are likely to
be incomplete
Addis Ababa Science & Technology University (AASTU) 5
Jan 19, 2019
Introduction to Pipes (cont...)
Writing to a pipe:
● By default, when a process attempts to write to a pipe that i
currently full, the write(2) call blocks until there is enough
space in the pipe
● If a process tries to write, say, 1000 bytes, and there is room for
500 bytes only, the call waits until 1000 bytes of space are
available
● If the read end of a pipe is closed and a process tries to write, the
kernel sends SIGPIPE to the writer process
Addis Ababa Science & Technology University (AASTU) 6
Jan 19, 2019
Introduction to Pipes (cont...)
● Size of Pipe:
● If multiple processes are writing to a single pipe, then it is
guaranteed that their data won’t be intermingled if they write no
more than PIPE_BUF bytes at a time
● This is because writing PIPE_BUF number of bytes to a pipe is
an atomic operation. On Linux, value of PIPE_BUF is 4096
● When writing more bytes than PIPE_BUF to a pipe, the kernel
may transfer the data in multiple smaller pieces, appending
further data as the reader removes bytes from the pipe. The
write() call blocks until all of the data has been written to the
pipe
● When there is a single writer process, this doesn’t matter. But in
case of multiple writer processes, this may cause problems
Addis Ababa Science & Technology University (AASTU) 7
Jan 19, 2019
Creating a UNIX Pipe
int pipe(int fd[2]);
● A pipe is created by calling the pipe() system call
● Creating a pipe is similar to opening two files. A successful call t
pipe() returns two open file descriptors in the array fd; one
contains the read descriptor of the pipe, fd[0], and the other contains
the write descriptor of the pipe fd[1]
● As with any file descriptor, we can use the read() and write()
system calls to perform I/O on the pipe. Once written to the write end
of a pipe, data is immediately available to be read from the read end. A
read() from a pipe blocks if the pipe is empty
● From an implementation point of view, a pipe is a fixed-size main
memory circular buffer created and maintained by the kernel. The
kernel handles the synchronization required for making the reader
process wait when the pipe is empty and the writer process wait when
the pipe is full
Addis Ababa Science & Technology University (AASTU) 9
Jan 19, 2019
Creating a UNIX Pipe
int pipe2(int fd[2], int flags);
● The pipe2() system call can also be used to create a pipe. The
second argument flags in pipe2() is used to control the attributes
of the pipe descriptors. A zero in the flags argument make pipe2()
behave like pipe() system call
● The second argument can be a bit-wise OR of two values:
● O_CLOEXEC: Set the close-on-exec flag on the pipe descriptors
i.e., when a process executes an exec() system call, it does not
inherit an already open pipe
● O_NONBLOCK: Set pipe descriptors for nonblocking I/O
● We can also use the stdio functions ( printf(), scanf(), and so
on) with pipes by first using fdopen() to obtain a file stream
corresponding to one of the descriptors in fd. However, when doing
this, we must be aware of the stdio buffering issues
Addis Ababa Science & Technology University (AASTU) 10
Jan 19, 2019
Use of Pipe in a Single Process
int fd[2];
pipe(fd);
int cw = write(fd[1],msg, strlen(msg));
int cr = read(fd[0], buf, cw);
write(1, buf, cr);
$ ./[Link]
Write descriptor of pipe Read descriptor of pipe
fd[1] fd[0]
PPFDT
flags ptr
User Space
0 stdin
Kernel Space 1 stdout
2 stderr
Uni-directional 3 fd[0]
4 fd[1]
fd[1] Byte Stream fd[0]
Write end of pipe Read end of pipe OPENMAX-1
Addis Ababa Science & Technology University (AASTU) 11
Jan 19, 2019
Use of Pipe Between two Related Processes
Parent Process (Writer) fork() Child Process (Reader)
fd[1] fd[0] fd[1] fd[0]
User Space
Kernel Space
PPFDT PPFDT
Uni-directional
flags ptr flags ptr
Byte Stream
0 0
1 1
2 2
3 fd[0] 3 fd[0]
4 fd[1] 4 fd[1]
OPENMAX-1 OPENMAX-1
Addis Ababa Science & Technology University (AASTU) 13
Jan 19, 2019
Bidirectional Comm Using Pipes
Parent Process fork() Child Process
fd2[1] fd1[0] fd1[1] fd2[0]
User Space
Kernel Space
PPFDT PPFDT
flags ptr flags ptr
0 Uni-directional 0
1 Byte Stream 1
2 2
3 fd1[0] 3 fd1[0]
4 fd1[1] Uni-directional 4 fd1[1]
5 fd2[0] 5 fd2[0]
Byte Stream
6 fd2[1] 6 fd2[1]
Addis Ababa Science & Technology University (AASTU) 15
Jan 19, 2019
Example: cat [Link] | wc
Let us try writing a program that simulate the shell command
cat [Link] | wc
cat [Link] wc
stdin stdout stdin stdout
User Space
Kernel Space
PPFDT of cat Uni-directional PPFDT of wc
flags ptr flags ptr
fd[1] Byte Stream fd[0]
0 stdin 0 fd[0]
1 fd[1] 1 stdout
2 stderr 2 stderr
3 3
4 4
OPENMAX-1 OPENMAX-1
Addis Ababa Science & Technology University (AASTU) 17
Jan 19, 2019
Example: man ls | grep ls | wc -l
Parent
Process
man grep wc
stdin stdout stdin stdout stdin stdout
User Space
Kernel Space
Uni-directional Uni-directional
fd1[1] Byte Stream fd1[0] fd2[1] Byte Stream fd2[0]
PPFDT of man PPFDT of grep PPFDT of wc
flags ptr flags ptr flags ptr
0 stdin 0 fd1[0] 0 fd2[0]
1 fd1[1] 1 fd2[1] 1 stdout
2 stderr 2 stderr 2 stderr
3 3 3
4 4 4
OPENMAX-1 OPENMAX-1 OPENMAX-1 19
Jan 19, 2019
UNIX FIFOs
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
Introduction to FIFOs
● Pipes have no names, and their biggest disadvantage is that they can be
only used between processes that have a parent process in common
(ignoring descriptor passing)
● UNIX FIFO is similar to a pipe, as it is a one way (half duplex) flow of
data. But unlike pipes a FIFO has a path name associated with it
allowing unrelated processes to access a single pipe
● FIFOs/named pipes are used for communication between related or
unrelated processes executing on the same machine
● A FIFO is created by one process and can be opened by multiple
processes for reading or writing. When processes are reading or writing
data via FIFO, kernel passes all data internally without writing it to the
file system. Thus a FIFO file has no contents on the file system; the
file system entry merely serves as a reference point so that processes
can access the pipe using a name in the file system
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
Use of FIFO Between Unrelated Processes
$ echo “Hello PUCIT” 1> fifo1 $ cat fifo1
P1 P2
User Space
Kernel Space
Uni-directional
Byte Stream
fifo1
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
UNIX Named Pipes (FIFOs)
On the Shell
Proof of Concept
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
mkfifo() Library Call
int mkfifo(const char*pathname, mode_t mode);
● Makes a FIFO special file with name pathname. The second
argument mode specifies the FIFO’s permissions. It is modified by the
process’s umask in the usual way: (mode & ~umask)
● Once you have created a FIFO, any process can open it for reading or
writing, in the same way as an ordinary file. Opening a FIFO for
reading normally blocks until some other process opens the same
FIFO for writing, and vice versa
● Call Fails when:
● Parent directory does not allow write permission
● Path name already exists
● Path name points outside accessible address space
● Path name too long
● Insufficient kernel memory
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
mknod() System Call
int mknod(const char*name, mode_t mode, dev_t device);
● The mknod() system call creates a FIFO file with name
mentioned as the first argument
● The second argument mode specifies both the permissions to use
and the type of node to be created. It should be a combination (using
bitwise OR) of one of the file types (S_IFREG, S_IFIFO,
S_IFCHR, S_IFBLK, S_IFSOCK ) and the permissions for the
file
● For creating a named pipe the third argument is set to zero. However,
to create a character or block special file we need to mention th
major and minor numbers of the newly created device special file
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
Communication Using FIFO
writer reader
writefd readfd
User Space
Kernel Space
Uni-directional
fifo1
PPFDT PPFDT
flags ptr flags ptr
0 0
1 1
2 2
3 writefd 3 readfd
4
fifo1 4
OPENMAX-1 OPENMAX-1
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
Bidirectional Comm Using FIFOs
3 6
readfd writefd
teacher student
1
writefd 2 readfd
4 5 User Space
Kernel Space
Uni-directional
fifo1
Uni-directional
PPFDT PPFDT
fifo2
flags ptr flags ptr
0 0
1 1
2 2
3 readfd 3 writefd
4 writefd fifo1 fifo2 4 readfd
OPENMAX-1 OPENMAX-1
Addis Ababa Science & Technology University (AASTU)
Jan 19, 2019
Message Queues
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
Introduction to Message Queues
● Message Queues can be used to pass messages between related
or unrelated processes executing on same machine. Message
queues are somewhat like pipes and fifos, but differ in two
important aspects:
➔ First, message boundaries are preserved, so that readers and
writers communicate in units of messages
➔ Second, message queues are kernel persistent
● A Message Queue can be thought of as a linked list of messages
in the kernel space. Processes with adequate permissions can put
messages onto the queue & processes with adequate permissions
can remove messages from the queue
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Difference between FIFOs & Message Queues
● Message Queues have Kernel persistence while FIFOS have
process persistence
● In pipes, a process read or write stream of bytes, while i
message queues a process read or write a complete delimited
message; it is not possible to read a partial message leaving
rest behind in IPC object
● In pipes a write makes no sense unless a reader also exists. In
message queues there is no requirement that some reader
must be waiting before a process writes a message to the
queue
● Message queues are priority driven. Queue always remain
sorted with the oldest message of the highest priority at front
● A process can determine the status of a message queue
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
SYSTEM-V vs POSIX Message Queues
The three IPC mechanisms (message queues, semaphores and shared
memory) are collectively referred to as either System-V IPC or the
POSIX IPC. Both System-V as well as the POSIX standard provides
API for the implementation of these IPC mechanism. The two major
differences between these two implementations for message queues
are:
● System-V message queues can return a message of any desire
priority, while POSIX message queue always return the oldest
message of highest priority
● POSIX message queues allow the generation of signal when a
message is placed onto an empty queue, where as nothing similar
is provided by System-V message queue
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
APIs Related to Message Queues
Interface System-V API POSIX API
Header file <sys/msg.h> <mqueue.h>
Data Structure msqid_ds mqd_t
Create/open msgget() mq_open()
Close none mq_close()
Perform IPC msgsnd(), msgrcv() mq_send(),mq_receive()
Control operations msgctl() mq_getattr(),mq_setattr(),
mq_notify()
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Creating Or Opening A Message Queue
int msgget(key_t key, int msgflag);
● To create a brand new message queue or to get the identifier of a
existing queue we use the msgget() system call, which on success
returns a unique message queue identifier
● If a message queue associated with the first argument, key already
exist, the call returns the identifier of the existing message queue,
otherwise it creates a new message queue
● We can use IPC_PRIVATE constant as first argument. A parent
process creates message queue prior to performing a fork(), and the
child inherits the returned message queue identifier. For unrelated
processes we can use this constant, but in that case the creator process
has to write the returned message queue identifier in a file that can be
read by the other process
● The second argument msgflag is normally IPC_CREAT|0666
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Creating Or Opening A Message Queue (cont...)
int msgget(key_t key, int msgflag);
● Instead of using IPC_PRIVATE constant as first argument,
processes can use the ftok() library call with the same
arguments to generate a unique key. The key is then used as first
argument to msgget() to either generate a new message queue
identifier or get an existing one
key_t ftok(char *pathname, int proj);
● The key returned by ftok() is a 32 bit value, created by taking:
● Least significant 8 bits from proj argument
● Least significant 8 bits of minor device number of the device
containing the filesystem on which the file in the first
argument reside
● Least significant 16 bits of the inode number of the file
referred by first argument pathname
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Sending Messages
int msgsnd(int msqid, const void* msgp, size_t msgsz,
int msgflg);
● The msgsnd() system call is used to send a message to the
message queue identified by its first argument, which is the
message queue identifier
● The second argument is a pointer to a structure of type msgbuf
having following two fields:
struct msgbuf{
long mtype; //used to retrieve a message by type
char mtext[512];
}
● The third argument msgsz is the size of mtext field in the
structure msgbuf
● The fourth argument msgflag can be 0 or IPC_NOWAIT
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Receiving Messages
int msgrcv(int msqid, void* msgp, size_t maxmsgsz,
long msgtype, int msgflag);
● The msgrcv()system call reads and removes a message from message
queue identified by its first argument msqid, and copies its contents into the
buffer pointed to by its second argument msgp
● The third argument maxmsgsz, specifies the maximum space available in
the mtext field
● The fifth argument msgflg is a bit mask, normally kept as IPC_NOWAIT
● The fourth argument msgtype is used to specify as to which message is to
be removed and returned to the caller based. This is achieved by specifying
the msgtype field of the struct msgbuf
msgtype Description
msgtype == 0 First message from queue is removed and returned
msgtype > 0 First message from queue whose mtype field equals to msgtype is
removed and returned to calling process
msgtype < 0 First message of the lowest mtype field less than or equal to absolute
value of msgtype is removed & returned
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Example: Receiving Messages
Suppose that we have a message queue containing msgs as shown Queue posn Msgtype Msg Body
and we perform msgrcv()calls of the following form
1 300 ….
msgrcv(id,&msg,maxmsgsz,0,0);
2 100 ….
Would retrieve msgs in following order:
1(mtypr=300) 3 200 ….
2(mtypr=100) 4 400 ….
3(mtypr=200) 5 100 ….
4(mtypr=400)
5(mtypr=100)
msgrcv(id,&msg,maxmsgsz,100,0);
Would retrive msgs in following order
2(mtypr=100)
5(mtypr=100)
Any further calls would block
msgrcv(id,&msg,maxmsgsz,-300,0)
Would retrive msgs in following order
2(mtypr=100)
5(mtypr=100)
3(mtypr=200)
1(mtypr=300)
Any further call would block, since type of the remaining message(400) exceeds 300
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Message Queue As Linked List In Kernel
sender receiver
User Space
Message table Kernel Space
MQ Entry
Message Record Message Record
Kernel Data Area
Next ptr Next ptr
MQ Entry
mtype mtype
--- Msg Size Msg Size
--- Kernel ptr Kernel ptr
Addis Ababa Science & Technology University(AASTU)
Jan 19, 2019
Shared Memory
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
Introduction to Shared Memory
● Shared Memory allows two or more processes to share a
memory region or segment of memory for reading and writing
purposes
● The problem with pipes, fifo and message queue is that mode
switches are involved as the data has to pass from one proces
buffer to the kernel buffer and then to another process buffer
● Since access to user-space memory does not require a mode
switch, therefore, shared memory is considered as one of the
quickest means of IPC
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
APIs to Shared Memory
Interface System-V API POSIX API
Header file <sys/shm.h> <mqueue.h>
Data Structure shmid_ds File descriptor
Create/open shmget(), shmat() shm_open()
Close shmdet() shm_unlink()
Perform IPC Access memory mmap(),memcpy()
Control operations shmctl()
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Creating/Opening Shared Memory Segment
int shmget(key_t key, size_t size, int shmflg);
● The shmget() system call creates a new shared memory segment or
obtains the identifier of an existing segment. The contents of a newly
created shared memory segment are initialized to 0. The return value is
the ID of the shared memory segment
● The first argument key can be the constant IPC_PRIVATE or can be
achieved using ftok() library call (as discussed in MQ session)
● The second argument size specifies the desired size of the segment in
bytes. Kernel round it up to next multiple of the system page size. If we
are using shmget() to obtain the identifier of an existing segment
then size has no effect on the segment
● The shmflg argument specifies the permissions to be placed on a new
shared memory segment or checked against an existing segment. In
addition, it can be a bit wise OR of constants likeIPC_CREAT and
IPC_EXCL
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Using Shared Memory Segment
void *shmat(int shmid, const void *shmaddr, int shmflg);
● The shmat() system call attaches the shared memory segment
identified by shmid to the address space of the calling process
● The second argument shmaddr is the address where the memory
segment will be attached. If we want the OS Kernel to select
suitable address, we keep it NULL
● The third argument shmflg can be SHM_RDONLY to attach the
shared memory segment for read-only access. We can place a
zero over there for giving both read and write access
● On success shmat() returns the address at which the shared
memory segment is attached, which can be treated like a normal
C pointer. We can assign the return value fromshmat() to a
pointer of some intrinsic data type or a programmer defined
structure
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Using Shared Memory Segment
int shmdt(const void *shmaddr);
● When a process no longer needs to access a shared memory segment, it
can call shmdt() to detach the segment from its address space
● The only argument to the call shmaddr identifies the segment to be
detached. It should be a value returned by a previous call to shmat()
● Detaching a shared memory segment is not the same as deleting it.
Deletion can be performed using the shmctl()
● A child created by fork() inherits its parent’s attached shared
memory segments. Thus, shared memory provides an easy method o
IPC between parent and child. However, after an exec(), all attached
shared memory segments are detached
● Shared memory segments are also automatically detached on process
termination
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Using Shared Memory Segment
int shmctl(int shmid, int cmd,struct shmid_ds *buf);
● The shmctl() system call is used to perform control operations on
the shared memory segment specified in its first argument shmid
● One of the basic control operation is deletion of the shared memor
segment. This can be done by givingIPC_RMID as the cmd in the
second argument. This will destroy the memory segment after the last
process detaches it
● For deletion operation of shared memory the third argument is kept
NULL
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Memory Mapping
Addis Ababa Science & Technology University (AASTU)
Jan 22, 2019
Memory Mapped Files
● A memory-mapped file is mostly a segment of virtual memory
that has been assigned a direct byte-for-byte correlation with
some portion of a file on disk
● Memory mapped I/O let us map a file on disk into a buffer in
process address space, so that, when we fetch bytes from the
buffer, the corresponding bytes of the file are read. Similarly,
when we store data in the buffer, the corresponding bytes are
automatically written to the file. This lets us perform I/O without
using read() or write() system calls
● There are two types of memory mapped files:
➔ Persisted / File Mapping
➔ Non-Persisted / Anonymous Mapping
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Location of Memory Mappings in Virtual Memory
len Memory Mapped Portion
Of File
Start address
MM portion
of file
offset
len
[Link]
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Shared File Mapping
● Multiple processes mapping the same region of a file share the
same physical pages of memory. Whenever a process tries to
write, the modifications to the contents of the mapping are
carried through to the file
● Main uses of shared file mapping is:
➔ Memory-mapped I/O
➔ IPC in a manner similar to System V shared memory
segments between related or unrelated processes
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
Private File Mapping
● Modifications are not visible to other processes. Multiple
processes mapping the same file initially share the same physical
pages of memory. Whenever a process tries to write, copy-on-
write technique is employed, so that changes to the mapping by
one process are invisible to other processes
● The main use of private file mapping is initializing a process’s
text and initialized data segments from the corresponding parts
of a binary executable file or a shared library file
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
mmap() System Call
void *mmap(void * addr , size_t len , int prot,
int flags, int fd , off_t offset);
● The mmap() system call is used to request the creation of memory
mappings in the address space of the calling process. On success, i
returns the starting address of the mapping
● The first argument addr argument indicates the virtual address at
which the mapping is to be located. Preferably, we should give NULL,
so that the kernel chooses a suitable address for the mapping that
doesn't conflict with any existing mapping
● The second argument len specifies the size of the mapping in bytes.
To map an entire file, we put len as size of the file. Normally, Kernel
creates mappings rounded up to the next multiple of the page size
● The third argument prot is a bit mask specifying the permissions
(PROT_READ, PROT_WRITE, PROT_EXEC)
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
mmap() System Call (cont...)
void *mmap(void * addr , size_t len , int prot,
int flags, int fd , off_t offset)
● The fourth argument flags can be either MAP_PRIVATE or
MAP_SHARED (as discussed)
● The fifth argument fd is a file descriptor identifying the file to be
mapped
● The sixth argument offset specifies the starting point of the
mapping in the file. To map the entire file, we would specify
offset as 0 and len as the size of the file
● The last two arguments are ignored for non-persisted or anonymous
mapping. We normally put -1 for fd and a zero foroffset for
anonymous mapping
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
msync() System Call
int msync(void *addr, size_t len, int flag);
● The msync() function causes the changes in part or all of th
memory segment to be written back to (or read from) the mapped
file
● The first argument addr is the address that is returned by the
mmap() call
● The second argument len specifies the length of mapping
● The third argument flags controls how the update should be
performed. It can have following three values:
Flag Description
MS_ASYNC Request update and returns immediately
MS_SYNC Request update and waits for it to complete
MS_INVALIDATE Invalidate other mappings of same file
Addis Ababa Science & Technology University(AASTU)
Jan 22, 2019
munmap() System Call
int munmap(void * addr, size_t len)
● This simply unmaps the memory mapped region pointed to by
addr with length len
● Normally, we unmap an entire mapping. Thus, we specify addr
as the address returned by a previous call to mmap(), and
specify the same length value as was used in the mmap() call
● The memory mapped region is automatically unmapped when a
process terminates or performs an exec
● Closing the file fd does not unmap the region
Addis Ababa Science & Technology University(AASTU)
End
Addis Ababa Science & Technology University(AASTU) 48