ICT374 Inter-Process Communications
_______________________________________________________________________
Inter-Process Communications Mechanisms in Unix
Objectives
Copyright By PowCoder代写 加微信 powcoder
• UnderstandwhyIPCsareneededforprocessesto communicate with each other.
• Understandandbeabletouseunnamedpipes
• Understandandbeabletousenamedpipes(FIFO)
• UnderstandandbeabletoprogramwithMessage Queues
• UnderstandandbeabletouseSemaphoresto synchronise multiple processes and to provide mutual exclusion.
• UnderstandandbeabletouseSharedMemoryto exchange data between processes.
• Thislecturenotes.
• Stevens & Rago: Ch 15 • Stallings:Ch5.3-5.7
Page 1 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
1. Introduction
We understand that each process works within its own virtual address space. Processes do not normally share memory space, so how do they communicate with each other?
• signals: – cannot pass data • shared files: – too slow
Unix systems provide several inter-process communication mechanisms (IPCs) to suit different applications:
SV streams Same as above
In this topic we will discuss pipes, FIFOs, message queues, semaphores and shared memory. Sockets will be dealt with in Topic 8 when we discuss network programming.
Stream pipes named stream pipes
on the same machine
message queues semaphores shared memory
on the same machine
Mainly for network applications running on different machines
Page 2 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
A pipe provides a one-way communication channel between related processes.
P1 PIPE P2
#include
int pipe (int filedesc[2]);
Return 0 if ok,
-1 on error
• Apipecanbeusedonlytocommunicatebetween processes sharing a common ancestry in which the pipe was created.
• Apipeisuni-directionalfromthewriteendtotheread end, although more than one process can read from (or write to) the same pipe.
• ApipeisimplementedwithaFirst-InFirst-Out(FIFO) buffer in the memory. A read is blocked if the pipe is empty. A write is blocked if the pipe is full.
• Ifapipecallissuccessful,filedesc[0]isopenedfor reading from the pipe and filedesc[1] is opened for writing to the pipe.
Page 3 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
#include
#include
#include
#define BUFSIZE 512
char *msg = “Hello, me!”;
int main() {
char buf[BUFSIZE];
int p[2]; // p[0] for read, p[1] for write
// open a pipe
if(pipe(p) < 0){
perror (“Pipe call”);
exit(1); }
// send msg to pipe
write(p[1], msg, strlen(msg)+1);
// include null character
// read from pipe
read(p[0], buf, BUFSIZE);
printf(“%s\n”, buf);
Page 4 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
#include
#include
#include
#define BUFSIZE 512
char *msg = “Hello”;
int main() {
char buf[BUFSIZE];
int p[2], pid;
if(pipe(p) < 0){
perror (“pipe call”); exit(1);
// the pipe must be created before
// the child process
if((pid=fork()) < 0){
perror(“Fork call”); exit(1);
if(pid > 0){ // parent
write (p[1], msg, strlen(msg) + 1);
wait ((int *) 0);
if(pid == 0){ // child
read (p[0], buf, BUFSIZE);
printf(“%s\n”, buf);
exit(0); }
Page 5 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
The above code can be modified so that there is a cleaner interface between the two processes:
write(p[1], msg, strlen(msg) + 1);
wait((int *)0);
// wait for the child to end first
if(pid == 0){ // child
close(p[1]);
read(p[0], buf, BUFSIZE)
printf(“%s\n”, buf);
Parent Child
if(pid > 0){ // parent
close(p[1]);
read(p[0], buf, BUFSIZE);
printf(“%s\n”, buf);
if (pid == 0){ // child
close(p[0]);
write(p[1], msg, strlen(msg) + 1);
if(pid > 0){ // parent
close(p[0]);
Page 6 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
3. The size of the pipe
A pipe is implemented using an internal buffer in the kernel space. The size of the buffer is fixed. POSIX.1 specifies that the size of the buffer must be at least 512 bytes. Most operating systems use a much larger buffer than this minimum size.
The size of the buffer and the number of bytes read or written could affect the behaviours of the read and write system calls:
• ifthepipeisempty,areadcallwillblockthecalling process
• ifthepipeisfull,awritecallwillblockthecallingprocess
• ifthewriteendofapipeisclosedandthepipeisempty,a
read will return 0 immediately
• ifthereadendofthepipeisclosed,theprocesswhich attempts to write to the pipe will be sent the signal SIGPIPE by the kernel. If the signal is not caught or ignored, the process will terminate; otherwise the write call will return –1.
Page 7 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
The following example demonstrates the above situations:
/* pipesync.c – demonstrating synchronisation
* and deadlocks when using pipes
#include
#include
#include
char *msg=”Hello, there!”; int BUFSIZE = 100;
// convert upper case to lower case or vice versa // (simulating some kind of service)
void conv(char *s, int len)
for(i=0; i
close(BA[1]); // L1
printf(” A sends: %s\n”, msg); write(AB[1], msg, strlen(msg)); close(AB[1]); // L2
printf(“A receives: “);
while((n=read(BA[0], buf, BUFSIZE))>0)
buf[n]=’\0′, printf(“%s”, buf);
printf(“\n”); exit(0);
if (pid==0) { /* porcess B */ close(BA[0]);
close(AB[1]); // L3
while((n=read(AB[0], buf, BUFSIZE))>0){
conv(buf, n);
write(BA[1], buf, n);
exit(0); }
Page 9 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
Refer to line labels in the previous program, explain: What would happen if:
(1) Line L1: “close(BA[1])” is deleted?
(2) Line L2: “close(AB[1])” is deleted?
(3) Line L3: “close(AB[1])” is deleted?
When discussing the behaviour of the program, please remember the behaviours of the read and write system calls when a pipe is empty or full, as detailed at the beginning of this section.
Page 10 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
4. Non-blocking Read and Write
Occasionally, we want to read from a pipe and also be able to return immediately if the pipe is empty. Similarly, we may want to write to a pipe and also be able to return immediately if the pipe is full. This is achieved by adding the file status flag O_NONBLOCK to the file descriptors:
#include
val = fcntl (fd, F_GETFL, 0);
val = val | O_NONBLOCK; // adding the bit if (fcntl( fd, F_SETFL, val) == -1)
perror(“fcntl call”);
Assuming that fd is the read end of a pipe and the pipe is empty, with non-blocking mode, read(fd, ….) will return immediately with return value -1 and the errno is set to EAGAIN.
Similarly, if fd is the write end of the pipe, write(fd, …) will return immediately with return value -1 and errno set to EAGAIN when the pipe is full.
Page 11 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
5. FIFOs or Named Pipes
Limitations of the conventional pipes:
• Theycannotbeusedtocommunicatebetweenunrelated processes (i.e., processes not sharing the same ancestor where the pipe was created)
• Theyarenotpermanent.Theyvanishwhentherelevant processes terminate.
To address this problem, Unix systems introduced FIFO or named pipes (because a FIFO is a special file with a file name.) Like any other special file, a FIFO can be opened or closed by system calls: open and close. Like a conventional pipe, a FIFO can be read or written with system calls read and write.
Create a FIFO
(1) On the command line:
% mkfifo example.fifo
The file access mode will be 666 & ~umask
(2) In a program:
#include
#include
int mkfifo (const char *pathname, mode_t mode); Returns: 0 if OK, -1 on error
if(mkfifo (“example.fifo”, 0666) < 0){
printf("Unable to create fifo\n");
Once a FIFO is created, the normal system calls such as open, read, write, close for files can then be used to access it.
Page 12 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
Create a fifo to communicate with its child
#include
#include
#include
#include
#define BUFSIZE 512
int main() {
char buf[BUFSIZE]; int pid, fd;
char *msg = “Hello”;
// create a fifo
unlink(“/tmp/example.fifo”);
if(mkfifo(“/tmp/example.fifo”, 0666) < 0){
printf("Unable to create fifo\n"); exit(1);
if((pid=fork() < 0){ printf(“Unable to fork\n”); exit(1);
// in child
if(pid == 0){
if((fd = open(“/tmp/example.fifo”, O_RDONLY)) < 0){
printf("Error opening /tmp/example.fifo\n");
exit(1); }
if(read(fd, buf, BUFSIZE) < 0)
printf("Error in reading FIFO\n"), exit(1);
printf("FIFO: %s\n", buf);
exit(0); }
// in parent
if((fd = open("/tmp/example.fifo", O_WRONLY)) < 0 ) printf(“Unable to open fifo \n”), exit (1);
write (fd, msg, strlen(msg) + 1);
exit(0); }
Page 13 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
6. System V IPC
In the next few sections, we will discuss three types of IPCs originated from System V, hence are often referred to as System V IPCs. These three types of IPCs are:
• MessageQueue:alinkedlistofmessages,eachwitha priority or type number. Multiple processes may access send and retrieve messages to/from the message queue at any order, not necessarily in the first-in-first-out order.
• Semaphores:semaphoresprovidecontrolledaccessto shared resources, such as a shared file or a shared memory. They are often used to implement mutual exclusions and synchronisations.
• Shared Memory: a shared memory is a piece of physical memory in the kernel. Multiple processes can each map a piece of their own virtual address spaces to that same physical memory, thus sharing it with each other. A shared memory provides a simple and fast way for multiple processes to exchange data. However, the access to the shared memory must be controlled to ensure the integrity of the data. The access control is often implemented with one or more semaphores.
Message queues, semaphores and shared memory are system-wide resources. Once created, they stay in the system until explicitly removed. Each IPC object is associated with a unique key, much like a pathname for a file. It is accessed through a unique id, similar to a file descriptor. The id is unique within the entire operating system, thus different processes would access the same IPC object with the same id. However, the id is allocated each time the IPC object is created (just like a file descriptor which is allocated each time you open the file). Hence, objects with the same key may have different ids at different times.
Page 14 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
As system-wide resources, we can query and remove message queues, semaphores and shared memories at the command line by using the command ipcs and ipcrm. For example, the following command shows that there is one shared memory object owned by the root, one semaphore object owned by root, and two message queues created by the user "hong".
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status 0x73727372 0 root 666 44172 1
------ Semaphore Arrays --------
key semid owner perms nsems status 0x6c737372 0 root 666 3
------ Message Queues --------
key msqid owner perms used-bytes messages 0x0001a000 1792 hong 644 0 0 0x0001a001 1793 hong 644 0 0
In the above example, each IPC object has a key (e.g., 0x0001a000) and an id (e.g., 1792). To create an IPC object, we must firstly have a unique key (an integer). When the object is created, it is allocated an id (another integer) for that object. Subsequent accesses to the object are through the id, not the key.
Like a file, each IPC object also has a set of access permissions that determines which processes are allowed to access it. However, the “execute” permission doesn’t make sense here.
The owner can remove an IPC object from the command line using ipcrm command, e.g.:
$ ipcrm msg 1793
resource deleted
Page 15 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
To create a new IPC object, we must either find a unique key explicitly or use the constant IPC_PRIVATE. If we use the latter, the system will allocate a unique key to the IPC object each time. There are pros and cons with each approach:
Using an explicit key: we can define the key in a header file. Both the client and the server can use the same key. The difficulty is how to avoid clashes (when the same key is also used by different programs).
Using the constant IPC_PRIVATE: it is guaranteed that each time the system will allocate a new key that is not already used by another IPC object. The difficulty is that, if Process A creates an object, using say, msgget(IPC_PRIVATE, ...), to be accessed by Process B, how would Process B gain access to that object, without knowing the key Process A used? Note that Process B cannot use msgget (IPC_PRIVATE, ...) to obtain the id, since each time IPC_PRIVATE is mapped to a new, unused key.
The good news is that the constant IPC_PRIVATE can be used when the communications are among the processes
that share the common ancestry where the IPC object was created. This is because these child processes would inherit the id from the parent process after forking. Using IPC_PRIVATE will save us the worry of key clashes.
Page 16 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
7. Message Queues
A message queue is a linked list of messages in the kernel memory. The message queue is system wide. Therefore, once the queue is created, a process can send a message to the queue. Another process can retrieve the message from that queue as long as the processes have the relevant permissions. Messages may have different sizes. Each message carries with it a message type, which may affect the order it is retrieved. Unlike a pipe, it is not necessary to retrieve the messages in the First-In-First-Out order.
Create a message queue:
#include
#include
#include
mqid = msgget(key_t key, int permflags);
The key is a long integer. To create a new message queue, the key must not already be used by another message queue. The permflags are access permissions for the queue, possibly bit-wise Ored with the following flags:
• IPC_CREAT: create a new message queue if the message queue associated with the key does not exist.
• IPC_EXCL: if IPC_CREAT and IPC_EXCL are both set, create the queue if no queue with the same key exists and return its id. If the queue already exists, return –1 with errno set to EEXIST.
Page 17 of 45
message type
message body
ICT374 Inter-Process Communications
_______________________________________________________________________
The system call returns the message queue id associated with the given key.
Example 1:
key_t key = 0x000A1800;
mqid = msgget(key, 0600 | IPC_CREAT);
If the queue with the key does not exist, create it and return its id. If it already exists, just return its id.
Example 2:
mqid = msgget(key, 0600 | IPC_CREAT | IPC_EXCL);
If the queue does not exist, create it and return its id. Otherwise, return –1 immediately with errno set to EEXIST.
Sending a message
#include
#include
#include
int msgsnd( int mqid, void *message, int size, int flag );
Here message must point to a structure similar to this:
struct mymsg{
long mtype; // message type, >0 char mtext[…]; // message text
The message type is used to categorise the messages. The value must be positive. The argument size is the length of the message stored in message->mtext.
The flag can be set to IPC_NOWAIT. Without this flag, the calling process will be blocked if the message cannot be sent out completely, for example, when the total length of the
Page 18 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
message queue exceeds the per-queue limit or the system- wide limit. With this flag, the call returns immediately in this situation with return value –1 and errno set to EAGAIN.
Retrieve a message
#include
#include
#include
int msgrcv(int mqid, void *message,
int size, long type, int flag );
Here message’s type is the same as in msgsnd, size is the size of the mtext buff in the message structure. The type decides how the message is retrieved.
type = 0: type > 0:
the first message in the queue is returned the first message in the queue whose message type mtype is type.
the first message in the queue whose message type mtype is the lowest value less than or equal to |type|.
The flag can be one of the following two:
• MSG_NOERROR: if the returned message is larger than the given size, the message is truncated. The truncated part of the message is lost. No notification is given. Without this flag, the system call returns with return value –1 and errno set to E2BIG. The message is not retrieved. It stays in the queue.
• MSG_NOWAIT: similar to msgsnd, if no message can be retrieved, the calling process is suspended if the flag is not set. With the flag, the system call returns immediately with return value –1 and errno = ENOMSG.
Page 19 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
8. A Client Server Example using Message Queues
This example consists of one server process that simulates a service (to keep it as simple as possible, it converts the case of letters in a message it received) to one or several clients. There are two message queues: up and down. The client reads (repeatedly) a message from the standard input, then sends the message to the server for processing via the up queue. The server retrieves a message from the up queue, then sends the processed message to the down queue for the client to collect. There can be several clients working simultaneously, but each client must have a unique client id which is inserted into its messages.
Client1 (id=1)
Client2 (id=2)
Page 20 of 45
ICT374 Inter-Process Communications
_______________________________________________________________________
/* name: *
* author: */
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com