Sie sind auf Seite 1von 121

CBND3203

INTERMEDIATE
NETWORK
PROGRAMMING
AND DESIGN

Copyright © Open University Malaysia (OUM)


Project Directors: Prof Dato’ Dr Mansor Fadzil
Assoc Prof Dr Norlia T. Goolamally
Open University Malaysia

Developer: Mr Toby S K Chan


Consultant

Coordinator: Dr Philip Tsang


OUHK

Members: Dr Wilson Chu


Dr George Lau
OUHK

External Course Assessor: Prof Cheung Kwok-wai


The Chinese University of Hong Kong

Production: ETPU Publishing Team

Adapted for
Open University Malaysia by: Assoc Prof Dr Nantha Kumar Subramaniam
Faculty of Information Technology and
Multimedia Communication, OUM

Developed by: Centre for Instructional Design and Technology


Open University Malaysia

Printed by: Meteor Doc. Sdn. Bhd.


Lot 47-48, Jalan SR 1/9, Seksyen 9,
Jalan Serdang Raya, Taman Serdang Raya,
43300 Seri Kembangan, Selangor Darul Ehsan

First Edition, December 2008


Copyright © The Open University of Hong Kong and Open University Malaysia,
February 2011, CBND3203
All rights reserved. No part of this material may be reproduced in any form by any means
without permission in writing from the President, The Open University of Hong Kong or Open
University Malaysia

Copyright © Open University Malaysia (OUM)


Table of Contents
Course Guide vii – xii

TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT 1


Introduction: The basics of IPC - programs and processes 1
The kernel: the heart of an operating system 2
IPC: communication on the same system between processes 2
System calls 3
System memory 4
Process memory 5
The u area 5
Process memory addresses 6
The process environment 7
Process ID 8
Parent process ID 8
Process group ID 9
Real and effective user and group IDs
File system information — file descriptor 10
Summary 11

TOPIC 2 USING PROCESS 12


The fork system call 12
Exec’s minions 14
Using fork and exec together 15
Ending a process 17
Waiting 17
Summary 20

TOPIC 3 PRIMITIVE COMMUNICATIONS AND PIPES 21


Signals 22
Blocking signals 26
Pipes 27
Dup and dup2 27
Unnamed pipes 30
Named pipes 38
Summary 44

TOPIC 4 MESSAGE QUEUES AND SEMAPHORsES 45


Message Queues 45
Semaphores and shared memory 53
Summary 55

Copyright © Open University Malaysia (OUM)


iv X TABLE OF CONTENTS

TOPIC 5 BERKELEY SOCKETS 56


Introduction 56
Why programming with sockets? 57
Basic properties of a socket 58
Connection mode of socket communication 60
Socket types 63
Communication domain of a socket 64
Elementary socket system calls 67
listen system call 70
accept system call 70
connect system call 72
Asynchronous I/O 86
Sockets and signals 87
Summary 88

TOPIC 6 REMOTE PROCEDURE CALLS 89


Introduction 89
Procedure calls mechanism 90
Remote procedure call functional model 92
RPC call semantics 95
RPC implementation 96
Sun RPC facility 96
Summary 105

Glossary 106

References 108

Copyright © Open University Malaysia (OUM)


COURSE GUIDE

Copyright © Open University Malaysia (OUM)


Copyright © Open University Malaysia (OUM)
COURSE GUIDE W vii

INTRODUCTION
Welcome to CBND3203 Intermediate Network Programming and Design. This course
is the continuation from the CBND3103 Introduction to Network Programming and
Design course.

This course is a three-credit, middle-level course on computer networking within


the Bachelor of Information Technology with Network Computing. CBND3203 is
one of the compulsory courses in this programme. The course is self-contained
and assumes no specific knowledge of computer networking concepts and network
programming techniques.

About the Course


CBND3203 Intermediate Network Programming and Design introduces you to
network programming. Theoretical concepts and practical techniques are
integrated with the practical analysis of case study design and network
programming problems.

Course Objective
In general, the overall aims of this course is to develop your capability to write
network applications, discuss the underlying techniques and algorithms used in
the network programming and develop network applications under the UNIX and
Internet environments. To achieve all these, at the end of this course, you should
be able to:

1 Discuss the concept of processes.

2 Demonstrate an understanding of daemon processes.

3 Describe interprocess communication facilities and their mechanisms.

4 Analyse the benefits and limitations of process control techniques.

5 Demonstrate an understanding of streams and messages.

6 Develop applications using interprocess communication facilities.

7 Discuss the concept of sockets.

8 Demonstrate an understanding of the mechanisms of Berkeley sockets.

Copyright © Open University Malaysia (OUM)


viii X COURSE GUIDE

9 Develop simple network applications using sockets.

10 Demonstrate an understanding of remote procedure calls.

Course Organization
There are six topics in this Network Programming and Design module, namely:

Topic 1 The Basics of IPC and Process Environment

Topic 2 Using Processes

Topic 3 Primitive Communication and Pipes

Topic 4 Message Queues and Semaphores

Topic 5 Berkeley Sockets

Topic 6 Remote Method Procedure

In order to understand the content of this course, you must analyse the course
materials and apply the concepts learned. We hope that you are able to apply the
knowledge and skills from this course throughout your career.

COURSE SYNOPSIS
In previous CBND3103 course, you learned that most networks use a client/server
model in which the client computer makes a request and the server computer
fulfils it. In a distributed computing network, both the client and the server
perform tasks. But the workload isn’t distributed evenly. Because the server is
typically more powerful and faster than the client, the server handles most of the
tasks, leaving the smaller ones for the client. Distributed computing is not limited
to two machines. Several clients and servers in a network can perform tasks.
Communication is key at all levels of a distributed computing network. At the
machine level, clients and servers must communicate with each other. For
example, when you run a distributed application (i.e. an application that splits
processing between the client and server), the various clients and servers must
communicate task-related information. At the operating system (OS) level, various
OS components need to communicate with each other.

UNIX provides interprocess communication (IPC) facilities so that processes can


communicate with each other. IPC is one of the most useful programming

Copyright © Open University Malaysia (OUM)


COURSE GUIDE W ix

techniques for communication between processes within the same system. It is the
principal concept for Topic 1 – Topic 2.
We start by introducing the fundamentals of network programming to help you
understand what is going on in this unit. We do this by presenting the basic
concepts of the following topics:
• programs and processes
• process environments
• useful system calls such as fork, exec, exit, wait

After you have learned the basic concepts of interprocess communications, we


discuss primitive communications (Topic 3) and various IPC techniques such as
pipes, message queues, semaphores and shared memory (Topic 4).

While some top students enjoyed studying Topic 1 – Topic 4, we did have
students who found these topics difficult to comprehend. If you find these topics
are difficult, we suggest you read through the topics to get a general understanding
of the concepts and terminologies. Also try out all the hands-on exercises. Do seek
help from your tutor if you need assistance.

The socket interface was first developed on Berkeley UNIX in 1982 as a general
mechanism for interprocess communication. It is now a very powerful tool in
network programming. Different from the IPC techniques described in Topic 1 –
Topic 4, which are usually restricted to the communications between processes on
the same host, applications developed with socket interfaces can communicate
with other hosts in remote systems. Socket techniques provide flexible ways for
programmers to develop complicated applications in the networking environment.
Because of the predominance of sockets in network programming, Topic 5
emphasizes this concept.

Various system calls for socket programming are introduced in these topics. You
can use these system calls in the development of network applications.

Besides socket programming, we briefly present another programming technique


for building network applications — remote procedure call (RPC) in Topic 6.

RPC programming techniques can be considered the most convenient way to do


network programming, because the RPC has provided special features to handle
complicated tasks that are required in socket programming. However, the
drawback of RPC is that the performance of the application response is relatively
slow compared with that of socket programming.

Copyright © Open University Malaysia (OUM)


x X COURSE GUIDE

YOUR STUDY PLAN


As you by know aware that three-credit course requires 120 learning which
involve the following tasks as shown on in the following Table 1:

Table 1
Activities Totals Hours
General understanding of module 5
Reading module (see guide in Table 2) 60
Attending tutorial: 5 times of 2 hours each 10
Access OUM website 12
Work on assignment 15
Revision 18
Total 120

It is expected student will do all the above tasks in order gain good knowledge on
this subject.

The following chart gives a general guideline on the topics to be discussed on each
tutorial by the tutor:

Table 2

Topic Tutorial
1 Tutorial 1
2 Tutorial 1
3 Tutorial 2
4 Tutorial 3
5 Tutorial 4
6 Tutorial 5

Please take note that each topic contains few external readings. These readings are
compulsory for you to read and they can be tested in the examination.

Copyright © Open University Malaysia (OUM)


COURSE GUIDE W xi

TEXTBOOKS
The following set textbooks will be used:
Dean, T (2003) Enhanced Network + Guide to Networks, enhanced edn, Course
Technology, Thomson Learning.
Andrews, J (2002) i-Net Guide to Internet Technologies, 2nd edn, Course
Technology.
Alternatively, you could also refer to any other relevant textbooks found in
OUM’s Digital Library.

REQUIRED EQUIPMENT
The minimum PC configuration for this course is as listed below:
Pentium 233 MMX CPU
Microsoft Windows 95
VGA display card and colour monitor
32MBRAM
600MB free hard disk space
3.5” (1.44MB) floppy disk drive
CD-ROM Drive (8x or better), sound card and speaker
56 kbps modem

COURSE ASSESSMENT
Refer to myVLE.

Copyright © Open University Malaysia (OUM)


xii X COURSE GUIDE

Copyright © Open University Malaysia (OUM)


Topic 1 The basics of 
IPC and 
Process 
Environment 
LEARNING OUTCOMES
When you have completed this topic, you will be able to:
1. Describe the meaning of IPC and its role in UNIX
2. Describe all the important concepts related to IPC
3. Describe the process environment and its related elements

INTRODUCTION: THE BASICS OF IPC -


PROGRAMS AND PROCESSES
In previous CBND3103 course, you learned that most networks use a client/server
model in which the client computer makes a request and the server computer
fulfils it. In a distributed computing network, both the client and the server
perform tasks. But the workload isn’t distributed evenly. Because the server is
typically more powerful and faster than the client, the server handles most of the
tasks, leaving the smaller ones for the client. Distributed computing is not limited
to two machines. Several clients and servers in a network can perform tasks.
Communication is key at all levels of a distributed computing network. At the
machine level, clients and servers must communicate with each other. For
example, when you run a distributed application (i.e. an application that splits
processing between the client and server), the various clients and servers must
communicate task-related information. At the operating system (OS) level,
various OS components need to communicate with each other.

UNIX provides interprocess communication (IPC) facilities so that processes can


communicate with each other. IPC is one of the most useful programming

Copyright © Open University Malaysia (OUM)


2  TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT

techniques for communication between processes within the same system. The
basic concepts of IPC will be covered in Topic 1 and Topic 2. Also, IPC is the
principal concept for Topic 3 – Topic 4.

You may feel scared and confused when you first study Interprocess
Communication (IPC). It may at first seem very complex and too detailed to
quickly understand. Ironically, the first few topics in this module which are very
much related to IPC is one that many students find satisfying as well as
challenging, and at the end of the course, many students often say that IPC was a
highlight of the course. So, my advice to you at the beginning of this topic is to
persevere and try not to be too scared by some of the complex ideas that we
present in our initial discussions. As we start to look at and run programs, some of
the ideas will become clearer. The readings that we have included from the
Stevens book will also help you get a better understanding of some of the details
of the process environment that supports IPC, so please take the time to look at
these readings. As mentioned earlier, Topic 1 is the fundamental for you to
understand Topic 2 – Topic 4. Thus, give full attention for this topic.

The kernel: the heart of an operating system


In CBND3103, we looked at the system architecture in some multi-user operating
systems like UNIX. All operating systems have some means of managing tasks.
The heart of an operating system is the kernel. This piece of software acts as a
layer between programmer and the hardware and provides a common environment
for both users and other software. The system architecture and kernel concept of
layer is an important one. By acting as a layer, the kernel can help ensure that no
action is taken on the hardware by any one rogue application that would threaten
system stability for all other users or programs. Similarly, it can prevent other
users from viewing files in your home directory.

IPC: communication on the same system between


processes
When you enter the name of a program to be run at the system prompt, the file is
sought along your system path variable and, when found, the executable file is
loaded into memory and executed. The UNIX network operating system manages
tasks using processes. A process can be thought of as a program that is running
within a system.

When communicating processes are running on the same system, they


communicate with each other using interprocess communication. Interprocess
communication (IPC) is a generic term describing the methods that processes use

Copyright © Open University Malaysia (OUM)


TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT  3

to communicate with each other. Without IPC, processes can still exchange data
or other information with each other through the file system, but there are certain
limitations to the kind of communication that can occur.

IPC is a broad subject. Books have been written just on the topic alone. Among
my favourite books is John Gray’s Interprocess Communications in UNIX: The
Nooks and Crannies (1988). To get a deep understanding of IPC, a network or
system programmer needs to understand the system architecture and operating
system of the machines he or she is programming or communicating to.

We therefore start our discussion of IPC by first recalling and further elaborating
on the concepts of operating system architecture. We look at some characteristics
of system architecture such as
• system calls,
• system memory and
• process memory.

An understanding of processes is critical to understanding the inner workings of


the UNIX operating system.

System calls
UNIX is a two-layered operating system consisting of a kernel and a series of
system programs. UNIX systems programs and utilities deliver requested
functionality to users by issuing system calls to the kernel. As shown in Figure
1.1, system calls provide the interface between executable programs and the
operating system. In the executable programs, system calls request the UNIX
operating system to directly perform some work on behalf of the invoking
process. A process consists of an executing program, its current values, state
information, and the resources used by the operating system to manage the
execution of the process. The system calls are then handled by the kernel.

Copyright © Open University Malaysia (OUM)


4  TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT

Figure 1.1: UNIX kernel architecture

System memory
The kernel provides a common environment under which to run the executable
programs. When we enter the name of a program to be run at the prompt, the file
is sought along our path variable and, when found, the executable file is loaded
into memory and executed. During the lifetime of its operation it is referred to as a
process and given a unique identifier.

The system memory within which the processes reside can be divided into two
distinct areas: user space and kernel space. A program is a user process and will
run in user space. It will be running in user mode. The kernel, by contrast,
executes in kernel space and is said to be running in kernel mode. A user process
can only access kernel space by making a system call — in other words, by asking
the kernel to perform some tasks. In doing so, the user process makes a context
switch and will, temporarily, be running in kernel mode. System calls are more
common than you may at first think. The C command printf() makes a system call
to write.

Copyright © Open University Malaysia (OUM)


TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT  5

Process memory
To manage processes, computers must have efficient memory management. This
is accomplished through the use of memory management techniques, which are
described in this section.

When residing in system memory, the user process is divided into three segments:
• text,
• data and
• stack.

Text segment: This is a read-only area of memory, containing the executable


program code and constant data. If we run the same application more than once,
then all processes will share this segment unless this option is disabled with the
compiler.

Data segment: This can be thought of as continuing from the text segment and is
divided into two areas: initialized (e.g. in C, variables that are declared as static or
are static) and uninitialized data. In C program language, the call to the library
routine malloc may be made to request more memory. System calls brk and sbrk
handle this by extending the size of the data segment toward the stack segment,
adding memory from the heap to the end of the uninitialized data area.

Stack segment: The location of the stack segment is system dependent and holds
register variables, automatic identifiers and function call information for the
process.

The u area
In addition to the text, data and stack segments, the operating system maintains
for each process a region called the u area (user area). This area of memory
contains information such as open files that is specific to a process alongside a
system stack segment. Were a process to make a system call, the information
needed would be stored in the system stack segment — an area not normally
accessible by the process. System calls are needed to access this information,
which is paged in and out by the kernel.

Copyright © Open University Malaysia (OUM)


6  TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT

Process memory addresses


In UNIX, virtual memory is a model for managing the resource of physical
memory, also called main memory. There are several models for a programmer to
use the memory space.

Physical addressing: The simplest model of program creation and execution


requires a programmer to determine what physical memory is available. The
program reserves the physical memory so that no one else uses it and writes a
program to use the memory locations reserved. However, this requires a rewrite of
the program every time it runs, but this task could be left to the operating system.
At process start-up, the operating system could modify every pointer reference in
the application to reflect the physical address at which the program is loaded. The
program as it resides in main memory references itself directly, and it contains
knowledge about the structure and organization of the physical memory.

Base+offset addressing: A second model is to write the program once using


pointer addresses that are not absolute but are instead relative offsets from the
beginning of the program. If the location of the program in physical memory is a
variable stored in a known hardware register, then at run-time one can load the
program wherever it fits in physical memory and place this location information
in the known register so that all memory references first incorporate this base
address and are then redirected to the correct physical location. The advantage is
that, as opposed to the previous scheme, knowledge of the memory-system
organization is not required to the program. The disadvantage is that the program
must be divided into a relatively small number of contiguous segments, and each
segment must fit entirely in main memory if it is to be used.

Virtual addressing: A third model is to write the program as if it is loaded at


physical memory location zero, load the program wherever it fits, and translate the
program’s addresses to the equivalent physical addresses while it is running. If the
translation is relatively small (i.e. the program is broken down into smaller pieces
that are translated independently of each other), the program can even be
fragmented in main memory —bits and pieces of the program can lie scattered
throughout main memory, and the program need not be entirely resident to
execute. The advantage of this scheme is that one never needs to rewrite the
program. The disadvantage is the potential overhead of the translation mechanism.

Copyright © Open University Malaysia (OUM)


TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT  7

This reading describes the concept of system memory, process memory, the u
area and process memory addresses. After this reading, you should have a
better understanding of the conceptual relationship between systems and
process memory.

Gray, J S (1998) Interprocess Communications in UNIX: The Nooks and


Crannies, pp.11–14.

ACTIVITY 1.1

Run program 1.4 in the above reading. The output demonstrates the
range of addresses for identifiers of different memory storage types.

SELF-TEST 1.1

Discuss the hardware requirements for the three memory models:


1 physical addressing,
2 base+offset addressing, and
3 virtual addressing.

THE PROCESS ENVIRONMENT


Before giving a detailed description of IPC techniques in the next topics, we
present some basic knowledge that is frequently applied in the process
environment. Why are these knowledge important? To answer this question, let’s
look at an example of a process currently running on your CT212 server machine
(plbpc011.ouhk.edu.hk). It can be seen using the program ps.
[ptsang@plbpc011 ptsang]$ ps
PID TTY TIME CMD
18188 pts/0 00:00:00 bash
18229 pts/0 00:00:00 ps
[ptsang@plbpc011 ptsang]$

Copyright © Open University Malaysia (OUM)


8  TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT

By looking at the process environment and by knowing a process ID, you can
write programs to do other functions, e.g. to stop or communicate the process.

This section looks at concepts such as process ID, parent process ID, process
group ID, user identifications and file descriptors. An understanding of these basic
concepts will help you understand how knowledge of the process environment
helps you learn IPC techniques in later sections/topics.

