Download as pdf or txt
Download as pdf or txt
You are on page 1of 8

Software Component Design

Chapter 4: IPC using Shared Memory, Pipes and


Sockets

[A] IPC through Shared Memory


Shared memory is a memory shared between two or more processes. Each process
has its own address space; if any process wants to communicate with some
information from its own address space to other processes, then it is only possible
with IPC (inter-process communication) techniques.

Shared memory is the fastest inter-process communication mechanism. The


operating system maps a memory segment in the address space of several processes
to read and write in that memory segment without calling operating system
functions.

How does IPC Using Shared Memory work?


A process creates a shared memory segment using shmget(). The original owner of a
shared memory segment can assign ownership to another user with shmctl(). It can
also revoke this assignment. Other processes with proper permission can perform
various control functions on the shared memory segment using shmctl().

Once created, a shared segment can be attached to a process address space


using shmat(). It can be detached using shmdt(). The attaching process must have
the appropriate permissions for shmat(). Once attached, the process can read or
write to the segment, as the permission requested in the attach operation allows. A
shared segment can be attached multiple times by the same process.

A shared memory segment is described by a control structure with a unique ID that points to
an area of physical memory. The identifier of the segment is called the shmid. The structure
definition for the shared memory segment control structures and prototypes can be found
in <sys/shm.h>.

Examples
We will write two programs for IPC using shared memory as an example. Program 1 will
create the shared segment, attach it, and then write some content in it. Then Program 2 will
attach itself to the shared segment and read the value written by Program 1.

Program 1: This program creates a shared memory segment, attaches itself to it, and then
writes some content into the shared memory segment.

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/shm.h>
#include<string.h>
int main()
{
int i;
void *shared_memory;
char buff[100];
int shmid;
shmid=shmget((key_t)2345, 1024, 0666|IPC_CREAT);
//creates shared memory segment with key 2345, having size 1024 bytes. IPC_CREAT is u
sed to create the shared segment if it does not exist. 0666 are the permissions on the sh
ared segment
printf("Key of shared memory is %d\n",shmid);
shared_memory=shmat(shmid,NULL,0);
//process attached to shared memory segment
printf("Process attached at %p\n",shared_memory);
//this prints the address where the segment is attached with this process
printf("Enter some data to write to shared memory\n");
read(0,buff,100); //get some input from user
strcpy(shared_memory,buff); //data written to shared memory
printf("You wrote : %s\n",(char *)shared_memory);
}

OUTPUT:

Key of shared memory is 0


Process attached at 0x7ffe040fb000
Enter some data to write to shared memory
Hello World
You wrote: Hello World

How does it work?

In the above program, the shmget() function creates a segment with key 2345, size
1024 bytes, and reads and writes permissions for all users. It returns the identifier of
the segment, which gets stored in shmid. This identifier is used in shmat() to attach
the shared segment to the process's address space.

NULL in shmat() means that the OS will itself attach the shared segment at a suitable
address of this process. Then some data is read from the user using
the read() system call, and it is finally written to the shared segment using
the strcpy() function.

Program 2: This program attaches itself to the shared memory segment created in
Program 1, and it reads the content of the shared memory.

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/shm.h>
#include<string.h>
int main()
{
int i;
void *shared_memory;
char buff[100];
int shmid;
shmid=shmget((key_t)2345, 1024, 0666);
printf("Key of shared memory is %d\n",shmid);
shared_memory=shmat(shmid,NULL,0); //process attached to shared memory segment
printf("Process attached at %p\n",shared_memory);
printf("Data read from shared memory is : %s\n",(char *)shared_memory);
}

OUTPUT

Key of shared memory is 0


Process attached at 0x7f76b4292000
Data read from shared memory is: Hello World

How does it work?

In this program, shmget() here generates the identifier of the same segment as
created in Program 1. Remember to give the same key value. The only change is, do
not write IPC_CREAT as the shared memory segment is already created.
Next, shmat() attaches the shared segment to the current process.

After that, the data is printed from the shared segment. In the output, you will see
the same data that you have written while executing Program 1.

Inter Process Communication - Pipes


Pipe is a communication medium between two or more related or interrelated
processes. It can be either within one process or a communication between the
child and the parent processes. Communication can also be multi-level such as
communication between the parent, the child and the grand-child, etc.
Communication is achieved by one process writing into the pipe and other reading
from the pipe. To achieve the pipe system call, create two files, one to write into the
file and another to read from the file.
Pipe mechanism can be viewed with a real-time scenario such as filling water with
the pipe into some container, say a bucket, and someone retrieving it, say with a
mug. The filling process is nothing but writing into the pipe and the reading process
is nothing but retrieving from the pipe. This implies that one output (water) is input
for the other (bucket).
Example Programs
ollowing are some example programs.
Example program 1 − Program to write and read two messages using pipe.

