共享内存是最快的IPC方式,一个进程中内存映射到其他进程的地址空间中,之后数据传递不再需要内核的系统调用,其他进程可以直接访问内存空间。示意图如下:
imgclip2.png

共享内存注意点:

  • 映射不能改变文件的大小。内容写回文件时,不能超过文件的大小。
  • 可用于进程间通信的有效地址空间不完全受限于被映射文件的大小。内核分配的空间可能比文件大小大。
  • 文件被映射后,所有对映射区域的访问实际上都是对内存区域的访问。

1 文件映射内存区

1.1 mmap函数

头文件:
函数定义: void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset)
功能: 将文件或设备空间映射到共享内存区
返回值: 成功返回共享内存区起始地址,失败返回-1
参数:

  • Addr:要映射的起始地址,通常为NULL,由内核自动选择
  • len: 映射内存区的字节数
  • prot: 映射区保护方式

imgclip3.png

  • flags:标志

imgclip4.png

  • fd:文件描述符
  • offset: 从文件头开始的偏移量

    1.2 munmap函数

    头文件:
    函数定义: void* munmap(void* addr, size_t len)
    功能: 取消mmap函数建立的映射
    返回值: 成功返回0,失败返回-1
    参数:

  • Addr:要映射的起始地址

  • len: 映射内存区的字节数

    1.3 msync函数

    头文件:
    函数定义: int msync(void* addr, size_t len, int flags)
    功能: 对共享内存中的数据执行同步操作
    返回值: 成功返回0,失败返回-1
    参数:

  • addr: 映射内存的起始地址

  • len: 映射到进程空间的字节数
  • flags: 标志

imgclip5.png