Process ID
A process is an instance of a program that is being executed by the operating
system. The only way a new process can be created by the UNIX system is by
issuing the fork system call. Details of the fork call can be found in the next
topic. Every process has unique process identification (process ID or PID). The
PID is an integer, typically in the range of 0 through 30000. The kernel assigns a
PID when a new process is created, and a process can obtain its PID using the
system call.

The process with process ID 1 is a special process called the init process
(whose basic responsibility is managing terminal lines and who is the parent
process of all other UNIX processes). Process ID 0 is also a special kernel process
termed either the swapper or the scheduler. In virtual memory
implementations of UNIX, the process with process ID 2 is typically a kernel
process termed the pagedaemon (responsible for paging). Other than these
special processes, no special meanings are associated with other process ID
values.

Parent process ID
Every process has a ‘parent’ process and corresponding parent process
identification (parent PID or PPID). The parent process ID is the process ID or
PID of the process’s parent. The parent is the process that created the current
process, which is its ‘child’. A process can obtain its value using the getppid
system call.

The following program prints the PID and the parent process ID of a process.
More detailed explanation of the system calls getpid and getppid can be
found in UNIX command manuals. Sun Microsystems host a very good online
UNIX command manual at the following website: http://docs.sun.com/

Copyright © Open University Malaysia (OUM)


TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT  9

main()
{
printf("Process ID = %d, Parent process ID = %d\n", getpid(),
getppid());
}

Figure 1.2: Process ID information

The corresponding process ID and its parent process ID will be sent to the
standard output file. The output of the above program looks like this:
Process ID = 6312, Parent process ID = 2334

The process with ID 2334 is the parent or creator of the process with ID 6312.

Process group ID
The group identification (GID) is a group number which is defined in the file
/etc/group. When a process is started, its GID is set to the GID of its parent.

The effective group identification (EGID) is related to the GID in the same way
that the EUID is related to the UID. If the process tries to access a file on which it
does not have owner permission, the kernel will automatically check to see if
permission may be granted on the basis of the EGID.

Real and effective user and group IDs


The user identification (UID) contains the user number of the user who created
the process. That user is considered the owner of the process. The owner of the
process can change its operational parameters.

The effective user identification (EUID) is the number that is used to determine
what resources the process has permission to access. In most processes, the UID
and EUID are the same; however, a program can set a flag that defines a feature:
‘when this program is executed by others users, change the EUID of the
process to be the UID of the owner of this file’.

Hence, other users can execute this program as if the owner of the program were
doing so.

Copyright © Open University Malaysia (OUM)


10  TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT

File system information — file descriptor


In addition to process ID information, the process environment contains file
system information. A file descriptor is a reference integer used to identify a file
that has been opened within a process. The process can refer to this file descriptor
for input/output (I/O) operations. The Operating System is responsible for
assigning file descriptor numbers to processes opening a file. Many UNIX
programs associate file descriptors with the standard input, standard output and
standard error of a process.

A process in UNIX systems requires several files to handle different purposes, for
example a file descriptor for input tasks, another descriptor for output tasks, and
so on. In UNIX, file descriptors 0, 1, and 2 correspond to the standard input,
standard output and standard error files, respectively.

The use of a descriptor involves three major steps.


1 The first step is to create and initialize a new descriptor. This step is to let
the system know that a brand new descriptor has been created. Processes can
refer the descriptor to do different operations.
2 After creating a descriptor, you may start the communication process
referring to the descriptor. The owner who creates the descriptor can send
and/or receive data to it. Depending on the corresponding techniques used to
create the descriptors, processes performed on other processes may not be
limited exclusively to the process owner. This can achieve data exchange
between different processes.
3 The final step for the use of the descriptor is to destruct the descriptor. It will
release the resource allocated by the kernel of the descriptor creation.

Many UNIX system calls rely on file descriptors. The low level open, close,
read, and write calls, for example, use file descriptors. In some instances,
such as the socket-programming interface, discussed in other topic, the only way
to perform input and output is through file descriptors.

A detailed explanation of the relationships of process ID, parent process ID, real
and effective user and group IDs is provided in the following reading.

Stevens, W R (1994) UNIX® Network Programming, pp. 22–24.

Copyright © Open University Malaysia (OUM)


TOPIC 1 THE BASICS OF IPC AND PROCESS ENVIRONMENT  11

ACTIVITY 1.2

Write a program to identify your process UID, EUID, GID and EGID.

SELF-TEST 1.2

What is the difference between real user ID and effective user ID?

In this topic, we have presented a basic introduction to interprocess


communications. The relationship of the parent process and the child process has
been introduced. Each process has its identifier. In a UNIX environment, you
have to use the special system call fork to create a new process. After the
generation of new processes, there is a need to have communication between
them.

Copyright © Open University Malaysia (OUM)


Topic 2 Using 
Process 
LEARNING OUTCOMES
When you have completed this topic, you will be able to:
1. Describe how to create and control processes
2. Write programs to create and control processes

 INTRODUCTION
In the previous topic, you saw that processes are at the very heart of the UNIX
operating system. In this topic, we see how to create and control processes by
using fork, exec,exit and wait system calls.

THE FORK SYSTEM CALL


When a process wants to create a new process, it begins by making a copy of
itself using the fork system call. fork duplicates a copy of a process. The
original process is called the parent process and the duplicate is called the child
process. The child and parent processes are identical except for the following
differences:
 The new process has a distinct PID.
 The new process’s parent PID or PPID references the original process.
 The new process is assumed to have used no resources.
 The new process has its own copy of the parent’s file descriptors.

The fork system call creates a copy of process that was executing and is unlike
any function that we’ve seen. It takes one process to call it but two processes
return from it. The process that executes the fork is called the parent process and
the other process is called the child process. Note that these names are not simply
convention — they correspond to an important relationship between the two
processes. The parent process can control certain aspects and get information
about its child processes in ways that the children cannot access their parents.

Copyright © Open University Malaysia (OUM)


TOPIC 2 USING PROCESS  13

From the child’s point of view, fork always returns zero. However, fork
returns a non-zero value to the parent process that is the PID of the newly created
child. If fork fails for any reason, a value of –1 is returned. An example of the
fork system call looks like this:

main(int argc, char *argv[])


{
int childpid;
childpid = fork();
if (childpid < 0)
{
printf("Something wrong in the fork. \n ");
return 0;
}
if ( childpid == 0)
{
/* Child process routine */
printf("This is the child process.\n");
}
else
{
/* Parent process routine */
printf("This is the parent process.\n");
}
}

Figure 2.1: Child process creation with the fork system call.

The output of the program shown in Figure 2.1 should look like this:
This is the child process.
This is the parent process.

After the fork operation, the child process routine will continue with its coding
in the child process routine (just as the parent does). After all, the child process is
an exact duplicate of the parent except for the value returned from fork. The
child process inherits several attributes from the parent process, including:
 a real user ID, real group ID, effective user ID and effective group ID
 a set of environment variables
 open file descriptors
 a current working directory
 a root directory.

Copyright © Open University Malaysia (OUM)


14  TOPIC 2 USING PROCESS

The child and parent can share data and exchange messages with each other in this
way, if this is what you want.

However, situations in which you would want two copies of the same program
running simultaneously are not common. In fact, a more general reason that a new
process gets ‘forked’ is so that it can execute another program by using the
exec family of system calls. The function of the exec system call is to change
the executable code that the process is running and reset the data and stack
segments to a predefined initial state. The function of the exec system call is
further explained in the next section.

The concept of the fork process system call is presented in the following
reading. It describes the fork operation in detail.

Stevens, W R (1994) UNIX® Network Programming, pp. 55–57.

EXEC’S MINIONS
Processes generate child processes for a number of reasons. In UNIX, there are
several long-lived processes that run in the background and perform a specified
operation at predefined times or in response to certain events. These processes,
called daemon processes, frequently generate child processes to carry out the
requested service. The term daemon is a UNIX term. Many other operating
systems provide support for daemons, though they’re sometimes called other
names. Windows, for example, refers to daemons as System Agents and services.
The term comes from Greek mythology, in which daemons were guardian spirits.
Typical daemon processes include print spoolers, email handlers, and other
programs that perform administrative tasks for the operating system.

A program generates a child process because it would like to transform the child
process by changing the program code the child process is executing. A set of six
system calls under the generalized heading of exec will do this. exec is a
family of related functions (execl, execlp, execle, execv, execvp, and
execve). exec in all its forms overlays a new process image on an old process.
This implies that exec starts the execution of a new program and throws away
the current program (there’s no way to get back to the original program if exec
succeeds; if exec is successful, it doesn’t return to the original program). The
new process image is constructed from an ordinary, executable file. This file is
either an executable object file or a file of data for an interpreter. The following is
part of the function calls in the exec family.
Copyright © Open University Malaysia (OUM)
TOPIC 2 USING PROCESS  15

