Inter Process Communication Tutorial
Inter Process Communication Tutorial
This tutorial covers a foundational understanding of IPC. Each of the chapters contain
related topics with simple and useful examples.
Audience
This tutorial is designed for beginners who seek to understand the basic concepts of inter
process communication and how its different components function.
Prerequisites
There are no particular prerequisites for this tutorial, however, a sound knowledge of
operating systems and its various concepts will be an added advantage in understanding
this tutorial.
All the content and graphics published in this e-book are the property of Tutorials Point (I)
Pvt. Ltd. The user of this e-book is prohibited to reuse, retain, copy, distribute or republish
any contents or a part of contents of this e-book in any manner without written consent
of the publisher.
We strive to update the contents of our website and tutorials as timely and as precisely as
possible, however, the contents may contain inaccuracies or errors. Tutoria ls Point (I) Pvt.
Ltd. provides no guarantee regarding the accuracy, timeliness or completeness of our
website or its contents including this tutorial. If you discover any errors on our website or
in this tutorial, please notify us at contact@tutorialspoint.com
i
Inter Process Communication
Table of Contents
About the Tutorial........................................................................................................................................................... i
Audience ........................................................................................................................................................................... i
Prerequisites .................................................................................................................................................................... i
iii
1. IPC - Overview Inter Process Communication
Between related processes initiating from only one process, such as parent and
child processes.
Following are some important terms that we need to know before proceeding further on
this topic.
Pipes: Communication between two related processes. The mechanism is half duplex
meaning the first process communicates with the second process. To achieve a full duplex
i.e., for the second process to communicate with the first process another pipe is required.
FIFO: Communication between two unrelated processes. FIFO is a full duplex, meaning
the first process can communicate with the second process and vice versa at the same
time.
Message Queues: Communication between two or more processes with full duplex
capacity. The processes will communicate with each other by posting a message and
retrieving it out of the queue. Once retrieved, the message is no longer available in the
queue.
Note: Almost all the programs in this tutorial are based on system calls under Linux
Operating System (executed in Ubuntu).
1
2. IPC - Process Information
Inter Process Communication
What is a program? A program is a file containing the information of a process and how to
build it during run time. When you start execution of the program, it is loaded into RAM
and starts executing.
Each process is identified with a unique positive integer called as process ID or simply PID
(Process Identification number). The kernel usually limits the process ID to 32767, which
is configurable. When the process ID reaches this limit, it is reset again, which is after the
system processes range. The unused process IDs from that counter are then assigned to
newly created processes.
The system call getpid() returns the process ID of the calling process.
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
This call returns the process ID of the calling process which is guaranteed to
be unique. This call is always successful and thus no return value to indicate
an error.
Each process has its unique ID called process ID that is fine but who created it? How to
get information about its creator? Creator process is called the parent process. Parent ID
or PPID can be obtained through getppid() call.
The system call getppid() returns the Parent PID of the calling process.
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
This call returns the parent process ID of the calling process. This call is
always successful and thus no return value to indicate an error.
2
Inter Process Communication
Following is a program to know the PID and PPID of the calling process.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int mypid, myppid;
printf("Program to know PID and PPID's information\n");
mypid = getpid();
myppid = getppid();
printf("My process ID is %d \n", mypid);
printf("My parent process ID is %d\n", myppid);
On compilation and execution of the above program, following will be the output.
3
Inter Process Communication
Note: The “C” library function system() execut es a shell command. The arguments passed
to system() are commands executed on shell. In the above program, command is “ps”,
which gives process status.
The complete information about all running processes and other system related
information are accessible from proc file system available at /proc location.
4
3. IPC - Process Image
Inter Process Communication
Now that we have seen how to get the basic information of process and its parent process,
it is time to look into the details of process/program information.
What exactly is process image? Process image is an executable file required while
executing the program. This image usually contains the following sections:
Code segment is a portion of object file or program’s virtual address space that consists
of executable instructions. This is usually read-only data segment and has a fixed size.
Initialized
Un-initialized
Initialized data segment is a portion of the object file or program’s virtual address space
that consists of initialized static and global variables.
5
Inter Process Communication
Un-initialized data segment is a portion of the object file or program’s virtual address
space that consists of uninitialized static and global variables. Un-initialized data segment
is also called BSS (Block Started by Symbol) segment.
Data segment is read-write, since the values of variables could be changed during run
time. This segment also has a fixed size.
Stack segment is an area of memory allotted for automatic variables and function
parameters. It also stores a return address while executing function calls. Stack uses LIFO
(Last-In-First-Out) mechanism for storing local or automatic variables, function
parameters and storing next address or return address. The return address refers to the
address to return after completion of function execution. This segment size is variable as
per local variables, function parameters, and function calls. This segment grows from a
higher address to a lower address.
Heap segment is area of memory allotted for dynamic memory storage such as for
malloc() and calloc() calls. This segment size is also variable as per user allocation. This
segment grows from a lower address to a higher address.
Let us now check how the segments (data and bss segments) size vary with a few sample
programs. Segment size is known by executing the command “size”.
Initial program
File: segment_size1.c
#include<stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
In the following program, an uninitialized static variable is added. This means uninitialized
segment (BSS) size would increase by 4 Bytes. Note: In Linux operating system, the size
of int is 4 bytes. Size of the integer data type depends on the compiler and operating
system support.
6
Inter Process Communication
File: segment_size2.c
#include<stdio.h>
int main()
{
static int mystaticint1;
printf("Hello World\n");
return 0;
}
In the following program, an initialized static variable is added. This means initialized
segment (DATA) size would increase by 4 Bytes.
File: segment_size3.c
#include<stdio.h>
int main()
{
static int mystaticint1;
static int mystaticint2 = 100;
printf("Hello World\n");
return 0;
}
In the following program, an initialized global variable is added. This means initialized
segment (DATA) size would increase by 4 Bytes.
File: segment_size4.c
#include<stdio.h>
int myglobalint1 = 500;
int main()
{
static int mystaticint1;
static int mystaticint2 = 100;
7
Inter Process Communication
printf("Hello World\n");
return 0;
}
In the following program, an uninitialized global variable is added. This means uninitialized
segment (BSS) size would increase by 4 Bytes.
File: segment_size5.c
#include<stdio.h>
int myglobalint1 = 500;
int myglobalint2;
int main()
{
static int mystaticint1;
static int mystaticint2 = 100;
printf("Hello World\n");
return 0;
}
Execution Steps
Compilation
8
Inter Process Communication
Execution/Output
9
4. IPC - Process Creation & Termination
Inter Process Communication
Till now we know that whenever we execute a program then a process is created and
would be terminated after the completion of the execution. What if we need to create a
process within the program and may be wanted to schedule a different task for it. Can this
be achieved? Yes, obviously through process creation. Of course, after the job is done it
would get terminated automatically or you can terminate it as needed.
Process creation is achieved through the fork() system call. The newly created process
is called the child process and the process that initiated it (or the process when execution
is started) is called the parent process. After the fork() system call, now we have two
processes - parent and child processes. How to differentiate them? Very simple, it is
through their return values.
After creation of the child process, let us see the fork() system call details.
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
Creates the child process. After this call, there are two processes, the existing one is called
the parent process and the newly created one is called the child process.
10
Inter Process Communication
Negative value to indicate an error, i.e., unsuccessful in creating the child process.
Returns a positive value for the parent process. This value is the process ID of the
newly created child process.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
fork();
printf("Called fork() system call \n");
return 0;
}
Execution Steps
Compilation
Execution/Output
$ ./basicfork
Called fork() system call
Called fork() system call
$
Note: Usually after fork() call, the child process and the parent process would perform
different tasks. If the same task needs to be run, then for each fork() call it would run 2
power n times, where n is the number of times fork() is invoked.
11
Inter Process Communication
In the above case, fork() is called once, hence the output is printed twice (2 power 1). If
fork() is called, say 3 times, then the output would be printed 8 times (2 power 3). If it is
called 5 times, then it prints 32 times and so on and so forth.
Having seen fork() create the child process, it is time to see the details of the parent and
the child processes.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid, mypid, myppid;
pid = getpid();
printf("Before fork: Process id is %d\n", pid);
pid = fork();
if (pid < 0)
{
perror("fork() failure\n");
return 1;
}
// Child process
if (pid == 0)
{
printf("This is child process \n");
mypid = getpid();
myppid = getppid();
printf("Process id is %d and PPID is %d\n", mypid, myppid);
}
// Parent process
else
{
sleep(2);
12
Inter Process Communication
The difference between _exit() and exit() is mainly the cleanup activity. The exit() does
some cleanup before returning the control back to the kernel, while the _exit() (or
_Exit()) would return the control back to the kernel immediately.
#include <stdio.h>
#include <stdlib.h>
void exitfunc()
{
printf("Called cleanup function - exitfunc()\n");
return;
}
int main()
13
Inter Process Communication
{
atexit(exitfunc);
printf("Hello, World!\n");
exit (0);
}
#include <stdio.h>
#include <unistd.h>
void exitfunc()
{
printf("Called cleanup function - exitfunc()\n");
return;
}
int main()
{
atexit(exitfunc);
printf("Hello, World!\n");
_exit (0);
}
14
Inter Process Communication
15
5. IPC – Child Process Monitoring
Inter Process Communication
As we have seen, whenever we create a child process from a program using fork, the
following happens:
What happens if the parent process finishes its task early than the child process and then
quits or exits? Now who would be the parent of the child process? The parent of the child
process is init process, which is the very first process initiating all the tasks.
To monitor the child process execution state, to check whether the child process is running
or stopped or to check the execution status, etc. the wait() system calls and its variants
is used.
Let us consider an example program, where the parent process does not wait for the child
process, which results into init process becoming the new parent for the child process.
#include<stdio.h>
int main()
{
int pid;
pid = fork();
// Child process
if (pid == 0)
{
system("ps -ef");
sleep(10);
system("ps -ef");
}
else
{
sleep(3);
}
16
Inter Process Communication
return 0;
}
sh-4.3$
Note: Observe that the parent process PID was 94 and the child process PID was 95. After
the parent process exits, the PPID of the child process changed from 94 to 1 (init process).
17
Inter Process Communication
Following are the variants of system calls to monitor the child process/es:
wait()
waitpid()
waitid()
The wait() system call would wait for one of the children to terminate and return its
termination status in the buffer as explained below.
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
This call returns the process ID of the terminated child on success and -1 on failure. The
wait() system call suspends the execution of the current process and waits indefinitely
until one of its children terminates. The termination status from the child is available in
status.
Let us modify the previous program, so that the parent process now waits for the child
process.
#include<stdio.h>
int main()
{
int pid;
int status;
pid = fork();
// Child process
if (pid == 0)
{
system("ps -ef");
sleep(10);
system("ps -ef");
return 3; //exit status is 3 from child process
}
else
{
18
Inter Process Communication
sleep(3);
wait(&status);
printf("In parent process: exit status from child is
decimal %d, hexa %0x\n", status, status);
}
return 0;
}
19
Inter Process Communication
Note: Even though child returns the exit status of 3, why the parent process sees that as
768. The status is stored in the higher order byte, so it is stored in hexadecimal format as
0X0300, which is 768 in decimal. Normal termination is as follows:
The wait() system call has limitation such as it can only wait until the exit of the next child.
If we need to wait for a specific child it is not possible using wait(), however, it is possible
using waitpid() system call.
The waitpid() system call would wait for specified children to terminate and return its
termination status in the buffer as explained below.
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options );
The above call returns the process ID of the terminated child on success and -1 on failure.
The waitpid() system call suspends the execution of the current process and waits
indefinitely until the specified children (as per pid value) terminates. The termination
status from the child is available in the status.
< -1: Wait for any child process whose process group ID is equal to the absolute value of
pid.
-1: Wait for any child process, which equals to that of wait() system call.
0: Wait for any child process whose process group ID is equal to that of the calling process.
>0: Wait for any child process whose process ID is equal to the value of pid.
By default, waitpid() system call waits only for the terminated children but this default
behavior can be modified using the options argument.
Now let us consider a program as an example, waiting for a specific process with its process
id.
/* Filename: waitpid_test.c */
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
20
Inter Process Communication
{
int pid;
int pids[3];
int status;
int numprocesses = 0;
int total_processes = 3;
21
Inter Process Communication
return 0;
}
Now, let us check for waitid() system call. This system call waits for the child process to
change state.
#include <sys/wait.h>
int waitpid(idtype_t idtype, id_t id, siginfo_t *infop, int options );
The above system call waits for the child process to change the state and this call suspends
the current/calling process until any of its child process changes its state. The argument
‘infop’ is to record the current state of the child. This call returns immediately, if the
process has already changed its state.
P_PID: Wait for any child process whose process ID is equal to that of id.
P_PGID: Wait for any child process, whose process group ID is equal to that of id.
The options argument is to specify which state changes and this can be formed with bitwise
OR operation with the below-mentioned flags:
WCONTINUED: Returns the status of any child that was stopped and has been continued.
WSTOPPED: Waits for the process of any child that has stopped, upon receipt of the
signal and returns the status.
This call returns 0, if it returns due to a change of the state of one of its children and
WNOHANG is used. It returns –1, in case of error and sets the appropriate error number.
/* Filename: waitid_test.c */
#include<stdio.h>
22
Inter Process Communication
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
int pid;
int pids[3];
int status;
int numprocesses = 0;
int total_processes = 3;
siginfo_t siginfo;
23
Inter Process Communication
24
6. IPC - Process Groups, SessionsInter
&Process
JobCommunication
Control
In this chapter, we will get familiar with Process Groups, Sessions and Job Control.
Process Group: Process group is a collection of one or more processes. A process group
constitutes of one or more processes sharing the same process group identifier (PGID). A
process group ID (PGID) is of the same type (pid_t) as the process ID. A process group
has a process group leader, which is the process that creates the group and whose process
ID becomes the process group ID of the group.
Job Control: This permits a shell user to simultaneously execute multiple commands (or
jobs), one in the foreground and all remaining in the background. It is also possible to
move the jobs from the foreground to the background and vice-versa.
Let us understand this with the help of example program/s using shell (BASH).
Shell script (in BASH) to perform basic commands (date, echo, sleep and cal)
named basic_commands.sh
#!/bin/bash
#basic_commands.sh
date
echo "Now sleeping for 250 seconds, so that testing job control functionality
is smooth"
sleep 250
cal
#!/bin/bash
#process_status.sh
ps
echo "Now sleeping for 200 seconds, so that testing job control functionality
is smooth"
sleep 200
25
Inter Process Communication
ps
Use chmod command to give the file the execute permissions. By default , the normal file
would get only read and write permissions and not execute permissions.
To stop the current running process, you need to enter CTRL+Z. This gives you a job
number. The job can be resumed either in the foreground or the background. If needed,
to resume the job in the foreground use ‘fg’ command. If needed, to resume the job in the
background, use ‘bg’ command. By using this, it would run only the last stopped process.
What if you want to start other than the last stopped process? Just use the job number
after fg or bg (say bg %2 or bg %3, etc). If the running job is in the background, you can
run any other tasks in the foreground. To get the list of jobs, use command, jobs. It is
also possible to terminate the process either with CTRL+C or kill command. You can pass
the job number while using the kill command.
Check the following output which demonstrates stopping the jobs, moving the jobs from
the foreground to the background and vice versa, terminating the jobs, etc.
./process_status.sh
^Z
[2]+ Stopped ./process_status.sh
$ fg %2
./process_status.sh
^Z
[2]+ Stopped ./process_status.sh
$ fg %1
./basic_commands.sh
^Z
[1]+ Stopped ./basic_commands.sh
$
$ jobs
[1]+ Stopped ./basic_commands.sh
[2]- Stopped ./process_status.sh
$
$ bg %2
[2]- ./process_status.sh &
$ fg
./basic_commands.sh
^Z
[1]+ Stopped ./basic_commands.sh
$ jobs
[1]+ Stopped ./basic_commands.sh
[2]- Running ./process_status .sh &
$ fg %2
./process_status.sh
^Z
[2]+ Stopped ./process_status.sh
$ jobs
[1]- Stopped ./basic_commands.sh
[2]+ Stopped ./process_status.sh
$ kill %1 %2
$
[1]- Terminated ./basic_commands.sh
[2]+ Terminated ./process_status.sh
$
28
7. IPC - Process Resources
Inter Process Communication
The process needs certain resources such as CPU and memory to perform the tasks. Now
we will look into the related commands and system calls to know the information on
resource utilization and monitoring. Also there are certain limits by default for each process
on the resources, and if required the limits can be enhanced to accommodate the
application requirements.
Following are the essential system or process resources information using commands:
The top command continuously displays the usage of system resources. If any process
puts the system in some kind of hang state (consuming more of CPU or Memory) it is
possible to note the process information and take appropriate action ( such as killing the
related process).
The ps command
$ ps
The ps command provides information about all the running processes. This helps to
monitor and control the processes.
The vmstat command reports the statistics of virtual memory subsystem. It reports the
information of processes (waiting to run, sleeping, runnable processes , etc.), memory
(virtual memory information such as free, used, etc.), swap area, IO devices, system
information (number of interrupts, context switches) and CPU (user, system and idle
time).
The lsof command prints the list of open files of all the current running processes, including
system processes.
29
Inter Process Communication
System calls related to accessing and setting resource limits viz., getrlimit(),
setrlimit(), prlimit().
The system call getrusage() returns the information on the system resource usage. This
can include information on self, children, or calling thread using flags RUSAGE_SE LF ,
RUSAGE_CHILDREN, RUSAGE_THREAD for the “who” variable. After the call, it returns the
information in the structure rusage.
/* Filename: sysinfo_getrusage.c */
#include<stdio.h>
#include<sys/time.h>
#include<sys/resource.h>
void main(void)
{
struct rusage res_usage;
int retval;
}
printf("Details of getrusage: \n");
printf("User CPU time (seconds) is %d\n",
(int)res_usage.ru_utime.tv_sec);
printf("User CPU time (micro seconds) is %d\n",
(int)res_usage.ru_utime.tv_usec);
printf("Maximum size of resident set (kb) is %ld\n",
res_usage.ru_maxrss);
printf("Soft page faults (I/O not required) is %ld \n",
res_usage.ru_minflt);
printf("Hard page faults (I/O not required) is %ld \n",
res_usage.ru_majflt);
printf("Block input operations via file system is %ld \n",
res_usage.ru_inblock);
printf("Block output operations via file system is %ld\n",
res_usage.ru_oublock);
printf("Voluntary context switches are %ld\n", res_usage.ru_nvcsw);
printf("Involuntary context switches are %ld \n", res_usage.ru_nivcsw);
return;
}
Let us now look at the system calls related to accessing and setting resource limits.
31
Inter Process Communication
#include <sys/time.h>
#include <sys/resource.h>
int prlimit(pid_t pid, int resource, const struct rlimit *new_limit, struct
rlimit *old_limit);
The system call getrlimit() gets the resource limits in structure rlimit by inputting the
resource one needs such as RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK, etc.
The system call setrlimit() sets the resource limits as mentioned in the rlimit structure
as far as within the limits.
The system call prlimit() is used for varius purposes, such as either for retrieving the
current resourc e limits or for updating the resource limits to new values.
RLIMIT_NOFILE – Returns the maximum number of file descriptors that can be opened by
this process. For example, if it returns 1024, then the process has file descriptors from 0
to 1023.
RLIMIT_NPROC – Maximum number of processes that can be created for a user of that
process.
RLIMIT_STACK – The maximum size in bytes of the st ack segment for that process.
All these calls would return “0” on success and “-1” on failure.
Let us consider the following example where we are using getrlimit() system call.
/* Filename: sysinfo_getrlimit.c */
#include<stdio.h>
#include<sys/time.h>
#include<sys/resource.h>
void main(void)
{
struct rlimit res_limit;
int retval;
int resources[] = {RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK};
32
Inter Process Communication
int max_res;
int counter = 0;
return;
}
Let us consider another example with getrlimit() system call but now with prlimit() system
call.
/* Filename: sysinfo_prlimit.c */
33
Inter Process Communication
#include<stdio.h>
#include<unistd.h>
#include<sys/time.h>
#include<sys/resource.h>
void main(void)
{
struct rlimit res_limit;
int retval;
int resources[] = {RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_STACK};
int max_res;
int counter = 0;
return;
}
34
Inter Process Communication
35
8. IPC – Other Processes
Inter Process Communication
So far, we have discussed about processes, its creation, parent and child processes , etc.
The discussion will be incomplete without discussing other related processes, such as the
Orphan process, Zombie process and Daemon process.
Orphan Process
As indicated by the name, orphan implies parentless process. When we run a program or
application, the parent process for the application is shell. When we create a process using
fork(), the newly created process is the child process and the process that created the
child is the parent process. In turn, the parent process of this is shell. Of course, the parent
of all the processes is init process (Process ID 1).
The above is a usual scenario, however, what happens if the parent process exits before
the child process. The result is, the child process now becomes the orphan process. Then
what about its parent, its new parent is the parent of all the processes, which is nothing
but init process (Process ID – 1).
#include<stdio.h>
#include<stdlib.h>
int main()
{
int pid;
system("ps -f");
pid = fork();
if (pid == 0)
{
printf("Child: pid is %d and ppid
is %d\n",getpid(),getppid());
sleep(5);
printf("Child: pid is %d and ppid
is %d\n",getpid(),getppid());
system("ps -f");
}
else
{
36
Inter Process Communication
return 0;
}
Zombie Process
In simple terms, assume that you have two processes, namely the parent and the child
process. It is the responsibility of the parent process to wait for child process and then
clean up the child process entry from the process table. What if the parent process is not
ready to wait for the child process, and in the meantime the child process gets its job done
and exits? Now, the child process would become the zombie process. Of course, the zombie
process is cleaned up after the parent process becomes ready.
37
Inter Process Communication
#include<stdio.h>
#include<stdlib.h>
int main()
{
int pid;
pid = fork();
if (pid == 0)
{
system("ps -f");
printf("Child: pid is %d and ppid
is %d\n",getpid(),getppid());
exit(0);
}
else
{
printf("Parent: pid is %d and ppid
is %d\n",getpid(),getppid());
sleep(10);
system("ps aux|grep Z");
}
return 0;
}
Daemon Process
In simple terms, the process which doesn’t have any associated shell or terminal is know n
as the daemon process. Why this is needed? These are the processes which run in the
background to perform actions at predefined intervals and also respond to certain events.
The daemon process should not have any user interaction, since it runs as a background
process.
The internal Linux daemon processes usually ends with the letter “d” such as Kernel
Daemons (ksoftirqd, kblockd, kswapd, etc.), Printing Daemons (cupsd, lpd, etc.), File
Service Daemons (smbd, nmbd, etc.), Administrative database daemons (ypbind, ypserv,
etc.), Electronic Mail Daemons (sendmail, popd, smtpd, etc.), Remote Login and Command
Execution Daemons (sshd, in.telnetd, etc.), Booting and Configuration Daemons (dhcpd,
udevd, etc.), init process (init), cron daemon, atd daemon, etc.
Now let us see how to create a daemon process. Following are the steps -
Step 1: Create a child process. Now we have two processes – the parent process and the
child process
Usually the process hierarchy is SHELL -> PARENT PROCESS -> CHILD PROCESS
Step 2: Terminate the parent process by exiting. The child process now becomes the
orphan process and is taken over by init process.
Step 3: Calling the setsid() system call creates a new session, if the calling process is not
a process group leader. Now the calling process becomes the group leader of the new
session. This process will be the only process in this new process group and in this new
session.
Step 4: Set the process group ID and session ID to PID of the calling process.
Step 5: Close the default file descriptors (standard input, standard output , and standard
error) of the process as the terminal and shell are now disconnected from the application.
/* Filename: daemon_test.c */
#include<stdio.h>
39
Inter Process Communication
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char *argv[])
{
pid_t pid;
int counter;
int fd;
int max_iterations;
char buffer[100];
if (argc < 2)
max_iterations = 5;
else
{
max_iterations = atoi(argv[1]);
if ( (max_iterations <= 0) || (max_iterations > 20) )
max_iterations = 10;
}
pid = fork();
// Unable to create child process
if (pid < 0) {
perror("fork error\n");
exit(1);
}
// Child process
if (pid == 0) {
return 1;
}
printf("Child: pid is %d and ppid is %d\n", getpid(), getppid());
printf("\nChild process before becoming session leader\n");
sprintf(buffer, "ps -ef|grep %s", argv[0]);
system(buffer);
setsid();
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
else {
printf("Parent: pid is %d and ppid is %d\n", getpid(), getppid());
printf("Parent: Exiting \n");
exit(0);
}
strcpy(buffer, "Done\n");
write(fd, buffer, strlen(buffer));
41
Inter Process Communication
close(fd);
return 0;
}
$
$ cat /tmp/DAEMON.txt
Daemon process: pid is 137 and ppid is 1
Daemon process: pid is 137 and ppid is 1
Daemon process: pid is 137 and ppid is 1
Done
$
42
9. IPC - Overlaying ProcessInterImage
Process Communication
Assume that we are running a program and we want to run another program from the
current program. Is this possible? Why not, if we implement the concept of overlaying the
process image. That’s fine but what about the current running program, can that be run
too. How is it possible, since we overlaid the current program with the new program. What
to do, if I want to run the two programs without losing the current running program, is it
possible? Yes, it is possible.
Create a child process, so that we have a parent process and a newly created child process.
Already we are running the current program in the parent process, so run the newly
created process in the child. In this way, we can run another program from the current
program. Not only a single program but we can run any number of programs from the
current program by creating that many number of child processes.
#include<stdio.h>
void main()
{
printf("Hello World\n");
return;
}
#include<stdio.h>
#include<unistd.h>
void main()
{
execl("./helloworld", "./helloworld", (char *)0);
printf("This wouldn't print\n");
return;
}
The above program would overlay the process image of execl_test with helloworld. That
is the reason, the process image code of execl_test (printf()) is not executed.
43
Inter Process Communication
Now, we will run the following two programs from one program, i.e.,
execl_run_two_prgms.c.
#include<stdio.h>
void main()
{
int value = 1;
return;
}
Following is the program to run two programs (one program from child and another
program from parent).
/* Filename: execl_run_two_prgms.c */
#include<stdio.h>
#include<unistd.h>
void main()
{
44
Inter Process Communication
int pid;
pid = fork();
/* Child process */
if (pid == 0) {
printf("Child process: Running Hello Wo rld Program\n");
execl("./helloworld", "./helloworld", (char *)0);
printf("This wouldn't print \n");
}
/* Parent process */
else {
sleep(3);
printf("Parent process: Running While loop Program\n");
execl("./while_loop", "./while_loop", (char *)0);
printf("Won't reach here \n");
}
return;
}
Note: Place sleep() call to make sure the child and parent processes run sequentially (do
not overlap the result).
45
Inter Process Communication
Now we would run two programs from one program i.e., execl_run_two_prgms.c, same
program as above but with command line arguments. So, we are running two progra ms
namely, helloworld.c in the child process, and the program while_loop.c in the parent
process. This is as follows:
Parent process executes while_loop.c program passing the command line argument
value as an argument to the program. If the command line arguments are not
passed, then the default is taken as 10. Otherwise, it takes the given argument
value. The argument value should be numeric ; code would not validate if given in
alphabets.
/* Filename: execl_run_two_prgms.c */
#include<stdio.h>
#include<string.h>
#include<unistd.h>
void main(int argc, char *argv[0])
{
int pid;
int err;
int num_times;
char num_times_str[5];
46
Inter Process Communication
pid = fork();
/* Child process */
if (pid == 0) {
printf("Child process: Running Hello World Program\n");
err = execl("./helloworld", "./helloworld", (char *)0);
printf("Error %d\n", err);
perror("Execl error: ");
printf("This wouldn't print \n");
}
/* Parent process */
else {
sleep(3);
printf("Parent process: Running While loop Program\n");
execl("./while_loop", "./while_loop", (char *)num_times_str,
(char *)0);
printf("Won't reach here \n");
}
return;
}
Following is the helloworld.c program called from the child process of the program,
execl_run_two_prgms.c
#include<stdio.h>
void main()
{
printf("Hello World\n");
return;
}
47
Inter Process Communication
Following is the while_loop.c program called from the parent process of the program,
execl_run_two_prgms.c. The argument to this program is passed from the program which
runs this i.e., execl_run_two_prgms.c.
/* Filename: while_loop.c */
#include<stdio.h>
void main(int argc, char *argv[])
{
int start_value = 1;
int end_value;
if (argc == 1)
end_value = 10;
else
end_value = atoi(argv[1]);
return;
}
48
Inter Process Communication
Argv[1] is 10
1 2 3 4 5 6 7 8 9 10
$ ./execl_run_two_prgms 15
num_times_str is 15
Child process: Running Hello World Program
Hello World
Parent process: Running While loop Program
Argv[1] is 15
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15
$
#include<unistd.h>
This function would overlay the current running process image with the new process as
mentioned in the arguments, path and arg. If any argument needs to passed to a new
process image, that would be send through “arg” arguments and the last argument should
be NULL.
This function would return a value only in case of an error. The process overlaying image
related calls are as mentioned below:
These calls would address passing command line arguments (argv[]), environment
variables (envp[]) and other parameters.
49
10. IPC - Related System Calls Inter
(System V)
Process Communication
Following table lists the various System calls along with their description.
System
Category Description
Call
This system call either opens an already existing file or
General open()
creates and opens a new file.
General read() Reads the contents of the file into the required buffer.
Named Pipes
mknod() Creates a memory device file or special file to create FIFOs
or Fifo
Named Pipes
mkfifo() Creates a new FIFO
or Fifo
Shared
shmdt() Detaches the shared memory segment.
Memory
Performs control operations for the shared memory. Few of
the generic control operations for the shared memory are
Shared removing the shared memory segment (IPC_RMID), receiving
shmctl()
Memory the information of the shared memory (IPC_STAT) and
updating new values of the existing shared memory
(IPC_SET).
Creates a new message queue or accesses an already existing
Message message queue and get s the handle or identifier to perform
msgget()
Queues operations with regard to message queue, such as sending
message/s to queue and receiving message/s from the queue.
Message msgsnd( Sends a message to the required message queue with the
Queues ) required identification number.
50
Inter Process Communication
Memory munmap
Unmapping the mapped files from the memory.
Mapping ()
51
11. IPC - System V & POSIX
Inter Process Communication
Following table lists the differences between System V IPC and POSIX IPC.
SYSTEM V POSIX
Uses keys and identifiers to identify the Uses names and file descriptors to identify
IPC objects. IPC objects
52
Inter Process Communication
Requires system calls such as shmctl(), Shared memory objects can be examined
commands (ipcs, ipcrm) to perform and manipulated using system calls such
status/control operations. as fstat(), fchmod()
53
12. IPC - Pipes Inter Process Communication
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).
#include<unistd.h>
This system call would create a pipe for one-way communication i.e., it creates two
descriptors, first one is connected to read from the pipe and other one is connected to
write into the pipe.
Descriptor pipedes[0] is for reading and pipedes[1] is for writing. Whatever is written into
pipedes[1] can be read from pipedes[0].
This call would return zero on success and -1 in case of failure. To know the cause of
failure, check with errno variable or perror() function.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
54
Inter Process Communication
Even though the basic operations for file are read and write, it is essential to open the file
before performing the operations and closing the file after completion of the required
operations. Usually, by default, 3 descriptors opened for every process, which are used for
input (standard input – stdin), output (standard output – stdout) and error (standard error
– stderr) having file descriptors 0, 1 and 2 respectively.
This system call would return a file descriptor used for further file operations of
read/write/seek (lseek). Usually file descriptors start from 3 and increase by one number
as the number of files open.
The arguments passed to open system call are pathname (relative or absolute path), flags
mentioning the purpose of opening file (say, opening for read, O_RDONLY, to writ e,
O_WRONLY, to read and write, O_RDWR, to append to the existing file O_APPEND, to
create file, if not exists with O_CREAT and so on) and the required mode providing
permissions of read/write/execute for user or owner/group/others. Mode can be
mentioned with symbols.
For example: Octal value (starts with 0), 0764 implies owner has read, write and execute
permissions, group has read and write permissions, other has read permissions. This can
also be represented as S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH, which implies or
operation of 0700|0040|0020|0004 0764.
This system call, on success, returns the new file descriptor id and -1 in case of error. The
cause of error can be identified with errno variable or perror() function.
#include<unistd.h>
The above system call closing already opened file descriptor. This implies the file is no
longer in use and resources associated can be reused by any other process. This system
call returns zero on success and -1 in case of error. The cause of error can be identified
with errno variable or perror() function.
#include<unistd.h>
The above system call is to read from the specified file with arguments of file descriptor
fd, proper buffer with allocated memory (either static or dynamic) and the size of buffer.
55
Inter Process Communication
The file descriptor id is to identify the respective file, which is returned after calling open()
or pipe() system call. The file needs to be opened before reading f rom the file. It
automatically opens in case of calling pipe() system call.
This call would return the number of bytes read (or zero in case of encountering the end
of the file) on success and -1 in case of failure. The return bytes can be smaller than the
number of bytes requested, just in case no data is available or file is closed. Proper error
number is set in case of failure.
To know the cause of failure, check with errno variable or perror() function.
#include<unistd.h>
The above system call is to write to the specified file with arguments of the file descriptor
fd, a proper buffer with allocated memory (either static or dynamic) and the size of buffer.
The file descriptor id is to identify the respect ive file, which is returned after calling open()
or pipe() system call.
The file needs to be opened before writing to the file. It automatically opens in case of
calling pipe() system call.
This call would return the number of bytes written (or zero in case nothing is written) on
success and -1 in case of failure. Proper error number is set in case of failure.
To know the cause of failure, check with errno variable or perror() function.
Example Programs
Following are some example programs.
Example program 1: Program to write and read two messages using pipe.
Algorithm:
Step 3: Retrieve the message from the pipe and write it to the standard output.
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 .
56
Inter Process Communication
#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
Execution/Output
sh-4.3$ ./simplepipe
Writing to pipe - Message 1 is Hi
Reading from pipe – Message 1 is Hi
57
Inter Process Communication
Example program 2: Program to write and read two messages through the pipe using
the parent and the child processes.
Algorithm:
Step 4: Child process retrieves the message from the pipe and writes it to the standard
output.
#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);
58
Inter Process Communication
Execution Steps
Compilation
Execution
$ ./ pipewithprocesses
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
Step 1: Create two pipes. First one is for the parent to write and child to read, say as
pipe1. Second one is for the child to write and parent to read, say as pipe2.
59
Inter Process Communication
Step 3: Close unwanted ends as only one end is needed for each communication.
Step 4: Close unwanted ends in the parent process, read end of pipe1 and write end of
pipe2.
Step 5: Close the unwanted ends in the child process, write end of pipe1 and read end of
pipe2.
Sample Programs
Algorithm:
Step 1: Create pipe1 for the parent process to write and the child process to read.
Step 2: Create pipe2 for the child process to write and the parent process to read.
Step 3: Close the unwanted ends of the pipe from the parent and child side.
Step 4: Parent process to write a message and child process to read and display on the
screen.
Step 5: Child process to write a message and parent process to read and display on the
screen.
60
Inter Process Communication
#include<stdio.h>
#include<unistd.h>
int main()
{
int pipefds1[2], pipefds2[2];
int returnstatus1, returnstatus2;
int pid;
char pipe1writemessage[20] = "Hi";
char pipe2writemessage[20] = "Hello";
char readmessage[20];
returnstatus1 = pipe(pipefds1);
if (returnstatus1 == -1)
{
printf("Unable to create pipe 1 \n");
return 1;
}
returnstatus2 = pipe(pipefds2);
if (returnstatus2 == -1)
{
printf("Unable to create pipe 2 \n");
return 1;
}
pid = fork();
if (pid != 0) // Parent process
{
close(pipefds1[0]); // Close the unwanted pipe1 read side
close(pipefds2[1]); // Close the unwanted pipe2 write side
printf("In Parent: Writing to pipe 1 – Message is %s\n",
pipe1writemessage);
write(pipefds1[1], pipe1writemessage, sizeof(pipe1writemessage));
read(pipefds2[0], readmessage, sizeof(readmessage));
printf("In Parent: Reading from pipe 2 – Message is %s\n",
readmessage);
}
61
Inter Process Communication
Execution Steps
Compilation
Execution
$ ./twowayspipe
In Parent: Writing to pipe 1 – Message is Hi
In Child: Reading from pipe 1 – Message is Hi
In Child: Writing to pipe 2 – Message is Hello
In Parent: Reading from pipe 2 – Message is Hello
62
13. IPC - Named Pipes
Inter Process Communication
Pipes were meant for communication between related processes. Can we use pipes for
unrelated process communication, say, we want to execute client program from one
terminal and the server program from another terminal? The answer is No. Then how can
we achieve unrelated processes communication, the simple answer is Named Pipes. Even
though this works for related processes, it gives no meaning to use the named pipes for
related process communication.
We used one pipe for one-way communication and two pipes for bi-directional
communication. Does the same condition apply for Named Pipes. The answer is no, we
can use single named pipe that can be used for two-way communication (communicat ion
between the server and the client, plus the client and the server at the same time) as
Named Pipe supports bi-directional communication.
Another name for named pipe is FIFO (First-In-First-Out). Let us see the system call
(mknod()) to create a named pipe, which is a kind of a special file.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
This system call would create a special file or file system node such as ordinary file, device
file, or FIFO. The arguments to the system call are pathname, mode and dev. The
pathname along with the attributes of mode and device information. The pathname is
relative, if the directory is not specified it would be created in the current directory. The
mode specified is the mode of file which specifies the file type such as the type of file and
the file mode as mentioned in the following tables. The dev field is to specify device
information such as major and minor device numbers.
63
Inter Process Communication
File
File Mode Description Description
Mode
Execute/search permission,
S_IRUSR Read permission, owner S_IXGRP
group
Read, write, execute/search
S_IWUSR Write permission, owner S_IRWXO
by others
Execute/search permission,
S_IXUSR S_IROTH Read permission, others
owner
File mode can also be represented in octal notation such as 0XYZ, where X represents
owner, Y represents group, and Z represents others. T he value of X, Y or Z can range from
0 to 7. The values for read, write and execute are 4, 2, 1 respectively. If needed in
combination of read, write and execute, then add the values accordingly.
Say, if we mention, 0640, then this means read and write (4 + 2 = 6) for owner, read (4)
for group and no permissions (0) for others.
This call would return zero on success and -1 in case of failure. To know the cause of
failure, check with errno variable or perror() function.
#include <sys/types.h>
#include <sys/stat.h>
This library function creates a FIFO special file, which is used for named pipe. The
arguments to this function is file name and mode. The file name can be either absolute
path or relative path. If full path name (or absolute path) is not given, the file would be
created in the current folder of the executing process. The file mode information is as
described in mknod() system call.
This call would return zero on success and -1 in case of failure. To know the cause of
failure, check with errno variable or perror() function.
Let us consider a program of running the server on one terminal and running the client on
another terminal. The program would only perform one-way communication. The client
accepts the user input and sends the message to the server, the server prints the message
on the output. The process is continued until the user enters the string “end”.
64
Inter Process Communication
Step 1: Create two processes, one is fifoserver and another one is fifoclient.
Creates a named pipe (using system call mknod()) with name “MYFIFO”, if not
created.
Here, created FIFO with permissions of read and write for Owner. Read for Group
and no permissions for Others.
If the message received from the client is not “end”, prints the message. If the
message is “end”, closes the fifo and ends the process.
Checks, if the user enters “end” or other than “end”. Either way, it sends a message
to the server. However, if the string is “end”, this closes the FIFO and also ends
the process.
/* Filename: fifoserver.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
65
Inter Process Communication
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
while(1)
{
fd = open(FIFO_FILE, O_RDONLY);
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("Received string: \"%s\" and length is %d\n", readbuf,
(int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
}
return 0;
}
66
Inter Process Communication
/* Filename: fifoclient.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd;
int end_process;
int stringlen;
char readbuf[80];
char end_str[5];
67
Inter Process Communication
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
return 0;
}
Already, we have seen the one-directional communication between named pipes, i.e., the
messages from the client to the server. Now, let us take a look at the bi-directional
communication i.e., the client sending message to the server and the server receiving the
message and sending back another message to the client using the same named pipe.
Following is an example:
Creates a named pipe (using library function mkfifo()) with name “fifo_twoway” in
/tmp directory, if not created.
Here, created FIFO with permissions of read and write for Owner. Read for Group
and no permissions for Others.
If the message received from the client is not “end”, prints the message and
reverses the string. The reversed string is sent back to the client. If the message
is “end”, closes the fifo and ends the process.
69
Inter Process Communication
Checks, if the user enters “end” or other than “end”. Either way, it sends a message
to the server. However, if the string is “end”, this closes the FIFO and also ends
the process.
If the message is sent as not “end”, it waits for the message (reversed string) from
the client and prints the reversed string.
/* Filename: fifoserver_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
while(1)
{
read_bytes = read(fd, readbuf, sizeof(readbuf));
70
Inter Process Communication
return 0;
}
last = strlen(str) - 1;
limit = last/2;
first = 0;
while (first < last) {
temp = str[first];
str[first] = str[last];
str[last] = temp;
first++;
last--;
}
71
Inter Process Communication
return;
}
/* Filename: fifoclient_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd;
int end_process;
int stringlen;
int read_bytes;
char readbuf[80];
char end_str[5];
72
Inter Process Communication
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
return 0;
}
73
Inter Process Communication
74
14. IPC - Shared Memory
Inter Process Communication
Shared memory is a memory shared between two or more processes. However, why do
we need to share memory or some other means of communication?
To reiterate, 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. As we are already aware,
communication can be between related or unrelated processes.
We have seen the IPC techniques of Pipes and Named pipes and now it is time to know
the remaining IPC techniques viz., Shared Memory, Message Queues, Semaphores,
Signals, and Memory Mapping.
We know that to communicate between two or more processes , we use shared memory
but before using the shared memory what needs to be done with the system calls, let us
see this -
Create the shared memory segment or use an already created shared memory
segment (shmget())
Attach the process to the already created shared memory segment (shmat())
Detach the process from the already attached shared memory segment (shmdt())
Control operations on the shared memory segment (shmctl())
75
Inter Process Communication
Let us look at a few details of the system calls related to shared memory.
#include <sys/ipc.h>
#include <sys/shm.h>
The above system call creates or allocates a System V shared memory segment. The
arguments that need to be passed are as follows:
The first argument, key, recognizes the shared memory segment. The key can be either
an arbitrary value or one that can be derived from the library function ftok(). The key can
also be IPC_PRIVATE, means, running processes as server and client (parent and child
relationship) i.e., inter-related process communiation. If the client wants to use shared
memory with this key, then it must be a child process of the server. Also, the child process
needs to be created after the parent has obtained a shared memory.
The second argument, size, is the size of the shared memory segment rounded to
multiple of PAGE_SIZE.
The third argument, shmflg, specifies the required shared memory flag/s such as
IPC_CREAT (creating new segment) or IPC_EXCL (Used with IPC_CREAT to create new
segment and the call fails, if the segment already exists). Need to pass the permissions
as well.
This call would return a valid shared memory identifier (used for further calls of shared
memory) on success and -1 in case of failure. To know the cause of failure, check with
errno variable or perror() function.
#include <sys/types.h>
#include <sys/shm.h>
The above system call performs shared memory operation for System V shared memory
segment i.e., attaching a shared memory segment to the address space of the calling
process. The arguments that need to be passed are as follows:
The first argument, shmid, is the identifier of the shared memory segment. This id is
the shared memory identifier, which is the return value of shmget() system call.
76
Inter Process Communication
shmaddr must be a page aligned address at which the shared memory attachment
occurs/starts.
The third argument, shmflg, specifies the required shared memory flag/s such as
SHM_RND (rounding off address to SHMLBA) or SHM_EXEC (allows the contents of
segment to be executed) or SHM_RDONLY (attaches the segment for read-only purpose ,
by default it is read-write) or SHM_REMAP (replaces the existing mapping in the range
specified by shmaddr and continuing till the end of segment).
This call would return the address of attached shared memory segment on success and -
1 in case of failure. To know the cause of failure, check with errno variable or perror()
function.
#include <sys/types.h>
#include <sys/shm.h>
The above system call performs shared memory operation for System V shared memory
segment of detaching the shared memory segment from the address space of the calling
process. The argument that needs to be passed is -
The argument, shmaddr, is the address of shared memory segment to be de tached. The
to-be-detached segment must be the address returned by the shmat() system call.
This call would return 0 on success and -1 in case of failure. To know the cause of failure,
check with errno variable or perror() function.
#include <sys/ipc.h>
#include <sys/shm.h>
The above system call performs control operation for a System V shared memory segment .
The following arguments needs to be passed -
The first argument, shmid, is the identifier of t he shared memory segment. This id is the
shared memory identifier, which is the return value of shmget() system call.
The second argument, cmd, is the command to perform the required control operation on
the shared memory segment.
IPC_STAT – Copies the information of the current values of each member of struct
shmid_ds to the passed structure pointed by buf. This command requires read permission
to the shared memory segment.
IPC_SET – Sets the user ID, group ID of the owner, permissions, etc. pointed to by
structure buf.
77
Inter Process Communication
IPC_RMID – Marks the segment to be destroyed. The segment is destroyed only after
the last process has detached it.
IPC_INFO – Returns the information about the shared memory limits and parameters in
the structure pointed by buf.
The third argument, buf, is a pointer to the shared memory structure named struct
shmid_ds. The values of this structure would be used for either set or get as per cmd.
This call returns the value depending upon the passed command. Upon success of
IPC_INFO and SHM_INFO or SHM_STAT returns the index or identifier of the shared
memory segment or 0 for other operations and -1 in case of failure. To know the cause of
failure, check with errno variable or perror() function.
Create two processes, one is for writing into the shared memory (shm_write.c) and
another is for reading from the shared memory (shm_read.c)
The program performs writing into the shared memory by write process
(shm_write.c) and reading from the shared memory by reading process
(shm_read.c)
In the shared memory, the writing process, creates a shared memory of size 1K
(and flags) and attaches the shared memory
The write process writes 5 times the Alphabets from ‘A’ to ‘E’ each of 1023 bytes
into the shared memory. Last byte signifies the end of buffer
Read process would read from the shared memory and write to the standard output
Reading process performs reading from the shared memory and displays on the
output until it gets indication of write process completion (complete variable in
struct shmseg)
Performs reading and writing process for a few times for simplication and also in
order to avoid infinite loops and complicating the program
78
Inter Process Communication
Following is the code for write process (Writing into Shared Memory – File: shm_write.c)
/* Filename: shm_write.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
struct shmseg {
int cnt;
int complete;
char buf[BUF_SIZE];
};
bufptr = shmp->buf;
spaceavailable = BUF_SIZE;
sleep(3);
}
shmp->complete = 1;
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
return 0;
80
Inter Process Communication
81
Inter Process Communication
Following is the code for read process (Reading from the Shared Memory and writing to
the standard output – File: shm_read.c)
/* Filename: shm_read.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
struct shmseg {
int cnt;
int complete;
char buf[BUF_SIZE];
};
return 1;
}
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
return 0;
}
83
Inter Process Communication
84
Inter Process Communication
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCC"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
DDDDDDDDDD"
Reading Process: Shared Memory: Read 1023 bytes
segment contains :
"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEE"
Reading Process: Shared Memory: Read 1023 bytes
Reading Process: Reading Done, Detaching Shared Memory
Reading Process: Complete
$
85
15. IPC - Message Queues
Inter Process Communication
Why do we need message queues when we already have the shared memory ? It would be
for multiple reasons, let us try to break this into multiple points for simplification:
Frequency of writing and reading using the shared memory is high, then it would
be very complex to implement the functionality. Not worth with regard to utilization
in this kind of cases.
What if all the processes do not need to access the shared memory but very few
processes only need it, it would be better to implement with message queues.
Ofcourse, the order of message queue is FIFO (First In First Out). The first message
inserted in the queue is the first one to be retrieved.
Using Shared Memory or Message Queues depends on the need of the application and how
effectively it can be utilized.
Writing into the shared memory by one process and reading from the shared
memory by another process. As we are aware, reading can be done with multiple
processes as well.
86
Inter Process Communication
Writing into the shared memory by one process with different data packets and
reading from it by multiple processes, i.e., as per message type.
Having seen certain information on message queues, now it is time to check for the system
call (System V) which supports the message queues.
To perform communication using message queues, follow ing are the steps:
Now, let us check the syntax and certain information on the above calls.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
87
Inter Process Communication
This system call creat es or allocates a System V message queue. Following argument s
need to be passed:
The first argument, key, recognizes the message queue. The key can be either an
arbitrary value or one that can be derived from the library function ftok().
The second argument, shmflg, specifies the required message queue flag/s such
as IPC_CREAT (creating message queue if not exists) or IPC_EXCL (Used with
IPC_CREAT to create the message queue and the call fails, if the message queue
already exists). Need to pass the permissions as well.
This call would return a valid message queue identifier (used for further calls of message
queue) on success and -1 in case of failure. To know the cause of failure, check with errno
variable or perror() function.
Various errors with respect to this call are EACCESS (permission denied), EEXIST (queue
already exists can’t create), ENOENT (queue doesn’t exist), ENOMEM (not enough memory
to create the queue), etc.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)
This system call sends/appends a message into the message queue (System V). Following
arguments need to be passed:
The first argument, msgid, recognizes the message queue i.e., message queue
identifier. The identifier value is received upon the success of msgget()
The second argument, msgp, is the pointer to the message, sent to the caller,
defined in the structure of the following form:
struct msgbuf {
long mtype;
char mtext[1];
};
The variable mtype is used for communicating with different message types, explained in
detail in msgrcv() call. The variable mtext is an array or other structure whose size is
specified by msgsz (posit ive value). If the mtext field is not mentioned, then it is
considered as zero size message, which is permitted.
88
Inter Process Communication
The third argument, msgsz, is the size of message (the message should end with
a null character)
The fourth argument, msgflg, indicates certain flags such as IPC_NOWAIT (returns
immediately when no message is found in queue or MSG_NOERROR (truncates
message text, if more than msgsz bytes)
This call would return 0 on success and -1 in case of failure. To know the cause of failure,
check with errno variable or perror() function.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, const void *msgp, size_t msgsz, long msgtype, int
msgflg)
This system call retrieves the message from the message queue (System V). Following
arguments need to be passed:
The first argument, msgid, recognizes the message queue i.e., the message queue
identifier. The identifier value is received upon the success of msgget()
The second argument, msgp, is the pointer of the message rec eived from the caller.
It is defined in the structure of the following form:
struct msgbuf {
long mtype;
char mtext[1];
};
The variable mtype is used for communicating with different message types. The variable
mtext is an array or other st ructure whose size is specified by msgsz (positive value). If
the mtext field is not mentioned, then it is considered as zero size message, which is
permitted.
The third argument, msgsz, is the size of the message received (message should
end with a null c haracter)
o If msgtype is +ve – Reads the first message in the queue of type msgtype (if
msgtype is 10, then reads only the first message of type 10 even though other
types may be in the queue at the beginning)
89
Inter Process Communication
o If msgtype is –ve – Reads the first message of lowest type less than or equal
to the absolute value of message type (say, if msgtype is -5, then it reads first
message of type less than 5 i.e., message type from 1 to 5)
The fifth argument, msgflg, indicates certain flags such as IPC_NOWAIT (returns
immediately when no message is found in the queue or MSG_NOERROR (truncates
the message text if more than msgsz bytes)
This call would return the number of bytes actually received in mtext array on success and
-1 in case of failure. To know the cause of failure, check with errno variable or perror()
function.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
This system call performs control operations of the message queue (System V). Following
arguments need to be passed -
The first argument, msgid, recognizes the message queue i.e., the message queue
identifier. The identifier value is received upon the success of msgget()
The second argument, cmd, is the command to perform the required control
operation on the message queue. Valid values for cmd are -
IPC_STAT – Copies information of the current values of each member of struct msqid_ds
to the passed structure pointed by buf. This command requires read permission on the
message queue.
IPC_SET – Sets the user ID, group ID of the owner, permissions etc pointed to by
structure buf.
IPC_INFO – Returns information about the message queue limits and parameters in the
structure pointed by buf, which is of type struct msginfo
The third argument, buf, is a pointer to the message queue structure named struct
msqid_ds. The values of this structure would be used for either set or get as per
cmd.
This call would return the value depending on the passed command. Success of IPC_INF O
and MSG_INFO or MSG_STAT returns the index or identifier of the message queue or 0
for other operations and -1 in case of failure. To know the cause of failure, check with
errno variable or perror() function.
90
Inter Process Communication
Having seen the basic information and system calls with regard to message queues, now
it is time to check with a program.
Step 1: Create two processes, one is for sending into message queue (msgq_send.c) and
another is for retrieving from the message queue (msgq_recv.c)
Step 2: Creating the key, using ftok() function. For this, initially file msgq.txt is created
to get a unique key.
To simplify, we are not using the message type for this sample. Also, one process is writing
into the queue and another process is reading from the queue. This can be extended as
needed i.e., ideally one process would write into the queue and multiple processes read
from the queue.
Now, let us check the process (message sending into queue) – File: msgq_send.c
/* Filename: msgq_send.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
91
Inter Process Communication
struct my_msgbuf {
long mtype;
char mtext[200];
};
int main(void)
{
struct my_msgbuf buf;
int msqid;
int len;
key_t key;
system("touch msgq.txt");
strcpy(buf.mtext, "end");
len = strlen(buf.mtext);
if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
92
Inter Process Communication
perror("msgsnd");
Following is the code from message receiving process (retrieving message from queue) –
File: msgq_recv.c
/* Filename: msgq_recv.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msgbuf {
93
Inter Process Communication
long mtype;
char mtext[200];
};
int main(void)
{
struct my_msgbuf buf;
int msqid;
int toend;
key_t key;
94
Inter Process Communication
95
16. IPC - Semaphores
Inter Process Communication
The first question that comes to mind is, why do we need semaphores? A simple answer,
to protect the critical/common region shared among multiple processes.
Let us assume, multiple processes are using the same region of code and if all want to
access parallelly then the outcome is overlapped. Say, for example, multiple users are
using one printer only (common/critical section), say 3 users, given 3 jobs at same time,
if all the jobs start parallelly, then one user output is overlapped with another. So, we
need to protect that using semaphores i.e., locking the critical section when one process
is running and unlocking when it is done. This would be repeated for each user/process so
that one job is not overlapped with another job.
Counting Semaphores: Semaphores which allow arbitrary resource count are called
counting semaphores.
Assume that we have 5 printers (to understand assume that 1 printer only accepts 1 job)
and we got 3 jobs to print. Now 3 jobs would be given for 3 printers (1 each). Again 4 jobs
came while this is in progress. Now, out of 2 printers available, 2 jobs have been scheduled
and we are left with 2 more jobs, which would be completed only after one of the
resource/printer is available. This kind of scheduling as per resource availability can be
viewed as counting semaphores.
Step 2: Perform operations on the semaphore i.e., allocate or release or wait for the
resources (semop())
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
96
Inter Process Communication
This system call creates or allocates a System V semaphore set. The following argument s
need to be passed:
The first argument, key, recognizes the message queue. The key can be either an
arbitrary value or one that can be derived from the library function ftok().
The second argument, nsems, specifies the number of semaphores. If binary then
it is 1, implies need of 1 semaphore set, otherwise as per the required count of
number of semaphore sets.
The third argument, semflg, specifies the required semaphore flag/s such as
IPC_CREAT (creating semaphore if it does not exist) or IPC_EXCL (used with
IPC_CREAT to create semaphore and the call fails, if a semaphore already exists).
Need to pass the permissions as well.
This call would return valid semaphore identifier (used for further calls of semaphores) on
success and -1 in case of failure. To know the cause of failure, check with errno variable
or perror() function.
Various errors with respect to this call are EACCESS (permission denied), EEXIST (queue
already exists can’t create), ENOENT (queue doesn’t exist), ENOMEM (not enough memory
to create the queue), ENOSPC (maximum sets limit exceeded), etc.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
This system call performs the operations on the System V semaphore sets viz., allocating
resources, waiting for the resources or freeing the resources. Following arguments need
to be passed:
The first argument, semid, indicates semaphore set identifier created by semget().
struct sembuf {
unsigned short sem_num; /* Semaphore set num */
short sem_op; /* Semaphore operation */
short sem_flg; /* Operation flags, IPC_NOWAIT, SEM_UNDO */
};
97
Inter Process Communication
Element, sem_op, in the above structure, indicates the operation that needs to be
performed:
o If sem_op is –ve, allocate or obtain resources. Blocks the calling process until
enough resources have been freed by other processes, so that this process can
allocate.
o If sem_op is zero, the calling process waits or sleeps until semaphore value reaches
0.
For example:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
This system call performs control operation for a System V semaphore. The following
arguments need to be passed:
The first argument, semid, is the identifier of the semaphore. This id is the
semaphore identifier, which is the return value of semget() system call.
The second argument, semnum, is the number of semaphore. The semaphores are
numbered from 0.
The third argument, cmd, is the command to perform the required control operation
on the semaphore.
The fourth argument, of type, union semun, depends on the cmd. For few cases,
the fourth argument is not applicable.
98
Inter Process Communication
union semun {
int val; /* val for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT and IPC_SET */
unsigned short *array; /* Buffer for GETALL and SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO and SEM_INFO*/
};
struct semid_ds {
struct ipc_perm sem_perm; /* Permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned long sem_nsems; /* Number of semaphores in the set */
};
IPC_STAT – Copies the information of the current values of each member of struct
semid_ds to the passed structure pointed by arg.buf. This command requires read
permission to the semaphore.
IPC_SET – Sets the user ID, group ID of the owner, permissions, etc. pointed to by the
structure semid_ds.
IPC_INFO – Returns the information about the semaphore limits and parameters in the
structure semid_ds pointed by arg.__buf.
This call would return value (non-negative value) depending upon the passed command.
Upon success, IPC_INFO and SEM_INFO or SEM_STAT returns the index or identifier of
the highest used entry as per Semaphore or the value of semncnt for GETNCNT or the
value of sempid for GETPID or the value of semval for GETVAL 0 for other operations on
success and -1 in case of failure. To know the cause of failure, check with errno variable
or perror() function.
99
Inter Process Communication
Create shared memory mainly needed to store the counter and other flags to
indicate end of read/write process into the shared memory.
The counter is incremented by count by both parent and child processes. The count
is either passed as a command line argument or taken as default (if not passed as
command line argument or the value is less than 10000). Called with certain sleep
time to ensure both parent and child accesses the shared memory at the same time
i.e., in parallel.
Since, the counter is incremented in steps of 1 by both parent and child, the final
value should be double the counter. Since, both parent and child processes
performing the operations at same time, the counter is not incremented as
required. Hence, we need to ensure the completeness of one process completion
followed by other process.
Since, we have separate files to read the value of counter in the shared memory
and don’t have any effect from writing, the reading program remains the same
(shm_read_cntr.c)
It is always better to execute the writing program in one terminal and reading
program from another terminal. Since, the program completes execution only after
the writing and reading process is complete, it is ok to run the program after
completely executing the write program. The write program would wait until the
read program is run and only finishes after it is done.
/* Filename: shm_write_cntr.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
100
Inter Process Communication
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
struct shmseg {
int cntr;
int write_complete;
int read_complete;
};
if (argc != 2)
total_count = 10000;
else {
total_count = atoi(argv[1]);
if (total_count < 10000)
total_count = 10000;
}
printf("Total Count is %d\n", total_count);
101
Inter Process Communication
shmp->cntr = 0;
pid = fork();
/* Parent Process - Writing Once */
if (pid > 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
}
else if (pid == 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
return 0;
}
else {
perror("Fork Failure\n");
return 1;
}
while (shmp->read_complete != 1)
sleep(1);
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
102
Inter Process Communication
return 0;
}
cntr = shmp->cntr;
shmp->write_complete = 0;
if (pid == 0)
printf("SHM_WRITE: CHILD: Now writing\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Now writing\n");
shmp->write_complete = 1;
if (pid == 0)
103
Inter Process Communication
/* Filename: shm_read_cntr.c */
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
struct shmseg {
int cntr;
int write_complete;
104
Inter Process Communication
int read_complete;
};
if (argc != 2)
total_count = 10000;
else {
total_count = atoi(argv[1]);
if (total_count < 10000)
total_count = 10000;
}
sleep(3);
}
printf("Reading Process: Shared Memory: Counter is %d \n", shmp->cntr);
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
return 0;
}
If you observe the above output, the counter should be 20000, however, since before
completion of one process task other process is also processing in parallel, the counter
value is not as expected. The output would vary from system to system and also it would
vary with each execution. To ensure the two processes perform the task after completion
of one task, it should be implemented using synchronization mechanisms.
/* Filename: shm_write_cntr_with_sem.c */
#include<stdio.h>
106
Inter Process Communication
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
if (argc != 2)
total_count = 10000;
else {
total_count = atoi(argv[1]);
if (total_count < 10000)
total_count = 10000;
}
107
Inter Process Communication
pid = fork();
/* Parent Process - Writing Once */
if (pid > 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
}
else if (pid == 0) {
shared_memory_cntr_increment(pid, shmp, total_count);
return 0;
}
else {
perror("Fork Failure\n");
return 1;
}
while (shmp->read_complete != 1)
sleep(1);
if (shmdt(shmp) == -1) {
perror("shmdt");
return 1;
}
108
Inter Process Communication
return 0;
}
sem_buf.sem_num = 0;
109
Inter Process Communication
printf("Second Process\n");
semid = semget(SEM_KEY, 1, 0);
if (semid < 0) {
perror("Semaphore GET: ");
return;
}
sem_buf.sem_num = 0;
sem_buf.sem_op = -1; /* Allocating the resources */
sem_buf.sem_flg = SEM_UNDO;
cntr = shmp->cntr;
shmp->write_complete = 0;
110
Inter Process Communication
if (pid == 0)
printf("SHM_WRITE: CHILD: Now writing\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Now writing\n");
shmp->write_complete = 1;
if (pid == 0)
printf("SHM_WRITE: CHILD: Writing Done\n");
else if (pid > 0)
printf("SHM_WRITE: PARENT: Writing Done \n");
return;
}
void remove_semaphore()
111
Inter Process Communication
{
int semid;
int retval;
112
Inter Process Communication
Execution Steps
$ ./shm_read_cntr
Reading Process: Shared Memory: Counter is 20000
Reading Process: Reading Done, Detaching Shared Memory
Reading Process: Complete
$
113
17. IPC - Signals Inter Process Communication
Signal can be specified with a number or a name, usually signal names start with SIG. The
available signals can be checked with the command kill –l (l for Listing signal names),
which is as follows:
Default Action
Handle the signal
Ignore the signal
114
Inter Process Communication
As discussed the signal can be handled altering the execution of default action. Signal
handling can be done in either of the two ways i.e., through system calls, signal() and
sigaction().
#include <signal.h>
The system call signal() would call the registered handler upon generation of signal as
mentioned in signum. The handler can be either one of the SIG_IGN (Ignoring the Signal),
SIG_DFL (Setting signal back to default mechanism) or user-defined signal handler or
function address.
This system call on success returns the address of a function that takes an integer
argument and has no return value. This call returns SIG_ERR in case of error.
Though with signal() the respective signal handler as registered by the user can be called,
fine tuning such as masking the signals that should be blocked, modifying the behavior of
a signal, and other functionalities are not possible. This are possible using sigaction()
system call.
#include <signal.h>
This system call is used to either examine or change a signal action. If the act is not null,
the new action for signal signum is installed from the act. If oldact is not null, the previous
action is saved in oldact.
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
The handler for sa_handler specifies the action to be performed based on the signum and
with SIG_DFL indicating default action or SIG_IGN to ignore the signal or pointer to a
signal handling function.
The handler for sa_sigaction specifies the signal number as the first argument, pointer to
siginfo_t structure as the second argument and pointer to user context (check getcontext()
or setcontext() for further details) as the third argument.
115
Inter Process Communication
The structure siginfo_t contains signal information such as the signal number to be
delivered, signal value, process id, real user id of sending process , etc.
int sa_mask;
This variable specifies the mask of signals that should be blocked during the execution of
signal handler.
int sa_flags;
This field specifies a set of flags which modify the behavior of the signal.
First, let us start with a sample program, which generates exception. In this program, we
are trying to perform divide by zero operation, which makes the system generate an
exception.
/* signal_fpe.c */
#include<stdio.h>
int main()
{
int result;
int v1, v2;
v1 = 121;
v2 = 0;
result = v1/v2;
printf("Result of Divide by Zero is %d \n", result);
return 0;
}
116
Inter Process Communication
Thus, when we are trying to perform an arithmetic operation, the system has generated a
floating point exception with core dump, which is the default action of the signal.
Now, let us modify the code t o handle this particular signal using signal() system call.
/* signal_fpe_handler.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void handler_dividebyzero(int signum);
int main()
{
int result;
int v1, v2;
void (*sigHandlerReturn)(int);
v1 = 121;
v2 = 0;
result = v1/v2;
printf("Result of Divide by Zero is %d \n", result);
return 0;
}
117
Inter Process Communication
return;
}
As discussed, signals are generated by the system (upon performing certain operations
such as divide by zero, etc.) or the user can also generate the signal programmatically. If
you want to generate signal programmatically, use the library function raise().
Now, let us take another program to demonstrate handling and ignoring the signal.
Assume that we have raised a signal using raise(), what happens then? After raising the
signal, the execution of the current process is stopped. Then what happens to the stopped
process? There can be two scenarios – First, continue the execution whenever required.
Second, terminate (with kill command) the process.
To continue the execution of the stopped process, send SIGCONT to that particular
process. You can also issue fg (foreground) or bg (background) commands to continue the
execution. Here, the commands would only re-start the execution of the last process. If
more than one process is stopped, then only the last process is resumed. If you want to
resume the previously stopped process, then resume the jobs (using fg/bg) along with job
number.
The following program is used to raise the signal SIGSTOP using raise() function. Signal
SIGSTOP can also be generated by the user press of CTRL + Z (Control + Z) key. After
issuing this signal, the program will stop executing. Send the signal (SIGCONT) to continue
the execution.
118
Inter Process Communication
In the following example, we are resuming the stopped process with command fg.
/* signal_raising.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
int main()
{
printf("Testing SIGSTOP \n");
raise(SIGSTOP);
return 0;
}
Now, enhance the previous program to continue the execution of the stopped process by
issuing SIGCONT from another terminal.
/* signal_stop_continue.c */
#include<stdio.h>
#include<signal.h>
#include <sys/types.h>
#include <unistd.h>
119
Inter Process Communication
int main()
{
pid_t pid;
printf("Testing SIGSTOP \n");
pid = getpid();
printf("Open Another Terminal and issue following command\n");
printf("kill -SIGCONT %d or kill -CONT %d or kill -18 %d\n", pid, pid,
pid);
raise(SIGSTOP);
return 0;
}
In another terminal
$ kill -SIGCONT 30379
So far, we have seen the program which handles the signal generated by the system. Now,
let us see the signal generated through program (using raise() function or through kil l
120
Inter Process Communication
command). This program generates signal SIGTSTP (terminal stop), whose default action
is to stop the execution. However, since we are handling the signal now instead of default
action, it will come to the defined handler. In this case, we are just printing the message
and exiting.
/* signal_raising_handling.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void handler_sigtstp(int signum);
int main()
{
void (*sigHandlerReturn)(int);
raise(SIGTSTP);
return 0;
}
121
Inter Process Communication
return;
}
We have seen the instances of performing default action or handling the signal. Now, it is
time to ignore the signal. Here, in this sample program, we are registering the signal
SIGTSTP to ignore through SIG_IGN and then we are raising the signal SIGTSTP (terminal
stop). When the signal SIGTSTP is being generated that would be ignored.
/* signal_raising_ignoring.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void handler_sigtstp(int signum);
int main()
{
void (*sigHandlerReturn)(int);
printf("Testing SIGTSTP\n");
raise(SIGTSTP);
122
Inter Process Communication
return 0;
}
$ gcc signal_raising_ignoring.c
$ ./a.out
Testing SIGTSTP
Signal SIGTSTP is ignored
$
So far, we have observed that we have one signal handler to handle one signal. Can we
have a single handler to handle multiple signals? The answer is Y es. Let us consider this
with a program.
Step 2: If the user generates signal SIGQUIT (either through kill command or keyboard
control with CTRL + \), the handler simply prints the message as return.
Step 3: If the user generates signal SIGINT (either through kill command or keyboard
control with CTRL + C) first time, then it modifies the signal to perform default action (with
SIG_DFL) from next time.
Step 4: If the user generates signal SIGINT second time, it performs a default action,
which is the termination of program.
/* Filename: sigHandler.c */
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void handleSignals(int signum);
int main(void)
{
void (*sigHandlerInterrupt)(int);
void (*sigHandlerQuit)(int);
void (*sigHandlerReturn)(int);
123
Inter Process Communication
while (1) {
printf("\nTo terminate this program, perform the following:
\n");
printf("1. Open another terminal\n");
printf("2. Issue command: kill %d or issue CTRL+C 2 times
(second time it terminates)\n", getpid());
sleep(10);
}
return 0;
}
case SIGQUIT:
printf("\nYou pressed CTRL+\\ \n");
124
Inter Process Communication
break;
default:
printf("\nReceived signal number %d\n", signum);
break;
}
return;
}
^C
Terminated
$
125
Inter Process Communication
Another Terminal
$ kill 71
$
Second Method
$ gcc sigHandler.c -
o sigHandler
$ ./sigHandler
^C
^C
We know that to handle a signal, we have two system calls i.e., either signal() or
sigaction(). Till now we have seen with signal() system call, now it is time for sigaction()
system call. Let us modify the above program to perform using sigaction() as follows:
/* Filename: sigHandlerSigAction.c */
126
Inter Process Communication
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void handleSignals(int signum);
int main(void)
{
void (*sigHandlerReturn)(int);
struct sigaction mysigaction;
mysigaction.sa_handler = handleSignals;
sigemptyset(&mysigaction.sa_mask);
mysigaction.sa_flags = 0;
mysigaction.sa_handler = handleSignals;
sigemptyset(&mysigaction.sa_mask);
mysigaction.sa_flags = 0;
case SIGQUIT:
printf("\nYou have entered CTRL+\\ \n");
break;
default:
printf("\nReceived signal number %d\n", signum);
break;
}
return;
}
Let us see the compilation and execution process. In the execution process, let us see
issue CTRL+C twice, remaining checks/ways (as above) you can try for this program as
well.
128
Inter Process Communication
129
18. IPC - Memory Mapping
Inter Process Communication
The mmap() system call provides mapping in the virtual address space of the calling
process that maps the files or devices into memory. This is of two types:
File mapping or File-backed mapping - This mapping maps the area of the process’
virtual memory to the files. This means reading or writing to those areas of memory causes
the file to be read or written. This is the default mapping type.
Anonymous mapping - This mapping maps the area of the process’ virtual memory
without backed by any file. The cont ents are initialized to zero. This mapping is similar to
dynamic memory allocation (malloc()) and is used in some malloc() implementations for
certain allocations.
The memory in one process mapping may be shared with mappings in other processes.
This can be done in two ways:
When two processes map the same region of a file, they share the same pages of
physical memory.
If a child process is created, it inherits the parent’s mappings and these mapping s
refer to the same pages of physical memory as that of the parent. Upon any change
of data in the child process, different pages would be created for the child process.
When two or more processes share the same pages, each process can see the changes of
the page contents made by other processes depending on t he mapping type. The mapping
type can be either private or shared:
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t
offset);
The above system call returns the starting address of the mapping on success or
MAP_FAILED on error.
The virtual address addr, can be either user specified or generated by the kernel (upon
passing addr as NULL). The field length indicated requires the size of mapping in bytes.
The field prot indicates memory protection values such as PROT_NONE, PROT_READ,
PROT_WRITE, PROT_EXEC meant for regions that may not be accessed, read, write or
executed respectively. This value can be single (PROT_NONE) or can be ORd with any of
the three flags (last 3). The field flags indicate mapping type either or MAP_PRIVATE or
130
Inter Process Communication
MAP_SHARED. The field ‘fd’ indicates the file descriptor identifying the file to be mapped
and the field ‘offset’ implies the starting point of the file, if need to map the entire file,
offset should be zero.
#include <sys/mman.h>
The system call munmap, performs the unmapping of the already memory mapped region.
The fields addr indicates the starting address of the mapping and the length indicates the
size in bytes of the mapping to be unmapped. Usually, the mapping and unmapping would
be for the entire mapped regions. If this has to be different , then it should be either
shrinked or cut in two parts. If the addr doesn’t have any mappings this call would have
no effect and the call returns 0 (success).
2 2 2 2 2 3 3 3 3 3 3 3 3 3 5 6 6
0 1 2 … …
5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
A B C … Z 0 1 2 3 4 5 6 7 8 9 A b c … x y z
Step 2: Map the file contents into memory using mmap() system call. This would return
the start address after mapped into the memory.
Step 3: Access the file contents using array notation (can also access with pointer
notation) as doesn’t read expensive read() system call. Using memory mapping, avoid
multiple copying between the user space, kernel space buffers and buffer cache.
Step 4: Repeat reading the file contents until the user enters “-1” (signifies end of access).
Step 5: Perform clean-up activities i.e., unmapping the mapped memory region
(munmap()), closing the file and removing the file.
/* Filename: mmap_test.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
131
Inter Process Communication
void write_mmap_sample_data();
int main()
{
struct stat mmapstat;
char *data;
int minbyteindex;
int maxbyteindex;
int offset;
int fd;
int unmapstatus;
write_mmap_sample_data();
minbyteindex = 0;
maxbyteindex = mmapstat.st_size - 1;
do {
printf("Enter -1 to quit or ");
printf("enter a number between %d and %d: ", minbyteindex,
maxbyteindex);
scanf("%d",&offset);
132
Inter Process Communication
close(fd);
system("rm -f MMAP_DATA.txt");
return 0;
}
void write_mmap_sample_data()
{
int fd;
char ch;
struct stat textfilestat;
// Write A to Z
ch = 'A';
while (ch <= 'Z')
{
write(fd, &ch, sizeof(ch));
133
Inter Process Communication
ch++;
}
// Write 0 to 9
ch = '0';
while (ch <= '9')
{
write(fd, &ch, sizeof(ch));
ch++;
}
// Write a to z
ch = 'a';
while (ch <= 'z')
{
write(fd, &ch, sizeof(ch));
ch++;
}
close(fd);
return;
}
134