#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PERM S_IRUSR|S_IWUSR
int main(int argc, char **argv)
{
key_t shmid;
char *p_addr, *c_addr;
pid_t pid;
if(argc != 2) {
fprintf(stderr, "Usage:%s/n/a", argv[0]);
exit(1);
}
if( (shmid = shmget(IPC_PRIVATE, 1024, PERM)) == -1 ) { //(1)申请一个共享内存
fprintf(stderr, "Create Share Memory Error:%s/n/a", strerror(errno));
exit(1);
}
pid = fork();
if(pid > 0) {
p_addr = (char *)shmat(shmid, 0, 0);//在主进程中获得次共享内存的地址
memset(p_addr, '/0', 1024);
strncpy(p_addr, argv[1], 1024);
wait();
exit(0);
}
else if (pid == 0){
sleep(1);
c_addr = (char *) shmat(shmid, 0, 0);//在子进程中获取此共享内存的地址
printf("Client get %s/n", c_addr);
exit(0);
}
}
注意:共享内存一旦创建就会一直存在系统中,直到手动删除或者重启系统。
转自:http://blog.youkuaiyun.com/liranke/article/details/5595740
共享内存:
使用共享内存和使用malloc()来分配内存区域很相似。使用共享内存的方法是:
1 对一个进程/线程使用shmget()分配内存区域。(进程使用shmget申请一个共享内存)
2 使用shmat()放置一个或多个进程/线程在共享内存中,也可以用shmctl()来获取信息或者控制共享区域。(在本线程中获取共享内存的地址)
3 使用shmdt()从共享区域中分离。
4 使用shmctl()解除分配空间
共享内存是Linux中最快速的IPC方法。它也是一个双向过程,共享区域内的任何进程都可以读写内存。这个机制的不利方面是其同步和协议都不受程序员控制,必须确保将句柄传递给了子进程和线程。
一. 函数: shmget介绍
功能:取得共享内存段
语法:#include <sys/types.h>;
#include <sys/ipc.h>;
#include <sys/shm.h>;
int shmget(key,size,shmflg)
key_t key;
int size,shmflg;
说明:本系统调用返回key相关的共享内存标识符.
共享内存标识符和相关数据结构及至少size字节的共享内存段能
正常创建,要求以下事实成立:
. 参数key等于IPC_PRIVATE.
. 参数key没有相关的共享内存标识符,同时(shmflg&IPC_CREAT)
值为真.
共享内存创建时,新生成的共享内存标识相关的数据结构被初始
化如下:
. shm_perm.cuid和shm_perm.uid设置为调用进程的有效UID.
. shm_perm.cgid和shm_perm.gid设置为调用进程的有效GID.
. shm_perm.mode访问权限比特位设置为shmflg访问权限比特位.
. shm_lpid,shm_nattch,shm_atime,shm_dtime设置为0.
. shm_ctime设置为当前系统时间.
. shm_segsz设置为0.
返回值:若调用成功则返回一个非0值,称为共享内存标识符,否则返回
值为-1.
key_t key
-----------------------------------------------
key标识共享内存的键值: 0/IPC_PRIVATE。 当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。
在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key)。通过“键”,进程能够识别所用的对象。“键”与IPC对象的关系就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。
Linux系统中的所有表示System V中IPC对象的数据结构都包括一个ipc_perm结构,其中包含有IPC对象的键值,该键用于查找System V中IPC对象的引用标识符。如果不使用“键”,进程将无法存取IPC对象,因为IPC对象并不存在于进程本身使用的内存中。
通常,都希望自己的程序能和其他的程序预先约定一个唯一的键值,但实际上并不是总可能的成行的,因为自己的程序无法为一块共享内存选择一个键值。因此,在此把key设为IPC_PRIVATE,这样,操作系统将忽略键,建立一个新的共享内存,指定一个键值,然后返回这块共享内存IPC标识符ID。而将这个新的共享内存的标识符ID告诉其他进程可以在建立共享内存后通过派生子进程,或写入文件或管道来实现。
int size(单位字节Byte)
-----------------------------------------------
size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE=4096字节)这样,新创建的共享内存的大小实际上是从size这个参数调整而来的页面大小。即如果size为1至4096,则实际申请到的共享内存大小为4K(一页);4097到8192,则实际申请到的共享内存大小为8K(两页),依此类推。
int shmflg
-----------------------------------------------
shmflg主要和一些标志有关。其中有效的包括IPC_CREAT和IPC_EXCL,它们的功能与open()的O_CREAT和O_EXCL相当。
IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则打开操作。
IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,或者返回-1。IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。对于用户的读取和写入许可指定SHM_R和SHM_W,(SHM_R>3)和(SHM_W>3)是一组读取和写入许可,而(SHM_R>6)和(SHM_W>6)是全局读取和写入许可。
返回值
-----------------------------------------------
成功返回共享内存的标识符;不成功返回-1,errno储存错误原因。
EINVAL 参数size小于SHMMIN或大于SHMMAX。
EEXIST 预建立key所致的共享内存,但已经存在。
EIDRM 参数key所致的共享内存已经删除。
ENOSPC 超过了系统允许建立的共享内存的最大值(SHMALL )。
ENOENT 参数key所指的共享内存不存在,参数shmflg也未设IPC_CREAT位。
EACCES 没有权限。
ENOMEM 核心内存不足。
二. 相关数据结构:
struct shmid_ds
-----------------------------------------------
shmid_ds数据结构表示每个新建的共享内存。当shmget()创建了一块新的共享内存后,返回一个可以用于引用该共享内存的shmid_ds数据结构的标识符。
include/linux/shm.h
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
struct ipc_perm
-----------------------------------------------
对于每个IPC对象,系统共用一个struct ipc_perm的数据结构来存放权限信息,以确定一个ipc操作是否可以访问该IPC对象。
struct ipc_perm {
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
Client get hello
$ ./a.out hello
http://blog.youkuaiyun.com/nellson/article/details/5398436
共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。因此,采用共享内存的通信方式效率是非常高的。
【应用场景】
1. 进程间通讯-生产者消费者模式
生产者进程和消费者进程通讯常使用共享内存,比如一个网络服务器,接入进程收到数据包后,直接写到共享内存中,并唤醒处理进程,处理进程从共享内存中读数据包,进行处理。当然,这里要解决互斥的问题。
2. 父子进程间通讯
由于fork产生的子进程和父进程不共享内存区,所以父子进程间的通讯也可以使用共享内存,以POSIX共享内存为例,父进程启动后使用MAP_SHARED建立内存映射,并返回指针ptr。fork结束后,子进程也会有指针ptr的拷贝,并指向同一个文件映射。这样父、子进程便共享了ptr指向的内存区。
3. 进程间共享-只读模式
业务经常碰到一种场景,进程需要加载一份配置文件,可能这个文件有100K大,那如果这台机器上多个进程都要加载这份配置文件时,比如有200个进程,那内存开销合计为20M,但如果文件更多或者进程数更多时,这种对内存的消耗就是一种严重的浪费。比较好的解决办法是,由一个进程负责把配置文件加载到共享内存中,然后所有需要这份配置的进程只要使用这个共享内存即可。
【共享内存分类】
1. POSIX共享内存对象
const char shmfile[] = "/tmp";
const int size = 100;
shm_open创建一个名称为tmp,大小为100字节的共享内存区对象后,在/dev/shm/下可以看到对应的文件,cat可以看到内容。
root:/home/#ls -al /dev/shm/tmp
-rw------- 1 root root 100 10-15 13:37 /dev/shm/tmp
访问速度:非常快,因为 /dev/shm 是tmpfs的文件系统, 可以看成是直接对内存操作的,速度当然是非常快的。
持续性:随内核,即进程重启共享内存中数据不会丢失,内核自举或显示调用shm_unlink或rm掉文件删除后丢失。
2. POSIX内存映射文件
const char shmfile[] = "./tmp.shm";
const int size = 100;
open在指定目录下创建指定名称后文件,cat可以看到内容。
root:/home/#ls -al ./tmp.shm
-rw------- 1 root root 100 10-15 13:42 tmp.shm
访问速度:慢于内存区对象,因为内核为同步或异步更新到文件系统中,而内存区对象是直接操作内存的。
持续性:随文件,即进程重启或内核自举不后丢失,除失显示rm掉文件后丢失。
3. SYSTEM V共享内存
共享内存创建后,执行ipcs命令,会打印出相应的信息,比如下面所示,key为申请时分配的,可以执行ipcrm -M 0x12345678 删除,nattch字段为1表示有一个进程挂载了该内存。
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x12345678 32769 root 644 10 1
访问速度:非常快,可以理解为全内存操作。
持续性: 随内核,即进程重启共享内存中数据不会丢失,内核自举或显示调用shmdt或使用ipcrm删除后丢失。
与POSIX V共享内存区对象不同的是,SYSTEM V的共享内存区对象的大小是在调用shmget创建时固定下来的,而POSIX共享内存区大小可以在任何时刻通过ftruncate修改。
【代码示例】
下面给出三种共享内存使用方法的示例代码,都采用父子进程间通讯,并未考虑互斥,仅做示例供大家参考。
1.POSIX共享内存对象
- /*
- * Posix shared memory is easy to use in Linux 2.6, in this program, we
- * shared a memory between parent process and child process, stored several
- * objects of struct namelist in it. We store number of items in ptr[0].
- */
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <errno.h>
- #define FILE_MODE (S_IRUSR | S_IWUSR)
- const char shmfile[] = "/tmp";
- const int size = 100;
- struct namelist
- {
- int id;
- char name[20];
- };
- int
- main(void)
- {
- int fd, pid, status;
- int *ptr;
- struct stat stat;
- // create a Posix shared memory
- int flags = O_RDWR | O_CREAT;
- fd = shm_open(shmfile, flags, FILE_MODE);
- if (fd < 0) {
- printf("shm_open failed, errormsg=%s errno=%d", strerror(errno), errno);
- return 0;
- }
- ftruncate(fd, size);
- ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- pid = fork();
- if (pid == 0) { // child process
- printf("Child %d: start/n", getpid());
- fd = shm_open(shmfile, flags, FILE_MODE);
- fstat(fd, &stat);
- ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- close(fd);
- struct namelist tmp;
- // store total num in ptr[0];
- *ptr = 3;
- ptr++;
- namelist *cur = (namelist *)ptr;
- // store items
- tmp.id = 1;
- strcpy(tmp.name, "Nellson");
- *cur++ = tmp;
- tmp.id = 2;
- strcpy(tmp.name, "Daisy");
- *cur++ = tmp;
- tmp.id = 3;
- strcpy(tmp.name, "Robbie");
- *cur++ = tmp;
- exit(0);
- } else{ // parent process
- sleep(1);
- struct namelist tmp;
- int total = *ptr;
- printf("/nThere is %d item in the shm/n", total);
- ptr++;
- namelist *cur = (namelist *)ptr;
- for (int i = 0; i< total; i++) {
- tmp = *cur;
- printf("%d: %s/n", tmp.id, tmp.name);
- cur++;
- }
- printf("/n");
- waitpid(pid, &status, 0);
- }
- // remvoe a Posix shared memory from system
- printf("Parent %d get child status:%d/n", getpid(), status);
- return 0;
- }
编译执行
root:/home/ftpuser/ipc#g++ -o shm_posix -lrt shm_posix.cc
root:/home/ftpuser/ipc#./shm_posix
Child 2280: start
There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie
Parent 2279 get child status:0
2.POSIX文件映射
- /*
- * Posix shared memory is easy to use in Linux 2.6, in this program, we
- * shared a memory between parent process and child process, stored several
- * objects of struct namelist in it. We store number of items in ptr[0].
- */
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #define FILE_MODE (S_IRUSR | S_IWUSR)
- const char shmfile[] = "./tmp.shm";
- const int size = 100;
- struct namelist
- {
- int id;
- char name[20];
- };
- int main(void)
- {
- int fd, pid, status;
- int *ptr;
- struct stat stat;
- // create a Posix shared memory
- int flags = O_RDWR | O_CREAT;
- fd = open(shmfile, flags, FILE_MODE);
- ftruncate(fd, size);
- ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- pid = fork();
- if (pid == 0) { // child process
- printf("Child %d: start/n", getpid());
- fd = open(shmfile, flags, FILE_MODE);
- fstat(fd, &stat);
- ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- close(fd);
- struct namelist tmp;
- // store total num in ptr[0];
- *ptr = 3;
- ptr++;
- namelist *cur = (namelist *)ptr;
- // store items
- tmp.id = 1;
- strcpy(tmp.name, "Nellson");
- *cur++ = tmp;
- tmp.id = 2;
- strcpy(tmp.name, "Daisy");
- *cur++ = tmp;
- tmp.id = 3;
- strcpy(tmp.name, "Robbie");
- *cur++ = tmp;
- exit(0);
- } else{ // parent process
- sleep(1);
- struct namelist tmp;
- int total = *ptr;
- printf("/nThere is %d item in the shm/n", total);
- ptr++;
- namelist *cur = (namelist *)ptr;
- for (int i = 0; i< total; i++) {
- tmp = *cur;
- printf("%d: %s/n", tmp.id, tmp.name);
- cur++;
- }
- printf("/n");
- waitpid(pid, &status, 0);
- }
- printf("Parent %d get child status:%d/n", getpid(), status);
- return 0;
- }
编译执行
root:/home/ftpuser/ipc#g++ -o map_posix map_posix.cc
root:/home/ftpuser/ipc#./map_posix
Child 2300: start
There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie
Parent 2299 get child status:0
3.SYSTEM V 共享内存对象
- /*
- * System V shared memory in easy to use in Linux 2.6, in this program, we
- * shared a memory between parent process and child process, stored several
- * objects of struct namelist in it. We store number of items in ptr[0].
- */
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)
- const char shmfile[] = "./tmp.shm";
- const int shmsize = 10;
- struct namelist
- {
- int id;
- char name[20];
- };
- int
- main(void)
- {
- int shmid, pid, status;
- int *ptr;
- struct shmid_ds buff;
- // create a systym V shared memory
- //shmid = shmget(ftok(shmfile, 0), shmsize, SVSHM_MODE | IPC_CREAT);
- shmid = shmget((key_t)0x12345678, shmsize, SVSHM_MODE | IPC_CREAT);
- pid = fork();
- if (pid == 0) { // child process
- printf("Child %d: start/n", getpid());
- //shmid = shmget(ftok(shmfile, 0), shmsize, SVSHM_MODE | IPC_CREAT);
- shmid = shmget((key_t)0x12345678, shmsize, SVSHM_MODE | IPC_CREAT);
- ptr = (int *) shmat(shmid, NULL, 0);
- shmctl(shmid, IPC_STAT, &buff);
- struct namelist tmp;
- // store total num in ptr[0];
- *ptr = 3;
- ptr++;
- namelist *cur = (namelist *)ptr;
- // store items
- tmp.id = 1;
- strcpy(tmp.name, "Nellson");
- *cur++ = tmp;
- tmp.id = 2;
- strcpy(tmp.name, "Daisy");
- *cur++ = tmp;
- tmp.id = 3;
- strcpy(tmp.name, "Robbie");
- *cur++ = tmp;
- exit(0);
- } else{ // parent process
- sleep(1);
- shmctl(shmid, IPC_STAT, &buff);
- ptr = (int *) shmat(shmid, NULL, 0);
- struct namelist tmp;
- int total = *ptr;
- printf("/nThere is %d item in the shm/n", total);
- ptr++;
- namelist *cur = (namelist *)ptr;
- for (int i = 0; i< total; i++) {
- tmp = *cur;
- printf("%d: %s/n", tmp.id, tmp.name);
- cur++;
- }
- printf("/n");
- waitpid(pid, &status, 0);
- }
- // remvoe a systym V shared memory from system
- shmctl(shmid, IPC_RMID, NULL);
- printf("Parent %d get child status:%d/n", getpid(), status);
- return 0;
- }
编译执行
root:/home/ftpuser/ipc#g++ -o shm_v shm_v.cc
root:/home/ftpuser/ipc#./shm_v
Child 2323: start
There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie
Parent 2322 get child status:0
【性能测试】
下面对三种方式进行性能测试,比较下差异。
测试机信息:
AMD Athlon(tm) Neo X2 Dual Core Processor 6850e
cpu:1.7G
os: Linux 2.6.18
测试方式:
打开大小为SIZE的共享内存,映射到一个int型的数组中,循环写数组、读数组。
重复10W次,计算时间开销。
内存大小 | Shmopen+mmap(ms) | Open+mmap | Shmget |
4k | 1504 | 1470 | 1507 |
16k | 6616 | 6201 | 5994 |
64k | 25905 | 24391 | 24315 |
256k | 87487 | 76981 | 69417 |
1M | 253209 | 263431 | 241886 |
重复1K次,计算时间开销。
内存大小 | Shmopen+mmap(ms) | Open+mmap(ms) | Shmget(ms) |
1M | 5458 | 5447 | 5404 |
4M | 21492 | 21447 | 21307 |
16M | 90880 | 93685 | 87594 |
32M | 178000 | 214900 | 193000 |
分析:
Sytem V方式读写速度快于POSIX方式,而POSIX 共享内存和文件映射方式相差不大, 共享内存性能略优。
附上测试源码:
- /*
- * 共享内存读写速度测试
- */
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include "module_call.h"
- #define FILE_MODE (S_IRUSR | S_IWUSR)
- #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)
- enum emType
- {
- SHMOPEN = 0x01,
- OPEN = 0x02,
- SHMGET = 0x04,
- };
- void * GetShmMem(emType type, int size)
- {
- void * ptr = NULL;
- switch (type)
- {
- case SHMOPEN:
- {
- const char shmfile[] = "/tmp";
- int flags = O_RDWR | O_CREAT;
- int fd = shm_open(shmfile, flags, FILE_MODE);
- if (fd < 0)
- {
- printf("shm_open failed, errormsg=%s errno=%d/n", strerror(errno), errno);
- return NULL;
- }
- ftruncate(fd, size);
- ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (MAP_FAILED == ptr)
- {
- printf("mmap failed, errormsg=%s errno=%d/n", strerror(errno), errno);
- return NULL;
- }
- break;
- }
- case OPEN:
- {
- const char shmfile[] = "./tmp.shm";
- int flags = O_RDWR | O_CREAT;
- int fd = open(shmfile, flags, FILE_MODE);
- if (fd < 0)
- {
- printf("ope failed, errormsg=%s errno=%d/n", strerror(errno), errno);
- return NULL;
- }
- ftruncate(fd, size);
- ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (MAP_FAILED == ptr)
- {
- printf("mmap failed, errormsg=%s errno=%d/n", strerror(errno), errno);
- return NULL;
- }
- break;
- }
- case SHMGET:
- {
- int shmid;
- struct shmid_ds buff;
- const char shmfile[] = "./tmp.shm_v";
- shmid = shmget(ftok(shmfile, 0), size, SVSHM_MODE | IPC_CREAT);
- if (shmid < 0)
- {
- printf("shmget failed, errormsg=%s errno=%d/n", strerror(errno), errno);
- return NULL;
- }
- ptr = (int *) shmat(shmid, NULL, 0);
- if ((void *) -1 == ptr)
- {
- printf("shmat failed, errormsg=%s errno=%d/n", strerror(errno), errno);
- return NULL;
- }
- shmctl(shmid, IPC_STAT, &buff);
- break;
- }
- }
- return ptr;
- }
- int realmain(int size, int loop, emType type)
- {
- int * array_int = NULL;
- /* get shmmem*/
- array_int = (int *)GetShmMem(type, size);
- if (NULL == array_int)
- {
- printf("GetShmMem failed/n");
- return -1;
- }
- /* loop */
- int array_num = size/sizeof(int);
- modulecall::call_start();
- while (0 != loop)
- {
- /* write */
- for (int i = 0; i < array_num; i++)
- {
- array_int[i] = i;
- }
- /* read */
- for (int i = 0; i < array_num; i++)
- {
- if (array_int[i] != i)
- {
- printf("ShmMem is invalid i=%d v=%d/n", i, array_int[i]);
- return -1;
- }
- }
- loop--;
- }
- modulecall::call_end();
- printf("timecost=%lld/n", modulecall::call_timecost());
- return 0;
- }
- int main(int argc, char ** argv)
- {
- if (argc < 4)
- {
- printf("usage: %s size loop shmtype(1-shmposix 2-mapposix 4-shmv 7-all)/n", argv[0]);
- return -1;
- }
- const int size = atoi(argv[1]);
- int loop = atoi(argv[2]);
- const int type = atoi(argv[3]);
- if ((type&SHMOPEN) == SHMOPEN)
- {
- printf("shmopen ");
- realmain(size, loop, SHMOPEN);
- }
- if ((type&OPEN) == OPEN)
- {
- printf("open ");
- realmain(size, loop, OPEN);
- }
- if ((type&SHMGET) == SHMGET)
- {
- printf("shmget ");
- realmain(size, loop, SHMGET);
- }
- return 0;
- }
基于文件映射的共享内存实战:
这个共享的内存其实就是在硬盘上的一个文件,但是对于进程而言,就可以看做是一块内存,用memcpy就能操作这块共享内存。
比如可以把一个结构体的数组写入这块内存,然后,进程结束,但是这个结构体就放入了硬盘的文件中,对于新的进程不需要再一行一行的读取文件到内存,只要把这个文件映射成内存的一个指针,然后,就像操作内存中的数组一样操作文件内的数组。
P1.cpp
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
const char shmfile[] = "shm";
const int size = 100;
struct AA {
int id;
char name[20];
};
int main() {
int fd = open(shmfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
ftruncate(fd, size);
unsigned char *ptr = (unsigned char *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
*ptr = 2;
++ptr;
AA *aptr = (AA *) ptr;
aptr->id = 1;
strcpy(aptr->name, "nike");
aptr++;
aptr->id = 2;
strcpy(aptr->name, "josh");
getchar();
return 0;
}
p2.cpp
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
const char shmfile[] = "shm";
const int size = 100;
struct AA {
int id;
char name[20];
};
int main() {
int fd = open(shmfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
ftruncate(fd, size);
unsigned char *ptr = (unsigned char *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
int num = *ptr;
++ptr;
AA *aptr = (AA *) ptr;
for (int i = 0; i < num; ++i) {
printf("%d: %s\n", aptr->id, aptr->name);
++aptr;
}
getchar();
return 0;
}
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
using namespace std;
int Open(const string& fn, int count, struct stat& st) {
int fd = open(fn.c_str(), O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);//1,open fd
ftruncate(fd, count+1);//2,allocate size
fstat(fd,&st);//3, statistic
return fd;
}
uint8_t *GetMem(int fd, int count) {
uint8_t *head = (uint8_t*)mmap(NULL, count+1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//4,create mapping from disk to memory
}
void Close(uint8_t *head,int fd, struct stat& st) {//6, close fd, write to disk
munmap(head, st.st_size);
close(fd);
}
void Write(int count, uint8_t *head) {//5, write as memory
int offset = 0;
int* memp = (int*)head;
memp[offset] = count;
++offset;
for (int i = 0; i < count; ++i)
memp[offset+i] = (i+1)*100;
}
void Read(uint8_t* head) {
int *memp = (int*)head;
int count = memp[0];
for (int i = 0; i < count; ++i)
cout << memp[i+1] <<" ";
cout <<endl;
}
int main(int argc, char *argv[]) {
if (argc != 4) {
cout << "filename count w|r\n";
return -1;
}
int count = atoi(argv[2]);
struct stat st;
int fd;
uint8_t *head;
if (argv[3][0] == 'w') {
fd = open(argv[1], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);//1,open fd
ftruncate(fd, count+1);//2,allocate size
fstat(fd,&st);//3, statistic
head = (uint8_t*)mmap(NULL, count+1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//4,create mapping from disk to memory
Write(count, head);
}
else {
fd = open(argv[1],O_RDONLY, S_IRUSR);//1, open fd
fstat(fd, &st);//2, get fd size
cout << "fstat st.st_size = "<<st.st_size <<endl;
head =(uint8_t*)mmap(NULL,st.st_size,PROT_READ,MAP_SHARED,fd,0);//3, create mapping from disk to memory
Read(head);
}
Close(head,fd, st);
return 1;
}
首先运行程序往共享内存中写入数据
再运行数据将磁盘上的文件映射到内存,只要得到其指针就能可以直接用。
需要注意的是,建立内存映射并没有实际拷贝数据,这时,MMU在地址映射表中是无法找到与ptr相对应的物理地址的,也就是MMU失败,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则会通过mmap()建立的映射关系,从硬盘上将文件读取到物理内存中
如果磁盘上的文件很大,需要再映射之后再对内存中的数据进行下预读,这样就可以将磁盘中的数据全部加载到内存中。
可以把vector用共享内存的方法进行写入和读取吗?
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <vector>
using namespace std;
void Close(uint8_t *head,int fd, struct stat& st) {//6, close fd, write to disk
munmap(head, st.st_size);
close(fd);
}
void Write(int count, uint8_t *head) {//5, write as memory
vector<int> vec(count,0);
for (int i = 0; i < count; ++i)
vec[i] = (i+1)*10;
memcpy(head,&vec,sizeof(vector<int>));//先把vector的信息写入mmap
memcpy(head+sizeof(vector<int>),vec.data(),sizeof(int)* count);//再把数据/数组信息写入接下来的内存
}
void Read(uint8_t* head) {
vector<int> vec;
memcpy(&vec,head,sizeof(vector<int>));//只把vecotor的信息拷贝,data指向的地址不能拷贝
}
int main(int argc, char *argv[]) {
if (argc != 4) {
cout << "filename count w|r\n";
return -1;
}
int count = atoi(argv[2]);
struct stat st;
int fd;
uint8_t *head;
if (argv[3][0] == 'w') {
size_t size = count*sizeof(int)+sizeof(vector<int>);
fd = open(argv[1], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);//1,open fd
ftruncate(fd, size);//2,allocate size
fstat(fd,&st);//3, statistic
head = (uint8_t*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//4,create mapping from disk to memory
Write(count, head);
}
else {
fd = open(argv[1],O_RDONLY, S_IRUSR);//1, open fd
fstat(fd, &st);//2, get fd size
cout << "fstat st.st_size = "<<st.st_size <<endl;
head =(uint8_t*)mmap(NULL,st.st_size,PROT_READ,MAP_SHARED,fd,0);//3, create mapping from disk to memory
Read(head);
}
Close(head,fd, st);
return 1;
}
暂时还是没发现有什么好的办法可以直接读取vector
总结,对于数组类型的数据可以直接用共享内存进行读取,但是对于复杂的数据结构,譬如vector暂时没有高效的方法一步就读取到内存
如果多个进程同时将一份磁盘文件映射到各自的进程空间呢,那么只有一份内存,这样就减少了内存的消耗