Interprocess
communication
• Interprocess
communication (IPC) is the transfer of data among processes.
• There
are various ways of communicating between parent and child processes, between
unrelated processes and even between processes on different machines
• Types
of communications
Types
of IPC
- Signals: which are used to indicate that an event has occurred.
- Shared memory: permits processes to communicate by simply reading and writing to a specified memory location.
- Pipes: permit sequential communication from one process to a related process.
- FIFOs: are similar to pipes, except that unrelated processes can communicate because the pipe is given a name in the filesystem.
- Sockets: support communication between unrelated processes even on different computers
Signals
Signals are often described as “software interrupts.” The arrival
of a signal informs a process that some event or exceptional condition has
occurred. There are various types of signals, each of which identifies a
different event or condition. Each signal type is identified by a different
integer, defined with symbolic names of the form SIGxxxx.
Signals are sent to a process by the kernel, by another process,
or by the process itself. For example, the kernel may send a signal to a
process when one of the following occurs:
•
the user typed the
interrupt by Control-C on the keyboard;
•
one of the process’s
children has terminated;
•
a timer (alarm clock) set
by the process has expired; or
•
the process attempted to
access an invalid memory address.
Within the shell, the kill command can be used to send a signal to
a process. The kill() system call provides the same facility within programs.
When
a process receives a signal, it takes one of the following actions, depending
on the signal:
•
it ignores the signal;
•
it is killed by the signal;
or
•
it is suspended until later
being resumed by receipt of a special-purpose signal.
Shared
memory
• One of the simplest interprocess communication methods
is using shared memory.
• Shared memory allows two or more processes to access
the same memory as if they all called malloc and were returned pointers to the
same actual memory.
• When one process changes the memory, all the other
processes see the modification.
• Shared memory is the fastest form of interprocess
communication because all processes share the same piece of memory.
– Access to this shared memory is as fast as accessing a
process’s nonshared memory, and it does not require a system call or entry to
the kernel. It also avoids copying data unnecessarily.
• Because the kernel does not synchronize accesses to
shared memory, you must provide your own synchronization.
– For example, a process should not read from the memory
until after data is written there, and two processes must not write to the same
memory location at the same time. A common strategy to avoid these race
conditions is to use semaphores
Shared
memory allocation
• A process allocates a shared memory segment using
shmget (“SHared Memory GET”).
– Its first parameter is an integer key that specifies
which segment to create.
– Its second parameter specifies the number of bytes in
the segment. Because segments are allocated using pages, the number of actually
allocated bytes is rounded up to an integral multiple of the page size.
– The third parameter is the bitwise or of flag values
that specify options to shmget. The flag values include these:
• IPC_CREAT—this flag indicates that a new segment
should be created.
• Mode flags—this value is made of 9 bits indicating
permissions (R, W, X) granted to owner, group, and world to control access to
the segment. .An easy way to specify permissions is to use the constants
defined in <sys/stat.h>.
• For
e.g. S_IRUSR and
S_IWUSR specify read and write permissions for the owner of the shared memory segment,
and S_IROTH and S_IWOTH specify read and write permissions for others.
– E.g
int segment_id = shmget (shm_key, getpagesize (),
IPC_CREAT | S_IRUSR | S_IWUSER);
• If the call succeeds, shmget returns a segment
identifier.
– Synopsis
• #include <sys/ipc.h>
• #include <sys/shm.h>
• int
shmget(key_t key, size_t size, int
shmflg);
Attachment
and detachment
• To make the shared memory segment available, a process
must use shmat, “Shared Memory ATtach.”
– First parameter is the shared memory segment
identifier SHMID returned by shmget.
– The second argument is a pointer that specifies where
in your process’s address space you want to map the shared memory; if you
specify NULL, Linux will choose an available address.
– The third argument is a flag, which can include the following:
• SHM_RND indicates that the address specified for the
second parameter should be rounded down to a multiple of the page size. If you
don’t specify this flag, you must page-align the second argument to shmat
yourself.
• SHM_RDONLY indicates that the segment will be only
read, not written.
– If the call succeeds, it returns the address of the
attached shared segment.
– Synonpsis
• #include <sys/types.h>
• #include <sys/shm.h>
• void
*shmat(int shmid, const void *shmaddr,
int shmflg);
Dettachment
• When you’re finished with a shared memory segment, the
segment should be detached using shmdt (“SHared Memory DeTach”).
– Pass it the address returned by shmat.
– If the segment has been deallocated and this was the
last process using it, it is removed.
– Calls to exit and any of the exec family automatically
detach segments.
– Synopsis
• #include <sys/types.h>
• #include <sys/shm.h>
• int
shmdt(const void *shmaddr);
Controlling and Deallocating
Shared Memory
• The shmctl (“SHared Memory ConTroL”) call returns information
about a shared memory segment and can modify it.
– The first parameter is a shared memory segment
identifier.
– The
second parameter is the operation to be performed on the shared memory
• IPC_STAT To obtain information about a shared memory segment,
• IPC_SET Write the values of some members of the shmid_ds
structure pointed to by buf to the kernel data structure associated with
this shared memory segment
• IPC_RMID Remove the segment
• The segment is removed when the last process that has
attached it finally detaches it.
• Each shared memory segment should be explicitly
deallocated using “shmctl” when you’re finished with it, to avoid violating the
system wide limit on the total number of shared memory segments. Invoking exit
and exec detaches memory segments but does not deallocate them.
• SYNOPSIS
– #include <sys/ipc.h>
– #include <sys/shm.h>
– int shmctl(int
shmid, int cmd, struct shmid_ds *buf);
– The buf argument is a pointer to a shmid_ds
structure, defined in <sys/shm.h> as follows:
– struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions
*/
size_t
shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat/shmdt */
shmatt_t shm_nattch; /* No. of current attaches */ ...
};
Example
of shared memory
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main ()
{ int
segment_id;
char* shared_memory;
struct
shmid_ds shmbuffer;
int
segment_size;
const
int shared_segment_size = 0x6400;
/*
Allocate a shared memory segment. */
segment_id
= shmget (IPC_PRIVATE, shared_segment_size,
IPC_CREAT
| IPC_EXCL | S_IRUSR | S_IWUSR);
/* Attach
the shared memory segment. */
shared_memory
= (char*) shmat (segment_id, 0, 0);
printf
(“shared memory attached at address %p\n”, shared_memory);
/*
Determine the segment’s size. */
shmctl
(segment_id, IPC_STAT, &shmbuffer);
segment_size
= shmbuffer.shm_segsz;
printf
(“segment size: %d\n”, segment_size);
/*
Determine the segment’s size. */
shmctl
(segment_id, IPC_STAT, &shmbuffer);
segment_size
= shmbuffer.shm_segsz;
printf
(“segment size: %d\n”, segment_size);
/* Write a string to the shared memory segment. */
sprintf
(shared_memory, “Hello, world.”);
/* Detach the shared memory segment. */
shmdt
(shared_memory);
/* Reattach the shared memory segment, at a different
address. */
shared_memory
= (char*) shmat (segment_id, (void*) 0x5000000, 0);
printf
(“shared memory reattached at address %p\n”, shared_memory);
/* Print out the string from shared memory. */
printf
(“%s\n”, shared_memory);
/* Detach the shared memory segment. */
shmdt
(shared_memory);
/* Deallocate the shared memory segment. */
shmctl
(segment_id, IPC_RMID, 0);
return
0;
}
Debugging
• The “ipcs” command provides information on
interprocess communication facilities, including shared segments.
• Use the -m flag to obtain information about shared
memory. For example, this code illustrates that one shared memory segment,
• numbered 1627649, is in use:
• $ ipcs -m
------ Shared Memory Segments --------
key
shmid owner perms
bytes nattch status
0x00000000 1627649
user 640 25600
0
• If this memory segment was erroneously left behind by
a program, you can use the “ipcrm” command to remove it.
• $ipcrm shm 1627649
Pros
and cons of share memory
• Shared memory segments permit fast bidirectional
communication among any number of processes.
• Each user can both read and write, but a program must
establish and follow some protocol for preventing race conditions such as
overwriting information before it is read.
• Also, for multiple processes to use a shared segment,
they must make arrangements to use the same key
Pipes
• A pipe is a communication device that permits
unidirectional communication.
• Data is written to the “write end” of the pipe and is
read back from the “read end.”
• Pipes are serial devices; the data is always read from
the pipe in the same order it was written.
• Typically, a pipe is used to communicate between two
threads in a single process or between parent and child processes.
• In a shell, the symbol “| “creates a pipe.
• For example, this shell command causes the shell to produce
two child processes, one for ls and one for less:
• $ ls | less
– The shell also creates a pipe connecting the standard
output of the “ls” subprocess with the standard input of the “less” process.
– The filenames listed by “ls” are sent to “less” in
exactly the same order as if they were sent directly to the terminal.
• A pipe’s data capacity is limited. If the writer
process writes faster than the reader process consumes the data, and if the
pipe cannot store more data, the writer process blocks until more capacity
becomes available.
• If the reader tries to read but no data is available,
it blocks until data becomes available. Thus, the pipe automatically
synchronizes the two processes
• To create a pipe, invoke the “pipe” command.
• Synonpsis
– #include <unistd.h>
– int pipe(int pipefd[2]);
– Supply an integer array of size 2. The array is used
to return two file descriptors referring to ends of the pipe
– The call to pipe stores the reading file descriptor in
array position 0 and the writing file descriptor in position 1.
– For example, consider this code:
int pipe_fds[2];
int read_fd;
int write_fd;
pipe (pipe_fds);
read_fd = pipe_fds[0];
write_fd = pipe_fds[1];
Communication between Parent
and Child Processes
• A call to pipe creates file descriptors, which are
valid only within that process and its children.
• A process’s file descriptors cannot be passed to
unrelated processes; however, when the process calls fork, file descriptors are
copied to the new child process.
• Thus, pipes can connect only related processes
Example: The parent writes the string contained in the program's command-line argument to the pipe, and the child reads this string a byte at a time from the pipe and echoes it on standard output.
#include <sys/wait.h>
#include <stdio.h>
#include
<stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t
cpid;
char
buf;
if
(argc != 2) {
exit(EXIT_FAILURE);
}
if
(pipe(pipefd) == -1) {
exit(EXIT_FAILURE);
}
cpid =
fork();
if
(cpid == -1) {
exit(EXIT_FAILURE);
}
if (cpid == 0) {
/* Child reads from pipe */
close(pipefd[1]);
// Close unused write end
while
(read(pipefd[0], &buf, 1) > 0) write(STDOUT_FILENO,
&buf, 1);
close(pipefd[0]);
exit(EXIT_SUCCESS);
}
else { /* Parent writes argv[1] to pipe */
close(pipefd[0]);
/* Close unused read end */
write(pipefd[1],
argv[1], strlen(argv[1]));
close(pipefd[1]);
wait(NULL);
/* Wait for child */ exit(EXIT_SUCCESS); } }
FIFOs
• A first-in, first-out (FIFO) file is a pipe that has a
name in the filesystem. Any process can open or close the FIFO; the processes
on either end of the pipe need not be related to each other.
• FIFOs are also called named pipes.
• You can make a FIFO using the “mkfifo” command.
Specify the path to the FIFO on the command line. For example, create a FIFO in
/tmp/fifo by invoking this command:
• $ mkfifo /tmp/fifo
• $ ls -l /tmp/fifo
prw-rw-rw- 1
samuel users 0 Jan 16 14:04 /tmp/fifo
– The first character of the output from ls is p, indicating
that this file is actually a FIFO (named pipe).
Creating a FIFO
• Create a FIFO programmatically using the mkfifo
function.
• Synopsis
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
– The first argument is the path at which to create the
FIFO;
– the second parameter specifies the pipe’s owner,
group, and world permissions,
Accessing a FIFO
• Access a FIFO just like an ordinary file.
• To communicate through a FIFO, one program must open
it for writing, and another program must open it for reading.
• Either low-level I/O functions (open, write, read,
close, and so on) or C library I/O functions (fopen, fprintf, fscanf, fclose,
and so on) may be used.
• For example, to write a buffer of data to a FIFO using
low-level I/O routines, you could use this code:
int fd = open (fifo_path, O_WRONLY);
write (fd, data, data_length);
close (fd);
• Bytes from each writer are written atomically up to a
maximum size of PIPE_BUF (4KB on Linux)
Sockets
• A socket is a bidirectional communication device that
can be used to communicate with another process on the same machine or with a
process running on other machines.
• Sockets are the only interprocess communication we’ll
discuss in this session that permit communication between processes on
different computers.
• Internet programs such as Telnet, FTP, and the World
Wide Web use sockets
No comments:
Post a Comment