Algorithm
Step 1 − Create a pipe.
Step 2 − Send a message to the pipe.
Step 3 − Retrieve the message from the pipe and write it to the standard output.
Step 4 − Send another message to the pipe.
Step 5 − Retrieve the message from the pipe and write it to the standard output.
Note − Retrieving messages can also be done after sending all messages.
Source Code: simplepipe.c
#include<stdio.h>
#include<unistd.h>

int main() {
int pipefds[2];
int returnstatus;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);

if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}

printf("Writing to pipe - Message 1 is %s\n",


writemessages[0]);
write(pipefds[1], writemessages[0], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 1 is %s\n", readmessage);
printf("Writing to pipe - Message 2 is %s\n",
writemessages[0]);
write(pipefds[1], writemessages[1], sizeof(writemessages[0]));
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Reading from pipe – Message 2 is %s\n", readmessage);
return 0;
}

Note − Ideally, return status needs to be checked for every system call. To simplify the
process, checks are not done for all the calls.
Execution Steps
Compilation
gcc -o simplepipe simplepipe.c

Execution/Output
Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
Writing to pipe - Message 2 is Hi
Reading from pipe – Message 2 is Hell

Example program 2 − Program to write and read two messages through the pipe
using the parent and the child processes.

Algorithm
Step 1 − Create a pipe.
Step 2 − Create a child process.
Step 3 − Parent process writes to the pipe.
Step 4 − Child process retrieves the message from the pipe and writes it to the
standard output.
Step 5 − Repeat step 3 and step 4 once again.
Source Code: pipewithprocesses.c
#include<stdio.h>
#include<unistd.h>

int main() {
int pipefds[2];
int returnstatus;
int pid;
char writemessages[2][20]={"Hi", "Hello"};
char readmessage[20];
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
pid = fork();

// Child process
if (pid == 0) {
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 1 is
%s\n", readmessage);
read(pipefds[0], readmessage, sizeof(readmessage));
printf("Child Process - Reading from pipe – Message 2 is
%s\n", readmessage);
} else { //Parent process
printf("Parent Process - Writing to pipe - Message 1 is
%s\n", writemessages[0]);
write(pipefds[1], writemessages[0],
sizeof(writemessages[0]));
printf("Parent Process - Writing to pipe - Message 2 is
%s\n", writemessages[1]);
write(pipefds[1], writemessages[1],
sizeof(writemessages[1]));
}
return 0;
}

Execution Steps
Compilation
gcc pipewithprocesses.c –o pipewithprocesses
Execution
Parent Process - Writing to pipe - Message 1 is Hi
Parent Process - Writing to pipe - Message 2 is Hello
Child Process - Reading from pipe – Message 1 is Hi
Child Process - Reading from pipe – Message 2 is Hello

Interprocess Communication with


Sockets
One of the ways to manage interprocess communication is by using sockets. They provide
point-to-point, two-way communication between two processes. Sockets are an endpoint of
communication and a name can be bound to them. A socket can be associated with one or
more processes.

Types of Sockets
The different types of sockets are given as follows −
• Sequential Packet Socket: This type of socket provides a reliable connection
for datagrams whose maximum length is fixed This connection is two-way as
well as sequenced.
• Datagram Socket: A two-way flow of messages is supported by the
datagram socket. The receiver in a datagram socket may receive messages
in a different order than that in which they were sent. The operation of
datagram sockets is similar to that of passing letters from the source to the
destination through a mail.
• Stream Socket: Stream sockets operate like a telephone conversation and
provide a two-way and reliable flow of data with no record boundaries. This
data flow is also sequenced and unduplicated.
• Raw Socket: The underlying communication protocols can be accessed
using the raw sockets.

Socket Creation
Sockets can be created in a specific domain and the specific type using the following
declaration −
int socket(int domain, int type, int protocol)
If the protocol is not specified in the above system call, the system uses a default
protocol that supports the socket type. The socket handle is returned. It is a
descriptor.
The bind function call is used to bind an internet address or path to a socket. This is
shown as follows −
int bind(int s, const struct sockaddr *name, int namelen)

Connecting Stream Sockets


Connecting the stream sockets is not a symmetric process. One of the processes
acts as a server and the other acts as a client. The server specifies the number of
connection requests that can be queued using the following declaration −
int listen(int s, int backlog)
The client initiates a connection to the server’s socket by using the following
declaration −
int connect(int s, struct sockaddr *name, int namelen)
A new socket descriptor which is valid for that particular connection is returned by
the following declaration −
int accept(int s, struct sockaddr *addr, int *addrlen)

Stream Data Transfer


The send() and recv() functions are used to send and receive data using sockets.
These are similar to the read() and write() functions but contain some extra flags.
The declaration for send() and recv() are as follows −
int send(int s, const char *msg, int len, int flags)
int recv(int s, char *buf, int len, int flags)

Stream Closing
The socket is discarded or closed by calling close().

You might also like