Introductory 4.4BSD IPC PSD:20-1
An Introductory 4.4BSD
Interprocess Communication Tutorial
Stuart Sechrest
Computer Science Research Group
Computer Science Division
Department of Electrical Engineering and Computer Science
University of California, Berkeley
ABSTRACT
Berkeley UNIX- 4.4BSD offers several choices for inter-
process communication. To aid the programmer in developing
programs which are comprised of cooperating processes, the
different choices are discussed and a series of example pro-
grams are presented. These programs demonstrate in a simple
way the use of pipes, socketpairs, sockets and the use of
datagram and stream communication. The intent of this docu-
ment is to present a few simple example programs, not to
describe the networking system in full.
1. Goals
Facilities for interprocess communication (IPC) and
networking were a major addition to UNIX in the Berkeley
UNIX 4.2BSD release. These facilities required major addi-
tions and some changes to the system interface. The basic
idea of this interface is to make IPC similar to file I/O.
In UNIX a process has a set of I/O descriptors, from which
one reads and to which one writes. Descriptors may refer to
normal files, to devices (including terminals), or to com-
munication channels. The use of a descriptor has three
phases: its creation, its use for reading and writing, and
its destruction. By using descriptors to write files,
rather than simply naming the target file in the write call,
one gains a surprising amount of flexibility. Often, the
program that creates a descriptor will be different from the
program that uses the descriptor. For example the shell can
create a descriptor for the output of the `ls' command that
will cause the listing to appear in a file rather than on a
terminal. Pipes are another form of descriptor that have
been used in UNIX for some time. Pipes allow one-way data
transmission from one process to another; the two processes
____________________
-UNIX is a trademark of AT&T Bell Laboratories.
PSD:20-2 Introductory 4.4BSD IPC
and the pipe must be set up by a common ancestor.
The use of descriptors is not the only communication
interface provided by UNIX. The signal mechanism sends a
tiny amount of information from one process to another. The
signaled process receives only the signal type, not the
identity of the sender, and the number of possible signals
is small. The signal semantics limit the flexibility of the
signaling mechanism as a means of interprocess communica-
tion.
The identification of IPC with I/O is quite longstand-
ing in UNIX and has proved quite successful. At first, how-
ever, IPC was limited to processes communicating within a
single machine. With Berkeley UNIX 4.2BSD this expanded to
include IPC between machines. This expansion has necessi-
tated some change in the way that descriptors are created.
Additionally, new possibilities for the meaning of read and
write have been admitted. Originally the meanings, or
semantics, of these terms were fairly simple. When you
wrote something it was delivered. When you read something,
you were blocked until the data arrived. Other possibilities
exist, however. One can write without full assurance of
delivery if one can check later to catch occasional
failures. Messages can be kept as discrete units or merged
into a stream. One can ask to read, but insist on not wait-
ing if nothing is immediately available. These new possi-
bilities are allowed in the Berkeley UNIX IPC interface.
Thus Berkeley UNIX 4.4BSD offers several choices for
IPC. This paper presents simple examples that illustrate
some of the choices. The reader is presumed to be familiar
with the C programming language [Kernighan & Ritchie 1978],
but not necessarily with the system calls of the UNIX system
or with processes and interprocess communication. The paper
reviews the notion of a process and the types of communica-
tion that are supported by Berkeley UNIX 4.4BSD. A series of
examples are presented that create processes that communi-
cate with one another. The programs show different ways of
establishing channels of communication. Finally, the calls
that actually transfer data are reviewed. To clearly present
how communication can take place, the example programs have
been cleared of anything that might be construed as useful
work. They can, therefore, serve as models for the program-
mer trying to construct programs which are comprised of
cooperating processes.
2. Processes
A program is both a sequence of statements and a rough
way of referring to the computation that occurs when the
compiled statements are run. A process can be thought of as
a single line of control in a program. Most programs execute
Introductory 4.4BSD IPC PSD:20-3
some statements, go through a few loops, branch in various
directions and then end. These are single process programs.
Programs can also have a point where control splits into two
independent lines, an action called forking. In UNIX these
lines can never join again. A call to the system routine
fork(), causes a process to split in this way. The result of
this call is that two independent processes will be running,
executing exactly the same code. Memory values will be the
same for all values set before the fork, but, subsequently,
each version will be able to change only the value of its
own copy of each variable. Initially, the only difference
between the two will be the value returned by fork(). The
parent will receive a process id for the child, the child
will receive a zero. Calls to fork(), therefore, typically
precede, or are included in, an if-statement.
A process views the rest of the system through a
private table of descriptors. The descriptors can represent
open files or sockets (sockets are communication objects
that will be discussed below). Descriptors are referred to
by their index numbers in the table. The first three
descriptors are often known by special names, stdin, stdout
and stderr. These are the standard input, output and error.
When a process forks, its descriptor table is copied to the
child. Thus, if the parent's standard input is being taken
from a terminal (devices are also treated as files in UNIX),
the child's input will be taken from the same terminal.
Whoever reads first will get the input. If, before forking,
the parent changes its standard input so that it is reading
from a new file, the child will take its input from the new
file. It is also possible to take input from a socket,
rather than from a file.
3. Pipes
Most users of UNIX know that they can pipe the output
of a program ``prog1'' to the input of another, ``prog2,''
by typing the command ``prog1 | prog2.'' This is called
``piping'' the output of one program to another because the
mechanism used to transfer the output is called a pipe. When
the user types a command, the command is read by the shell,
which decides how to execute it. If the command is simple,
for example, ``prog1,'' the shell forks a process, which
executes the program, prog1, and then dies. The shell waits
for this termination and then prompts for the next command.
If the command is a compound command, ``prog1 | prog2,'' the
shell creates two processes connected by a pipe. One process
runs the program, prog1, the other runs prog2. The pipe is
an I/O mechanism with two ends, or sockets. Data that is
written into one socket can be read from the other.
Since a program specifies its input and output only by
the descriptor table indices, which appear as variables or
PSD:20-4 Introductory 4.4BSD IPC
#include <stdio.h>
#define DATA "Bright star, would I were steadfast as thou art . . ."
/*
* This program creates a pipe, then forks. The child communicates to the
* parent over the pipe. Notice that a pipe is a one-way communications
* device. I can write to the output socket (sockets[1], the second socket
* of the array returned by pipe()) and read from the input socket
* (sockets[0]), but not vice versa.
*/
main()
{
int sockets[2], child;
/* Create a pipe */
if (pipe(sockets) < 0) {
perror("opening stream socket pair");
exit(10);
}
if ((child = fork()) == -1)
perror("fork");
else if (child) {
char buf[1024];
/* This is still the parent. It reads the child's message. */
close(sockets[1]);
if (read(sockets[0], buf, 1024) < 0)
perror("reading message");
printf("-->%s\n", buf);
close(sockets[0]);
} else {
/* This is the child. It writes a message to its parent. */
close(sockets[0]);
if (write(sockets[1], DATA, sizeof(DATA)) < 0)
perror("writing message");
close(sockets[1]);
}
}
Figure 1 Use of a pipe
constants, the input source and output destination can be
changed without changing the text of the program. It is in
this way that the shell is able to set up pipes. Before
executing prog1, the process can close whatever is at stdout
and replace it with one end of a pipe. Similarly, the pro-
cess that will execute prog2 can substitute the opposite end
of the pipe for stdin.
Introductory 4.4BSD IPC PSD:20-5
Let us now examine a program that creates a pipe for
communication between its child and itself (Figure 1). A
pipe is created by a parent process, which then forks. When
a process forks, the parent's descriptor table is copied
into the child's.
In Figure 1, the parent process makes a call to the
system routine pipe(). This routine creates a pipe and
places descriptors for the sockets for the two ends of the
pipe in the process's descriptor table. Pipe() is passed an
array into which it places the index numbers of the sockets
it created. The two ends are not equivalent. The socket
whose index is returned in the low word of the array is
opened for reading only, while the socket in the high end is
opened only for writing. This corresponds to the fact that
the standard input is the first descriptor of a process's
descriptor table and the standard output is the second.
After creating the pipe, the parent creates the child with
which it will share the pipe by calling fork(). Figure 2
illustrates the effect of a fork. The parent process's
descriptor table points to both ends of the pipe. After the
fork, both parent's and child's descriptor tables point to
the pipe. The child can then use the pipe to send a message
to the parent.
Just what is a pipe? It is a one-way communication
mechanism, with one end opened for reading and the other end
for writing. Therefore, parent and child need to agree on
which way to turn the pipe, from parent to child or the
other way around. Using the same pipe for communication both
from parent to child and from child to parent would be pos-
sible (since both processes have references to both ends),
but very complicated. If the parent and child are to have a
two-way conversation, the parent creates two pipes, one for
use in each direction. (In accordance with their plans, both
parent and child in the example above close the socket that
they will not use. It is not required that unused descrip-
tors be closed, but it is good practice.) A pipe is also a
stream communication mechanism; that is, all messages sent
through the pipe are placed in order and reliably delivered.
When the reader asks for a certain number of bytes from this
stream, he is given as many bytes as are available, up to
the amount of the request. Note that these bytes may have
come from the same call to write() or from several calls to
write() which were concatenated.
4. Socketpairs
Berkeley UNIX 4.4BSD provides a slight generalization
of pipes. A pipe is a pair of connected sockets for one-way
stream communication. One may obtain a pair of connected
sockets for two-way stream communication by calling the rou-
tine socketpair(). The program in Figure 3 calls
PSD:20-6 Introductory 4.4BSD IPC
arc at 5.407,4.723 from 5.375,4.838 to 5.362,4.612 cw
arc at 5.907,7.973 from 5.875,8.088 to 5.862,7.862 cw
line from 5.963,5.513 to 6.925,5.513
line from 5.963,5.650 to 6.925,5.650
line from 5.963,5.787 to 6.925,5.787
line from 5.963,5.912 to 6.925,5.912
line from 5.963,6.050 to 6.925,6.050
line from 5.963,6.200 to 6.925,6.200 to 6.925,5.375 to 5.963,5.375 to 5.963,6.200
ellipse at 6.388,6.713 wid 0.475 ht 0.475
line from 6.388,6.463 to 6.388,6.200
line from 3.150,6.200 to 4.112,6.200 to 4.112,5.375 to 3.150,5.375 to 3.150,6.200
line from 3.150,6.050 to 4.112,6.050
line from 3.150,5.912 to 4.112,5.912
line from 3.150,5.787 to 4.112,5.787
line from 3.150,5.650 to 4.112,5.650
line from 3.150,5.513 to 4.112,5.513
ellipse at 3.575,6.713 wid 0.475 ht 0.475
line from 3.575,6.463 to 3.575,6.200
line from 3.650,8.762 to 4.612,8.762
line from 3.650,8.900 to 4.612,8.900
line from 3.650,9.037 to 4.612,9.037
line from 3.650,9.162 to 4.612,9.162
line from 3.650,9.300 to 4.612,9.300
line from 3.650,9.450 to 4.612,9.450 to 4.612,8.625 to 3.650,8.625 to 3.650,9.450
ellipse at 4.075,9.963 wid 0.475 ht 0.475
ellipse at 3.950,4.725 wid 0.225 ht 0.225
ellipse at 4.450,7.975 wid 0.225 ht 0.225
dashwid = 0.037i
line dotted from 1.925,7.513 to 8.238,7.513
line from 6.050,6.138 to 5.737,6.138 to 5.737,4.700 to 5.550,4.700
line from 5.650,4.725 to 5.550,4.700 to 5.650,4.675
line from 6.050,6.013 to 4.050,4.888
line from 4.125,4.958 to 4.050,4.888 to 4.149,4.915
line from 3.975,6.000 to 4.525,5.987 to 3.925,4.875
line from 3.950,4.975 to 3.925,4.875 to 3.994,4.951
line from 3.975,6.112 to 5.650,6.112 to 5.650,4.750 to 5.550,4.763
line from 5.652,4.775 to 5.550,4.763 to 5.646,4.725
line from 4.075,9.713 to 4.075,9.450
line from 4.475,9.363 to 6.150,9.363 to 6.150,8.000 to 6.050,8.012
line from 6.152,8.025 to 6.050,8.012 to 6.146,7.975
line from 4.475,9.250 to 5.025,9.238 to 4.425,8.125
line from 4.450,8.225 to 4.425,8.125 to 4.494,8.201
line from 4.362,4.775 to 4.162,4.725 to 4.362,4.675
line from 4.162,4.725 to 4.838,4.725
line from 3.962,4.600 to 5.375,4.600
line from 3.950,4.838 to 5.375,4.838
line from 4.450,8.088 to 5.875,8.088
line from 4.463,7.850 to 5.875,7.850
line from 4.862,8.025 to 4.662,7.975 to 4.862,7.925
line from 4.662,7.975 to 5.338,7.975
"Child" at 6.362,7.106
"Parent" at 3.362,7.096 ljust
Introductory 4.4BSD IPC PSD:20-7
"Parent" at 3.862,10.346 ljust
"PIPE" at 4.987,4.671 ljust
"PIPE" at 5.425,7.921 ljust
Figure 2 Sharing a pipe between parent and child
socketpair() to create such a connection. The program uses
the link for communication in both directions. Since sock-
etpairs are an extension of pipes, their use resembles that
of pipes. Figure 4 illustrates the result of a fork follow-
ing a call to socketpair().
Socketpair() takes as arguments a specification of a
domain, a style of communication, and a protocol. These are
the parameters shown in the example. Domains and protocols
will be discussed in the next section. Briefly, a domain is
a space of names that may be bound to sockets and implies
certain other conventions. Currently, socketpairs have only
been implemented for one domain, called the UNIX domain. The
UNIX domain uses UNIX path names for naming sockets. It only
allows communication between sockets on the same machine.
Note that the header files <sys/socket.h> and
<sys/types.h>. are required in this program. The constants
AF_UNIX and SOCK_STREAM are defined in <sys/socket.h>, which
in turn requires the file <sys/types.h> for some of its
definitions.
5. Domains and Protocols
Pipes and socketpairs are a simple solution for commun-
icating between a parent and child or between child
processes. What if we wanted to have processes that have no
common ancestor with whom to set up communication? Neither
standard UNIX pipes nor socketpairs are the answer here,
since both mechanisms require a common ancestor to set up
the communication. We would like to have two processes
separately create sockets and then have messages sent
between them. This is often the case when providing or
using a service in the system. This is also the case when
the communicating processes are on separate machines. In
Berkeley UNIX 4.4BSD one can create individual sockets, give
them names and send messages between them.
Sockets created by different programs use names to
refer to one another; names generally must be translated
into addresses for use. The space from which an address is
drawn is referred to as a domain. There are several domains
for sockets. Two that will be used in the examples here are
the UNIX domain (or AF_UNIX, for Address Format UNIX) and
the Internet domain (or AF_INET). UNIX domain IPC is an
experimental facility in 4.2BSD and 4.3BSD. In the UNIX
domain, a socket is given a path name within the file system
name space. A file system node is created for the socket and
PSD:20-8 Introductory 4.4BSD IPC
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#define DATA1 "In Xanadu, did Kublai Khan . . ."
#define DATA2 "A stately pleasure dome decree . . ."
/*
* This program creates a pair of connected sockets then forks and
* communicates over them. This is very similar to communication with pipes,
* however, socketpairs are two-way communications objects. Therefore I can
* send messages in both directions.
*/
main()
{
int sockets[2], child;
char buf[1024];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) {
perror("opening stream socket pair");
exit(1);
}
if ((child = fork()) == -1)
perror("fork");
else if (child) { /* This is the parent. */
close(sockets[0]);
if (read(sockets[1], buf, 1024, 0) < 0)
perror("reading stream message");
printf("-->%s\n", buf);
if (write(sockets[1], DATA2, sizeof(DATA2)) < 0)
perror("writing stream message");
close(sockets[1]);
} else { /* This is the child. */
close(sockets[1]);
if (write(sockets[0], DATA1, sizeof(DATA1)) < 0)
perror("writing stream message");
if (read(sockets[0], buf, 1024, 0) < 0)
perror("reading stream message");
printf("-->%s\n", buf);
close(sockets[0]);
}
}
Figure 3 Use of a socketpair
Introductory 4.4BSD IPC PSD:20-9
ellipse at 5.787,8.012 wid 0.275 ht 0.275
ellipse at 4.175,8.012 wid 0.275 ht 0.275
dashwid = 0.037i
line dotted from 5.550,8.012 to 4.362,8.012
line from 4.462,8.037 to 4.362,8.012 to 4.462,7.987
line dotted from 4.362,7.950 to 5.550,7.950
line from 5.450,7.925 to 5.550,7.950 to 5.450,7.975
ellipse at 3.737,4.763 wid 0.275 ht 0.275
ellipse at 5.350,4.763 wid 0.275 ht 0.275
line dotted from 3.925,4.700 to 5.112,4.700
line from 5.013,4.675 to 5.112,4.700 to 5.013,4.725
line dotted from 5.112,4.763 to 3.925,4.763
line from 4.025,4.788 to 3.925,4.763 to 4.025,4.737
line from 5.963,5.513 to 6.925,5.513
line from 5.963,5.650 to 6.925,5.650
line from 5.963,5.787 to 6.925,5.787
line from 5.963,5.912 to 6.925,5.912
line from 5.963,6.050 to 6.925,6.050
line from 5.963,6.200 to 6.925,6.200 to 6.925,5.375 to 5.963,5.375 to 5.963,6.200
ellipse at 6.388,6.713 wid 0.475 ht 0.475
line from 6.388,6.463 to 6.388,6.200
line from 3.150,6.200 to 4.112,6.200 to 4.112,5.375 to 3.150,5.375 to 3.150,6.200
line from 3.150,6.050 to 4.112,6.050
line from 3.150,5.912 to 4.112,5.912
line from 3.150,5.787 to 4.112,5.787
line from 3.150,5.650 to 4.112,5.650
line from 3.150,5.513 to 4.112,5.513
ellipse at 3.575,6.713 wid 0.475 ht 0.475
line from 3.575,6.463 to 3.575,6.200
line from 3.650,8.762 to 4.612,8.762
line from 3.650,8.900 to 4.612,8.900
line from 3.650,9.037 to 4.612,9.037
line from 3.650,9.162 to 4.612,9.162
line from 3.650,9.300 to 4.612,9.300
line from 3.650,9.450 to 4.612,9.450 to 4.612,8.625 to 3.650,8.625 to 3.650,9.450
ellipse at 4.075,9.963 wid 0.475 ht 0.475
line from 3.975,6.112 to 5.650,6.112 to 5.650,4.750 to 5.550,4.763
line from 5.652,4.775 to 5.550,4.763 to 5.646,4.725
line from 6.050,6.138 to 5.737,6.138 to 5.737,4.700 to 5.550,4.700
line from 5.650,4.725 to 5.550,4.700 to 5.650,4.675
line dotted from 1.925,7.513 to 8.238,7.513
line from 6.050,6.013 to 4.050,4.888
line from 4.125,4.958 to 4.050,4.888 to 4.149,4.915
line from 3.975,6.000 to 4.525,5.987 to 3.925,4.875
line from 3.950,4.975 to 3.925,4.875 to 3.994,4.951
line from 4.075,9.713 to 4.075,9.450
line from 4.475,9.363 to 6.150,9.363 to 6.150,8.000 to 6.050,8.012
line from 6.152,8.025 to 6.050,8.012 to 6.146,7.975
line from 4.475,9.250 to 5.025,9.238 to 4.425,8.125
line from 4.450,8.225 to 4.425,8.125 to 4.494,8.201
"Child" at 6.362,7.106
"Parent" at 3.362,7.096 ljust
"Parent" at 3.862,10.346 ljust
PSD:20-10 Introductory 4.4BSD IPC
Figure 4 Sharing a socketpair between parent and child
other processes may then refer to the socket by giving the
proper pathname. UNIX domain names, therefore, allow commun-
ication between any two processes that work in the same file
system. The Internet domain is the UNIX implementation of
the DARPA Internet standard protocols IP/TCP/UDP. Addresses
in the Internet domain consist of a machine network address
and an identifying number, called a port. Internet domain
names allow communication between machines.
Communication follows some particular ``style.''
Currently, communication is either through a stream or by
datagram. Stream communication implies several things. Com-
munication takes place across a connection between two sock-
ets. The communication is reliable, error-free, and, as in
pipes, no message boundaries are kept. Reading from a stream
may result in reading the data sent from one or several
calls to write() or only part of the data from a single
call, if there is not enough room for the entire message, or
if not all the data from a large message has been
transferred. The protocol implementing such a style will
retransmit messages received with errors. It will also
return error messages if one tries to send a message after
the connection has been broken. Datagram communication does
not use connections. Each message is addressed individu-
ally. If the address is correct, it will generally be
received, although this is not guaranteed. Often datagrams
are used for requests that require a response from the reci-
pient. If no response arrives in a reasonable amount of
time, the request is repeated. The individual datagrams will
be kept separate when they are read, that is, message boun-
daries are preserved.
The difference in performance between the two styles of
communication is generally less important than the differ-
ence in semantics. The performance gain that one might find
in using datagrams must be weighed against the increased
complexity of the program, which must now concern itself
with lost or out of order messages. If lost messages may
simply be ignored, the quantity of traffic may be a con-
sideration. The expense of setting up a connection is best
justified by frequent use of the connection. Since the per-
formance of a protocol changes as it is tuned for different
situations, it is best to seek the most up-to-date informa-
tion when making choices for a program in which performance
is crucial.
A protocol is a set of rules, data formats and conven-
tions that regulate the transfer of data between partici-
pants in the communication. In general, there is one proto-
col for each socket type (stream, datagram, etc.) within
each domain. The code that implements a protocol keeps track
Introductory 4.4BSD IPC PSD:20-11
of the names that are bound to sockets, sets up connections
and transfers data between sockets, perhaps sending the data
across a network. This code also keeps track of the names
that are bound to sockets. It is possible for several proto-
cols, differing only in low level details, to implement the
same style of communication within a particular domain.
Although it is possible to select which protocol should be
used, for nearly all uses it is sufficient to request the
default protocol. This has been done in all of the example
programs.
One specifies the domain, style and protocol of a
socket when it is created. For example, in Figure 5a the
call to socket() causes the creation of a datagram socket
with the default protocol in the UNIX domain.
6. Datagrams in the UNIX Domain
Let us now look at two programs that create sockets
separately. The programs in Figures 5a and 5b use datagram
communication rather than a stream. The structure used to
name UNIX domain sockets is defined in the file <sys/un.h>.
The definition has also been included in the example for
clarity.
Each program creates a socket with a call to socket().
These sockets are in the UNIX domain. Once a name has been
decided upon it is attached to a socket by the system call
bind(). The program in Figure 5a uses the name ``socket'',
which it binds to its socket. This name will appear in the
working directory of the program. The routines in Figure 5b
use its socket only for sending messages. It does not
create a name for the socket because no other process has to
refer to it.
Names in the UNIX domain are path names. Like file
path names they may be either absolute (e.g.
``/dev/imaginary'') or relative (e.g. ``socket''). Because
these names are used to allow processes to rendezvous, rela-
tive path names can pose difficulties and should be used
with care. When a name is bound into the name space, a file
(inode) is allocated in the file system. If the inode is
not deallocated, the name will continue to exist even after
the bound socket is closed. This can cause subsequent runs
of a program to find that a name is unavailable, and can
cause directories to fill up with these objects. The names
are removed by calling unlink() or using the rm(1) command.
Names in the UNIX domain are only used for rendezvous. They
are not used for message delivery once a connection is esta-
blished. Therefore, in contrast with the Internet domain,
unbound sockets need not be (and are not) automatically
given addresses when they are connected.
PSD:20-12 Introductory 4.4BSD IPC
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
/*
* In the included file <sys/un.h> a sockaddr_un is defined as follows
* struct sockaddr_un {
* short sun_family;
* char sun_path[108];
* };
*/
#include <stdio.h>
#define NAME "socket"
/*
* This program creates a UNIX domain datagram socket, binds a name to it,
* then reads from the socket.
*/
main()
{
int sock, length;
struct sockaddr_un name;
char buf[1024];
/* Create socket from which to read. */
sock = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sock < 0) {
perror("opening datagram socket");
exit(1);
}
/* Create name. */
name.sun_family = AF_UNIX;
strcpy(name.sun_path, NAME);
if (bind(sock, &name, sizeof(struct sockaddr_un))) {
perror("binding name to datagram socket");
exit(1);
}
printf("socket -->%s\n", NAME);
/* Read from the socket */
if (read(sock, buf, 1024) < 0)
perror("receiving datagram packet");
printf("-->%s\n", buf);
close(sock);
unlink(NAME);
}
Figure 5a Reading UNIX domain datagrams
Introductory 4.4BSD IPC PSD:20-13
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#define DATA "The sea is calm tonight, the tide is full . . ."
/*
* Here I send a datagram to a receiver whose name I get from the command
* line arguments. The form of the command line is udgramsend pathname
*/
main(argc, argv)
int argc;
char *argv[];
{
int sock;
struct sockaddr_un name;
/* Create socket on which to send. */
sock = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sock < 0) {
perror("opening datagram socket");
exit(1);
}
/* Construct name of socket to send to. */
name.sun_family = AF_UNIX;
strcpy(name.sun_path, argv[1]);
/* Send message. */
if (sendto(sock, DATA, sizeof(DATA), 0,
&name, sizeof(struct sockaddr_un)) < 0) {
perror("sending datagram message");
}
close(sock);
}
Figure 5b Sending a UNIX domain datagrams
There is no established means of communicating names to
interested parties. In the example, the program in Figure
5b gets the name of the socket to which it will send its
message through its command line arguments. Once a line of
communication has been created, one can send the names of
additional, perhaps new, sockets over the link. Facilities
will have to be built that will make the distribution of
names less of a problem than it now is.
7. Datagrams in the Internet Domain
The examples in Figure 6a and 6b are very close to the
previous example except that the socket is in the Internet
domain. The structure of Internet domain addresses is
defined in the file <netinet/in.h>. Internet addresses
specify a host address (a 32-bit number) and a delivery
PSD:20-14 Introductory 4.4BSD IPC
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
/*
* In the included file <netinet/in.h> a sockaddr_in is defined as follows:
* struct sockaddr_in {
* short sin_family;
* u_short sin_port;
* struct in_addr sin_addr;
* char sin_zero[8];
* };
*
* This program creates a datagram socket, binds a name to it, then reads
* from the socket.
*/
main()
{
int sock, length;
struct sockaddr_in name;
char buf[1024];
/* Create socket from which to read. */
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("opening datagram socket");
exit(1);
}
/* Create name with wildcards. */
name.sin_family = AF_INET;
name.sin_addr.s_addr = INADDR_ANY;
name.sin_port = 0;
if (bind(sock, &name, sizeof(name))) {
perror("binding datagram socket");
exit(1);
}
/* Find assigned port value and print it out. */
length = sizeof(name);
if (getsockname(sock, &name, &length)) {
perror("getting socket name");
exit(1);
}
printf("Socket has port #%d\n", ntohs(name.sin_port));
/* Read from the socket */
if (read(sock, buf, 1024) < 0)
perror("receiving datagram packet");
printf("-->%s\n", buf);
close(sock);
}
Figure 6a Reading Internet domain datagrams
Introductory 4.4BSD IPC PSD:20-15
slot, or port, on that machine. These ports are managed by
the system routines that implement a particular protocol.
Unlike UNIX domain names, Internet socket names are not
entered into the file system and, therefore, they do not
have to be unlinked after the socket has been closed. When a
message must be sent between machines it is sent to the pro-
tocol routine on the destination machine, which interprets
the address to determine to which socket the message should
be delivered. Several different protocols may be active on
the same machine, but, in general, they will not communicate
with one another. As a result, different protocols are
allowed to use the same port numbers. Thus, implicitly, an
Internet address is a triple including a protocol as well as
the port and machine address. An association is a temporary
or permanent specification of a pair of communicating sock-
ets. An association is thus identified by the tuple <proto-
col, local machine address, local port, remote machine
address, remote port>. An association may be transient when
using datagram sockets; the association actually exists dur-
ing a send operation.
The protocol for a socket is chosen when the socket is
created. The local machine address for a socket can be any
valid network address of the machine, if it has more than
one, or it can be the wildcard value INADDR_ANY. The wild-
card value is used in the program in Figure 6a. If a machine
has several network addresses, it is likely that messages
sent to any of the addresses should be deliverable to a
socket. This will be the case if the wildcard value has
been chosen. Note that even if the wildcard value is chosen,
a program sending messages to the named socket must specify
a valid network address. One can be willing to receive from
``anywhere,'' but one cannot send a message ``anywhere.''
The program in Figure 6b is given the destination host name
as a command line argument. To determine a network address
to which it can send the message, it looks up the host
address by the call to gethostbyname(). The returned struc-
ture includes the host's network address, which is copied
into the structure specifying the destination of the mes-
sage.
The port number can be thought of as the number of a
mailbox, into which the protocol places one's messages.
Certain daemons, offering certain advertised services, have
reserved or ``well-known'' port numbers. These fall in the
range from 1 to 1023. Higher numbers are available to gen-
eral users. Only servers need to ask for a particular
number. The system will assign an unused port number when an
address is bound to a socket. This may happen when an expli-
cit bind call is made with a port number of 0, or when a
connect or send is performed on an unbound socket. Note that
port numbers are not automatically reported back to the
user. After calling bind(), asking for port 0, one may call
PSD:20-16 Introductory 4.4BSD IPC
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define DATA "The sea is calm tonight, the tide is full . . ."
/*
* Here I send a datagram to a receiver whose name I get from the command
* line arguments. The form of the command line is dgramsend hostname
* portnumber
*/
main(argc, argv)
int argc;
char *argv[];
{
int sock;
struct sockaddr_in name;
struct hostent *hp, *gethostbyname();
/* Create socket on which to send. */
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("opening datagram socket");
exit(1);
}
/*
* Construct name, with no wildcards, of the socket to send to.
* Getnostbyname() returns a structure including the network address
* of the specified host. The port number is taken from the command
* line.
*/
hp = gethostbyname(argv[1]);
if (hp == 0) {
fprintf(stderr, "%s: unknown host0, argv[1]);
exit(2);
}
memmove(&name.sin_addr, hp->h_addr, hp->h_length);
name.sin_family = AF_INET;
name.sin_port = htons(atoi(argv[2]));
/* Send message. */
if (sendto(sock, DATA, sizeof(DATA), 0, &name, sizeof(name)) < 0)
perror("sending datagram message");
close(sock);
}
Figure 6b Sending an Internet domain datagram
getsockname() to discover what port was actually assigned.
The routine getsockname() will not work for names in the
UNIX domain.
Introductory 4.4BSD IPC PSD:20-17
The format of the socket address is specified in part
by standards within the Internet domain. The specification
includes the order of the bytes in the address. Because
machines differ in the internal representation they ordi-
narily use to represent integers, printing out the port
number as returned by getsockname() may result in a misin-
terpretation. To print out the number, it is necessary to
use the routine ntohs() (for network to host: short) to con-
vert the number from the network representation to the
host's representation. On some machines, such as 68000-
based machines, this is a null operation. On others, such
as VAXes, this results in a swapping of bytes. Another rou-
tine exists to convert a short integer from the host format
to the network format, called htons(); similar routines
exist for long integers. For further information, refer to
the entry for byteorder in section 3 of the manual.
8. Connections
To send data between stream sockets (having communica-
tion style SOCK_STREAM), the sockets must be connected. Fig-
ures 7a and 7b show two programs that create such a connec-
tion. The program in 7a is relatively simple. To initiate a
connection, this program simply creates a stream socket,
then calls connect(), specifying the address of the socket
to which it wishes its socket connected. Provided that the
target socket exists and is prepared to handle a connection,
connection will be complete, and the program can begin to
send messages. Messages will be delivered in order without
message boundaries, as with pipes. The connection is des-
troyed when either socket is closed (or soon thereafter).
If a process persists in sending messages after the connec-
tion is closed, a SIGPIPE signal is sent to the process by
the operating system. Unless explicit action is taken to
handle the signal (see the manual page for signal or
sigvec), the process will terminate and the shell will print
the message ``broken pipe.''
Forming a connection is asymmetrical; one process, such
as the program in Figure 7a, requests a connection with a
particular socket, the other process accepts connection
requests. Before a connection can be accepted a socket must
be created and an address bound to it. This situation is
illustrated in the top half of Figure 8. Process 2 has
created a socket and bound a port number to it. Process 1
has created an unnamed socket. The address bound to process
2's socket is then made known to process 1 and, perhaps to
several other potential communicants as well. If there are
several possible communicants, this one socket might receive
several requests for connections. As a result, a new socket
is created for each connection. This new socket is the end-
point for communication within this process for this connec-
tion. A connection may be destroyed by closing the
PSD:20-18 Introductory 4.4BSD IPC
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define DATA "Half a league, half a league . . ."
/*
* This program creates a socket and initiates a connection with the socket
* given in the command line. One message is sent over the connection and
* then the socket is closed, ending the connection. The form of the command
* line is streamwrite hostname portnumber
*/
main(argc, argv)
int argc;
char *argv[];
{
int sock;
struct sockaddr_in server;
struct hostent *hp, *gethostbyname();
char buf[1024];
/* Create socket */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
/* Connect socket using name specified by command line. */
server.sin_family = AF_INET;
hp = gethostbyname(argv[1]);
if (hp == 0) {
fprintf(stderr, "%s: unknown host0, argv[1]);
exit(2);
}
memmove(&server.sin_addr, hp->h_addr, hp->h_length);
server.sin_port = htons(atoi(argv[2]));
if (connect(sock, &server, sizeof(server)) < 0) {
perror("connecting stream socket");
exit(1);
}
if (write(sock, DATA, sizeof(DATA)) < 0)
perror("writing on stream socket");
close(sock);
}
Figure 7a Initiating an Internet domain stream connection
Introductory 4.4BSD IPC PSD:20-19
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1
/*
* This program creates a socket and then begins an infinite loop. Each time
* through the loop it accepts a connection and prints out messages from it.
* When the connection breaks, or a termination message comes through, the
* program accepts a new connection.
*/
main()
{
int sock, length;
struct sockaddr_in server;
int msgsock;
char buf[1024];
int rval;
int i;
/* Create socket */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
/* Name socket using wildcards */
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = 0;
if (bind(sock, &server, sizeof(server))) {
perror("binding stream socket");
exit(1);
}
/* Find out assigned port number and print it out */
length = sizeof(server);
if (getsockname(sock, &server, &length)) {
perror("getting socket name");
exit(1);
}
printf("Socket has port #%d\n", ntohs(server.sin_port));
/* Start accepting connections */
listen(sock, 5);
do {
msgsock = accept(sock, 0, 0);
if (msgsock == -1)
perror("accept");
else do {
bzero(buf, sizeof(buf));
PSD:20-20 Introductory 4.4BSD IPC
if ((rval = read(msgsock, buf, 1024)) < 0)
perror("reading stream message");
i = 0;
if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", buf);
} while (rval != 0);
close(msgsock);
} while (TRUE);
/*
* Since this program has an infinite loop, the socket "sock" is
* never explicitly closed. However, all sockets will be closed
* automatically when a process is killed or terminates normally.
*/
}
Figure 7b Accepting an Internet domain stream connection
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#define TRUE 1
/*
* This program uses select() to check that someone is trying to connect
* before calling accept().
*/
main()
{
int sock, length;
struct sockaddr_in server;
int msgsock;
char buf[1024];
int rval;
fd_set ready;
struct timeval to;
/* Create socket */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
/* Name socket using wildcards */
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = 0;
if (bind(sock, &server, sizeof(server))) {
Introductory 4.4BSD IPC PSD:20-21
perror("binding stream socket");
exit(1);
}
/* Find out assigned port number and print it out */
length = sizeof(server);
if (getsockname(sock, &server, &length)) {
perror("getting socket name");
exit(1);
}
printf("Socket has port #%d\n", ntohs(server.sin_port));
/* Start accepting connections */
listen(sock, 5);
do {
FD_ZERO(&ready);
FD_SET(sock, &ready);
to.tv_sec = 5;
if (select(sock + 1, &ready, 0, 0, &to) < 0) {
perror("select");
continue;
}
if (FD_ISSET(sock, &ready)) {
msgsock = accept(sock, (struct sockaddr *)0, (int *)0);
if (msgsock == -1)
perror("accept");
else do {
bzero(buf, sizeof(buf));
if ((rval = read(msgsock, buf, 1024)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", buf);
} while (rval > 0);
close(msgsock);
} else
printf("Do something else\n");
} while (TRUE);
}
Figure 7c Using select() to check for pending connections
PSD:20-22 Introductory 4.4BSD IPC
"Process 1" at 3.800,7.106 rjust
"Process 2" at 6.612,7.106 rjust
line from 3.150,6.200 to 4.112,6.200 to 4.112,5.375 to 3.150,5.375 to 3.150,6.200
line from 3.150,6.050 to 4.112,6.050
line from 3.150,5.912 to 4.112,5.912
line from 3.150,5.787 to 4.112,5.787
line from 3.150,5.650 to 4.112,5.650
line from 3.150,5.513 to 4.112,5.513
ellipse at 3.575,6.713 wid 0.475 ht 0.475
line from 3.575,6.463 to 3.575,6.200
line from 5.963,5.513 to 6.925,5.513
line from 5.963,5.650 to 6.925,5.650
line from 5.963,5.787 to 6.925,5.787
line from 5.963,5.912 to 6.925,5.912
line from 5.963,6.050 to 6.925,6.050
line from 5.963,6.200 to 6.925,6.200 to 6.925,5.375 to 5.963,5.375 to 5.963,6.200
ellipse at 6.388,6.713 wid 0.475 ht 0.475
line from 6.388,6.463 to 6.388,6.200
line from 3.087,8.637 to 4.050,8.637
line from 3.087,8.775 to 4.050,8.775
line from 3.087,8.912 to 4.050,8.912
line from 3.087,9.037 to 4.050,9.037
line from 3.087,9.175 to 4.050,9.175
line from 3.087,9.325 to 4.050,9.325 to 4.050,8.500 to 3.087,8.500 to 3.087,9.325
ellipse at 3.513,9.838 wid 0.475 ht 0.475
line from 3.513,9.588 to 3.513,9.325
line from 5.900,9.325 to 6.862,9.325 to 6.862,8.500 to 5.900,8.500 to 5.900,9.325
line from 5.900,9.175 to 6.862,9.175
line from 5.900,9.037 to 6.862,9.037
line from 5.900,8.912 to 6.862,8.912
line from 5.900,8.775 to 6.862,8.775
line from 5.900,8.637 to 6.862,8.637
ellipse at 6.325,9.838 wid 0.475 ht 0.475
line from 6.325,9.588 to 6.325,9.325
"Process 2" at 6.550,10.231 rjust
"Process 1" at 3.737,10.231 rjust
ellipse at 6.112,4.888 wid 0.275 ht 0.275
ellipse at 5.350,4.763 wid 0.275 ht 0.275
ellipse at 3.737,4.763 wid 0.275 ht 0.275
ellipse at 4.550,7.950 wid 0.275 ht 0.275
ellipse at 5.487,7.950 wid 0.275 ht 0.275
line from 6.050,6.013 to 5.175,6.013 to 5.987,5.013
line from 5.905,5.074 to 5.987,5.013 to 5.944,5.106
line from 6.050,6.138 to 5.737,6.138 to 5.737,4.700 to 5.550,4.700
line from 5.650,4.725 to 5.550,4.700 to 5.650,4.675
dashwid = 0.037i
line dotted from 1.925,7.513 to 8.238,7.513
line from 3.975,6.000 to 4.525,5.987 to 3.925,4.875
line from 3.950,4.975 to 3.925,4.875 to 3.994,4.951
line dotted from 5.112,4.763 to 3.925,4.763
line from 4.025,4.788 to 3.925,4.763 to 4.025,4.737
line dotted from 3.925,4.700 to 5.112,4.700
line from 5.013,4.675 to 5.112,4.700 to 5.013,4.725
Introductory 4.4BSD IPC PSD:20-23
line from 6.050,9.012 to 5.487,9.012 to 5.487,8.137
line from 5.462,8.237 to 5.487,8.137 to 5.513,8.237
line from 3.737,9.137 to 4.550,9.137 to 4.550,8.137
line from 4.525,8.237 to 4.550,8.137 to 4.575,8.237
"NAME" at 6.737,4.918 rjust
"NAME" at 6.112,8.043 rjust
Figure 8 Establishing a stream connection
corresponding socket.
The program in Figure 7b is a rather trivial example of
a server. It creates a socket to which it binds a name,
which it then advertises. (In this case it prints out the
socket number.) The program then calls listen() for this
socket. Since several clients may attempt to connect more or
less simultaneously, a queue of pending connections is main-
tained in the system address space. Listen() marks the
socket as willing to accept connections and initializes the
queue. When a connection is requested, it is listed in the
queue. If the queue is full, an error status may be
returned to the requester. The maximum length of this queue
is specified by the second argument of listen(); the maximum
length is limited by the system. Once the listen call has
been completed, the program enters an infinite loop. On
each pass through the loop, a new connection is accepted and
removed from the queue, and, hence, a new socket for the
connection is created. The bottom half of Figure 8 shows
the result of Process 1 connecting with the named socket of
Process 2, and Process 2 accepting the connection. After
the connection is created, the service, in this case print-
ing out the messages, is performed and the connection socket
closed. The accept() call will take a pending connection
request from the queue if one is available, or block waiting
for a request. Messages are read from the connection socket.
Reads from an active connection will normally block until
data is available. The number of bytes read is returned.
When a connection is destroyed, the read call returns
immediately. The number of bytes returned will be zero.
The program in Figure 7c is a slight variation on the
server in Figure 7b. It avoids blocking when there are no
pending connection requests by calling select() to check for
pending requests before calling accept(). This strategy is
useful when connections may be received on more than one
socket, or when data may arrive on other connected sockets
before another connection request.
The programs in Figures 9a and 9b show a program using
stream communication in the UNIX domain. Streams in the
UNIX domain can be used for this sort of program in exactly
the same way as Internet domain streams, except for the form
of the names and the restriction of the connections to a
PSD:20-24 Introductory 4.4BSD IPC
single file system. There are some differences, however, in
the functionality of streams in the two domains, notably in
the handling of out-of-band data (discussed briefly below).
These differences are beyond the scope of this paper.
9. Reads, Writes, Recvs, etc.
UNIX 4.4BSD has several system calls for reading and
writing information. The simplest calls are read() and
write(). Write() takes as arguments the index of a descrip-
tor, a pointer to a buffer containing the data and the size
of the data. The descriptor may indicate either a file or a
connected socket. ``Connected'' can mean either a connected
stream socket (as described in Section 8) or a datagram
socket for which a connect() call has provided a default
destination (see the connect() manual page). Read() also
takes a descriptor that indicates either a file or a socket.
Write() requires a connected socket since no destination is
specified in the parameters of the system call. Read() can
be used for either a connected or an unconnected socket.
These calls are, therefore, quite flexible and may be used
to write applications that require no assumptions about the
source of their input or the destination of their output.
There are variations on read() and write() that allow the
source and destination of the input and output to use
several separate buffers, while retaining the flexibility to
handle both files and sockets. These are readv() and wri-
tev(), for read and write vector.
It is sometimes necessary to send high priority data
over a connection that may have unread low priority data at
the other end. For example, a user interface process may be
interpreting commands and sending them on to another process
through a stream connection. The user interface may have
filled the stream with as yet unprocessed requests when the
user types a command to cancel all outstanding requests.
Rather than have the high priority data wait to be processed
after the low priority data, it is possible to send it as
out-of-band (OOB) data. The notification of pending OOB
data results in the generation of a SIGURG signal, if this
signal has been enabled (see the manual page for signal or
sigvec). See [Leffler 1986] for a more complete description
of the OOB mechanism. There are a pair of calls similar to
read and write that allow options, including sending and
receiving OOB information; these are send() and recv().
These calls are used only with sockets; specifying a
descriptor for a file will result in the return of an error
status. These calls also allow peeking at data in a stream.
That is, they allow a process to read data without removing
the data from the stream. One use of this facility is to
read ahead in a stream to determine the size of the next
item to be read. When not using these options, these calls
have the same functions as read() and write().
Introductory 4.4BSD IPC PSD:20-25
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#define DATA "Half a league, half a league . . ."
/*
* This program connects to the socket named in the command line and sends a
* one line message to that socket. The form of the command line is
* ustreamwrite pathname
*/
main(argc, argv)
int argc;
char *argv[];
{
int sock;
struct sockaddr_un server;
char buf[1024];
/* Create socket */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
/* Connect socket using name specified by command line. */
server.sun_family = AF_UNIX;
strcpy(server.sun_path, argv[1]);
if (connect(sock, &server, sizeof(struct sockaddr_un)) < 0) {
close(sock);
perror("connecting stream socket");
exit(1);
}
if (write(sock, DATA, sizeof(DATA)) < 0)
perror("writing on stream socket");
}
Figure 9a Initiating a UNIX domain stream connection
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#define NAME "socket"
/*
* This program creates a socket in the UNIX domain and binds a name to it.
* After printing the socket's name it begins a loop. Each time through the
* loop it accepts a connection and prints out messages from it. When the
PSD:20-26 Introductory 4.4BSD IPC
* connection breaks, or a termination message comes through, the program
* accepts a new connection.
*/
main()
{
int sock, msgsock, rval;
struct sockaddr_un server;
char buf[1024];
/* Create socket */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
/* Name socket using file system name */
server.sun_family = AF_UNIX;
strcpy(server.sun_path, NAME);
if (bind(sock, &server, sizeof(struct sockaddr_un))) {
perror("binding stream socket");
exit(1);
}
printf("Socket has name %s\n", server.sun_path);
/* Start accepting connections */
listen(sock, 5);
for (;;) {
msgsock = accept(sock, 0, 0);
if (msgsock == -1)
perror("accept");
else do {
bzero(buf, sizeof(buf));
if ((rval = read(msgsock, buf, 1024)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", buf);
} while (rval > 0);
close(msgsock);
}
/*
* The following statements are not executed, because they follow an
* infinite loop. However, most ordinary programs will not run
* forever. In the UNIX domain it is necessary to tell the file
* system that one is through using NAME. In most programs one uses
* the call unlink() as below. Since the user will have to kill this
* program, it will be necessary to remove the name by a command from
* the shell.
*/
close(sock);
unlink(NAME);
}
Figure 9b Accepting a UNIX domain stream connection
Introductory 4.4BSD IPC PSD:20-27
To send datagrams, one must be allowed to specify the
destination. The call sendto() takes a destination address
as an argument and is therefore used for sending datagrams.
The call recvfrom() is often used to read datagrams, since
this call returns the address of the sender, if it is avail-
able, along with the data. If the identity of the sender
does not matter, one may use read() or recv().
Finally, there are a pair of calls that allow the send-
ing and receiving of messages from multiple buffers, when
the address of the recipient must be specified. These are
sendmsg() and recvmsg(). These calls are actually quite gen-
eral and have other uses, including, in the UNIX domain, the
transmission of a file descriptor from one process to
another.
The various options for reading and writing are shown
in Figure 10, together with their parameters. The parame-
ters for each system call reflect the differences in func-
tion of the different calls. In the examples given in this
paper, the calls read() and write() have been used whenever
possible.
10. Choices
This paper has presented examples of some of the forms
of communication supported by Berkeley UNIX 4.4BSD. These
have been presented in an order chosen for ease of presenta-
tion. It is useful to review these options emphasizing the
factors that make each attractive.
Pipes have the advantage of portability, in that they
are supported in all UNIX systems. They also are relatively
simple to use. Socketpairs share this simplicity and have
the additional advantage of allowing bidirectional communi-
cation. The major shortcoming of these mechanisms is that
they require communicating processes to be descendants of a
common process. They do not allow intermachine communica-
tion.
The two communication domains, UNIX and Internet, allow
processes with no common ancestor to communicate. Of the
two, only the Internet domain allows communication between
machines. This makes the Internet domain a necessary choice
for processes running on separate machines.
The choice between datagrams and stream communication
is best made by carefully considering the semantic and per-
formance requirements of the application. Streams can be
both advantageous and disadvantageous. One disadvantage is
that a process is only allowed a limited number of open
streams, as there are usually only 64 entries available in
the open descriptor table. This can cause problems if a
PSD:20-28 Introductory 4.4BSD IPC
/*
* The variable descriptor may be the descriptor of either a file
* or of a socket.
*/
cc = read(descriptor, buf, nbytes)
int cc, descriptor; char *buf; int nbytes;
/*
* An iovec can include several source buffers.
*/
cc = readv(descriptor, iov, iovcnt)
int cc, descriptor; struct iovec *iov; int iovcnt;
cc = write(descriptor, buf, nbytes)
int cc, descriptor; char *buf; int nbytes;
cc = writev(descriptor, iovec, ioveclen)
int cc, descriptor; struct iovec *iovec; int ioveclen;
/*
* The variable ``sock'' must be the descriptor of a socket.
* Flags may include MSG_OOB and MSG_PEEK.
*/
cc = send(sock, msg, len, flags)
int cc, sock; char *msg; int len, flags;
cc = sendto(sock, msg, len, flags, to, tolen)
int cc, sock; char *msg; int len, flags;
struct sockaddr *to; int tolen;
cc = sendmsg(sock, msg, flags)
int cc, sock; struct msghdr msg[]; int flags;
cc = recv(sock, buf, len, flags)
int cc, sock; char *buf; int len, flags;
cc = recvfrom(sock, buf, len, flags, from, fromlen)
int cc, sock; char *buf; int len, flags;
struct sockaddr *from; int *fromlen;
cc = recvmsg(sock, msg, flags)
int cc, socket; struct msghdr msg[]; int flags;
Figure 10 Varieties of read and write commands
single server must talk with a large number of clients.
Another is that for delivering a short message the stream
setup and teardown time can be unnecessarily long. Weighed
against this are the reliability built into the streams.
This will often be the deciding factor in favor of streams.
Introductory 4.4BSD IPC PSD:20-29
11. What to do Next
Many of the examples presented here can serve as models
for multiprocess programs and for programs distributed
across several machines. In developing a new multiprocess
program, it is often easiest to first write the code to
create the processes and communication paths. After this
code is debugged, the code specific to the application can
be added.
An introduction to the UNIX system and programming
using UNIX system calls can be found in [Kernighan and Pike
1984]. Further documentation of the Berkeley UNIX 4.4BSD IPC
mechanisms can be found in [Leffler et al. 1986]. More
detailed information about particular calls and protocols is
provided in sections 2, 3 and 4 of the UNIX Programmer's
Manual [CSRG 1986]. In particular the following manual pages
are relevant:
creating and naming sockets socket(2), bind(2)
establishing connections listen(2), accept(2), connect(2)
transferring data read(2), write(2), send(2), recv(2)
addresses inet(4F)
protocols tcp(4P), udp(4P).
Acknowledgements
I would like to thank Sam Leffler and Mike Karels
for their help in understanding the IPC mechanisms and
all the people whose comments have helped in writing and
improving this report.
This work was sponsored by the Defense Advanced
Research Projects Agency (DoD), ARPA Order No. 4031,
monitored by the Naval Electronics Systems Command under
contract No. N00039-C-0235. The views and conclusions
contained in this document are those of the author and
should not be interpreted as representing official poli-
cies, either expressed or implied, of the Defense
Research Projects Agency or of the US Government.
PSD:20-30 Introductory 4.4BSD IPC
References
B.W. Kernighan & R. Pike, 1984,
The UNIX Programming Environment.
Englewood Cliffs, N.J.: Prentice-Hall.
B.W. Kernighan & D.M. Ritchie, 1978,
The C Programming Language,
Englewood Cliffs, N.J.: Prentice-Hall.
S.J. Leffler, R.S. Fabry, W.N. Joy, P. Lapsley, S. Miller & C. Torek, 1986,
An Advanced 4.4BSD Interprocess Communication Tutorial.
Computer Systems Research Group,
Department of Electrical Engineering and Computer Science,
University of California, Berkeley.
Computer Systems Research Group, 1986,
UNIX Programmer's Manual, 4.4 Berkeley Software Distribution.
Computer Systems Research Group,
Department of Electrical Engineering and Computer Science,
University of California, Berkeley.
Generated on 2013-04-27 00:20:00 by $MirOS: src/scripts/roff2htm,v 1.77 2013/01/01 20:49:09 tg Exp $
These manual pages and other documentation are copyrighted by their respective writers;
their source is available at our CVSweb,
AnonCVS, and other mirrors. The rest is Copyright © 2002‒2013 The MirOS Project, Germany.
This product includes material
provided by Thorsten Glaser.
This manual page’s HTML representation is supposed to be valid XHTML/1.1; if not, please send a bug report – diffs preferred.