背景
在linux系统开发当中,存在多种方案可以实现进程之间的通信。但最高效的方法莫过于共享内存。但是共享内存传输的数据尽量不要太大。
原理
linux操作系统共享内存是借助tmpfs这个文件系统来实现的,tmpfs文件系的目录为/dev/shm,/dev/shm是驻留在内存 RAM 当中的,因此读写速度与读写内存速度一样,/dev/shm的容量默认尺寸为系统内存大小的一半大小,使用df -h命令可以看到。但实际上它并不会真正的占用这块内存,如果/dev/shm/下没有任何文件,它占用的内存实际上就是0字节,仅在使用shm_open文件时,/dev/shm才会真正占用内存。
共享内存接口
/*创建共享内存文件*/ int shm_open(const char *name, int oflag, mode_t mode); /*实现文件映射到内存*/ 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); /*重置文件大小*/ int ftruncate(int fd, off_t length)
创建接口
int shm_open(const char *name, int oflag, mode_t mode);
功能说明:shm_open()用于创建或者打开共享内存文件。shm_open()函数的使用和open 使用很像。 区别之处是shm_open()操作的文件一定是位于tmpfs文件系统里的,常见的Linux发布版的tmpfs文件系统的存放目录就是/dev/shm。
返回值: 成功返回fd>0, 失败返回-1
参数说明:
name:要打开或创建的共享内存文件名,由于shm_open()打开或操作的文件都是位于/dev/shm目录的,因此name最好不能带路径。例如:/var/myshare 这样的名称是错误的,而 myshare 是正确的,因为 myshare 不带任何路径。如果你一定要在name添加路径,那么,请在/dev/shm目录里创建一个目录,例如,如果你想创建一个 bill/myshare 的共享内存文件,那么请先在/dev/shm目录里创建 bill这个子目录,由于不同厂家发布的linux系统的tmpfs的位置也许不是/dev/shm,因此带路径的名称也许在别的环境下打开不成功。
oflag:打开的文件操作属性:O_CREAT、O_RDWR、O_EXCL的按位或运算组合。
mode:文件共享模式,例如 0777
映射接口
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能说明: 将打开的文件映射到内存,一般是将shm_open打开的文件映射到内存,当然也可以将硬盘上的用open函数打开的文件映射到内存。这个函数只是将文件映射到内存中,使得我们用操作内存指针的方式来操作文件数据。
参数说明:
addr:要将文件映射到的内存地址,一般应该传递NULL来由Linux内核指定。
length:要映射的文件数据长度。
prot:映射的内存区域的操作权限(保护属性),包括PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags:标志位参数,包括:MAP_SHARED、MAP_PRIVATE与MAP_ANONYMOUS。
MAP_SHARED: 建立共享,用于进程间通信,如果没有这个标志,则别的进程即使能打开文件,也看不到数据。
MAP_PRIVATE: 只有进程自己用的内存区域
MAP_ANONYMOUS:匿名映射区
fd: 用来建立映射区的文件描述符,用 shm_open打开或者open打开的文件。
offset:映射文件相对于文件头的偏移位置,应该按4096字节对齐。默认输入0即可
返回值:成功返回映射的内存地址指针,可以用这个地址指针对映射的文件内容进行读写操作,读写文件数据如同操作内存一样;如果 失败则返回NULL。
取消内存映射
int munmap(void *addr, size_t length);
功能说明: 取消内存映射,addr是由mmap成功返回的地址,length是要取消的内存长度,munmap 只是将映射的内存从进程的地址空间撤销,如果不调用这个函数,则在进程终止前,该片区域将得不到释放。
删除文件
int shm_unlink(const char *name);
功能说明:删除/dev/shm目录的文件,shm_unlink 删除的文件是由shm_open函数创建于/dev/shm目录的。可以用系统函数unlink来达到同样的效果,用/dev/shm + name 组成完整的路径即可,但一般不要这么做,因为系统的tmpfs的位置也许不是/dev/shm。用shm_open 创建的文件,如果不调用此函数删除,会一直存在于/dev/shm目录里,直到操作系统重启或者调用linux命令rm来删除为止。
重置文件大小
int ftruncate(int fd, off_t length);
功能说明:重置文件大小。任何open打开的文件都可以用这个函数,不限于shm_open打开的文件。
编译
头文件 #include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #include <fcntl.h> /* For O_* constants */ 编译 Link with -lrt.
例子
下面代码是简单例子 还能够优化
/** * @file shm_send.c * @author Fischer (2092086704@qq.com) * @brief 创建两个进程,通过send进程调用recv进程,并把相应的信息传入到recv进程。 * 进程之间通信采用share memory. * @version 0.1 * @date 2021-12-03 * * @copyright Copyright (c) 2021 * */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <time.h> #include "shm_com.h" #define MMAP_DATA_SIZE 1024 int main (int argc, char **argv) { /*1.获取共享内存的名字*/ unsigned char shm_name [256]; struct timeval tv; //struct timezone tz; gettimeofday(&tv, NULL); sprintf(shm_name, "%d", tv.tv_sec/tv.tv_usec + tv.tv_sec%tv.tv_usec); int oflag = O_RDWR | O_CREAT | O_EXCL; mode_t mode = 0755; int shmfd = shm_open(shm_name, oflag, mode); if (shmfd < 0) { printf("send shm_open error!\n"); return -1; } /*调整shm 重置文件大小*/ int ret = ftruncate(shmfd, MMAP_DATA_SIZE); if (ret < 0) { printf("The reset the file size error!\n"); return -1; } /**构建文件映射 */ int prot = PROT_READ|PROT_WRITE; int flags = MAP_SHARED; shm_info_t * shm_share_info = (shm_info_t *)mmap(NULL, MMAP_DATA_SIZE, prot, flags, shmfd, 0 ); if ( shm_share_info == NULL) { printf("send the file map to memory faild!\n"); return -1; } char * shmbuf = "This is hello!"; strcpy(shm_share_info->identify, "sendtorecv"); strcpy(shm_share_info->shm_data, shmbuf); shm_share_info->shm_length = strlen(shmbuf); printf("send shm_name :%s\n",shm_name); printf("send shm_identfy :%s shm_data :%s length:%u \n", shm_share_info->identify, shm_share_info->shm_data, shm_share_info->shm_length); /* put info to shm**/ char *args[3] = {NULL}; args[0] = "./shm_recv"; args[1] = shm_name; args[2] = NULL; if (execv("./shm_recv", args)) { printf(" execv failed\n"); return -1; } close(shmfd); /** 取消文件映射*/ int retm = munmap(shm_share_info, MMAP_DATA_SIZE); if (retm < 0) { printf("send Unmap memory faild!\n"); return -1; } /**删除sham_open 创造出的文件*/ int retu = shm_unlink(shm_name); if (retu < 0) { printf("send delete share memeory file!\n"); return -1; } return 0; }
/** * @file shm_recv.c * @author your name (you@domain.com) * @brief * @version 0.1 * @date 2021-12-03 * * @copyright Copyright (c) 2021 * */ #include "shm_com.h" #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <string.h> #include <unistd.h> #define MMAP_DATA_SIZE 1024 int main(int argc ,char **argv) { printf("The shm_recv running!\n"); if (argc != 2) { printf("argc should be 2, but it's %d",argc); } printf("argv[1]: %s\n",argv[1]); char shm_name [32] = {0}; strcpy(shm_name, argv[1]); //printf("shm_name : %s -- %s\n",shm_name, argv[1] ); /***/ int oflags = O_RDWR; mode_t mode = 0755; int shmfd = shm_open(argv[1], oflags, mode); if (shmfd < 0) { printf("recv shm_open is faild!\n"); return -1; } int prot = PROT_READ|PROT_WRITE; int flags = MAP_SHARED; shm_info_t * shmbuf = (shm_info_t *)mmap(NULL, MMAP_DATA_SIZE, prot, flags, shmfd, 0 ); if (shmbuf == NULL) { printf("recv shmbuf is faild !\n"); return -1; } printf("shm_identfy: %s shm_length: %u shm_data:%s \n",shmbuf->identify, shmbuf->shm_length, shmbuf->shm_data); int ret = munmap(shmbuf, MMAP_DATA_SIZE); if (ret < 0) { printf("recv unmap memeory !\n"); return -1; } close (shmfd); int retu = shm_unlink(shm_name); if (retu < 0) { printf("recv unlink shm_open file!\n"); return -1; } return 0; }
/** * @file shm_com.h * @author your name (you@domain.com) * @brief * @version 0.1 * @date 2021-12-03 * * @copyright Copyright (c) 2021 * */ /*This the file is the shm_send and shm_recv shared*/ #ifndef __SHM_COM_H__ #define __SHM_COM_H__ #define SHM_MAX_LENGTH (1024 -1) #define SHM_MAX_INDENTIFY (32 - 1) typedef struct shm_info_s shm_info_t; /**< 重命名结构体*/ struct shm_info_s { unsigned int shm_length; unsigned char shm_data[SHM_MAX_LENGTH]; unsigned char identify[SHM_MAX_INDENTIFY]; }; #endif
1 #!/bin/bash 2 3 gcc -o shm_send shm_send.c -lrt 4 5 gcc -o shm_recv shm_recv.c -lrt