1 Overview
The shared memory area is the fastest available form of IPC. Once such a memory area is mapped to the address space of the processes sharing it, the transfer of data between these processes will no longer involve the kernel [that is, processes will no longer transfer data to each other by executing system calls of any kernel]. Obviously, the kernel must allow each process to share the memory mapping relationship of the memory area, and then always manage the memory area (handling page faults, etc.).
Reverse example
Consider the usual steps involved in an example client server file replicator used to deliver various types of information:
- The server reads from the input file. The data of the file is read into its own memory space by the kernel, and then copied from the kernel to the server process [file input - > kernel memory - > server process]
- The server writes this data in the form of a message to a pipeline, FIFO or message queue [copy from the process to the kernel]
- The client reads the data from the IPC channel, that is, copies the data from the kernel to the process
- Copies this data from the client buffer specified by the second parameter of the write function to the output file
Use shared memory to copy file data from the server to the client
- The server uses a semaphore to obtain the right to access a shared memory area object;
- The server reads data from the input file into the shared memory object. The data buffer specified by the second parameter of the read function points to the shared memory object.
- When the server finishes reading, it uses a semaphore to notify the client.
- The client writes this data out of the shared memory area object to the output file.
Note: by default, a child process derived from fork does not share memory with its parent process. The following example asks the parent-child process to add 1 to a global integer named count
- Set the standard output to non buffered because both parent and child processes have to write results to it, which can prevent the output of the two processes from crossing improperly.
#include "unpipc.h" #define SEM_NAME "mysem" int count = 0; int main(int argc, char const *argv[]) { int i, nloop; sem_t *mutex; if (argc != 2) err_quit("usage: incr1 <#loops>"); nloop = atoi(argv[1]); // creat, initialize, and unlink semaphore mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1); Sem_unlink(Px_ipc_name(SEM_NAME)); // Set the standard output to non buffered because both parent and child processes have to write results to it, which can prevent the output of these two processes // Improper crossing setbuff(stdout, NULL); if ((Fork() == 0)) { for ( i = 0; i < nloop; ++i) { Sem_wait(mutex); printf("child: %d\n", count++); Sem_post(mutex); } exit(0); } for ( i = 0; i < nloop; ++i) { Sem_wait(mutex); printf("parent: %d\n", count++); Sem_post(mutex); } exit(0); }
2 mmap, munmap and msync functions
The mmap function maps a file or a Posix shared memory area object to the address space of the calling process. This function is used for three purposes:
-
(1) Use normal files to provide memory mapped I/O;
-
(2) Use special files to provide anonymous memory mapping;
-
(3) Using shm_open to provide a Posix shared memory area between unrelated processes.
#include <sys/mman.h> // If successful, the starting address of the mapped area is returned; otherwise, map is returned_ FAILED void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
-
(1) addr can specify that the descriptor fd is mapped to the starting address of the in-process space, usually NULL, to tell the kernel to select the starting address.
-
(2)len is the number of bytes mapped to the process address space.
-
(3) len starts from the offset byte from the beginning of the mapped file.
-
(4) The protection of the memory mapping area is specified by the prot parameter:
prot explain PROT_READ Data readability PROT_WRITE Data writable PROT_EXEC Data executable PROT_NONE Data inaccessible -
flag
Flags explain remarks MAP_SHARED MAP is shared The modifications made by the calling process to the mapped data are visible to all processes sharing the object. [one of the methods of sharing memory area between parent and child processes is that the parent process specifies MAP_SHARED to call mmap before calling fork] MAP_PRIVATE MAP is private The modifications made by the calling process to the mapped data are only visible to the process without changing its underlying support object (file object / shared memory area object) MAP_FIXED
munmap deletes a mapping relationship from the address space of a process:
#include <sys/mman.h> // Successfully returned 0 Otherwise - 1 is returned int munmap(void *addr, size_t len);
3 continuously increment the counter by 1 in the memory mapping file
-
Using Posix named semaphores
Writes an integer with a value of 0 to the file
#include "unpipc.h" #define SEM_NAME "mysem" int main(int argc, char const *argv[]) { int fd, i, nloop, zero = 0; int *ptr; sem_t *mutex; if (argc != 3) err_quit("usage: incr2 <#pathname> <#loops>"); nloop = atoi(argv[2]); // open file, initialize to 0, map into memory fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE); // Writes an integer with a value of 0 to the file Write(fd, &zero, sizeof(int)); ptr = Mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // Close descriptor after mmap Close(fd); // Using Posix named semaphores mutex = Sem_open(Px_ipc_name(SEM_NAME), O_CREAT | O_EXCL, FILE_MODE, 1); Sem_unlink(Px_ipc_name(SEM_NAME)); // Set standard output to unbuffered setbuff(stdout, NULL); if ((Fork() == 0)) { for ( i = 0; i < nloop; ++i) { Sem_wait(mutex); printf("child: %d\n", (*ptr)++); Sem_post(mutex); } exit(0); } for ( i = 0; i < nloop; ++i) { Sem_wait(mutex); printf("parent: %d\n", (*ptr)++); Sem_post(mutex); } exit(0); }
-
Memory based semaphores using Posix
struct shared
{
sem_t mutex; // Posix memory based semaphores
int count;
} shared;
#include "unpipc.h" struct shared { sem_t mutex; // Posix memory based semaphores int count; } shared; int main(int argc, char const *argv[]) { int fd, i, nloop; struct shared *ptr; if (argc != 3) err_quit("usage: incr3 <#pathname> <#loops>"); nloop = atoi(argv[2]); // open file, initialize to 0, map into memory fd = Open(argv[1], O_RDWR | O_CREAT, FILE_MODE); Write(fd, &shared, sizeof(struct shared)); ptr = Mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); Close(fd); Sem_init(&ptr->mutex, 1, 1); // Set standard output to unbuffered setbuff(stdout, NULL); if ((Fork() == 0)) { for ( i = 0; i < nloop; ++i) { Sem_wait(mutex); printf("child: %d\n", ptr->count++); Sem_post(&ptr->mutex); } exit(0); } for ( i = 0; i < nloop; ++i) { Sem_wait(mutex); printf("child: %d\n", ptr->count++); Sem_post(&ptr->mutex); } exit(0); }
4 BSD anonymous memory mapping
- BSD provides anonymous memory mapping, which completely avoids the creation and opening of files. The method is to specify the flags parameter of mmap as MAP_SHARED | MAP_ANON, specify the fd parameter as - 1
5 Summary
- Shared memory is the fastest form of IPC available because a single copy of data in a shared memory is available to all threads or processes sharing the memory.