共享内存可以由多个程序同时访问的内存,能够避免进程间通信过程中的冗余数据拷贝,是IPC中最快的一种,特别适合用来作大块数据的传输。共享内存可以映射到不同的进程空间,这些进程间的数据传递就不再涉及内核。这个过程其实是把同一块物理内存映射到不同进程的虚拟地址,这些进程可以同时对共享内存进行读写。
共享内存的使用主要分为以下三步:
- 创建一个共享内存
- 设置共享内存的大小
- 将共享内存映射到进程用户空间
进行到第二步的时候就已经在内核空间创建了一块内存空间,只要再把它映射到进程用户空间就可以使用了。
在Linux中主要提供了两套接口来使用共享内存,分别是posix接口和system V接口。其中posix接口的可移植性更好。
posix接口
int shm_open(const char *name, int oflag, mode_t mode);//打开一个共享内存文件
int ftruncate(int fildes, off_t length);//设置共享内存的大小
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);//将共享内存映射到进程地址空间,返回值是共享内存映射到进程空间的地址
int munmap(void *addr, size_t length);//取消内存映射
int shm_unlink(const char *name);//删除共享内存
shm_open() 创建并打开一个新的或打开一个现有的 POSIX 共享内存对象。 POSIX 共享内存对象实际上是一个句柄,不相关的进程可以使用它来 mmap(2) 共享内存的同一区域。 shm_unlink() 函数执行相反的操作,删除先前由 shm_open() 创建的对象。
shm_open() 的操作类似于 open。 name 指定要创建或打开的共享内存对象。
oflag 是一个位掩码,它通过将 O_RDONLY 或 O_RDWR 中的一个与以下列出的任意标志进行 OR 运算来创建:
O_RDONLY
以只读模式打开。 以这种方式打开的共享内存对象只能被 mmap(2) 用于读取 (PROT_READ) 访问。
O_RDWR
以读写模式打开。
O_CREATE
如果共享内存对象不存在,则创建它。 对象的用户和组所有权取自调用进程对应的有效ID,对象的权限位按照参数mode的低9位设置,不过在process file mode创建掩码中设置的那些位 (请参阅umask(2))会被清除。 open(2) 中列出了一组可用于定义mode的宏常量。 (这些常量的符号定义可以通过包含<sys/stat.h>来获得。)
一个新的共享内存对象最初的长度为零——对象的大小可以使用 ftruncate 设置。 共享内存对象新分配的字节会自动初始化为 0。
O_EXCL
如果同时指定了 O_CREAT,并且给定name的共享内存对象已经存在,则返回错误。 检查对象是否存在,以及不存在时创建新对象的过程是原子的。此标识是为了避免: 如果对象存在,但是制定了O_CREATE,那么默认的操作是打开该对象,这样就不知道是打开了原有的还是创建了新的,而O_EXCL确保,一定是创建一个新的,否则就要报错
O_TRUNC
如果对象存在,则将其长度大小截断为0
成功完成后,shm_open() 返回一个引用共享内存对象的新文件描述符。
文件描述符通常用于后续调用 ftruncate(用于新创建的对象)和 mmap。 调用 mmap 后,可能会关闭文件描述符而不影响内存映射。
shm_unlink() 的操作类似于 unlink:它删除一个共享内存对象名称,并且一旦所有进程都取消映射该对象(执行unmap),就释放并销毁相关内存区域的内容。 在成功 shm_unlink() 之后,尝试 shm_open() 具有相同名称的对象失败(除非指定了 O_CREAT,在这种情况下会创建一个新的、不同的对象)。
成功时,shm_open() 返回一个文件描述符(一个非负整数)。 成功时,shm_unlink() 返回 0。失败时,两个函数都返回 -1 并设置 errno 以指示错误。
system V 接口
int shmget(key_t key, size_t size, int shmflg);//打开一个共享内存文件
void *shmat(int shmid, const void *shmaddr, int shmflg)//将共享内存映射到进程地址空间
int shmdt(const void *shmaddr);//删除共享内存
posix接口使用示例
生产者消费者模型,一个进程写一个进程读
生产者代码:
/**
* Simple program demonstrating shared memory in POSIX systems.
*
* This is the producer process that writes to the shared memory region.
*
* Figure 3.17
*
* @author Silberschatz, Galvin, and Gagne
* Operating System Concepts - Ninth Edition
* Copyright John Wiley & Sons - 2013
*
* modifications by dheller@cse.psu.edu, 31 Jan. 2014
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <errno.h>
void display(char *prog, char *bytes, int n);
int main(void)
{
const char *name = "/shm-example"; // file name
const int SIZE = 4096; // file size
const char *message0 = "Studying ";
const char *message1 = "Operating Systems ";
const char *message2 = "Is Fun!";
const char *msg_end = "\n";
int shm_fd; // file descriptor, from shm_open()
char *shm_base; // base address, from mmap()
char *ptr; // shm_base is fixed, ptr is movable
/* create the shared memory segment as if it was a file */
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
if (shm_fd == -1) {
printf("prod: Shared memory failed: %s\n", strerror(errno));
exit(1);
}
/* configure the size of the shared memory segment */
ftruncate(shm_fd, SIZE);
/* map the shared memory segment to the address space of the process */
shm_base = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shm_base == MAP_FAILED) {
printf("prod: Map failed: %s\n", strerror(errno));
// close and shm_unlink?
exit(1);
}
/**
* Write to the mapped shared memory region.
*
* We increment the value of ptr after each write, but we
* are ignoring the possibility that sprintf() fails.
*/
display("prod", shm_base, 64);
ptr = shm_base;
ptr += sprintf(ptr, "%s", message0);
ptr += sprintf(ptr, "%s", message1);
ptr += sprintf(ptr, "%s", message2);
ptr += sprintf(ptr, "%s", msg_end);
display("prod", shm_base, 64);
/* remove the mapped memory segment from the address space of the process */
if (munmap(shm_base, SIZE) == -1) {
printf("prod: Unmap failed: %s\n", strerror(errno));
exit(1);
}
/* close the shared memory segment as if it was a file */
if (close(shm_fd) == -1) {
printf("prod: Close failed: %s\n", strerror(errno));
exit(1);
}
return 0;
}
void display(char *prog, char *bytes, int n)
{
printf("display: %s\n", prog);
for (int i = 0; i < n; i++)
{ printf("%02x%c", bytes[i], ((i+1)%16) ? ' ' : '\n'); }
printf("\n");
}
消费者:
/**
* Simple program demonstrating shared memory in POSIX systems.
*
* This is the consumer process
*
* Figure 3.18
*
* @author Gagne, Galvin, Silberschatz
* Operating System Concepts - Ninth Edition
* Copyright John Wiley & Sons - 2013
*
* modifications by dheller@cse.psu.edu, 31 Jan. 2014
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
void display(char *prog, char *bytes, int n);
int main(void)
{
const char *name = "/shm-example"; // file name
const int SIZE = 4096; // file size
int shm_fd; // file descriptor, from shm_open()
char *shm_base; // base address, from mmap()
/* open the shared memory segment as if it was a file */
shm_fd = shm_open(name, O_RDONLY, 0666);
if (shm_fd == -1) {
printf("cons: Shared memory failed: %s\n", strerror(errno));
exit(1);
}
/* map the shared memory segment to the address space of the process */
shm_base = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
if (shm_base == MAP_FAILED) {
printf("cons: Map failed: %s\n", strerror(errno));
// close and unlink?
exit(1);
}
/* read from the mapped shared memory segment */
display("cons", shm_base, 64); // first as bytes, then as a string
printf("%s", shm_base);
/* remove the mapped shared memory segment from the address space of the process */
if (munmap(shm_base, SIZE) == -1) {
printf("cons: Unmap failed: %s\n", strerror(errno));
exit(1);
}
/* close the shared memory segment as if it was a file */
if (close(shm_fd) == -1) {
printf("cons: Close failed: %s\n", strerror(errno));
exit(1);
}
/* remove the shared memory segment from the file system */
if (shm_unlink(name) == -1) {
printf("cons: Error removing %s: %s\n", name, strerror(errno));
exit(1);
}
return 0;
}
void display(char *prog, char *bytes, int n)
{
printf("display: %s\n", prog);
for (int i = 0; i < n; i++)
{ printf("%02x%c", bytes[i], ((i+1)%16) ? ' ' : '\n'); }
printf("\n");
}