int execl(const char *path, const char *arg0, ..., const char
*argn, char * /* (char *) 0 */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

Figure 2.2: Some commonly used exec system calls

In general, all forms of the system functions in the exec family behave in the
same way but have their parameters specified in different formats. The first
difference in these functions is that the first two take a pathname argument
whereas the last one takes a filename argument. When a filename argument is
specified, one of the following results:
 If the filename contains a slash, it is taken as a pathname.
 Otherwise, the executable file is searched for in the directories specified by
the environment variable.

For simplicity of explaining the variants exec, a frequently used call execvp is
selected to demonstrate the system call. The rest of the exec family is similar;
you can browse through the manual pages to understand all of the differences
among the species of exec. In general, the execv family is easiest to program;
the execp functions take care of some other details, and execvp is a nice
combination.

The concepts of the exec and daemon process system calls are presented in
the following reading. It describes the six functions in exec operation in
detail. Also, it discusses how a daemon process can be started and the
characteristics of typical system daemons.

Stevens, W R (1994) UNIX® Network Programming, pp. 57–59 and 72–73.

USING fork AND exec TOGETHER


In most programs, the fork and exec system calls are used in conjunction with
one another (in some operating systems, the fork and exec calls are packaged
as a single spawn system call). A simple example of how fork and exec are
used to spawn a new process is shown in the code below.

Copyright © Open University Malaysia (OUM)


16  TOPIC 2 USING PROCESS

int pid;
pid = fork ();
if (pid == -1) {
printf ("Something is wrong!\n");
}
else if (pid == 0) {
char *args [2];
args [0] = "ls";
args [1] = NULL;
execvp (args [0], args);
exit (1);
}
else {
printf ("I’m the parent, and my child is %d.\n", pid);
}

Figure 2.3: Generation of a new process with fork and exec

The child process starts a new process that issues the list file system command
ls. The effect is identical to inputting ls from the UNIX command prompt. The
output of the above program looks like this:
fork-exec mpipe pipe pipe2 test.c test1.cc
fork-exec.c p1 pipe.c pipe2.c test.out
I’m the parent, and my child is 20128.

The file name in the current directory is shown, and the last message is the
printout from the parent process. The corresponding PID of the child process is
20128.

ACTIVITY 2.1

1 Refer to the program code shown in the above example. Explain


whether the first argument to execvp is redundant or not.
2 Refer to the same program code. If the exec system call will not
return to the original process, why it is necessary to have the call to
exit after the execvp?

SELF-TEST 2.1

What are the differences between the child process and the parent
process?

Copyright © Open University Malaysia (OUM)


TOPIC 2 USING PROCESS  17

ENDING A PROCESS
Eventually all things must come to an end. Every UNIX process terminates with
an exit value. For example, a process has been generated. If the process does not
receive a terminating signal and the system has not crashed, we will terminate a
process by calling the exit system call. When exit is called, an integer exit
status is passed by the process to the kernel. This exit status is then available to
the parent process through the wait system call (described below). By
convention, a process that terminates normally returns an exit status of zero,
whereas the nonzero values are used to indicate an error condition.

WAITING
When a program forks and the child finishes before the parent, the kernel keeps
some of its information about the child in case the parent needs it — for example,
the parent may need to check the child’s exit status. To be able to get this
information, the parent calls wait() . When this happens, the kernel can discard
the information.

You need to ensure that your parent process calls wait() (or waitpid(),
wait3() and so on) for every child process that terminates; or, on some systems,
you can instruct the system that you are uninterested in child exit states.

Once the parent has forked off a process (usually followed immediately by an
exec), the child runs along doing whatever it will while the parent also continues
to run. At some point in the future, however, the parent may become interested in
the eventual fate (or current condition) of the child. For example, when a child
terminates, either normally or abnormally, the parent will receive a special signal
transmitted by the kernel. Since the termination of a child can happen at any time
while the parent is running, this signal is the asynchronous notification from the
kernel to the parent. In order to get information about its children, the parent can
use the wait system call.

Like exec, wait comes in several formats to suit a wide range of needs and
tastes. In general, the wait3 system call is the most widely used but it is also the
most complicated, and wait and waitpid are sufficient for many uses.

As its name implies, the purpose of the wait system call is to allow the parent to
wait for the child to finish running and ‘die’ (or perish for some other reason), get
the exit status, and continue. However, with the advent of job control (which
allows each parent to control any number of children, who can all be running in
the foreground and/or background simultaneously), things are no longer quite so
simple. For example, consider the chores that the windowing system on your

Copyright © Open University Malaysia (OUM)


18  TOPIC 2 USING PROCESS

computer must do. It is interested in the fate of all your individual programs, each
running in its own window, but it can’t be completely preoccupied with watching
them every moment, for it must be ready to respond to a mouse click or keyboard
event intended for it as well. To accomplish these things, wait3 is well suited.

Returning once again to the previous example, we modify it so that the parent
waits for something to happen to the child process to finish before continuing.
(Note that we’re not checking to find out what happened, i.e. whether the child
exited, crashed or merely stopped for some random reason.)

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

main()
{
int pid;
pid = fork ();
if (pid == -1) {
printf ("Something is wrong!\n");
}
else if (pid == 0) {
char *args [2];
args [0] = “ls”;
args [1] = NULL;
execvp (args[0], args);
return 0;
}
else {
int status;
/* To issue the first ps -elf in the command prompt manually*/
sleep(5);
printf(“waiting for the exit of the child\n”);
wait(&status);
/* To issue the second ps -elf in the command prompt manually*/
sleep(5);
printf (“I’m the parent, and my child is %d.\n”, pid);
}
/* To issue the third ps -elf in the command prompt manually*/
}

Figure 2.4 Illustration of the wait system call

Note that when a child exits, UNIX will not remove the process until the parent
has seen the exit status and any other related information. This means that each
fork should have a matching wait, and that wait should eventually wait until
the child is dead. Processes that have exited but have not had their exit status
reported to their parents are left inactive. The process in this status is called the
zombie process. That is, they cannot be killed and cannot be removed until their
parents wait for them (or their entire ancestor processes exit, thereby removing the
possibility that any process will ever wait for them). They will clutter up the
system, causing other users to grumble.

Copyright © Open University Malaysia (OUM)


TOPIC 2 USING PROCESS  19

Let’s use the above program to illustrate the effect of system call wait in the
parent process. The application runs into three stages:
1 The child exits but the parent does not clean up the child’s entry in the
process table.
2 The child’s entry in the process table has been removed by the parent
process.
3 The end of the waiting program.

You can verify this status according to the instructed ps command. The output of
the status should look like Figure 2.5 below:

First : ps -efl
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
8 S pkser 20120 20118 0 40 20 50814cd0 137 50814ea0 13:45:09 pts/3 0:00 -csh
8 Z pkser 20469 20468 0 0 0:00 <defunct>
8 S pkser 20401 20399 0 41 20 5083ccd8 137 5083cea8 16:09:03 pts/4 0:00 -csh
8 S pkser 20468 20120 0 60 20 508f0cc8 139 508f0e98 16:24:42 pts/3 0:00 waiting
Second : ps -efl
8 S pkser 20120 20118 0 40 20 50814cd0 137 50814ea0 13:45:09 pts/3 0:00 -csh
8 S pkser 20401 20399 0 41 20 5083ccd8 137 5083cea8 16:09:03 pts/4 0:00 -csh
8 S pkser 20468 20120 0 45 20 508f0cc8 139 508f0e98 16:24:42 pts/3 0:00 waiting
Third : ps -efl
8 S pkser 20120 20118 0 40 20 50814cd0 137 508711ce 13:45:09 pts/3 0:00 -csh
8 S pkser 20401 20399 0 41 20 5083ccd8 137 5083cea8 16:09:03 pts/4 0:00 -csh

Figure 2.5: The output of the program in Figure 2.4

In the first ps command, you will get a zombie process. For the example in
Figure 2.4, a zombie process with bold text is shown in Figure 2.5. The
corresponding item in the last column of the result is defunct, which means the
process is left inactive. The reason is that the child has completed its task but its
entry is still recorded in the process table. At this moment, the child becomes a
zombie and the status flag in the second column is Z to indicate that it is a zombie.
After sleeping for five seconds, the parent process starts to work again and will
remove the zombie entry. This is why the defunct does not show in the second ps
command. Finally, the last ps command only lists out the user log-on shell. The
waiting program exits.

Copyright © Open University Malaysia (OUM)


20  TOPIC 2 USING PROCESS

You can learn more about the prototype and return values of the system call
wait in this reading.

Stevens, W R (1994) UNIX® Network Programming, pp. 60–62.

ACTIVITY 2.2

1 What will happen if the parent process is exited without calling


wait to handle the exit status of its child process (zombie)? Which
process will take care of the zombie?
2 How do you prevent the process table in the system from being
exhausted by the zombie?

SELF-TEST 2.2

What are the three conditions for which wait returns a process ID as its
return value?

In this topic, we see how to create and control processes by using fork,
exec,exit and wait system calls.

The next topic will deal on primitive communication and pipes.

Copyright © Open University Malaysia (OUM)


Topic 3 Primitive 
Communication 
and Pipes 
LEARNING OUTCOMES
When you have completed this topic, you will be able to:
1. Discuss primitive communication techniques
2. Discuss interprocess communications techniques using designed
interprocess facilities.
3. Discuss the pipes interprocess communications techniques —
unnamed pipes and FIFOs.
4. Write program codes for the above concepts

 INTRODUCTION
This topic focuses the discussion on interprocess communication (IPC) to help
you in developing programs that consist of processes that use the program codes
presented in this topic and the previous topics. If you would like to know more
system calls or commands provided in the UNIX OS, you are encouraged to refer
to a UNIX manual or use the help command man in UNIX for a detailed
explanation of other system calls.

We start by discussing primitive communication techniques. Although these


techniques get the job done, they also have certain limitations. Then, we will
explore interprocess communications techniques using designed interprocess
facilities. First, we introduce two system calls — dup and dup2 — used in pipes.
Then we discuss the pipes interprocess communications techniques — unnamed
pipes and FIFOs.

Copyright © Open University Malaysia (OUM)


22  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

SIGNALS
One primitive interprocess communication technique involves the use of signals.
Signals can be considered as interrupts that are sent from the system to a process.
The process has no control over when they arrive. Signals are sometimes called
‘software interrupts’. Signals usually occur asynchronously. This implies that
processes do not know ahead of time exactly when a signal will happen. Different
events in a system will generate different signals. A signal will be sent to a
process when the event occurs in the system. Signals can either be sent by:
 one process to another process (or to itself); or
 by the kernel to a process.

Examples of such events include hardware faults, timer expiration and terminal
activity, as well as the invocation of special system calls such as kill. In some
circumstances, the same event generates signals for multiple processes. A process
may request a detailed notification of the source of the signal and the reason why
it was generated.

What happens when your process has a signal delivered to it is somewhat up to


the program; the program can specify specific signal handlers (also called signal
vectors or dispositions) that should be run when a signal is delivered. This is called
‘catching the signal’. The set of system signal actions for a process is initialized from
that of its parent. Once an action is installed for a specific signal, it usually remains
installed until another handler is explicitly requested by a call to sigaction,
signal or sigset, or until the process is executed. When a process is executed,
all signals whose handlers have been set to catch the signal will be set to default
actions.

All UNIX programs have a full set of signal handlers. Each signal has a name and its
default action. The default action can be classified into four types:
 exit
 exit with core image
 ignore
 stop process.

A core image is to the dumping of the system memory image to a file. All of the
signal handlers that we’ve been using in all of the programs to date are the default
signal handlers. However, we can also choose to include tailor-made handlers in
programs. The signals currently defined by <signal.h> are shown in the
following table.

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  23

Table 3.1: The signals currently defined by <signal.h>


Name Value Default Event
SIGHUP 1 Exit Hangup
SIGINT 2 Exit Interrupt
SIGQUIT 3 Core Quit
SIGILL 4 Core Illegal Instruction
SIGTRAP 5 Core Trace/Breakpoint Trap
SIGABRT 6 Core Abort
SIGEMT 7 Core Emulation Trap
SIGFPE 8 Core Arithmetic Exception
SIGKILL 9 Exit Killed
SIGBUS 10 Core Bus Error
SIGSEGV 11 Core Segmentation Fault
SIGSYS 12 Core Bad System Call
SIGPIPE 13 Exit Broken Pipe
SIGALRM 14 Exit Alarm Clock
SIGTERM 15 Exit Terminated
SIGUSR1 16 Exit User Signal 1
SIGUSR2 17 Exit User Signal 2
SIGCHLD 18 Ignore Child Status Changed
SIGPWR 19 Ignore Power Fail/Restart
SIGWINCH 20 Ignore Window Size Change
SIGURG 21 Ignore Urgent Socket Condition
SIGPOLL 22 Exit Pollable Event
SIGSTOP 23 Stop Stopped (signal)
SIGTSTP 24 Stop Stopped (user)
SIGCONT 25 Ignore Continued
SIGTTIN 26 Stop Stopped (tty input)
SIGTTOU 27 Stop Stopped (tty output)
SIGVTALRM 28 Exit Virtual Timer Expired
SIGPROF 29 Exit Profiling Timer Expired
SIGXCPU 30 Core CPU time limit exceeded
SIGXFSZ 31 Core File size limit exceeded
SIGWAITING 32 Ignore Concurrency signal reserved by threads library
SIGLWP 33 Ignore Inter-LWP signal reserved by threads library
SIGFREEZE 34 Ignore Check point Freeze
SIGTHAW 35 Ignore Check point Thaw
SIGCANCEL 36 Ignore Cancellation signal reserved by threads
library
(Source: Sun Microsystems docs.sun.com•man, ‘Pages(5): headers, tables and macros’:
http://docs.sun.com/?p=/doc/805-3177/6j31gjo55&a=view#signal-5-indx-1)

Copyright © Open University Malaysia (OUM)


24  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

The default action for each signal is selected from those shown in Table 3.2
below.

Table 3.2: Default actions for signalling

Default Default action

Exit When it gets the signal, the receiving process is to be terminated with all the
consequences.

Core When it gets the signal, the receiving process is to be terminated with all the
consequences. The core file contains all of the process information pertinent to
debugging: contents of hardware registers, process status and process data. In
addition, a ‘core image’ of the process is dumped in the current working
directory.

Stop When it gets the signal, the receiving process is to stop. When a process is
stopped, all threads within the process also stop executing.

Ignore When it gets the signal, the receiving process is to ignore it. This is identical to
setting the handler to ignore.

An example of this is the SIGINT (or interrupt) signal, which you have seen
many times. If your program is running out of control, the general technique for
stopping it is to press ‘control-C’. Of course, there’s nothing in the program that
you wrote that knows anything about ‘control-C’. Instead, what happens is that
when you type ‘control-C’, UNIX notes this and knows that the default action to
take when a ‘control-C’ is typed is to send an SIGINT signal to all the processes
attached to that terminal. When UNIX processes receive an SIGINT, the default
action is to die, so your program exits.

However, it is possible to change what happens when your program receives a


signal. For example, it is possible to tell your program that when it gets a
SIGINT, it should just ignore it. This is what your shell does; otherwise, your
shell would exit (and log you out) every time you pressed ‘control-C’.
Here is the prototype for signal:

#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);

The signal prototype requires two arguments as input, and it always returns the
previous value of func for the specified signal. The first parameter is the signal
value. The second parameter is a pointer to a function that returns void and takes
a single integer as its argument: The return value of signal itself is a pointer to

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  25

a function that returns void and takes a single integer argument. There are two
special values for the func argument: SIG_DFL to specify that the signal is to
be handled in the default way, and SIG_IGN to set a signal that is pending causes
the pending signal to be discarded. Any queued values pending are also discarded;
the resources used to queue them are released and made available to queue other
signals. The second parameter to signal is a pointer to the function to call
whenever the signal is delivered. The return value is the previous signal handler
that was installed before the new one was added.

As the first example of this we’ll write a program that ignores ‘control-C’. When
it receives an INT signal, it will simply ignore it. We’ll just install the default
‘ignore it’ signal handler, which is named SIG_IGN.

#include <signal.h>

int main (int argc, char **argv)


{
/* To specify the SIGINT is ignored. */
signal (SIGINT, SIG_IGN);
for (;;) {
...
}
}

Figure 3.1: Section of program code to illustrate the signal system call

On many systems, once a signal handler has been invoked it reverts to its default.
If this is the situation, then the second ‘control-C’ would kill this program. To fix
this, it is necessary to set the SIGINT to handler after the first handle is called, as
shown in Figure 3.2.

void handler(int sig) /* sig is the corresponding signal number*/


{
signal (SIGINT, handler);
printf (“control-C is ignored.\n”);
}

Figure 3.2: Signal handler for interrupt

Note that a parent process can send signals to its child processes at will using the
kill system call. All signals, other than SIGKILL, can be ignored. The
SIGKILL signal is special because the system administrators need to have a
guaranteed way of terminating any process. We recommend that you reference
kill in a UNIX manual for information about what each signal is used for.

Copyright © Open University Malaysia (OUM)


26  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

Blocking signals
A global mask specifies which signals are currently blocked. The system call
sigblock adds the signals specified in the mask to the set of signals currently
being blocked from delivery. Signals are blocked if the appropriate bit in the mask
is a 1. The system calls sigsetmask and sigpause can be used to unblock
signals by restoring the original mask. In the file <signal.h> is a call
sigmask that makes it convenient to set the signal mask for the call to
sigblock. sigmask is provided to construct the mask for a given signal mask
number.

In normal usage, a signal is blocked using sigblock. To begin an uninterrupted


section, variables modified on the occurrence of the signal are examined to
determine that there is no work to be done, and the process pauses awaiting work
by using sigpause and the mask returned by sigblock. For example, when it
is necessary to block a signal such as SIGINT , the sequence shown below in
Figure 3.3 can be used.

newmask = sigmask (SIGINT); /* A mask with SIGINT is blocked */


oldmask = sigblock (newmask); /* To block the SIGINT signal */
Uninterrupted section /* SIGINT is blocked */
sigsetmask (oldmask); /* To recover the original signal
mask */

Figure 3.3: Pseudo code for uninterrupted section with signal masking

Note that it is not possible to block SIGKILL, SIGSTOP or SIGCONT. This


restriction is silently imposed by the system.

This reading provides a detailed description of each signal event. If you would
like to design a single handler, you have to understand the meaning of each
signal event.

Stevens, W R (1994) UNIX® Network Programming, pp. 43–50.

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  27

Optional
The following website provides information on the signalling concept for the
Linux OS. Information about SIGKILL, SIGSTOP and blocked is
introduced for your reference.

Rusling, D (1996–98) ‘Interprocess communication mechanisms: signals’ in


The Linux Kernel, pp. 1–2, http://www.eee.hku.hk/LDP/LDP/tlk/ipc/ipc.html

ACTIVITY 3.1

Imagine that a user program prints out a message “Control-C is


ignored.” whenever there is a receipt of an interrupt signal. Write a
section of program code that can handle this requirement.

SELF-TEST 3.1

1 What kind of header file should be included in C program in using


signals?
2 Describe four actions that the default handler will perform for a
signal.

PIPES
In the previous section, we addressed primitive techniques for communicating by
using signals. In this section, we explore interprocess communications techniques
using designed interprocess facilities. First, we introduce two system calls — dup
and dup2 — used in pipes. Then we discuss the pipes interprocess
communications techniques — unnamed pipes and FIFOs.

dup and dup2


Both dup and dup2 are used to duplicate an open file descriptor in the system.
The protocols of these functions are:
#include <unistd.h>
int dup(int fildes);

Copyright © Open University Malaysia (OUM)


28  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

#include <unistd.h>
int dup2(int fildes, int fildes2);

dup returns a new file descriptor having the following in common with the
original open file descriptor fildes. However, dup2 causes the file descriptor
fildes2 to refer to the same file as fildes. fildes is a file descriptor
referring to an open file, and fildes2 is a non-negative integer less than the
current value for the maximum number of open file descriptors allowed the
calling process. If fildes2 already referred to an open file, not fildes, it is
closed first. If fildes2 refers to fildes, or if fildes is not a valid open file
descriptor, fildes2 will not be closed first.

As a simple example of this call (just using dup and not using pipe), consider
the following command (which again is portable to just about any UNIX shell):
sort < foo > bar

This command invokes the sort program, but instead of reading input from the
user’s keyboard (as it would ordinarily do), input is read from a file named foo.
Similarly, instead of the output being displayed on the screen, it is placed in a file
named bar. This is a relatively simple example, because only one process is
involved. The only thing that we need to do is somehow fiddle with this process
so that its input file descriptor (fd 0) is reading from foo instead of the keyboard,
so that the output file descriptor (fd 1) writes to bar instead of the screen.

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  29

int pid;

pid = fork ();


if (pid == -1) {
printf ("Something is wrong!\n");
}
else if (pid == 0) {
char *args [2];
int input_fd;
int output_fd;
args [0] = "sort";
args [1] = NULL;

/* Open inputdata and outputdata */


input_fd = open ("inputdata", O_RDONLY);
output_fd = open ("outputdata", O_WRONLY|O_TRUNC|O_CREAT, 0);
close (0); /* to close the stdin */
close (1); /* to close the stdout */
/* And dup input_fd for 0, and */
/* output_fd for 1. */
dup (input_fd); /* It becomes the stdio for sort */
dup (output_fd); /* It becomes the stdout for sort */
execvp (args [0], args);
exit (1);
}
else {
/* Parent Process routine */
}

Figure 3.4: Illustration of the dup system call

In Figure 3.4, two file descriptors, input_fd and output_fd, were created for
input/output operations. The input_fd is used as an input file. It is opened with
the O_RDONLY, which stands for read only. The output_fd is the output file of
the program. It is opened with the O_WRONLY, which stands for write only. If
output_fd exists, the O_CREAT has no effect, but O_TRUNC will remove all
data corresponding to this descriptor. Otherwise, a new file descriptor is created.
The new file descriptor returned by dup is guaranteed to be the lowest numbered
available file descriptor. In our example, since we just closed file descriptors 0
and 1, we know that the first thing we duplicate will go into 0 and the next into 1.
However, this is a little risky (imagine that you really wanted to duplicate
something into fd 6 but didn’t know whether or not fds 0 through 5 existed).
To get around this, there is another function called dup2 that takes two file
descriptors as its arguments. You can read about dup2 in the following reading.

Copyright © Open University Malaysia (OUM)


30  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

The definitions of the dup and dup2 system call are introduced in this
reading.

Stevens, W R (1994) UNIX® Network Programming, p. 41.

ACTIVITY 3.2

Complete the program code given in the above paragraph for the dup
function, given that the input file contains the following five lines:
 This is a boy.
 What is the time?
 123
 Abc
 023
What is the output of the result?

SELF-TEST 3.2

What kind of information in the original file descriptor fildes is shared


by the new file descriptor?

Unnamed pipes
It is well known that users in the UNIX platform can pipe the result of a
program to the input of another. The syntax is: 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. A simple example of this compound command
is: who | grep root. The first program who will show all users who have
logged into the system. Instead of having the result of the command who sent to
the standard output stdout, the output is redirected as the standard input of the
grep command.

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  31

The effect of the compound command ‘who | grep root’ can be similar
to the following:
root console Tue 8pm 4days 4:39 2:47 xlock

root pts/1 Tue 8pm 23:35 /bin/csh

The UNIX shell does the steps shown above to create two processes with a pipe
between them. This corresponds to the fact that the standard output (stdout) of
the first process is the standard input (stdin) of the second process. Its
mechanism can be shown as in Figure 3.5.

Figure 3.5: Pipe in a single process


[Source: Stevens (1994 fig. 3.4, p. 103) UNIX® Network Programming
by W R Stevens. © 1994. Simon and Schuster (Asia) Pte Ltd.]

Pipes provide a one-way flow of data from one process to another. They are a
form of descriptor that has been used in UNIX. The processes communicating
over a pipe must be related; typically, they are parent and child. A disadvantage of
using a pipe is that it may be too slow: Data have to be transferred from the
writing process to the kernel and back again to the reader, although no I/O is
performed.

The interprocess channel of a pipe is created by the pipe() system call.


#include <unistd.h>
int pipe(int filedes[2]);

pipe() creates an I/O mechanism called a pipe and returns two file descriptors.
The two descriptors are not equivalent. The descriptor whose index is returned in

Copyright © Open University Malaysia (OUM)


32  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

the low word of the array is filedes[0] which is open for reading, whereas the
filedes[1] in the high end is open only for writing. A pipe is a stream
communication mechanism; that is, all messages sent through the pipe are placed
in order and reliably delivered. When the read process asks for a certain number
of bytes from this stream, the process will receive as many bytes as are available
in the pipe, up to the amount of the request. For writing data to a particular pipe,
data may have come from a single system call write() or from multiple calls to
write() which have been concatenated.

The simple example shown below illustrates the generation, writing and reading
from a pipe.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

int main()
{
int filedes[2];
char buf[30];

if (pipe(filedes) == -1) {
perror("pipe");
exit(1);
}

printf("writing to file descriptor #%d\n", filedes[1]);


write(filedes[1], "test", 5);
printf("reading from file descriptor #%d\n", filedes[0]);
read(filedes[0], buf, 5);
printf("read \"%s\"\n", buf);
}

Figure 3.6: A single pipe program

The output of the above program should look like:


writing to file descriptor 5
reading from file descriptor 6
read "test"

As you can see, pipe() takes an array of two integers as an argument. Assuming
no errors, it connects two file descriptors and returns them in the array. The first
element of the array is the reading end of the pipe; the second is the writing end.
The output of this program will send three statements to the terminal:
 "writing to file descriptor #4"

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  33

 "reading from file descriptor #3"


 "read "test""

The file descriptors of the pipe are 3 and 4 respectively.

Pipes are typically used to communicate between two related processes in the
following way. Let us now examine a program that creates a pipe for
communication between the parent and its child process. First, a process creates a
pipe and then forks to create a copy of itself, as shown in Figure 3.7. The
parent’s descriptor table is copied into the child’s table.

Figure 3.7: Pipe in a single process, immediately after fork


[Source: Stevens (1994 fig. 3.5, p. 103) UNIX® Network Programming by W R Stevens
© 1994 Simon and Schuster (Asia) Pte Ltd. Reproduced by permission of the publisher.]

A parent process makes a call to the system routine pipe. The returns of the two
descriptors filedes are used as the two ends of the pipe in the process’s
descriptor table. After the fork, both the parent’s and the child’s descriptor table
points to the pipe. The child can then use the pipe to send a message to the parent.
Next, the parent process closes the read end of the pipe and the child process
closes the write end of the pipe. This provides a one-way flow of data between the
two processes as shown in Figure 3.8.

Copyright © Open University Malaysia (OUM)


34  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

Figure 3.8: Pipe between two processes


[Source: Stevens (1994, fig. 3.6, p.104) UNIX® Network Programming by W R Stevens
© 1994 Simon and Schuster (Asia) Pte Ltd. Reproduced by permission of the publisher.]

The corresponding program to implement Figures 3.7 and 3.8 is shown below in
Figure 3.9.

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  35

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

#define DATA "This is the first message for the PIPE program" main()
{
int childpid, pipe1[2];
char buf[1024];

if (pipe(pipe1) < 0)
perror("can’t create pipes");

if ((childpid = fork()) < 0) {


perror("can’t fork");
} else if (childpid > 0) {
/* This is still the parent */
close(pipe1[1]);

if(read(pipe1[0], buf, 1024)<0)


perror("reading process");
printf("Output : %s\n", buf);
close(pipe1[0]);
} else {
/* This is the child */
close(pipe1[0]);

if(write(pipe1[1], DATA, sizeof(DATA))<0)


perror("Writing process");

close(pipe1[1]);
}
}

Figure 3.9 A single pipe communication

The output of this program will look like:


Output : This is the first message for the PIPE program

Note that all the pipes shown so far have been one-way communication
mechanisms. In order to provide two-way communication between the parent and
child processes, two pairs of pipe descriptors have to be created for these
processes. One of the descriptor pairs is used for the parent to send data to child
and the other pair is used for the child to the parent. The program structure of a
two-way communication can be constructed in a similar way to the pseudo code
shown in Figure 3.10.

Copyright © Open University Malaysia (OUM)


36  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

/* To create two pairs of descriptors */


pipe(pipe1);
pipe(pipe2);

/* Check the create of the pipes success or not */


......

/* To create the Parent and Child process */


fork()

/* For the parent process */


/* To close a one-way communication from child to parent */
close(pipe1[0]);
close(pipe2[1]);
/* Wait the child return and execute the rest of the parent process
if necessary; */
.........

/* After the finish of the communication, close a one-way


communication from parent to child */
close(pipe1[1]);
close(pipe2[0]);

/* For the child process */


/* To close a one-way communication from parent to child */
close(pipe1[1]);
close(pipe2[0]);

/* Child Process */
......

/* To close a one-way communication from child to parent */


close(pipe1[0]);
close(pipe2[1]);

Figure 3.10: A pseudo code of two-pipe communication between child and parent
process

A detailed description of the pipe technique is presented in this reading. You


will see examples of the pipe communication in difference processes.

Stevens, W R (1994) UNIX® Network Programming, pp. 102–6.

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  37

The following online guide describes the pipe and fork system calls, giving
many suitable examples and answers. You can duplicate a copy of the given
program code and try to compile the program yourself to reinforce your
understanding of the piping concept. This Web page also provides a sufficient
help manual for applicable system functions such as fork(), dup(),
exec(), pipe(), and so on. If you find understanding the programming
syntax or logic difficult, you should refer to the corresponding help manual
provided in the URL.

Hall, B (1997) ‘Pipes’ in Beej’s Guide to Unix Interprocess Communication,


Version 0.9.3: http://www.ecst.csuchico.edu/~beej/guide/ipc/pipes.html (14
May 1997, cited 8 July 1998).

Optional
The following Web page provides a good description of the IPC pipe
mechanism in Linux OS.
Rusling, D (1996–98) ‘Interprocess communication mechanisms: pipes’ in
The Linux Kernel, pp. 2–4: http://www.eee.hku.hk/LDP/LDP/tlk/ipc/ipc.html

ACTIVITY 3.3

Go the following website:


http://users.actcom.co.il/~choo/lupg/tutorials/multi-process/multi-
process.html#pipe
Try the example of a two-process system in which one (the parent
process) reads input from the user and sends it to the other (the
child), which then prints the data to the screen. Sending the data is
done using the pipe, and the protocol simply states that every byte
passed via the pipe represents a single character typed by the user.
The complete c source code can be found in the following website:
http://users.actcom.co.il/~choo/lupg/tutorials/multi-process/one-
way-pipe.

Copyright © Open University Malaysia (OUM)


38  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

NAMED PIPES
UNIX provides for a second type of pipe called a named pipe or FIFO (we use the
terms interchangeably). FIFO stands for ‘first in, first out’. A UNIX FIFO is
similar to a pipe. It is a one-way flow of data, and the first byte written to it is the
first byte read from it. Unlike pipes, however, a FIFO has a name associated with
it, allowing unrelated processes to access a single FIFO. Indeed, FIFOs are also
called named pipes. A named pipe looks and acts like a file. It has a file name,
with permissions and a path, and you can open it for reading or writing.

To create a named pipe interactively, use either mknod() or mkfifo(). On


some systems, a UNIX command mknod will be found in the directory of /etc.
The use of the mknod command is described in UNIX help manuals.

The protocol to declare the system call mkfifo is:


#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(char *pathname, int mode);

The pathname is a normal UNIX pathname, and this is the name of the FIFO. The
argument specifies the file type and access mode (read and write permissions
for the owner, group and other users). The file type is specified in the mode,
which must be set to one of the values shown below in Table 3.3.

Table 3.3: File type for mknod

Mode symbol File type

S_IFIFO FIFO special

S_IFCHR character special

S_IFDIR directory

S_IFBLK block special

S_IFREG ordinary file

The following example shows how to make a named pipe within a C program
using mkfifo .

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  39

if (mkfifo ("testfifo", S_IFIFO | 0644) != 0)


printf ("Could not make this fifo\n");
else
printf ("FIFO was successfully made!\n");

Figure 3.11: Generation of a FIFO with mkfifo call

In this example, we created testfifo in the current directory and gave


permission for everyone to read it, although only the owner can write and change
it. The second argument is logically OR with the S_IFIFO flag from
<sys/stat.h> to specify that a FIFO is being created. The mode value 0644
sets access permissions of that file to be rw-r--r-- ; that is, everyone can read
the FIFO but only the owner for FIFO is allowed to write data to it. This
permission is the same as the one you would set using the chmod UNIX
command.

If you don’t have mkfifo() , you’ll have to use mknod(). mknod() is able to
make a directory, or a special or ordinary file, by the path name pointed to by path
argument of mknod(). You can also initialize the file type and permission of the
created file. This call is normally reserved to the superuser to create new device
entries, but any user can create a FIFO.
#include <sys/types.h>
#include <sys/stat.h>

int mknod(char *pathname, int mode, int dev);

To make a named pipe within a C program, use mknod() as shown in Figure


3.12.

if (mknod("test_fifo", S_IFIFO | 0755, 0))


printf ("Could not make this fifo\n");
else
printf ("FIFO was successfully made!\n");

Figure 3.12: Generation of FIFO with mknod

Note the dev value is ignored for a FIFO.

Once a FIFO is created, it must be opened for reading or writing, using either the
open () system call or one of the standard I/O open functions. Note that it takes
three system calls to create a FIFO and open it for reading and writing
(mknod(), open(), and write()) whereas the single pipe() system call
does the same thing.

Copyright © Open University Malaysia (OUM)


40  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

A pseudo program sequence with mknod() to create a FIFO is shown below in


Figure 3.13. The child process will have an IPC with its parent; that is a bi-
directional communication. Two FIFOs were created in order to establish a
communication channel from parent process to child one and is from child to
parent.

#define FIFO1 "/tmp/fifo.1"


#define FIFO2 "/tmp/fifo.2"
#define PERMS 0666 /* permission -rw-rw-rw- */

main()
{
int childpid, readfd, writefd;

/* To create the named pipes for communication */


mknod(FIFO1, S_IFIFO | PERMS, 0);
mknod(FIFO2, S_IFIFO | PERMS, 0);

/* To spawn a new process */


fork();

/* For parent process */


writefd = open(FIFO1, 1); /* Open FIFO1 for writing data*/
readfd = open(FIFO2, 0); /* Open FIFO2 for reading data*/
. . . /* Parent process routine */

/* close all opened file descriptor and unlink all FIFOs */


. . .

/* For child process */


readfd = open(FIFO1, 0)
writefd = open(FIFO2, 1)
. . . /* Child process routine */

/* Close all opened file descriptors; */


. . .
}

Figure 3.13: Pseudo code of bi-directional communication between two processes

In the above program, we first call mknod() to create the FIFOs, realizing that
they might already exist. After the fork , both processes must open each of the
two FIFOs as desired. The parent process removes the FIFOs with the unlink()
system call, after waiting for the child to terminate.

The order of the open() calls is important and avoids a deadlock condition.
When the parent opens FIFO1 for writing, it will block the writing. This
implies it waits until the child opens it for reading. If the first call to open() in
the child were for FIFO2 instead of FIFO1 , then the child would wait for the

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  41

parent to open FIFO2 for writing. Each process would be waiting for the other
and neither would proceed. This is called a deadlock.

There is a flag in the system call open which can specify the opening operation
not to block. The way to do this is to call open with the O_NDELAY flag set in
the mode argument:
readfd = open(FIFONAME, O_RDONLY|O_NDELAY)

This will cause open to return –1 if there are no processes that have the file open
for reading.

Note that when we used pipes to implement this example, the client and server
had to originate from the same process, since pipes cannot be shared between
unrelated processes. With FIFOs, however, we do not have this restriction.

FIFO is able to support communication between unrelated processes, but the later
is not. Hence, it gives us greater flexibility with interprocess communication.
However, this creates another problem for using the FIFO technique in
programming. In the simplest situation of one process Proc1 writing to a FIFO
and another one Proc2 reading from the FIFO, everyone is happy with the
connection. However, imagine that a new process Proc3 would like to use the
existing FIFO to talk to Proc2. This creates a problem if Proc2 is not able to
sort the data from the two writers.

One remedy is to define the same amount of data every time (let’s say, 1024
bytes). Then Proc2 reads 1024 bytes at a time, and Proc1 and Proc3 also
write the 1024 bytes data unit. This approach can solve part of the problem. That
is, every time Proc2 reads from the FIFO, it is guaranteed that the whole block
of data is either from Proc1 or Proc3. However, there is no way to identify
which writer sent which packet. A more practical approach is to attach a header
on every piece of data sent from the writers. A unique identifier should be
included as an identity of the data unit, which can let the reader determine which
writer sent the packet. By writing a data unit as a structure such as:
typedef structure {
short identifier; /* To specify the corresponding
writer*/
char data_unit[1024]; /* Actual data */
}

the structure design for process communication is more intelligent. In fact, there is
another IPC technique called ‘message queues’ which applies this approach, as is
explained further on.

Copyright © Open University Malaysia (OUM)


42  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

The concept of the FIFO mechanism is explained in this reading. It also


provides a simple client-server example using FIFOs.

Stevens, W R (1994) UNIX® Network Programming, pp. 110–14.

The following Web page describes the concept and technique of FIFOs. It
demonstrates how a new FIFO is created. It gives two programs that will send
data through a FIFO. You can duplicate a copy of the given programs code
and try to compile the programs yourself to reinforce your understanding of
the FIFO concept.

Hall, B (1997) ‘FIFOs’ in Beej’s Guide to Unix Interprocess Communication,


Version 0.9.3: http://www.ecst.csuchico.edu/~beej/guide/ipc/fifos.html (14
May 1997, cited 8 July 1998).

