Linux系统开发中多进程交换数据,共享内存高效且实现与容量占用相关
在 linux 系统开发中,时常需要在多个进程之间进行数据交换。在多个进程之间交换数据有多种方法,然而其中最高效的方法就是共享内存。

使用 df -h 命令可以看到 /dev/shm 的容量情况。实际上它不会真正占用这块内存。如果在 /dev/shm/ 下没有任何文件,那么它占用的内存实际上是 0 字节。只有在使用 shm_open 文件时,/dev/shm 才会真正占用内存。
在Linux系统使用共享内存,一般用到以下几个函数:
创建或打开一个共享内存对象,通过指定的名称、打开标志和模式来进行操作。可以指定一个已存在的共享内存对象进行打开操作,也可以创建一个新的共享内存对象。具体的操作取决于打开标志 oflag 的设置。同时,mode 参数用于设置共享内存对象的访问权限。
- 第六个参数是 off_t 类型的 offset。
int 函数 munmap ,其参数为一个 void 类型的指针 addr 和一个 size_t 类型的长度 length ,用于执行解除映射操作。
调用 shm_unlink 函数,参数为一个字符指针类型的常量 name 。
函数 ftruncate 带有两个参数,一个是文件描述符 fd,另一个是长度值 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, 失败返回fd
参数说明:
要打开或创建共享内存的文件名是 name。因为 shm_open 打开或操作的文件都在 /dev/shm 目录中,所以 name 不能带路径。例如,/var/myshare 这样的名称是错误的,而 myshare 是正确的,因为 myshare 不带任何路径。如果你必须在 name 中添加路径,那么需在 /dev/shm 目录创建一个目录。比如,若要创建 bill/myshare 的共享内存文件,需先在 /dev/shm 目录创建 bill 这个子目录。因为不同厂家发布的 linux 系统的 tmpfs 位置或许不是 /dev/shm,所以带路径的名称在其他环境下可能打开不成功。
打开的文件操作属性为 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_READ、PROT_WRITE 以及 PROT_READ|PROT_WRITE 。
标志位参数 flags 包含 MAP_SHARED、MAP_PRIVATE 以及 MAP_ANONYMOUS 。
建立共享是用于进程间通信的。如果没有这个标志,那么别的进程即使能够打开文件,也无法看到数据。
MAP_PRIVATE: 只有进程自己用的内存区域
MAP_ANONYMOUS:匿名映射区
用来建立映射区的文件描述符,是通过 shm_open 打开或者 open 打开的文件。
映射文件相对于文件头有一个偏移位置,这个偏移位置叫做 offset ,并且这个偏移位置应该按照 4096 字节来进行对齐。
成功时返回映射的内存地址指针,可利用此地址指针对映射的文件内容进行读写操作,读写文件数据就像操作内存一样;失败时则返回 NULL。
请提供需要改写的横线下的句子呀,没有具体内容呢。------
int munmap(void *addr, size_t length);
功能说明:要取消内存映射,其中 addr 是通过 mmap 成功返回的地址,length 是需要取消的内存长度。munmap 仅仅是将映射的内存从进程的地址空间中撤销掉。如果不调用这个函数,那么在进程终止之前,该片区域将无法得到释放。
请提供需要改写的句子呀,你没有给出具体的横线下的句子呢。
int shm_unlink(const char *name);
删除 /dev/shm 目录的文件,由 shm_open 函数在 /dev/shm 目录创建的文件可通过 shm_unlink 删除。系统函数 unlink 能达到相同效果,使用 /dev/shm + name 可组成完整路径,但一般不这样做,因为系统的 tmpfs 位置或许不是 /dev/shm。用 shm_open 创建的文件,若不调用此函数进行删除操作,就会一直留存于 /dev/shm 目录中。这种情况会持续到操作系统重启,或者调用 linux 命令 rm 来执行删除操作为止。
------------------------------------------------------------------------------------------------------------
int ftruncate(int fd, off_t length);
功能说明:对文件大小进行重置。对于那些被 open 打开的文件,无论是通过何种方式打开的,包括 shm_open 打开的文件,都可以使用这个函数来进行操作。
好了,以下是共享内存操作的实践代码:
1.writer.c ,创建内存共享文件并写入数据。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MMAP_DATA_SIZE 1024
#define USE_MMAP 1
主函数的参数为整型的 argc 和字符型指针数组的 argv 。 主函数以整型的 argc 和字符型指针数组的 argv 作为参数。 整型的 argc 和字符型指针数组的 argv 被用作主函数的参数。 主函数接收整型的 argc 和字符型指针数组的 argv 作为参数。 整型的 argc 以及字符型指针数组的 argv 被当作主函数的参数。 主函数的参数包含整型的 argc 和字符型指针数组的 argv 。 主函数以整型的 argc 以及字符型指针数组的 argv 为参数。 整型的 argc 与字符型指针数组的 argv 被用于主函数的参数。 主函数接收包含整型的 argc 和字符型指针数组的 argv 作为参数。
{
char * data;
创建或打开一个名为"shm-file0001"的共享内存对象,使用 O_CREAT|O_RDWR 标志,权限设置为 0777,将返回的文件描述符存储在 fd 变量中。
if (fd < 0) {
printf("shm_open failed!\n");
return -1;
}
ftruncate(fd, MMAP_DATA_SIZE);
if (USE_MMAP) {
data = (char*)mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (!data) {
printf("mmap failed\n");
close(fd);
}
sprintf(data, "This is a share memory! %d\n", fd);
munmap(data, MMAP_DATA_SIZE);
}
else {
char buf[1024];
int len = sprintf(buf,"This is a share memory by write! ! %d\n",fd);
if (write(fd, buf, len) <= 0) {
printf("write file %d failed!%d\n",len,errno);
}
}
close(fd);
getchar();
shm_unlink("shm-file0001");
return 0;
}
编译:
使用 gcc 命令进行编译操作,将生成名为 writer 的可执行文件,编译源文件为 writer.c,并且链接了名为 rt 的库。
2. reader.c 打开内存共享文件读数据
#include
#include
#include
#include
#include
#include
#include
#include
#define MMAP_DATA_SIZE 1024
int main(int argc,char * argv[])
{
char * data;
定义一个整数变量 fd ,通过 shm_open 函数打开名为 "shm-file0001" 的共享内存,使用 O_RDWR 标志以读写模式打开,权限设置为 0777 。
if(fd < 0)
{
printf("error open shm object\n");
return -1;
}
data = (char*)mmap(NULL, MMAP_DATA_SIZE, PROT_READ, MAP_SHARED, fd, 0);
if (!data) {
printf("mmap failed!\n");
close(fd);
return -1;
}
printf(data);
munmap(data,MMAP_DATA_SIZE);
close(fd);
getchar();
return 0;
}
编译:
使用 gcc 命令进行编译操作,将生成名为 reader 的可执行文件,编译的源文件是 reader.c,同时链接了 -lrt 库。
如果写入程序调用了 shm_unlink("shm-file0001"),那么读者程序就无法打开共享内存文件。
笔者认为,共享内存和内存映射是两个不同的概念。并非使用 shm_open 打开的文件就一定要通过 mmap 来映射才能在进程间共享,即便使用常规的 Linux 函数 write 写入数据,也依然能够实现共享。