共享内存基本概念
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据(如图)。

共享内存 VS. 其他IPC形式
用管道/消息队列传递数据
用共享内存传递数据
(内核为每个IPC对象维护一个数据结构)
共享内存生成之后,传递数据并不需要再走Linux内核,共享内存允许两个或多个进程共享一个给定的存储区域,数据并不需要在多个进程之间进行复制,因此,共享内存的传输速度更快!!
System V共享内存数据结构与基本API
[cpp] view plain copy
- //基本数据结构
- struct shmid_ds
- {
- struct ipc_perm shm_perm;
- size_t shm_segsz;
- time_t shm_atime;
- time_t shm_dtime;
- time_t shm_ctime;
- pid_t shm_cpid;
- pid_t shm_lpid;
- shmatt_t shm_nattch;
- ...
- };
共享内存函数
[cpp] view plain copy
- #include <sys/ipc.h>
- #include <sys/shm.h>
- int shmget(key_t key, size_t size, int shmflg);
- void *shmat(int shmid, const void *shmaddr, int shmflg);
- int shmdt(const void *shmaddr);
- int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmget函数
功能:创建共享内存,并将该内存的内容初始化为0
原型:
[cpp] view plain copy
- <span style="font-family:Courier New;">int shmget(key_t key, size_t size, int shmflg);</span>
参数:
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:
成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
[cpp] view plain copy
- //实验1:打开已经存在的共享内存
- int main()
- {
- // int shmget(key_t key, size_t size, int shmflg);
- //打开已经存在的共享内存
- int shmid = shmget(0x225,1024,0666);
- if (shmid == -1)
- {
- if (errno == ENOENT)
- {
- cout << "ENOENT = " << errno << endl;
- }
- err_exit("shmget error");
- }
- return 0;
- }
[cpp] view plain copy
- //实验2:共享内存如若存在,则直接使用;若不存在,则创建. [IPC_CREAT选项作用]
- int main()
- {
- // int shmget(key_t key, size_t size, int shmflg);
- int shmid = shmget(0x225,1024,0666|IPC_CREAT);
- if (shmid == -1)
- {
- if (errno == ENOENT)
- {
- cout << "ENOENT = " << errno << endl;
- }
- err_exit("shmget error");
- }
- else
- {
- cout << "shmid = " << shmid << endl;
- }
- return 0;
- }
[cpp] view plain copy
- //实验3:共享内存如果没有,则创建;如果存在,则返回错误. [IPC_CREAT|IPC_EXCL组合作用]
- int main()
- {
- // int shmget(key_t key, size_t size, int shmflg);
- int shmid = shmget(0x225,1024,0666|IPC_CREAT|IPC_EXCL);
- if (shmid == -1)
- {
- if (errno == ENOENT)
- {
- cout << "ENOENT = " << errno << endl;
- }
- else if (errno == EEXIST)
- {
- cout << "File is exits... EEXIST = " << errno << endl;
- }
- err_exit("shmget error");
- }
- else
- {
- cout << "shmid = " << shmid << endl;
- }
- return 0;
- }
shmat函数
功能:将共享内存段连接到进程地址空间
原型:
[cpp] view plain copy
- void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:
成功返回一个指针,指向共享内存第一个节;失败返回-1
Shmaddr与shmflg组合说明
shmaddr为NULL,Linux内和将自动为进程连接到该内存(推荐使用)
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
[cpp] view plain copy
- //示例1
- int main()
- {
- // int shmget(key_t key, size_t size, int shmflg);
- //获取或者打开共享内存
- int shmid = shmget(0x1576422, sizeof(Student), 0666 | IPC_CREAT);
- if (shmid == -1)
- {
- err_exit("shmget error");
- }
- //将ID为shmid的共享内存连接到该进程
- Student *pStudent = static_cast<Student *>(shmat(shmid, NULL, 0));
- //向内存中写入数据
- Student studentA = {"xiaofang",2012};
- memcpy(pStudent,&studentA,sizeof(Student));
- cout << pStudent ->name << " " << pStudent ->number << endl;
- //亦可获取该内存内容(其实跟本地内存没有什么区别)
- Student *pNewStudent = pStudent;
- cout << pNewStudent -> name << " " << pNewStudent -> number << endl;
- return 0;
- }
[cpp] view plain copy
- //示例2:将共享内存当成数组
- int main()
- {
- // int shmget(key_t key, size_t size, int shmflg);
- //获取或者打开共享内存
- int shmid = shmget(0x15764221, 1024 * sizeof(int), 0666 | IPC_CREAT);
- if (shmid == -1)
- {
- err_exit("shmget error");
- }
- //将ID为shmid的共享内存连接到该进程
- int *pArray = static_cast<int *>(shmat(shmid, NULL, 0));
- if (pArray == (void *)-1)
- {
- err_exit("shmat error");
- }
- for (int i = 0; i != 1024; ++i)
- {
- pArray[i] = i+1;
- }
- for (int i = 0; i != 1024; ++i)
- {
- cout << pArray[i] << endl;
- }
- return 0;
- }