Copyright © Open University Malaysia (OUM)


TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES  43

ACTIVITY 3.4

1. In this activity, you are required to create a FIFO by using the UNIX
command mknod. Follow the instructions shown below in Table 3.4
to learn how to send the output of a process to the input of another
process via FIFO. Explain the result of this activity.

Table 3.4: Creating and using a FIFO with mknod , more and who
Step Instruction Description
1 # mknod testfifo p This step is to create a FIFO with name
testfifo.testfifo
2 # more testfifo To show the content of testfifo. At this
stage there is no output on the screen. The
reason is that the content of testfifo is
emPte.
3 Open another window and
perform the following step in
that window in that window
4 # who > testfifo who is a UNIX command to show the
existing login users in the system. The
output of who is redirected to the FIFO
testfifo.

Note: # indicates the command prompt of the system.

2. Compile the speak.c and tick.c program given at the Reading 3.13
URL:
Hall, B (1997) ‘FIFOs’ in Beej’s Guide to Unix Interprocess
Communication, Version 0.9.3: http://www.ecst.csuchico.edu/~beej/
guide/ipc/fifos.html (14 May 1997, cited 8 July 1998).
Try to understand the logic of the program. Practice the speak and
tick programs. What is the output of these?

SELF-TEST 3.3

What is the biggest disadvantage in unnamed pipe communication


compared with FIFO?

Copyright © Open University Malaysia (OUM)


44  TOPIC 3 PRIMITIVE COMMUNICATION AND PIPES

This topic has focused the discussion on interprocess communication (IPC) to


help you in developing programs that consist of processes that use the program
codes presented in this topic and the previous topics. We start by discussing
primitive communication techniques. Although these techniques get the job done,
they also have certain limitations. Then, we have explored interprocess
communications techniques using designed interprocess facilities. First, we
introduce two system calls — dup and dup2 — used in pipes. Then we discuss
the pipes interprocess communications techniques — unnamed pipes and FIFOs.

Copyright © Open University Malaysia (OUM)


Topic 4  Message 
Queues and 
Semaphores 
LEARNING OUTCOMES
When you have completed this topic, you will be able to:
1. Describe how message queues works and write program codes to
illustrate the this concept
2. Describe the concept of semaphores and shared memory

 INTRODUCTION
This topic is divided into two: Message queues and semaphores.

The examples shown so far in the previous topics for unnamed pipes and FIFOs
have used the stream I/O model. There are no message boundaries, and reads
and writes do not examine the data at all. This type of data is a stream of bytes
with no interpretation by the system. This may cause problems, particularly in
multiple writers and readers with the same named pipe. To solve this, we can use
message queue. A message queue is like a pipe and is used to transfer messages
between processes in a UNIX system.

Semaphores, on the other hand, are not used to exchange data between processes;
instead they are used to synchronize two or more processes. They prevent two
processes from simultaneously accessing a shared resource.

MESSAGE QUEUES
The examples shown so far for unnamed pipes and FIFOs have used the stream
I/O model. There are no message boundaries, and reads and writes do not
examine the data at all. This type of data is a stream of bytes with no
interpretation by the system. This may cause problems, particularly in multiple
writers and readers with the same named pipe.

Copyright © Open University Malaysia (OUM)


46  TOPIC 4 MESSAGE QUEUES AND SEMAPHORES

A message queue is like a pipe and is used to transfer messages between processes
in a UNIX system. Unlike a pipe and FIFO, however, it retains message
boundaries and hence is a much more flexible way for many processes to use the
same IPC. A message is typically a small amount of data (~500 bytes) that is sent
to a message queue; any process with appropriate permissions can receive
messages from a queue.

System V allows a process to pass messages to any other active process in the
system. In the System V implementation of message queues, all messages are
stored in the kernel and have an associated message queue identifier or message
descriptor. This identifier identifies a particular queue of messages. Processes can
read or write messages to arbitrary queues, each queue identified uniquely via its
message queue identifier. Unlike other IPC mechanisms such as pipes or FIFOs,
where it makes no sense to have a writer process unless a reader process exists as
well, it is possible for a writer process to write a message to a queue, exit and
have a reader process retrieve the message at some later time.

Every message on a queue should be a structure that has, at the very least, two
fields:
 a long integer (used to multiplex messages in a single queue); and
 a buffer to hold the message text (the text can contain binary or ASCI data).
typedef struct {
long mtype /* message type */
char message[MSG_BUF]; /* The actual message */
} Message;

A new message is created, or an existing message queue is accessed via the


msgget() system call. The call msgget() returns a message queue identifier,
msqid, that defines a message queue.
int msgget(key_t key, int msgflag);

The key argument is a system-wide unique identifier describing the queue you
want to connect to (or create). Every other process that wants to connect to this
queue will have to use the same key. The msgflag value is a combination of
the constants used to set the access right of the message queue. To create a new
queue, this field must be set equal to IPC_CREAT bit-wise OR with the
permissions for this queue where OR is a logical operation. A simple example of
msgget is shown below:
key = ftok("/tmp/testing", "abc"); /* To define a key with
project ‘abc’*/
msqid = msgget(key, 0666 | IPC_CREAT); /*To create a new
message queue */

Copyright © Open University Malaysia (OUM)


TOPIC 4 MESSAGE QUEUES AND SEMAPHORES  47

The permission flag is set as 0666. It stands for -rw-rw-rw- for the queue,
meaning everyone is allowed to read or write to the queue. The IPC_CREAT will
cause the message queue to be created if it does not already exist. And now we
have msqid, which will be used to send and receive messages from the queue.

Once a message queue is opened, a writer inserts data in the queue using a
msgsnd() system call.
int msgsnd(int msqid, Message *ptr, int length,
int flag);

The length argument to msgsnd specifies the length of the message in bytes. The
flag argument can be specified as either IPC_NOWAIT or as zero. The
IPC_NOWAIT value allows the system call to return immediately if there is no
room on the message queue for the new message.

Alternatively, a reader reads data from the queue using a msgrcv() system call.
int msgrcv(int msqid, Message *ptr, int length,
long msgtype, int flag);

The ptr argument is like the one for msgsnd() and specifies where the received
message is stored. The third argument is length, which specifies the size of the
data portion of the structure pointed to by ptr. This is maximum amount of data
that is returned by the system call. If the MSG_NOERROR bit in the flag argument
is set, this specifies that if the actual data portion of the received message is
greater than length, just to truncate the data portion and return without an error.
Not specifying the MSG_NOERROR flag causes an error return if length is not
large enough to receive the entire message.

The long integer msgtype argument specifies which message in the queue is
desired.
 If msgtype is zero, the first message on the queue is returned. Since each message
queue is maintained as a FIFO list, a msgtype of zero specifies that the oldest
message on the queue is to be returned.
 If msgtype is greater than zero, the first message with a type equal to msgtype is
returned.
 If msgtype is less than zero, the first message with the lowest type that is less than
or equal to the absolute value of msgtype is returned.

The call msgctl() includes options to set and return parameters associated with
a message queue identifier and remove identifiers (with cmd = IPC_RMID ).
int msgctl(int msqid, int cmd, struct msqid_ds *buff);

Copyright © Open University Malaysia (OUM)


48  TOPIC 4 MESSAGE QUEUES AND SEMAPHORES

The following program sections demonstrate the use of message queues. A server
runs first and creates a message queue. It also populates the message queue with
some text. A client can be run later; on invocation, it opens the message queue,
reads the data left for it by the server, deletes the message queue and exits.

Figure 4.1 shows a header file for client/server message program (msg.h). Figure
4.2 and Figure 4.3, respectively, show a message server program and a message
client program.

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <memory.h>
#include <sys/errno.h>

extern int errno;

#define Q_KEY 1234L /* Message queue identifier */


#define Q_PERMISSION 0666 /* Pubic queue: all can read/write */

/* message structure */
#define MSG_BUF 512
typedef struct _Message {
long mtype;
char message[MSG_BUF];
} Message;

Figure 4.1: msg.h header file of a message process

Copyright © Open University Malaysia (OUM)


TOPIC 4 MESSAGE QUEUES AND SEMAPHORES  49

#include "msg.h"

int main(int argc, char * argv[])


{
int writeld;
Message msg;

/* step 1: create the queue */


writeld = msgget(Q_KEY, Q_PERMISSION | IPC_CREAT);
if (writeld == -1) {
fprintf(stderr, "%s: error creating message queue\n", *argv);
perror("System error");
return 1;
}

/* step 2: Prepare the message to be put in the queue */


memset((void *) &msg, 0, sizeof(msg));
msg.mtype = 11;
strcpy(msg.message, "Hello World!!");

/* step 3: now put the message in the queue */


if (msgsnd(writeld, (const void *) &msg, MSG_BUF, 0) != 0) {
fprintf(stderr, “%s: error writing to message queue\n”, *argv);
perror(“System error”);
return 1;
}

return 0;
}

Figure 4.2: Message server program

Copyright © Open University Malaysia (OUM)


50  TOPIC 4 MESSAGE QUEUES AND SEMAPHORES

#include "msg.h"

int main(int argc, char*argv[])


{
int readld, bytesRead;
Message msg;

/* step 1: open an existing message queue */


readld = msgget(Q_KEY, 0);
if (readld == -1) {
fprintf(stderr, “%s: error opening message queue\n”, *argv);
perror(“System error”);
return 1;
}

/* step 2: get ready to read in the message */


memset((void *) &msg, 0, sizeof(msg));
bytesRead = msgrcv(readld, (void *) &msg, MSG_BUF, 0L, 0);
if (bytesRead != MSG_BUF) {
fprintf(stderr,"%s: number of bytes read (%d)", *argv, bytesRead);
fprintf(stderr, " do not equal number written (%d)\n", MSG_BUF);
return 1;
}
printf(“Messag type = %ld\n”, msg.mtype);
printf(“Message = %s\n”, msg.message);

/* step 3: delete the message queue */


if (msgctl(readld, IPC_RMID, (struct msqid_ds *) NULL)) < 0) {
fprintf(stderr, “%s: cannot delete message queue\n”, *argv);
perror(“System error”);
return 1;
}