1.4 代码实例

  1. //==================================write
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <sys/mman.h>
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <errno.h>
  9. #include <string.h>
  10. #include <fcntl.h>
  11. #define ERR_EXIT(m)\
  12. do\
  13. {\
  14. perror(m);\
  15. exit(EXIT_FAILURE);\
  16. }while(0)
  17. typedef struct student
  18. {
  19. char name[4];
  20. int age;
  21. }STU;
  22. int main(int argc, char* argv[])
  23. {
  24. if (argc != 2)
  25. {
  26. printf("Usage: %s, <filename>\r\n", argv[0]);
  27. exit(EXIT_FAILURE);
  28. }
  29. int fd;
  30. fd = open(argv[1], O_CREAT | O_RDWR | O_TRUNC, 0666); //打开文件
  31. if (fd == 1)
  32. ERR_EXIT("open failed");
  33. lseek(fd, sizeof(STU) * 5 -1, SEEK_SET);//定位到某位置
  34. write(fd, "", 1);//写入数据
  35. STU* p = (STU*)mmap(NULL, sizeof(STU) * 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//将文件映射为共享内存
  36. if (p == NULL)
  37. ERR_EXIT("mmap failed");
  38. char ch = 'a';
  39. for (int i = 0; i < 5; i++)
  40. {
  41. memcpy((p+i)->name, &ch, 1);
  42. (p+i)->age = 20 + i;
  43. ch++;
  44. }
  45. printf("initialize over\r\n");
  46. munmap(p, sizeof(STU) * 5);
  47. printf("exit\r\n");
  48. return 0;
  49. }
  50. //==================================read
  51. int main(int argc, char* argv[])
  52. {
  53. if (argc != 2)
  54. {
  55. printf("Usage: %s, <filename>\r\n", argv[0]);
  56. exit(EXIT_FAILURE);
  57. }
  58. int fd;
  59. fd = open(argv[1], O_RDWR, 0666); //打开文件
  60. if (fd == 1)
  61. ERR_EXIT("open failed");
  62. STU* p = (STU*)mmap(NULL, sizeof(STU) * 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//将文件映射为共享内存
  63. if (p == NULL)
  64. ERR_EXIT("mmap failed");
  65. char ch = 'a';
  66. for (int i = 0; i < 5; i++)
  67. {
  68. printf("name = %s, age = %d\r\n", (p+i)->name, (p+i)->age);
  69. }
  70. munmap(p, sizeof(STU) * 5);
  71. printf("exit\r\n");
  72. return 0;
  73. }

2 system V共享内存

统一头文件:

  1. #include <sys/ipc.h>
  2. #include <sys/shm.h>

2.1 shmget

函数定义: int shmget(key_t key, size_t size, int shmflg);
功能: 创建共享内存
返回值: 成功返回共享内存段的标识码,失败返回-1
参数:

  • key: 共享内存段的名称
  • size:共享内存大小
  • shmflg:由九个权限标志组成,类似创建文件时的mode权限

    2.2 shmat

    函数定义: void* shmat(int shmid, const void* shmaddr, int shmflg);
    功能: 将共享内存段连接到进程地址空间
    返回值: 成功返回共享内存起始地址,失败返回-1
    参数:

  • shmid:共享内存段标识码

  • shmaddr:指定连接地址
  • shmflg:SHM_RND或SHM_RDONLY

备注:

  • shmaddr=NULL, 内核自动选择一个地址返回
  • shmaddr!=NULL,shmflg!=SHM_RND, 以shmaddr为连接地址
  • shmaddr!=NULL,shmflg=SHM_RND,内核会自动调整对齐地址,公式:addr - (addr % SHMLBA)

    2.3 shmdt

    函数定义: int shmdt(const void* shmaddr);
    功能: 将共享内存段与当前进程脱离,但并未删除共享内存段
    返回值: 成功返回0,失败返回-1
    参数:

  • shmaddr:共享内存段起始地址

2.4 shmclt

函数定义: int shmctl(int shmid, int cmd, struct shmid_ds* buf);
功能: 修改或删除共享内存段
返回值: 成功返回0,失败返回-1
参数:

  • shmid:共享内存标识码
  • cmd:将要采取的操作

imgclip6.png

  • buf: 想要配置的值

2.5 代码实例

  1. //======================================write
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <errno.h>
  8. #include <string.h>
  9. #include <fcntl.h>
  10. #include <sys/ipc.h>
  11. #include <sys/shm.h>
  12. #define ERR_EXIT(m)\
  13. do\
  14. {\
  15. perror(m);\
  16. exit(EXIT_FAILURE);\
  17. }while(0)
  18. typedef struct student
  19. {
  20. char name[4];
  21. int age;
  22. }STU;
  23. int main(int argc, char* argv[])
  24. {
  25. int shmid;
  26. shmid = shmget(1234, sizeof(STU), IPC_CREAT| 0666);
  27. if (shmid == -1)
  28. ERR_EXIT("shmget faile");
  29. STU *p;
  30. p = (STU*)shmat(shmid, NULL, SHM_RND);//映射内存到进程的地址空间
  31. if (p == (void*)-1)
  32. ERR_EXIT("shmat failed");
  33. strcpy(p->name, "barr");//直接对共享内存进行操作
  34. p->age = 20;
  35. shmdt(p);//解除映射
  36. return 0;
  37. }
  38. //===================================read
  39. int main(int argc, char* argv[])
  40. {
  41. int shmid;
  42. shmid = shmget(1234, sizeof(STU), 0);//打开填0即可
  43. if (shmid == -1)
  44. ERR_EXIT("shmget faile");
  45. STU *p;
  46. p = (STU*)shmat(shmid, NULL, SHM_RND);//映射内存到进程的地址空间
  47. if (p == (void*)-1)
  48. ERR_EXIT("shmat failed");
  49. //读取内存中值
  50. printf("name:%s, age:%d", p->name, p->age);
  51. shmdt(p);//解除映射
  52. shmctl(shmid, IPC_RMID, NULL);//删除共享内存
  53. return 0;
  54. }

3 posix共享内存

头文件

  1. #include <sys/mman.h>
  2. #include <sys/stat.h> /* For mode constants */
  3. #include <fcntl.h> /* For O_* constants */

3.1 posix共享内存函数

shm_open

函数定义:int shm_open(const char *name, int oflag, mode_t mode);
功能: 创建或打开共享内存对象
返回值: 成功返回文件描述符,失败返回-1
参数:

  • name:共享内存名字
  • oflag:取值有O_RDONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC
  • mode: 权限,oflag指定O_CREAT时需要设置,其他可以设置为0

    ftruncate

    函数定义:int ftruncate(int fd, off_t length);
    功能: 修改共享内存对象的大小
    返回值: 成功返回0,失败返回-1
    参数:

  • fd:文件描述符

  • length:要修改的长度

    fstat

    函数定义:int fstat(int fd, struct stat *buf);
    功能: 获取共享内存对象的信息
    返回值: 成功返回0,失败返回-1
    参数:

  • fd:文件描述符

  • buf:详细内存状态

    shm_unlink

    函数定义:int shm_unlink(const char *name);
    功能: 删除共享内存对象
    返回值: 成功返回0,失败返回-1
    参数:

  • name:共享内存的名字

3.2 共享内存映射

POSIX共享内存映射同样要使用mmapmunma函数来映射和取消映射。

  1. #include "common.h"
  2. #include <sys/mman.h>
  3. #include <sys/stat.h> /* For mode constants */
  4. #include <fcntl.h> /* For O_* constants */
  5. struct student
  6. {
  7. char name[32];
  8. int age;
  9. };
  10. int main()
  11. {
  12. //创建共享内存
  13. int shmid = shm_open("/xyz", O_CREAT | O_RDWR, 0666);
  14. if (shmid == -1)
  15. ERR_EXIT("shm_open failed");
  16. //设置共享内存大小
  17. if (ftruncate(shmid, sizeof(student)) == -1)
  18. ERR_EXIT("ftruncate failed");
  19. struct stat buf;
  20. //获取共享内存信息
  21. if (fstat(shmid, &buf) == -1)
  22. ERR_EXIT("fstat failed");
  23. printf("size=%d, mode=%d\r\n", buf.st_size, buf.st_mode & 0777);
  24. //映射共享内存到进程地址空间
  25. student* p = (student*)mmap(NULL, buf.st_size, PROT_WRITE, MAP_SHARED, shmid, 0);
  26. if (p == MAP_FAILED)
  27. ERR_EXIT("mmap failed");
  28. strcpy(p->name, "test");
  29. p->age = 29;
  30. if (shm_unlink("/xyz") == -1)
  31. ERR_EXIT("shm_unlink");
  32. return 0;
  33. }