return 0;
}

Figure 4.3: Message client program

After a message queue is created it will exist in the system between program calls.
If you do not use msgctl() , you could leave the message queue hanging
around. There is a UNIX command to find this out. The command is called ipcs.
An example of ipcs is shown as below.
# ipcs
IPC status from <running system> as of Thu Mar 5 19:35:42 1998
T ID KEY MODE OWNER GROUP
Message Queues:
q 50 0x4200766d -Rrw-rw-rw- pkser mygroup
Shared Memory facility not in system.
Semaphore facility not in system.

Copyright © Open University Malaysia (OUM)


TOPIC 4 MESSAGE QUEUES AND SEMAPHORES  51

This tells us that there is one message queue owned by the user named ‘pkser’; its
identifier is 50 and it was created with 0666, which gives read/write privileges to
everyone. Any number of pkser’s processes could access this message queue.

To delete a message queue from UNIX, use ipcrm:


# ipcrm -q 50

The -q 50 means delete a message queue whose ID is 50.

Occasionally you may need to do a non-blocking send or a non-blocking receive.


For example, if you have a central program, like a database server,
communicating with multiple other programs through message queues, it needs to
check on one incoming queue to see if anything was sent to it there and attend to
it if there was. If there was no data, it cannot just hang up on that one line but
instead must move on to the next. The last parameter of msgsnd() and
msgrcv() is the blocking status. For receivers, 0 means block or ‘put me to
sleep until a message has arrived’. For senders, it means ‘if the queue is full then
wait until there is room’.

The implementation of the message queues is like a link list. You can realize
the mechanism of kernel to store the message queue identifier to the system.
This reading shows the message queues implementation and demonstrates the
message queue structures in kernel. Finally, it discusses the use of arguments
in msgsnd system call.

Stevens, W R (1994) UNIX® Network Programming, pp. 126–31.

Copyright © Open University Malaysia (OUM)


52  TOPIC 4 MESSAGE QUEUES AND SEMAPHORES

This Web page clearly explains the concept and technique of the message
queue. It shows how to connect to a queue and send/receives messages.
Finally, it describes how to destroy a message queue to save system resources.
This Web page gives two programs that will communicate using message
queues. You can duplicate a copy of the given programs code and try to
compile the programs yourself to reinforce your understanding of the message
queue concept.

Hall, B (1997) ‘Message queues’ in Beej’s Guide to Unix Interprocess


Communication, Version 0.9.3: http:// www.ecst.csuchico.edu/~beej/guide/ipc
/ mq.html (14 May 1997, cited 8 July 1998).

ACTIVITY 4.1
Compile the kirk.c and spock.c provided at the Reading 4.15 URL:
Hall, B (1997) ‘Message queues’ in Beej’s Guide to Unix Interprocess
Communication, Version 0.9.3: http://www.ecst.csuchico.edu/~beej/
guide/ipc/mq.html (14 May 1997, cited 8 July 1998).

Run the programs according to the following steps:


 Run kirk program.
 Record the corresponding message queue information. (Which
command should you use to view the information?)
 Enter lines of text.
 Open a new window and run the spock program. (What is the
output?)

SELF-TEST 4.1

What is the difference in message queuing communication between the


previous unnamed pipes and FIFOs?

Copyright © Open University Malaysia (OUM)


TOPIC 4 MESSAGE QUEUES AND SEMAPHORES  53

SEMAPHORES AND SHARED MEMORY


Semaphores are not used to exchange data between processes; instead they are
used to synchronize two or more processes. They prevent two processes from
simultaneously accessing a shared resource. Consider a semaphore as an integer
value variable that is a resource counter. The value of the semaphore is a number
to indicate whether the resources are available or not. A semaphore has only two
values: 0 and 1. Since the use of semaphores is to provide shared resources
synchronization, the semaphore value must be stored in the kernel as shown in
Figure 4.4.

Figure 4.4: Semaphore value stored in kernel


[Source: Stevens (1994 fig. 3.20, p.138) UNIX® Network Programming by W R Stevens
© 1994 Simon and Schuster (Asia) Pte Ltd. Reproduced by permission of the publisher.]

The use of the shared memory is usually accompanied by the semaphore. The
fastest way of moving data between processes is not moving the data at all —
both can share some memory. When data are written to a shared memory
segment, they are immediately available to the other process. The only trick in
using shared memory is synchronizing access to a given region among multiple
processes. If one process is placing data into the shared memory, the other
processes should not access the corresponding data in the memory.

Just as in other IPC techniques, a user identifies the shared memory by a system
identifier. A shared memory identifier (shmid) is a unique positive integer
created by a shmget system call. Each shmid has a segment of memory
(referred to as a shared memory segment) and a data structure associated with it.
Details of the concepts of semaphore and shared memory are not covered in this
course. If you are interested in knowing more about these topics, please refer to
the following readings.

Copyright © Open University Malaysia (OUM)


54  TOPIC 4 MESSAGE QUEUES AND SEMAPHORES

This reading introduces a detailed description of topics in kernel data


structures for a semaphore set and file locking with semaphores are introduced
in the reading.

Stevens, W R (1994) UNIX® Network Programming, pp. 137–43.

Optional
A visit to the following Web page is highly recommended. The content is
clear and easy to understand, and the author, Brian Hall, is familiar with the
differences between the semctl function on the Linux OS and HPUX (HP
UNIX) OS.

Hall, B (1997) ‘Semaphores’ in Beej’s Guide to Unix Interprocess


Communication, Version 0.9.3: http://www.ecst.csuchico.edu/~beej/guide
/ipc/semaphores.html

(14 May 1997, cited 8 July 1998).

Theoretically, shared memory is the fastest form of communication between


processes. In this reading, an example of client and server communication is
explained to illustrate the feature of shared memory.

Stevens, W R (1994) UNIX® Network Programming, pp. 153–60.

Copyright © Open University Malaysia (OUM)


TOPIC 4 MESSAGE QUEUES AND SEMAPHORES  55

For the establishment of communication between processes, we have introduced


different techniques that can be used to develop interprocess communication
applications in Topic 1 – Topic 4. IPC is a traditional network programming
technique. Various IPC mechanisms for network programming such as signals,
unnamed pipes, FIFOs, messages, semaphores and shared memory have been
covered. The pipe is the simplest method in IPC programming. However, only
processes with a child and parent relationship can apply the pipe techniques. FIFO
and message techniques are able to overcome the limitations of pipe ones. The
FIFO is able to provide communication between any two processes in a system.
For a communication situation involving more than two processes, FIFO is not
able to identify the corresponding data for each process. Messages are suitable for
communication with multiple processes. By using the message type defined by
each process, the receiver is able to identify the data from each process. Hence, a
process can use this technique to communicate with multiple processes
simultaneously.

The next topic discusses the Socket, a programming technique for building
network application.

Copyright © Open University Malaysia (OUM)


Topic 5  Berkeley 
Sockets 
LEARNING OUTCOMES
When you have completed this topic, you will be able to:
1 Discuss the concept of sockets.
2 Demonstrate an understanding of the mechanisms of Berkeley sockets.
3 Develop simple network applications using sockets.

 INTRODUCTION
Networking is essential in today’s computer environments. The benefits of
networking are numerous. A user in a local host can easily access shared
resources provided by a distant host. In order to accomplish this, the processes in
the local host and in the remote host have to establish communication. The
process in the local host is considered as the client requests certain services from
the remote host. In this situation, the remote host is called the server, which is
regarded as the services provider to the client.

Client/server applications refer to communication between two distinct entities.


Typically, these entities reside on separate systems. For example, when you use
the telnet command to connect to a remote system, you are actually invoking
the telnet client application on the system on which the command was
executed. The telnet client application, through the inetd process, establishes
a connection with the telnet server application. Common mechanisms that are
used to develop client/server applications include:
 Berkeley sockets and
 Remote procedure calls (RPCs).

As UNIX systems proliferate the commercial marketplace, there is an even greater


need for distributed applications that take advantage of resources and information
now available on the network. A distributed application may involve designing a
client process that runs on the local system and uses the services provided by a
Copyright © Open University Malaysia (OUM)
TOPIC 5 BERKELEY SOCKETS  57

server process that may be running on a remote system. The main advantage of a
distributed software model is that, even in a heterogeneous environment (which
means concerned systems involving different types of hardware and software),
each part of the program can be developed independently of the others, thus
taking advantage of special-purpose hardware and software. Besides the telnet
application, many applications such as File Transfer Protocol (ftp), electronic
mail, Web server, and clients etc. can be developed by socket programming.

TCP/IP-based distributed applications may be developed on the basis of Berkeley


sockets or AT&T’s System V Transport Layer Interface (TLI). Both Berkeley
sockets and AT&T System V TLI are application program interfaces (API) to
underlying network protocols such as TCP, UDP, or IP.

Distributed applications, in general, are constructed to use the services available


from network protocols such as TCP, UDP, and IP. These services may be
grouped into two categories: connection-oriented services (TCP) and
connectionless services (UDP). Connection-oriented services provide reliable,
sequenced data delivery, whereas connectionless services are unreliable but
considerably faster. Thus, performance and data integrity may determine how
your application is developed.

We focus on the Berkeley socket in this topic. The implementation of sockets


routines is thoroughly explained. The concept of RPC techniques and simple
examples is introduced in the next topic.

What is the difference between IPC and socket & RPC?


Do you remember the interprocess communication (IPC) techniques you
learned in Topic 1 – Topic 4? How IPC is different from socket and RPC
techniques? Those IPC techniques really enhance the flexibility of
programming by allowing different processes to communicate. However, there
is a great limitation — the processes must reside on the same system. For
programming in the networking environment, it is a common design to have
processes running on different systems to communicate with each other and
therefore, those IPC techniques are not applicable.

WHY PROGRAMMING WITH SOCKETS?


To overcome this limitation, socket-programming techniques that enable the
communication between processes on different systems may be used. A process
may communicate with another process by creating sockets and sending messages
between them. You learn the details of the creation of sockets and how messages
are transferred among them in the later sections of this topic.
Copyright © Open University Malaysia (OUM)
58  TOPIC 5 BERKELEY SOCKETS

Certainly, you can design processes running on the same system to communicate
via sockets. Does this mean the socket programming techniques can totally
replace those IPC techniques? Indeed, you may consider the functionality of IPC
to be a subset of socket programming. However, you should also bear in mind that
IPC and sockets are totally different mechanisms and each has its own pros and
cons. The implementation of sockets is much more complicated than that of IPC,
and so it is less efficient than IPC in providing a communication channel between
processes. If two processes are residing on the same system, IPC is preferred over
sockets to provide the communication channel.

It is common for a client/server application that the server process has to serve
client requests from both the remote and local systems. With sockets, all client
requests can be handled. However, this may not be an optimal design, as sockets
also handle the client requests from the same system. A better design may be
implementing both sockets and IPC on the server process — that is use sockets to
handle client requests from remote systems while using IPC to handle the requests
from the local system.

BASIC PROPERTIES OF A SOCKET


A socket is an endpoint of a communication channel. In order to allow other
processes to reach it, a socket can be assigned a name. This task is called binding
a socket. Each socket in use has a type and one or more associated processes.
There are various types of socket; stream socket and datagram socket are typical
examples. Different socket types may be different in the kind of communication
they support and the way the address space of the sockets is defined. Sockets exist
within communication domains (or address families). A communication domain is
an abstraction introduced to bundle common properties of processes
communicating through sockets. One such property is the scheme that is used to
name sockets. For example, in the UNIX communication domain, sockets are
given UNIX path names; e.g. a socket may be named ‘/dev/foo’. Connection-
oriented and connectionless are common network connection modes. The
mechanism for socket communication is different in different connection modes.

Figure 5.1 below shows two applications using sockets. The differences between
sockets and normal file descriptors occur in the creation of a socket and through a
variety of special operations to control a socket. These operations are different
between sockets and normal file descriptors because of the additional complexity
in establishing network connections compared with normal disk access.

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  59

Figure 5.1: Sockets

From a programmer’s point of view, a socket looks and behaves much like a file
descriptor, because commands such as read and write work with sockets in the
same way they do with files and pipes. As far as your application is concerned,
the socket is the end of the channel. When you create the socket, the socket
routines return a file descriptor used when accessing the endpoint.

The following reading provides a brief introduction of the concept of sockets. It


describes the process communications via sockets in the client-server model. It
also presents the three attributes (i.e. domain, type and protocol) in various kinds
of socket.

Glass, G (1993) UNIX Programmers and Users — A Complete Guide,


Englewood Cliffs, NJ: Prentice Hall, pp. 449–51.

The next reading introduces the Berkeley socket interface. This may be quite
helpful to you in the elementary stage. It compares network I/O to file I/O. It also
shows some of the steps required to use sockets and compares it with other
techniques such as message queues and FIFOs, which you learned in Topic 4.
You can see the complexity imposed by the networking routines compared with
message queues and FIFOs.

Copyright © Open University Malaysia (OUM)


60  TOPIC 5 BERKELEY SOCKETS

Stevens, W R (1990), Unix Network Programming, Englewood Cliffs, NJ:


Prentice Hall, pp. 255–60.

CONNECTION MODE OF SOCKET


COMMUNICATION
The first step in designing a socket application is to choose the modes of
communication. Using sockets over a channel provides two different modes of
communication:
 the connection-oriented mode and
 the connectionless mode.

An analogy of connection-oriented socket communication is a telephone call. You


have a telephone socket and a telephone number, which is assigned or bounded to
the socket. Whenever you want to call your friend, you have to dial his or her
phone number and then connect to his or her socket. Afterwards, the conversation
is transferred along the established connection. The established connection is only
dedicated for the conversation between you and your friend. A socket application
that is connection oriented is similar to this situation. Once the sockets between
the two processes are created on systems, they have to make a connection request
before sending data to each other.

Connectionless communication is different. An analogy of this communication


mode is the postal system. If you want to send a letter to your friend, you need to
stamp the letter (socket) and write the address (bind) of your friend on the
envelope before posting it. You don’t have to wake your friend up to establish the
connection before sending the letter, because no pre-established connection is
required and you don’t care about the route to deliver the letter. In the
connectionless socket applications, there is no requirement for the connection
establishment. The sender will send data without considering the status of the
receiver. An obvious advantage of using the connectionless socket is the
simplicity and the efficiency of programming. However, the connection-oriented
mode is definitely more reliable than the connectionless one. You may have to
consider this factor when choosing the mode for application development.

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  61

Connection-oriented mode
Figure 5.3 shows a time line of the typical scenario that takes place for a
connection-oriented transfer: First the server is started, and then sometime later a
client is started that connects to the server. A brief description of the mechanism
is explained below the figure.

Figure 5.2: Socket system calls for a connection-oriented protocol.

On the server
Create a socket. This action is like establishing a connection using a telephone.
The socket routine returns a socket descriptor.
bind a socket. This is the assignment of the phone number to the socket.
listen to the socket. This is similar to plugging the telephone into the socket.
accept the connection. This process waits for the request of the connection from
the client.
read, send, recv and write operation. Communication is started.

Copyright © Open University Malaysia (OUM)


62  TOPIC 5 BERKELEY SOCKETS

On the client
Create a socket. This is the client’s telephone.
bind a socket. This stage is optional for the client process, because the server
does not have to dial back to the client.
connect the server socket. The connection is established with this system call.
read, send, recv and write operation.

Connectionless mode
For a client-server using a connectionless protocol, the system calls are different.
Figure 5.3 shows these system calls.

Figure 5.3: Socket system calls for connectionless protocol.

The client does not establish a connection with the server, but the client side needs
to know the socket information of the server. The client then sends a datagram to
the server using the sendto system call, which requires the address of the
destination (the server) as a parameter. Similarly, the server does not have to
accept a connection from a client. Instead, the server just issues a recvfrom
system call that waits until data arrive from a client. The recvfrom returns the
network address of the client process, along with the datagram, so the server can
send its response to the correct process. Compared with the connection-oriented

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  63

communication, the mechanism of socket programming in connectionless


communication is relatively simple.

Socket types
Sockets are categorized according to the communication properties visible to a
user. Processes are presumed to communicate only between sockets of the same
type, although nothing prevents communication between sockets of different
types, should the underlying communication protocols support this. Four types of
socket are currently available to a user.
 A stream socket (SOCK_STREAM) provides for the bi-directional, reliable,
sequenced, and unduplicated flow of data without record boundaries. Aside
from the bi-directionality of data flow, a pair of connected stream sockets
provides an interface nearly identical to that of pipes. SOCK_STREAM type of
socket has the ability to queue incoming connection requests, which is a lot
like having ‘call waiting’ for your telephone. If you are busy handling a
connection, the connection request will wait until you can deal with it.
 A datagram socket (SOCK_DGRAM) supports bi-directional flow of data
which are not necessarily sequenced, reliable, or unduplicated. That is, a
process receiving messages on a datagram socket may find messages
duplicated and possibly in an order different from the order in which it was
sent. An important characteristic of a datagram socket is that record
boundaries in data are preserved. Datagram sockets closely model the
facilities found in many contemporary packet-switched networks such as the
Ethernet.
 A raw socket (SOCK_RAW) is a protocol under the transport layer. Raw
sockets are not intended for the general user; they have been provided mainly
for those interested in developing new communication protocols.
 A sequenced packet socket (SOCK_SEQPACKET) is similar to a stream socket
and maintains message boundaries. However, TCP/IP does not support
message boundaries; SOCK_SEQPACKET does not exist in TCP/IP.

Another potential socket type that has interesting properties is the reliably
delivered message socket. The reliably delivered message socket has similar
properties to those of a datagram socket but with reliable delivery. There is
currently no support for this type of socket, but a reliably delivered message
protocol similar to Xerox’s Packet Exchange Protocol (PEX) may be simulated at
the user level.

The most commonly used sockets are the first two mentioned above, so we deal
mostly with the SOCK_STREAM and SOCK_DGRAM in this topic.

Copyright © Open University Malaysia (OUM)


64  TOPIC 5 BERKELEY SOCKETS

Communication domain of a socket


‘Domains’ are the area where the communication process exists. The socket
facilities support four separate communication domains:
 the UNIX domain, for on-system communication;
 the Internet domain, which is used by processes that communicate using the
Internet standard communication protocols;
 the NS domain, which is used by processes which communicate using the
Xerox standard communication protocols; and
 the ISO OSI protocols.

The underlying communication facilities provided by these domains have a


significant influence on the internal system implementation as well as on the
interface to socket facilities available to a user. In this topic, we mainly
concentrate on the UNIX domain and the Internet domain.

The communication domain, or address family, to which a socket belongs,


specifies a certain address format. All later operations on a socket will interpret
the supplied address according to this specified format. The various address
formats are defined as manifest constants in the file <sys/socket.h>.
Examples are AF_Unix (UNIX path names), AF_INET (DARPA Internet
addresses). The general form of an address is represented by the sockaddr
structure defined in <sys/socket.h>.
struct sockaddr {
u_short sa_family; /* address family: AF_xxx
value */
char sa_data[14]; /* up to 14 bytes of protocol-
specific address */
};

sa_family can be a variety of things. It is AF_Unix in the UNIX domain and


AF_INET in the Internet domain. sa_data contains a destination address and
port number for the socket.

UNIX domain
In the UNIX domain, a socket is addressed by a UNIX path name that may be up
to 105 characters long. The binding of a path name to a socket results in the
allocation of an inode and an entry of the path name into the file system. This
necessitates removing the path name from the file system (using the unlink

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  65

system call) when the socket is closed. The created file is only used to provide a
name for the socket and does not play a role in the actual transfer of data. When
using sockets in the UNIX domain, it is advisable to only use path names for
directories (such as /tmp) directly mounted on the local disk. The UNIX domain
only allows interprocess communication for processes working on the same
machine. The structure sockaddr_un used to define the UNIX address format
can be found in <sys/un.h>.
struct sockaddr_un {
short sun_family; /* AF_Unix */
char sun_path[105]; /* pathname */
};

Internet domain
In the DARPA Internet domain, addresses consist of two parts — a host address
(consisting of a network number and a host number) and a port number. This host
address allows processes on different machines to communicate. The port number
in turn is like a mailbox that allows multiple addresses on the same host. The
structure sockaddr_in describing an address in the Internet domain is defined
in the file <netinet/in.h>.
struct in_addr {
u_long s_addr; /* 32-bit netid/hostid,
network byte ordered*/
};
struct sockaddr_in {
short sin_family; /* AF_INET */
u_short sin_port; /* 16-bit port number */
/* network byte ordered */
struct in_addr sin_addr; /* 32-bit netid/
hostid */
/* network byte ordered */
char sin_zero[5]; /* unused */
};

The declaration of the data type is useful in binding a name to a socket. The step
to bind a socket is introduced in a later section. The structure sockadd_in
defines what you need to specify a socket in system. For example, if you declare
my_addr with sockadd_in, the my_addr.sin_family will store the
domain. Meanwhile, the IP and port number of the socket to bind are saved in
my_addr.sin_port and my_addr.sin_addr.s_addr. A simple
initialization of the my_addr is given below:
my_addr.sin_family = AF_INET; /* host byte order */

Copyright © Open University Malaysia (OUM)


66  TOPIC 5 BERKELEY SOCKETS

my_addr.sin_port = htons(1555); /* short, network byte


order */
my_addr.sin_addr.s_addr = inet_addr("192.123.5.10");
bzero(&(my_addr.sin_zero), 5); /* zero the rest of
the struct*/

The above lines introduce two new system calls, htons and inet_addr. The
htons call is used to convert the port number 1555 into a number with the
format of network byte order. The use of inet_addr is a simple convert an IP
with ‘dot’ format to a 32-bit number with network byte order.

Byte ordering routines


Referring to the struct sockaddr_in, the type of sin_port is a 16-bit port number,
stored in network byte order. However, different systems may store integers in
different order formats, as shown in Figure 5.4. This requires the conversion of
data formats for integers in Intel 50x56, VAX, to that of network byte order,
which is big-endian format. Systems provide byte-ordering routines such as
htonl, htons, ntohl and ntohls to convert to/from host 16- and 32-bit
integers from/to ‘network byte order’. These functions are designed for the
Internet protocols as discussed above.

Figure 5.4: Byte ordering in different systems

Procedure name function


htonl converts 32-bit host value into network byte order
htons converts 16-bit host value into network byte order
ntohl converts 32-bit network byte order value into host order

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  67

ntohls converts 16-bit network byte order value into host order

SELF-TEST 5.1

1 Describe the steps to create a server application using the


Berkeley socket interface.
2 Which two socket types are the most commonly available to
users?

ELEMENTARY SOCKET SYSTEM CALLS


You have come across the major concepts of sockets. It is time to move on to the
details of socket programming. Various system calls are involved in the socket
communications. You may already have a rough idea from Figures 5.2 and 5.3.
We introduce the system calls in this section by giving a brief description,
showing their declarations, explaining their parameters and describing their use.

But just before going into those details, it may be helpful for your understanding
if you can get an overall picture of how the system calls are positioned in a typical
connection-oriented socket program. The following reading shows a complete
program listing of a simple client/server program with a detailed analysis of the
code. The program is called chef/cook and uses connection-oriented
communication. You may just want to browse through those descriptions for the
system calls, as they are described in detail in this section.

Glass, G (1993) UNIX Programmers and Users — A Complete Guide,


Englewood Cliffs, NJ: Prentice Hall, pp. 451–62.

To introduce the system calls required for socket communication, we start from
those on the server program. Typically, the server process has to execute the
following system calls before a client can connect to a server process:
 socket() — for creating a socket
 bind() — for assigning a name to the socket
 listen() — for specifying the maximum number of pending client
connections

Copyright © Open University Malaysia (OUM)


68  TOPIC 5 BERKELEY SOCKETS

 accept() — for accepting the connection requests from clients.

You should keep in mind that the sequence of executing these system calls is
important, as there are dependencies between them. You cannot assign a name to
a socket before its creation, right?

socket system call


The creation of a socket is uniquely determined by a <domain, type, protocol>
triple. To perform network I/O, the first thing a process must do is call the
socket system call, specifying the type of communication protocol desired.
#include <sys/types.h>
#include <sys/socket.h>

int socket (int family, int type, int protocol);

This call requests that the system create a socket in the specified family and of
the specified type. The family can be AF_Unix (UNIX internal protocols) or
AF_INET (Internet protocols). The AF_ prefix stands for address family.

The socket type is one of the following:


SOCK_STREAM stream socket
SOCK_DGRAM datagram socket
SOCK_RAW raw socket
SOCK_SEQPACKET sequenced packet socket.

Not all combinations of socket family and type are valid. The following list
shows the valid combinations, along with the actual protocol that is selected by
the pair.
AF_Unix AF_INET
SOCK_STREAM Yes TCP
SOCK_DGRAM Yes UDP
SOCK_RAW IP
SOCK_SEQPACKET

The boxes marked ‘Yes’ are valid but don’t have handy acronyms. The empty
boxes are not implemented.

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  69

A particular protocol may also be requested. If the protocol is left unspecified


(a value of 0), the system will select an appropriate protocol from those that
comprise the communication domain and that may be used to support the
requested socket type.

This call returns a small positive integer called a socket descriptor, which can be
used as a parameter to reference the socket in subsequent system calls. Socket
descriptors are similar to file descriptors returned by the open system call. Each
open or socket call will return the smallest unused integer. Thus, a given number
denotes either an open file or a socket. Socket and file descriptors may be used
interchangeably in many system calls. For example, the close system call is used
to destroy sockets.

ACTIVITY 5.1

1. What is the system call to create a stream socket in the Internet


domain?
2. What is the system call to create a datagram socket for an online
machine?

bind system call


A socket is created without a name. Until a name is bound to a socket, processes
have no way to reference it and, consequently, no messages may be received on it.
Communicating processes are bound by an entity. In the Internet domain, the
entity is composed of local and foreign addresses and local and foreign ports,
whereas in the UNIX domain, it is composed of local and foreign path names (the
phrase ‘foreign pathname’ means a pathname created by a foreign process, not a
path name on a foreign system). In most domains, associations must be unique.
The bind system call assigns a name to an unnamed socket.

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, int addrlen);

The first argument is a socket descriptor that was returned by the socket system
call. The second argument is a pointer to a protocol-specific address, and the third
argument is the size of this address structure. Client applications are not required
to be bound to a specific address or local port number — the operating system
takes care of that.

Copyright © Open University Malaysia (OUM)


70  TOPIC 5 BERKELEY SOCKETS

SELF-TEST 5.2

What is the function of bind system call?

ACTIVITY 5.2
If you wanted to bind the name ‘/tmp/foo’ to a UNIX domain socket,
what should the code be?

listen system call


The listen call prepares a socket to accept connections. Only a connection-
oriented server uses this system call. It indicates the server’s willingness to accept
any connection requests from the client side of the application.
int listen(int sockfd, int backlog);

It is usually executed after both the socket and bind system calls and
immediately before the accept system call. The sockfd argument is a file
descriptor corresponding to a socket. The socket must be a type of
SOCK_STREAM or SOCK_SEQPACKET. The backlog argument specifies how
many connection requests can be queued by the system while it waits for the
server to execute the accept system call. Most systems silently limit this number
to about 20; you can probably get away with setting it to 5 or 10.

accept system call


Connection-oriented servers use this system call. The server side of your
application issues this system call to accept incoming connection requests. The
accept() system call takes the first network connection request from the queue and
fills the address structure of the incoming client. If your application is in the block
mode, the accept routine sleeps until a connection request arrives. This system call
returns with an error if no connection request exists in a non-block mode of
applications. The prototype of the accept is shown below.
#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *peer, int *addrlen);

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  71

The sockfd argument is a socket that is of a type SOCK_STREAM or


SOCK_SEQPACKET. The socket must be bound to an address, and you must have
previously issued the listen routine on the socket. The accept fills the address of
the caller into the peer argument. Sufficient buffers are required to hold the
addresses and pass a pointer to those buffers. The addrlen argument is used to
return the number of bytes for the peer structure. addrlen is called a value-result
argument: The caller sets its value before the system call, and the system call
stores a result in the variable. Often these value-result arguments are integers that
the caller sets to the size of a buffer, and the system call changes this value on the
return to the actual amount of data stored in the buffer. For this system call, the
caller sets addrlen to the size of the sockaddr structure whose address is passed as
the peer argument. On return, addrlen contains the actual number of bytes that the
system call stores in the peer argument.

When a connection request arrives in block mode communication, the accept


creates a new socket, accepts the connection on the new socket, and returns a file
descriptor corresponding to the new socket. The new socket can be used for all
subsequent communication with the client side of the application. The original
socket can be used to accept more connection requests.

If the accept() system call is successful, it creates a new socket that will be
used for exchanging data with the client. The call accept() returns the new
socket descriptor to the calling program. The original server socket continues to
listen for incoming connection request messages.

After a connection-oriented server executes the listen system call, an actual


connection from some client process is waited for by having the server execute
the accept system call. accept takes the first connection request on the queue and
creates another socket with the same properties as the initial socket. If no
connection requests are pending, this call blocks the caller until one arrives. The
typical scenario is as follows:
int sockfd, newsockfd;
if ((sockfd = socket(...)) < 0)
error_handle("socket error");
if (bind(sockfd, ...) < 0)
error_handle("bind error");
if (listen(sockfd, ...) < 0)
error_handle("listen error);
for ( ; ; )
{
newsockfd=accept(sockfd, ...); /* blocks */
if (newsockfd < 0)
error_handle("accept error");
if (fork() == 0)

Copyright © Open University Malaysia (OUM)


72  TOPIC 5 BERKELEY SOCKETS

{
close(sockfd); /* child */
doit(newsockfd); /* process the request */
exit(0);
}
close(newsockfd); /* parent */
}

When a connection request is received and accepted, the process forks, the child
process serves the connection and the parent waits for another connection request.
All five elements of the 5-tuple associated with newsockfd have been filled in on
return from accept.

SELF-TEST 5.3

1. What is the function of listen system call?


2. What is the function of accept system call?

ACTIVITY 5.3

Suppose someone in a remote host tries to connect to your machine on


a port that you are listening on. His or her connection will be queued up
waiting to be accepted. Write the program code to accept this call. Your
application has to reserve the port number 3490, and the maximum
number of the pending processes in the listening queue is 10.

After introducing the system calls that are necessary for preparing the server
process to connect with the client processes, we now come to discuss the system
calls that have to be executed in the client process before it can connect to the
server process. Basically, this involves two system calls — socket() and
connect(). The socket() system call in the client process is also used to
create a socket, and the connect() system call is used for making connection to
the server process. As the socket() system call is introduced above, only the
connect() system call is described below.

connect system call


The bind call only allows specification of a local address. To specify the remote
side of an address connection, the connect call is used. This system call is used by
the client process to establish a connection with the server.

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  73

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *servaddr, int addrlen);

The sockfd is a socket descriptor that was returned by the socket system call.
The second and third arguments are a pointer to a socket address and its size,
respectively. Typically, in connection-oriented applications the client program
executes the connect system call, which results in a connection between the
local and remote systems.

SELF-TEST 5.4

What is the difference between bind system call and connect


system call?

ACTIVITY 5.4

Suppose you are required to get a socket file descriptor and then
connect to ‘123.45.6.10’ on port ‘23’. What will be the program code?
(Hint: In order to convert an IP address into a 32-bit integer with
network byte order, the inet_addr call should be used. This routine can
interpret character strings representing numbers expressed in the
Internet standard ‘.’ notation, returning numbers suitable for use as
Internet addresses.)

Once the socket for the communication is created and bound and the
establishment of the communication channel is ready, the next step is to transfer
data between the client hosts and the servers. Various system calls are provided by
UNIX systems for Input/Output operations. The simplest way to perform the
read/write operations is to apply the standard read() and write() system
calls. As well, send() and recv() system calls may be used. They are also
described below.

read and write system calls


As mentioned above, the socket system call returns a socket descriptor. There
are no differences between a socket descriptor and a file descriptor, and they can
be used to read from and write to the network. The protocol of these two calls is
shown below.

Copyright © Open University Malaysia (OUM)


74  TOPIC 5 BERKELEY SOCKETS

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
int read(int sockfd, char *buff, int size);

#include <unistd.h>
int write(int sockfd, char *buff, int size);

Let’s briefly review the use of these system calls. The sockfd argument is an
open descriptor. size bytes are read or written from the file associated with
sockfd into the buffer pointed to by buff. Note that write blocks until data
are transferred to the socket buffers.

send and recv system calls


These system calls are similar to the standard read and write system calls and
have one extra argument for additional options. The additional options may be
used to glance at the data or for out-of-band (urgent) data to be exchanged
between the server and the client processes.

These two functions are for communicating over stream sockets or connected
datagram sockets.
#include <sys/types.h>
#include <sys/socket.h>

int send(int sockfd, char *buff, int size, int flag);


int recv(int sockfd, char *buff, int size, int flag);

For the send socket call, sockfd is the socket descriptor you want to send data to
(whether it’s the one returned by socket or the one you got with accept).
buff is a pointer to the data you want to send, and size is the length of that data
in bytes. The flag argument is what differentiates the send routine from the
write system call. If the flag argument is 0, the send routine behaves exactly as
the write system call. If it is not 0, it can be logical OR of the following values:
MSG_OOB Send out-of-band (urgent) data on sockets that support
this notion. The underlying protocol must also support
‘out-of-band’ data. Only SOCK_STREAM sockets created
in the AF_INET address family support out-of-band data.
MSG_DONTROUTE The SO_DONTROUTE option is turned on for the duration
of the operation. Only diagnostic or routing programs use
it.

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  75

For the recv socket call, sockfd is the socket descriptor to read from, buff is
the buffer to read the information into, size is the maximum length of the buffer,
and flag can again be set to 0. recv() returns the number of bytes actually
read into the buffer, or -1 on error. The flags argument is what differentiates the
recv from the read system call. If the flag argument is 0, the recv routine
behaves exactly as the read system call. Otherwise, the flags parameter is
formed by ORing one or more of the following:
MSG_OOB Read any ‘out-of-band’ data present on the socket rather than the
regular ‘in-band’s data.
MSG_PEEK ‘Peek’ at the data present on the socket; the data are returned but
not consumed, so that a subsequent receive operation will see the
same data.

This prevents any more reads and writes to the socket. Anyone attempting to read
or write the socket on the remote end will receive an error.

After performing all of the necessary communication between the client and
server processes, the connection may be closed using the close() system call.

close system call


The close() system call accepts both file and socket descriptors and closes the
connection.
int close(int fd);

This will prevent any more reads and writes to the socket. Anyone attempting to
read or write the socket on the remote end will receive an error.

SELF-TEST 5.5

1. What is the function of send system call? When would you use
send system call or write system call?
2. What is the function of recv system call? When would you use
recv system call or read system call?

Copyright © Open University Malaysia (OUM)


76  TOPIC 5 BERKELEY SOCKETS

A simple example of connection-oriented socket


program
To summarize the system calls for the connection-oriented communication
described above, a simple program example is described in this section, so that
you can have a consolidation on their use in a typical socket program.

The application is a client/server time machine. The client process connects to the
server host to request the return of the timestamp from the server. The implement
of the client process is listed in Figure 5.2. The client created a socket and
connects to the server’s socket named serv_addr. After the setup of the
connection, data transmission can start and then the client can read the timestamp
from the server. From the client’s point of view, what it needed to set up the
connection is the server IP address and the corresponding port number. In this
example, the port number of the timeserver is defined at 5113.
/* TCP client that finds the time from a server */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SIZE 1024


char buf[SIZE];

#define TIME_PORT 5113


int main(int argc, char *argv[])
{
int sockfd;
int nread;
struct sockaddr_in serv_addr;

if (argc != 2)
{
fprintf(stderr, “usage: %s IPaddr\n”, argv[0]);
exit(1);
}

/* create endpoint */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror(NULL);
exit(2);
}

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  77

/* connect to server */
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(TIME_PORT);
if (connect(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
{
perror(NULL);
exit(3);
}
/* transfer data */
nread = read(sockfd, buf, SIZE);
write(1, buf, nread);
close(sockfd);
exit(0);
}

Similar to the client process, the server implementation is listed in Fig.5.2. The
server creates a socket and binds it to port 5113. The binding step is necessary for
the server process; otherwise, the client process cannot connect to it. After the
binding, the server will stop and wait (listen) for the client request. Once a
client request arrives, the server process will keep on going and create a new
socket client_sockfd to handle the communication with the client process.
The server socket sockfd will then be free to accept other new coming requests.
/* TCP time server with the port number 5113 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SIZE 1024


char buf[SIZE];

#define TIME_PORT 5113


int main(int argc, char *argv[])
{
int sockfd, client_sockfd;
int nread, len;
struct sockaddr_in serv_addr, client_addr;
time_t t;
/* create endpoint */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror(NULL);
exit(2);

Copyright © Open University Malaysia (OUM)


78  TOPIC 5 BERKELEY SOCKETS

/* bind address */
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(TIME_PORT);
if (bind(sockfd, &serv_addr, sizeof(serv_addr)) < 0)
{
perror(NULL);
exit(3);
}
/* specify queue */
listen(sockfd, 5);
for (;;)
{
len = sizeof(client_addr);
client_sockfd = accept(sockfd, &client_addr, &len);
if (client_sockfd == -1)
{
perror(NULL);
continue;
}

/* transfer data */
time(&t);
sprintf(buf, “%s”, asctime(localtime(&t)));
len = strlen(buf) + 1;
write(client_sockfd, buf, len);
close(client_sockfd);
}
}

ACTIVITY 5.5

Refer to the section ‘A connection-oriented example’ in the following


URL: http://www.ecst.csuchico.edu/~chafey/prog/sockets/sinfo1.html
A connection-oriented client server environment is implemented with
the socket interface. The standard Input/Output operations are
performed by the read and write system calls. Try to compile and
execute the given client and server program. What is the result of the
programs?

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  79

SELF-TEST 5.6

There are two types of socket system call (i.e. listen and accept) used by
the server program in Activity 5.5. Please describe their functions.

You now have a clear idea on writing connection-oriented socket programs. It is


time to introduce you to the system calls involved in connectionless socket
programs.

Since no connection is established between the client and server processes, there
will not be system calls that are analogue to listen(), accept() and
connect() in the connection-oriented socket programs. The connectionless
server program will also create and assign a name to a socket using socket()
and bind(). After that, the server process waits for the arrival of data from the
client process through the recvfrom() system call. In response to client’s
request, the server process will also send datagram to the clients using the
sendto() system call. The close() system call is also used to prevent any
further read/write from a socket.

A similar mechanism is involved in the connectionless client program. The client


process creates a socket using the socket() system call. Different from the
connection-oriented clients, the connectionless clients have to assign a name to
the created sockets using the bind() system call. Otherwise, the server process
is not able to send datagram to them. After that, the client sends datagram to the
server process using the sendto() system call, and the communication cycle
between the server and client process begins.

sendto() and recvfrom() system calls are described below. Before going
into the details, the following example shows the framework of a typical
connectionless socket program. This may help you get an overall picture of how
the system calls are positioned in a typical connectionless socket program.

Example 5.1
Have you tried using ICQ? You can ‘talk’ with someone via the ICQ server. In
this example, we refer to the program code in Figure 5.1 to design a program for
two clients’ communication. Details of the code are not shown here, but the flow
of the design is listed for your reference.

Both clients run the same program code on two machines, HostA and HostB. The
server HostC needs to know their corresponding IP addresses. In the client
program, it is very similar to the source in Figure 5.1. However, this client

Copyright © Open University Malaysia (OUM)


80  TOPIC 5 BERKELEY SOCKETS

program requires a read function, recvfrom(), to receive the messages from its
partner.

/* Client Process*/

/* To define the client’s port, server’s port and IP */

Main program start


{
/* Data type initialization */

/* Create a socket for the client */


sockid = socket (...)
...

/* Binding the client’s socket with a name */


bind (...)
...

/* To create the socket address for server */


bzero(...);
...

/* Start communication until Ctrl-C to break the program */


for( I=1 ; I > 0 ; )
{

/* Waiting for user input and then send to the server */


gets( );
retcode = sendto(...);
...
/* Waiting for the receiver’s response via the server */
nread = recvfrom(...);
print the received message;
...
}

/* close socket */
close(sockid);
}

The function of the server works as a message exchanger. Once the server
receives a message from HostA, it would use sendto() to transmit to the
HostB. The other way round is also true.
/* Server */

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  81

/* To define the ports and IP for HostA and HostB */

Main program start


{
/* Date declaration */

/* Create socket for the server *


sockid = socket (...)
...

/* Binding a socket ID */
bind (...)
...

/* To create the socket addresses for HostA and HostB */


bzero(...) for HostA
bzero(...) for HostB
...

/* Start communication until Ctrl C to break the program */


for( I=1 ; I > 0 ; )
{

/* Waiting for incoming message */


nread = recvfrom(...);
...

/* Identify the sender of the corresponding message and retransmit


the message to its partner */
retcode = sendto(...);
...
}

close(sockid);
}

sendto and recvfrom system calls


These system calls are used in connectionless applications. The sendto() and
recvfrom calls are used for input and output operations. These system calls
have two arguments that relate to the destination address and the size of the
address in bytes. The protocols of these functions are shown below.
#include <sys/types.h>
#include <sys/socket.h>

Copyright © Open University Malaysia (OUM)


82  TOPIC 5 BERKELEY SOCKETS

int sendto(int sockfd, char *buff, int size, int flag,


struct sockaddr *to, int addrlen);
int recvfrom(int sockfd, char *buff, int size, int flag,
struct sockaddr *from, int *addrlen);

The sockfd argument is a file descriptor in a socket file. The buff argument
contains the data you want to send or receive, and the addrlen is the number of
bytes in the data. The to argument in the sendto system call contains the
destination address of the message (datagram) sent to the network. You must use
the sendto routine if a datagram transport provider is being used and you want
to send datagrams to several different sockets. Because you specify the destination
address on every message, the sendto routine lets you vary the destination on
every datagram.

The recvfrom routine can be used on both the connection and connectionless
oriented modes. The from argument in the recvfrom system call contains the
source address of the message received from the network. After the recvfrom
routine fills the from argument with the address of the socket that sent the data, it
resets the addrlen argument to contain the number of bytes in the address.

SELF-TEST 5.7

1. What is the difference between sendto and send system call?


2. What is the difference between recvfrom and recv system call?

A simple example of a connectionless application


To summarize the system calls for the connectionless communication described
above, a simple program example is described in this section.

These applications require the user in the client process to input the user’s name.
The user’s name will then be sent to the server process and displayed to the
terminal. The server IP address is 202.40.219.245, which is the host of
plbpc011.ouhk.edu.hk. In order to execute the process, you have to run the server
process first. Then, the server process waits for the client connection and data.
After executing the server process, you can start to run the client process. [Note:
you will be given an account on this new machine when we cover this topic.]

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  83

/* Client Process*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define MY_PORT_ID 6059
#define SERVER_PORT_ID 6090
#define SERV_HOST_ADDR "202.40.219.245" /*plbpc011.ouhk.edu.hk host*/

main()
{
int sockid, retcode, length;
struct sockaddr_in my_addr, server_addr;
char yourname[55];
printf("Client: creating socket\n");
if ( (sockid = socket(AF_INET,SOCK_DGRAM, 0)) < 0)
{
printf("Client: socket failed: %d\n",errno);
exit(0);
}
printf("Client: binding my local socket\n");
bzero((char *) &my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_port = htons(MY_PORT_ID);
if ( ( bind(sockid, (struct sockaddr *) &my_addr, sizeof(my_addr))
< 0) )
{
printf("Client: bind fail: %d\n",errno);
exit(0);
}

printf("Client: creating addr structure for server\n");


bzero((char *) &server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(SERV_HOST_ADDR);
server_addr.sin_port = htons(SERVER_PORT_ID);
printf("Client: initializing message and sending\n");
printf("What is your name ? ");
gets(yourname);
for (length=0; length <56 && yourname[length]!='\0' ; length++)
;
if(length >=56 )
{
printf("Name buffer overflow\n");
exit(0);

Copyright © Open University Malaysia (OUM)


84  TOPIC 5 BERKELEY SOCKETS

}
retcode = sendto(sockid,yourname,length,0,(struct sockaddr *)
&server_addr, sizeof(server_addr));
if (retcode <= -1)

{
printf("client: sendto failed: %d\n",errno);
exit(0);
}
/* close socket */
close(sockid);
}

Program 5.1 Client process by using sendto (program5.1-client)

/* receiver */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define MY_PORT_ID 6090 /* a number > 5000 */

main()
{
int sockid, nread, addrlen;
struct sockaddr_in my_addr, client_addr;
char msg[55];
printf("Server: creating socket\n");
if ( (sockid = socket (AF_INET,SOCK_DGRAM, 0)) < 0)
{
printf("Server: socket error:%d\n",errno);
exit(0);
}
printf("Server: binding my local socket\n");
bzero((char *) &my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htons(INADDR_ANY);
my_addr.sin_port = htons(MY_PORT_ID);
if ( ( bind (sockid, (struct sockaddr *) &my_addr, sizeof(my_addr))
< 0) )
{
printf("Server: bind fail: %d\n",errno);
exit(0);
}
printf("Server: starting blocking message read\n");
nread = recvfrom(sockid,msg,56,0, (struct sockaddr*)&client_addr,
&addrlen);
if (nread >0) printf("Welcome. Your name is: %.54s\n",msg);
close(sockid);
}
 
Program 5.2: Server process by using recvfrom (program5.2-server)

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  85

The output of programs 5.1 and 5.2 should look like this:

On the client side


yourusername@plbpc011$ ./program5.1-client
Client: creating socket
Client: binding my local socket
Client: creating addr structure for server
Client: initializing message and sending
What is your name ? CT212 Course
yourusername@plbpc011$

On the server side


yourusername@plbpc011$ ./program5.2-server
Server: creating socket
Server: binding my local socket
Server: starting blocking message read
Welcome. Your name is: CT212 Course
yourusername@plbpc011$

[Note: There is a secure bug in the codes of Program 5.1 and Program 5.2. Despite
this bug, the program is still useful in illustrating the concepts of client and server
proramming. You will learn how to fix the bug in the respective TMA.]

ACTIVITY 5.6

Try to modify programs 5.1 and 5.2 to a connection-oriented


application. The client process will send a string ‘CT212 socket
programming in connection-oriented.’ to the server process. The server
process will echo the same message to the client process.

It is recommended that you do the following readings. These references provide


much more information for you to build on what you have learned here about
socket programming. They provide details of explanations for all system calls
such as socket, bind, connect, sendto, recvfrom etc. If you had problems in the
above sections, you should look at these before proceeding to the following
sections.

Copyright © Open University Malaysia (OUM)


86  TOPIC 5 BERKELEY SOCKETS

Padovano M (1993) Networking Applications on UNIX System V Release 4,


Englewood Cliffs, NJ: Prentice Hall, pp. 465–54.

You may want to design some client-server applications using socket


programming. The following website presents a detailed procedure for
developing network applications: http://www.cis.temple.edu/~ingargio/
old/cis307f95/readings/unix4.html

This reading describes the multiple ways to set options that affect a socket,
setsockopt, fcntl and ioctl system calls. It lists all socket options for
those system calls.

Stevens, W R (1990) UNIX Network Programming, Englewood Cliffs, NJ:


Prentice Hall, pp. 312–25.

ASYNCHRONOUS I/O
Asynchronous I/O allows the process to tell the kernel to notify it when a
specified descriptor is ready for I/O. It is also called signal-driven I/O. The
notification from the kernel to the user process takes place with a signal, the
SIGIO signal.

To do asynchronous I/O, a process must perform the following three steps:


 The process must establish a handler for the SIGIO signal. This is done by
calling the signal system call.
 The process must set the process ID or the process group ID to receive the
SIGIO signals. This is done with the fcntl system call, using the
F_SETOWN command.

Copyright © Open University Malaysia (OUM)


TOPIC 5 BERKELEY SOCKETS  87

 The process must enable asynchronous I/O using the fcntl system call,
using the F_SETFl command and the FASYNC argument.

A brief summary of the most frequently used system calls for socket
programming can be obtained from this website. This site also provides the
help manual for system calls such as htonl, htons, ntohl, ntohs,
inet_addr, inet_network, gethostbyaddr and
gethostbyname: http://www.scit.wlv.ac.uk/cgi-bin/mansec?3N+htons

Sockets and signals


As we saw in the asynchronous I/O section, signals have closed relations with
sockets. Actually, three signals can be generated for a socket.
SIGIO This signal indicates that a socket is ready for asynchronous I/O. The
signal is sent to the process group of the signal. This process group is
established by calling ioctl with a command of either
FIOSETOWN or SIOCSPGRP, or by calling fcntl with a
command of F_SETOWN. This signal is sent to the process group if
the process has enabled asynchronous I/O on the socket by calling
ioctl with a command of FIOASYNC or by calling fcntl with a
command of F_SETFl and an argument of FASYNC.
SIGURG This signal indicates that an urgent condition is present on a socket.
An urgent condition is either the arrival of out-of-band (urgent) data
on the socket or the presence of control status information to be read
from the master side of a pseudo-terminal that has been put into
packet mode. The signal is sent to the profess group of the signal.
This process group is established by calling ioctl using a
command of either FIOSETOWN or SIOCSPGRP, or by calling
fcntl with a command of F_SETOWN.
SIGPIPE This signal indicates that we can no longer write to a socket, pipe or
FIFO. Nothing special is required by a process to receive this signal,
but unless the process arranges to catch the signal, the default action
is to terminate the process. This signal is sent only to the process
associated with the socket; the process group of the socket is not
used for this signal.

Copyright © Open University Malaysia (OUM)


88  TOPIC 5 BERKELEY SOCKETS

In conclusion, sockets provide the user with a means of interprocess


communication whereby the processes involved can reside on different
workstations on a network. The most common socket types are connection
sockets, which provide a logical connection between processes and support the
reliable exchange of data, and connectionless sockets, which are connectionless
and may be unreliable. A series of socket network system calls are used to
establish socket-based communications. The socket system call is used to create
a socket of a specific type using a particular protocol. The bind system call
establishes a relationship between the socket and a system address. In a
connection-oriented setting, the serving process then creates a queue for incoming
connections using the listen system call. When a connection from a client
process is made, the server then uses the accept system call to generate a new
socket which will be used for actual connection. The connection-oriented client
process creates its own socket and uses the connect system to initiate a
connection with the server process. Once a connection is established, the
processes involved can use read-write system calls to exchange data.

In this topic, we examined mechanism that can be used to develop network


applications. We explored the various possibilities available to application
developers to design client/server applications using Berkeley sockets.

Many commonly used system calls of sockets have been introduced to help you
understand how to write client/server applications. The concept of socket
programming is a prerequisite for developing network applications. Detailed
explanations of system calls such as connect, listen, send, recv and so on are
presented in this topic.

As promised earlier, next topic examines remote procedure calls (RPC). RPC is
another mechanism is used for interprocess communications in network
applications.

Copyright © Open University Malaysia (OUM)


Topic 6  Remote 
Procedure 
Calls 
LEARNING OUTCOMES
When you have completed this topic, you will be able to:
1. Demonstrate an understanding of remote procedure calls (RPC)
2. Write program codes to illustrate the RPC concept

 INTRODUCTION
A remote procedure call (RPC) facility can make it possible to implement client-
server applications without needing to explicitly issue requests for communication
services from within the application program. The idea behind a remote procedure
call facility is that procedure calls are a well-understood mechanism for
transferring control and data from one procedure to another in a computing
application. It is very useful to extend the procedure call mechanism from a set of
procedures in a single-computer environment to a set of procedures in a
distributed, client-server environment. Application programs that use a client-
server approach can then be developed using the same procedure call techniques
that are used in the single-host environment.

Copyright © Open University Malaysia (OUM)


90  TOPIC 6 REMOTE PROCEDURE CALLS

PROCEDURE CALL MECHANISM


Figure 6.1 shows the basic concept behind a procedure call mechanism.

Figure 6.1: Procedure call mechanism.

Procedure A makes a function or procedure call, possibly referencing some


arguments, which passes control to procedure B. While procedure B executes,
procedure A waits. When procedure B finishes its processing, it returns to the
caller, which causes control to be passed to the statement immediately after the
statement in procedure A, thus invoking procedure B. In most programming
language and operating system environments, function or procedure calls can be
nested to any desired level, as procedure A in Figure 6.1 invokes procedure C,
which in turn invokes procedure D.

Procedure calls in a client-server environment


An RPC facility allows the procedure call mechanism to work in a client-server
computing environment where the calling procedure (the client) and the called
procedure (the server) reside in different host computers connected by the
Internet. Ideally, this should be done so that the calling client procedure can call a
remote server procedure using exactly the same technique it would use to call a
procedure residing on the local host. In other words, the mechanisms that the
remote procedure call facility use should be hidden from both the client and the
server procedures.

Copyright © Open University Malaysia (OUM)


TOPIC 6 REMOTE PROCEDURE CALLS  91

Difficulties in achieving transparency


None of today’s RPC facilities has achieved complete transparency. The
difficulties associated with making an RPC facility work in an identical way to a
local procedure call facility lie in three major areas:
 locating the called procedure,
 passing argument values and results, and
 binding.

Locating the called procedure


The RPC facility must provide a means for locating the called procedure in the
network, and the calling procedure must provide the information required to do
this. A directory service such as the Domain Name System (DNS) can be used to
provide the means for locating remote procedures.

Passing arguments and results


With procedure call mechanisms implemented in traditional programming
language environments, communication between the two procedures is based on a
shared address space. Argument values are sometimes passed by reference, which
means the calling procedure passes the called procedure pointers to the argument
values rather than the values themselves. When the two procedures reside on
different host computers, there is no common address space. Therefore, an RPC
facility must be able to handle the passing of arguments and results in both
directions without the calling and called procedures having the benefit of a shared
address space. There may also be differences in data formats from one type of
host computer to another, including differences in integer octet ordering, number
of bits used to represent binary integer values, and floating point data
representation. Such differences must be resolved by the RPC facility, and data
format conversions take place when required.

Binding the called procedure to the calling procedure


With a conventional procedure call mechanism, many techniques can be used for
binding. With early binding, a linking mechanism is used to construct a single
program module containing both the calling and the called procedure. Both the
calling and the called procedures are then loaded into storage together. With late
binding, the procedure call mechanism implemented by the operating system may
allow the called procedure to be dynamically loaded into computer storage at the

Copyright © Open University Malaysia (OUM)


92  TOPIC 6 REMOTE PROCEDURE CALLS

time the procedure is actually invoked. With a remote procedure call facility,
binding is even more complex because it involves finding the host computer on
which the desired procedure resides and loading the procedure into memory if
required.

REMOTE PROCEDURE CALL FUNCTIONAL MODEL

Figure 6.2: RPC facility functional model

A functional model of a remote procedure call facility is shown in Figure 6.2. In


this functional model, a calling client procedure executes a procedure call in the
same way as if it were executing a procedure call to a local procedure. The called
server procedure runs as if it executed in the same host as the client procedure. A
module called a stub in the local host mimics the presence of the actual procedure
to which the calling procedure is attempting to pass control. The stub, in turn,
requests the services of the remote procedure call facility.

Client-server rendezvous
During actual operation, the RPC facility running in the client procedure’s host
may use a directory service such as DNS to determine on which remote host in the

Copyright © Open University Malaysia (OUM)


TOPIC 6 REMOTE PROCEDURE CALLS  93

Internet the called procedure resides. The server host is typically found before the
calling procedure actually invokes the remote procedure.

When the RPC facility in the remote host receives the argument information
generated as a result of the procedure call, it determines whether the requested
server procedure already resides in computer storage there. If it does not, a facility
in the remote host loads the program module containing the requested procedure
and passes control to it, again using a stub unique to that procedure. The called
procedure then passes results back to the calling procedure and passes control
back to it using a process similar to that described for the calling procedure.

Marshalling
The RPC facility uses the services of the TCP/IP networking software to transmit
argument information and results between the RPC facility in the local host and
the RPC facility in the remote host. The process of converting the argument
information in the local host into data units that can be transmitted over the
Internet and perform the same process in the opposite direction for results is
called marshalling.

The marshalling process is straightforward if the two procedures represent


argument values using the same form of data representation. However, to be
useful in a heterogeneous environment, it is necessary for a remote procedure call
facility to handle the situation in which the calling procedure and the called
procedure use different forms of data representation. Therefore, the marshalling
routines must be capable of performing the necessary data conversions. RPC
facilities use various methods for handling the marshalling process.

The socket method discussed earlier in previous topic is a message-based system,


in which one process writes a message to another. This is a long way from the
procedural model. The remote procedure call is intended to act like a procedure
call but act across the network transparently. The process makes a remote
procedure call by pushing its parameters and a return address onto the stack and
jumping to the start of the procedure. The procedure itself is responsible for
accessing and using the network. After the remote execution is over, the
procedure jumps back to the return address. The calling process then continues.

The following example socket calls show how to implement a procedure to find
the time on a remote machine as a string, using the IP socket calls and using IP
socket calls.

Copyright © Open University Malaysia (OUM)


94  TOPIC 6 REMOTE PROCEDURE CALLS

Using IP socket calls


int remote_time(char *machine,char *time_buf)
{ struct sockaddr_in serv_addr;
int sockfd;
int nread;

if (sockfd =
socket(AF_INET,
SOCK_STREAM, 0))
< 0)
return 1;
serv_addr.sin_family =
AF_INET;
serv_addr.sin_addr.s_addr =
inet_addr(machine);
serv_addr.sin_port =
htons(13);
if (connect(sockfd,
&serv_addr,
sizeof(serv_addr))
< 0)
return 2;
nread = read(sockfd,
time_buf,
sizeof(time_buf));
time_buf[nread] = '\0';
close(sockfd);
return 0;
}

Using ordinary procedure calls


The calling process executes remote_time(machine, time_buf). All networking
should be done by the RPC implementation, such as connecting to the remote
machine. On the remote machine, this simple function gets executed:
int remote_time(char *time_buf)
{ struct tm *time;
time_t t;
time(&t);
time = localtime(&t);
strcpy(time_buf,
asctime(time));
return 0;
}

Copyright © Open University Malaysia (OUM)


TOPIC 6 REMOTE PROCEDURE CALLS  95

ACTIVITY 6.1

 As printmessage() takes a string as input and returns an integer as


output, write a protocol specification in RPC language that
describes the remote version of printmessage():
 Define the remote procedure.
 Declare the main client program that will call the remote procedure.

RPC call semantics


When executing procedure calls in the local environment, the question of the
number of times a procedure has been executed does not typically arise. If the
calling procedure receives control from the called procedure, the calling
procedure knows that the called procedure was executed exactly once. But when
the procedure is executing on a remote host and the calling procedure does not
regain control after some time has passed, the calling procedure does not know if
the remote procedure has executed or not. It is possible that the remote procedure
has not executed at all, it may have executed once, and it may have executed more
than once.

For some types of procedure, it does not matter whether a procedure is executed
multiple times, as long as it can be determined that the procedure has executed at
least once. Such a procedure is called idempotent. A procedure that simply returns
an uncomplicated result, such as the time of day, or makes a complete
replacement of a data value is idempotent. A procedure that adds a value to a data
element, such as a bank balance, is not idempotent.

The differences between local procedure calling and remote procedure calling
make it necessary to distinguish among three types of remote procedure call
semantics that an RPC facility may be designed to handle: at least once, at most
once, and exactly once.

At least once
If an RPC facility provides at least once semantics, it allows the calling procedure
to determine only that the procedure was executed at least one time. Such
semantics are useful only for idempotent procedures. The RPC facility in the
client host can keep trying the request, and once the called procedure finally
responds, control can be returned to the calling procedure. The calling procedure
then knows that the called procedure was executed one or more times. Many RPC

Copyright © Open University Malaysia (OUM)


96  TOPIC 6 REMOTE PROCEDURE CALLS

facilities allow the user to specify that the called procedure is idempotent and that
at least once semantics are all that is required for a particular call.

At most once semantics


If an RPC facility provides at most once semantics, it allows the calling procedure
to determine that if the called procedure executed at all, it executed only one time.
With these semantics, if the calling procedure receives control at the statement
following the statement that invoked the called procedure, it knows that the called
procedure executed exactly one time. But if the called procedure does not receive
control back and times out, it does not know whether the procedure executed or
not. Most RPC facilities provide mechanisms that allow the user to request at
most once semantics.

Exactly once semantics


If an RPC facility provides exactly once semantics, it allows the calling procedure
to determine that the called procedure was called once and only once. Such
semantics are hard for an RPC facility to achieve, because of the possibility of
server failures. Most RPC facilities do not provide exactly once semantics.

RPC implementation
Many software subsystems are typically used to provide remote procedure call
facilities in the TCP/IP environment. In this topic, however, we only mention one
— the RPC facility developed by Sun Microsystems — to show the RPC
implementation. This Sun RPC facility provides at most once semantics and
allows the use of at least once semantics for the applications that do not require at
most once semantics.

Sun RPC facility


An implementation of the Sun RPC facility is provided in most BSD
implementations of the UNIX operating system. The Sun RPC facility is also
provided on a number of other UNIX and non-UNIX operating systems and is
normally available with any implementation of Sun’s Network File System
(NFS). The Sun RPC facility consists of the following components:
 Rpcgen compiler
The Rpcgen compiler accepts remote procedure call interface definitions and
generates client and server stubs that can be included with the client and
server application programs.

Copyright © Open University Malaysia (OUM)


TOPIC 6 REMOTE PROCEDURE CALLS  97

 External Data Representation (XDR) Facility


The XDR facility provides a standard method for encoding argument data and
results so they can be used in a portable way in a heterogeneous host
environment.
 Runtime library
The runtime library is a collection of executable routines that are required to
allow client and server programs to invoke the Sun RPC facilities.
 Portmapper facility
The portmapper facility provides programs with information about the
location of callable services.

Sun RPC example


This section shows a sample code used in order to implement remote procedure
calls using the Sun RPC facility. You are also given another UNIX account
running on Sun’s System. This will be posted on WebCT. We will assume that the
remote server process implements the following two callable procedures that
provide date and time-of-day services:
 bin_time_1
Returns the server host’s internal date and time of day in binary format.
 str_time_1
Converts a binary date and time value into character format.

In order to implement an RPC program on SUN platforms, the following steps


have to be followed in sequence.
1 Create an RPC specification file (e.g. date.x).

2 Then run rpcgen data.x to produce three files: a server stub (date_svc.c), a client
stub (date_clnt.c) and a header file (date.h).

3 Create your server procedures (e.g. date_proc.c) and client main function (e.g.
rdate.c)

4 Then compile these files to create the server and client programs:
 client: gcc -o rdate rdate.c date_clnt.c –lnsl
 server: gcc -o date_svc date_proc.c date_svc.c -lnsl

Copyright © Open University Malaysia (OUM)


98  TOPIC 6 REMOTE PROCEDURE CALLS

RPC program preparation


In addition to writing the code for the client and the server procedures, an RPC
specification file must be prepared that describes the characteristics of the two
server procedures. The Sun RPC Rpcgen compiler processes the RPC
specification file. The Rpcgen compiler generates the source code for a client
stub, a server stub, and a header file. The client stub is included with the program
module that contains the calling procedure code, and the server stub is included
with the program module containing the server procedure. The header file
contains identifier and data structure definitions that must be included in both the
client and server source code modules.

Figure 6.3 summarizes the process that is used to prepare the client and server
program modules for execution.

Figure 6.3: Procedure call mechanism

Copyright © Open University Malaysia (OUM)


TOPIC 6 REMOTE PROCEDURE CALLS  99

Sun RPC specification statements


The following shows what the RPC specification file might look like to define our
two date and time procedures.

Sun RPC specification statements:

/* bin_time_1() returns the binary date and time (no arguments).


str_time_1() takes a binary date and time value and returns a
character string.
program TIME_PROG
{
version TIME_VERS
{
long BIN_TIME(void) = 1; /* procedure number = 1 */
string STR_TIME(long) = 2; /* procedure number = 2 */
} = 1; /* version number = 1 */
} = 0x31234567; /* program number = 0x3000001 */

Figure 6.4: RPC specification file

In the above code, the BIN_TIME procedure is defined as producing a 32-bit


binary integer result, the ‘void’ indicates that the procedure accepts no argument,
and the procedure is identified as procedure number 1 in this RPC definition. The
STR_TIME procedure is defined as producing a string result; it accepts a single
32-bit binary integer, and the procedure is identified as procedure number 2.

This procedure definition is identified as version 1, and a 32-bit program number


is assigned to the RPC definition using hexadecimal notation.

Sun has assigned RPC program numbers as follows:


00000000 - 1fffffff defined by Sun
20000000 - 3fffffff defined by the user
40000000 - 5fffffff transient
60000000 - ffffffff reserved

Sun Rpcgen compiler output


The Sun Rpcgen compiler generates the header code shown below as a result of
the previous RPC specification statements:
#define TIME_PROG ((u_long) 0x30000001)
#define TIME_VERS ((u_long) 1)
#define BIN_TIME ((u_long) 1)

Copyright © Open University Malaysia (OUM)


100  TOPIC 6 REMOTE PROCEDURE CALLS

extern long *bin_time_1();


#define STR_TIME ((u_long) 2)
extern char ** str_time_1();

Note that the BIN_TIME procedure is called using the name bin_time_1, and
the STR_TIME procedure is called using the name str_time_1.

RPC server procedure code


We next look at the code that would be used in the server program module to
make the two remote procedures available to the client program module.

long *
bin_time_1()
{
static long timeval;
long time();
timeval = time((long *) 0);
return (&timeval);
}

Figure 6.5: Remote procedure code for bin_time_1

The code for the bin_time_1 procedure is identical to the code that would be
used if the procedure were going to be called locally. It simply obtains a binary
time value using a standard UNIX function and passes control back to the calling
procedure.

char ** str_time_1(bintime)
long *bintime;
{
static char *ptr;
char *ctime();
ptr = ctime(bintime);
return (&ptr);
}

Figure 6.6: Remote procedure code for str_time_1

The code for the str_time_1 procedure is also identical to the code that would
be used if it were called locally. It accepts a long integer argument and converts
the time value contained in the argument to a string time value. It then returns a
pointer to the string time value to the called procedure.

Copyright © Open University Malaysia (OUM)


TOPIC 6 REMOTE PROCEDURE CALLS  101

RPC client calling code


Now we look at the relevant code in the client program module that is used to
invoke the two remote procedures.

Before either of the procedures can be invoked, the client program module must
call a procedure named clnt_create() that is provided with the Sun RPC
facility.

if ((cl = clnt_create(server, TIME_PROG, TIME_VERS, "udp")) == NULL)


{
/* couldn¡¦t establish connection with server. */
clnt_pcreaterror(server);
exit(2);
}

Figure 6.7: Procedure code for clnt_create

The clnt_create() procedure is provided as part of the Sun RPC facility.


The clnt_create() procedure creates a descriptor (cl in this example) that
can be referenced when invoking the remote procedures. Its arguments include the
name of the server host, the program number, the version number, and the
transport layer protocol to use. The Sun RPC facility permits the use of either
UDP or TCP to implement RPC communication.

if ((lresult = bin_time_1(NUll, cl)) == NUll)


{
clnt_perror(cl, server);
exit(3);
}

Figure 6.8: To invoke the bin_time_1 remote procedure

Note that the argument list indicates that the remote procedure has no arguments
and references that the descriptor established previously. The statement invoking
the remote procedure specifies that the long integer result be placed into lresult.

if ((sresult = str_time_1(lresult, cl)) == NUll) {


clnt_perror(cl, server);
exit(4);
}

Figure 6.9: To invoke the str_time_1 procedure

Copyright © Open University Malaysia (OUM)


102  TOPIC 6 REMOTE PROCEDURE CALLS

For the details of Sun RPC, you should refer to the following reading. It
describes the actual RPC implementation with the Sun RPC system. A simple
date time RPC example is provided to illustrate the development of RPC
application.

Stevens, W R (1990) UNIX Network Programming, Englewood Cliffs, NJ:


Prentice Hall, pp. 700–8.

A more detailed description about the concept of RPC and its implementation
is given in the following URL. It illustrates an example of finding the time on
a remote machine as a string. You may take a look if you would like to know
more about RPC: http://pandonia.canberra.edu.au/OS/l14_1.html

SELF-TEST 6.1

1. Describe a whole process of RPC communications between client


and server.
2 What are the major components in the Sun RPC system?

Copyright © Open University Malaysia (OUM)


TOPIC 6 REMOTE PROCEDURE CALLS  103

ACTIVITY 6.2
1. The following program is run on a single machine. Suppose now
the procedure printmessage() is required to run over the network
(turned to a remote procedure). Modify the program using the RPC
method.
/*
* printmsg.c: print a message on the console
*/
#include <stdio.h>
main(int argc, char **argv)
{
char *message;

if (argc < 2) {
fprintf(stderr, "usage: %s <message>\n",
argv[0]);
exit(1);
}
message = argv[1];

if (!printmessage(message)) {
fprintf(stderr, "%s: couldn’t print your
message\n", argv[0]);
exit(1);
}
printf("Message Delivered!\n");
exit(0);
}

/*
* Print a message to the console. Return a boolean
* indicating whether the message was actually
* printed.
*/
printmessage(char *msg)
{
FILE *f;
f = fopen("/dev/console", "w");
if (f == NULL) {
return(0);
}
fprintf(f, "%s\n", msg);
fclose(f);
return(1);
}
Copyright © Open University Malaysia (OUM)
104  TOPIC 6 REMOTE PROCEDURE CALLS

Programming with RPC allows the programmer to write distributed applications


whereby a process residing on one workstation can request another ‘remote’
workstation to execute a specified procedure. Because of their complexity, most
RPC-based programs use a protocol compiler such as Sun Microsystems’s
rpcggen. It provides the basic programming framework for the RPC-based
application. In RPC applications, the client and server processes do not need to
know the details of network protocols use. Data passed between the processes are
converted to/from an external data representation (XDR) format by predefined
filters.

In this topic, we examined another mechanism that can be used to develop


network applications, that is using RPC. RPC is a totally different concept
compare to socket programming. The stubs of client and server can simplify the
development of the application. A full picture of writing client/server programs by
using RPC is shown. It is hoped that this topic, along with the references and the
knowledge you gained in the previous topic, can help you implement your
distributed applications.

Copyright © Open University Malaysia (OUM)


atomic — An instruction may be said to do several things
‘atomically’; i.e. all of the things are done
immediately, and there is no chance of the instruction
being half-completed or of another being
interspersed. Used especially to convey that an
operation cannot be interrupted.

Connection- — A protocol that requires that a channel of


oriented protocol communication be established.

Connectionless — A protocol that requires no prior establishment of a


protocol connection.

deadlock — A situation in which two or more processes are


unable to proceed because each is waiting for one of
the others to do something.

kernel — The essential part of UNIX or other operating


systems, responsible for resource allocation, low-
level hardware interfaces, security, and so on.

PID — The Process ID is used by the UNIX kernel to


identify each task operating in the system.

Pipe — A pipe is a metaphorical concept representing a


channel of data files between one process and another
on the same host.

process — An instance of a program that is being executed by


the operating system. An executing program. A
process consists of the program code (which may be
shared with other processes executing the same
program) and some private data. The only way a new
process can be created by the UNIX system is by
issuing the fork system call.

Socket — A socket is an endpoint in network communications.


A pair of sockets is required for connection-oriented
communications.

Thread — A single sequential flow of control within a process.

Copyright © Open University Malaysia (OUM)


106  GLOSSARY

zombie — In the interval between the ‘child’ terminating and the


‘parent’ calling wait() , the child is said to be a
‘zombie’ (if you do ps , the child will have a ‘Z’ in
its status field to indicate this). Even though it’s not
running, it’s still taking up an entry in the process
table.

Zombie — This is a UNIX process that has terminated or exited


gracefully.

Copyright © Open University Malaysia (OUM)


Glass, G (1993), UNIX Programmers and Users — A Complete Guide,
Englewood Cliffs, NJ: Prentice Hall.

Padovano M (1993) Networking Applications on UNIX System V Release 4,


Englewood Cliffs, NJ: Prentice Hall, 468–84.

Stevens, W R (1990), Unix Network Programming, Englewood Cliffs, NJ:


Prentice Hall.

Gray, J S (1998) Interprocess Communications in UNIX: The Nooks and


Crannies, Prentice Hall PTR.

Stevens, W R (1994) UNIX® Network Programming, Singapore: Simon and


Schuster (Asia) Pte Ltd.
Online references
http://www.cis.temple.edu/~ingargio/old/cis307f95/readings/unix4.html

http://www.scit.wlv.ac.uk/cgi-bin/mansec?3N+htons

http://pandonia.canberra.edu.au/OS/l14_1.html

Hall, B (1997) ‘FIFOs’ in Beej’s Guide to Unix Interprocess Communication,


Version 0.9.3, URL:
http://www.ecst.csuchico.edu/~beej/guide/ipc/fifos.html (14 May 1997,
cited 8 July 1998).

Hall, B (1997) ‘Message queues’ in Beej’s Guide to Unix Interprocess


Communication, Version 0.9.3, URL:
http://www.ecst.csuchico.edu/~beej/guide/ipc/mq.html (14 May 1997, cited
8 July 1998).

Hall, B (1997) ‘Pipes’ in Beej’s Guide to Unix Interprocess Communication,


Version 0.9.3, URL:
http://www.ecst.csuchico.edu/~beej/guide/ipc/pipes.html (14 May 1997,
cited 8 July 1998).

Hall, B (1997) ‘Semaphores’ in Beej’s Guide to Unix Interprocess


Communication, Version 0.9.3, URL:
http://www.ecst.csuchico.edu/~beej/guide/ipc/semaphores.html (14 May
1997, cited 8 July 1998).

Copyright © Open University Malaysia (OUM)


108  REFERENCES

Rusling, D (1996–98) ‘Interprocess Communication Mechanisms: Pipes’, The


Linux Kernel, pp. 2–4: http://www.eee.hku.hk/LDP/LDP/tlk/ipc/ipc.html

Rusling, D (1996-98) ‘Interprocess Communication Mechanisms: Signals’in The


Linux Kernel, pp.1–2 http://www.eee.hku.hk/LDP/LDP/tlk/ipc/ipc.html

Sun Microsystems (1997) docs.sun.com•man, ‘Pages(5): Headers, Tables and


Macros’: http://docs.sun.com/?p=/doc/805-3177/6j31gjo55&a=view#signal-
5-indx-1

Copyright © Open University Malaysia (OUM)


MODULE FEEDBACK
MAKLUM BALAS MODUL

If you have any comment or feedback, you are welcome to:

1. E-mail your comment or feedback to modulefeedback@oum.edu.my

OR

2. Fill in the Print Module online evaluation form available on myVLE.

Thank you.

Centre for Instructional Design and Technology


(Pusat Reka Bentuk Pengajaran dan Teknologi)
Tel No.: 03-27732578
Fax No.: 03-26978702

Copyright © Open University Malaysia (OUM)

Das könnte Ihnen auch gefallen