前面带小伙伴们学习了共享内存相关的内容,先简单介绍下共享内存,然后进行结构及函数的学习,最后撸代码使用一下这些函数使用一下共享内存,希望对大家有所帮助哈!
1 共享内存的概念及使用过程
1)共享内存的概念
共享内存是IPC机制中的一种。
共享内存:即允许两个或多个进程共享一个给定的存储区。
2)共享内存的使用过程
① 进程1创建共享内存,接着映射共享内存。
② 进程2获取共享内存,映射共享内存。
③ 交互完成,进程1分离共享内存,进程2分离共享内存。
④ 进程1删除共享内存。
2 共享内存相关的结构及函数
0)共享内存相关的结构
内核为每个共享存储段维护着一个结构,该结构至少要为每个共享存储段包含以下成员。
- 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
- pid_t shm_lpid; // 上个shmat(2)/shmdt(2)的PID
- shmatt_t shm_nattch; // 当前附加到该段的进程的个数
- ...
- };
系统为每一个IPC对象保存一个ipc_perm结构体,该结构说明了IPC对象的权限和所有者,每一个版本的内核各有不用的ipc_perm结构成员。
- struct ipc_perm
- {
- key_t __key; // 为 shmget(2) 调用提供的键值
- uid_t uid; // 共享内存所有者的有效用户UID
- gid_t gid; // 共享内存所有者所属组的有效组GID
- uid_t cuid; // 共享内存创建者的有效用户UID
- gid_t cgid; // 共享内存创建者所属组的有效组ID
- unsigned short mode; // 特权 + SHM_DEST 和SHM_LOCKED 标志
- unsigned short __seq; // 序列号
- };
1)shmget函数
shmget函数用于创建或者获取共享内存,并返回其描述符id。
① 函数原型。
- int shmget(key_t key,size_t sizie,int shmflg)
② 头文件。
- include <sys/ipc.h>
- include <sys/shm.h>
③ 参数。
key:共享内存的键值。
size:共享内存的大小。
shmflg:打开标志,如果使用了IPC_CREAT,则会新创建一块共享内存。
④ 返回值。
成功:返回创建或者获取到的共享内存的描述符。
失败:-1。
2)shmat函数
shmat函数用于映射共享内存,即将进程连接到它的地址空间。
① 函数原型。
- void *shmat(int shmid,const void *shmaddr,int shmflg)
② 头文件。
- include <sys/types.h>
- include <sys/shm.h>
③ 参数。
shmid:要映射的共享内存的描述符。
shmaddr:共享内存的地址。
shmflg:打开标志,如果使用了IPC_CREAT,则会新创建一块共享内存。
④ 返回值。
成功:返回创建或者获取到的共享内存的描述符。
失败:-1。
3)shmdt函数
shmdt函数用于分离共享内存,即操作完存储段后,用此函数可以将进程与此存储段脱离开,即断掉与共享内存的联系。
① 函数原型。
- int shmdt(const void *shmaddr)
② 头文件。
- #include <sys/types.h>
- #include <sys/shm.h>
③ 参数。
shmaddr:要断开的共享内存的映射地址。
④ 返回值。
成功:0。
失败:-1。
4)shmctl函数
shmctl函数用于控制共享内存,通过参数可以对共享内存进行特定的操作。
① 函数原型。
- int shmctl(int shmid, int cmd, struct shmid_ds *buf)
② 头文件。
- #include <sys/ipc.h>
- #include <sys/shm.h>
③ 参数。
shmid:要控制的共享内存的id。
cmd:决定执行什么样的控制操作,如IPC_RMID(表示删除)。
buf:获取linux中描述共享内存的shmid_ds结构。基本不使用。
cmd可去的参数如下,需要参照上面的结构shmid_ds和ipc_perm :
IPC_STAT:取此段的shmid_ds结构,并将它存储在由buf指向的结构中。
IPC_SET:按buf指向的结构中的值设置与此共享存储段相关的shmid_ds结构中的下列3个字段:shmperm.uid、shm perm.gid和shmperm.mode。
此命令只能由下列两种进程执行:一种是其有效用户ID等于shm_perm.cuid或shmperm.uid的进程;另一种是具有超级用户特权的进程。
IPC_RMID:从系统中删除该共享存储段。
除非使用该段的最后一个进程终止或与该段分离,否则不会实际上删除该存储段。
不管此段是否仍在使用,该段标识符都会被立即删除,所以不能再用shmat与该段连接。
此命令只能由下列两种进程执行:一种是其有效用户ID等于shm_perm.cuid或shm_perm.uid的进程;另一种是具有超级用户特权的进程。
下面两个命令只能由超级用户执行:
SHM_LOCK:在内存中对共享存储段加锁。
SHM_UNLOCK:解锁共享存储段。
④ 返回值。
成功:根据不同的操作返回不同的值。
失败:-1。
3 实例代码
下面用两个进程,给大家演示下共享内存的使用过程。
实例代码如下,说明都在代码注释中了。
WriteMemory.c。
- #include <sys/types.h>
- #include <sys/shm.h>
- #include <sys/ipc.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #define SIZE 1024 // 可输入1K字符串
- struct SharedMemoryST
- {
- int ReadWriteFlag; // 表明是谁放进去的
- char CharData[SIZE]; // 字符数组保存用户输入数据
- };
- int main(int argc,char *argv[])
- {
- int shmid;
- int ReadStatusFlag = 1; // 内存中数据是否被读走,1未被读走
- struct SharedMemoryST *shm; // 共享内存结构变量
- char buffer[SIZE];
- key_t key=ftok("/tmp",12); // 创建共享内存的键值,如果提示创建失败(一般是没有quit引起的),可以修改读写进程的键值,都要改成同一数字
- //1 创建共享内存
- shmid = shmget(key,sizeof(struct SharedMemoryST),IPC_CREAT|IPC_EXCL|0777);
- if(shmid == -1) // 如果创建失败
- {
- printf("\nCreating share memory fail!\n\n");
- exit(1);
- }
- //2 映射共享内存
- shm = shmat(shmid,NULL,0); // 内存id,映射的位置,映射的标志(此无特殊要求)
- //3 查询写入的
- while(ReadStatusFlag) // 循环检查写入共享内存的数据是否被读走,读走后退出循环
- {
- while(shm->ReadWriteFlag == 1)
- {
- sleep(1);
- printf("\nWaiting read memory!\n");
- }
- // 获取用户输入
- printf("\nPlease input data or input 'quit' to exit!\n\n");
- fgets(buffer,SIZE,stdin); // 参数:字符串的位置,长度,获取的方式位置
- // 将用户输入的字符串放入共享内存
- strncpy(shm->CharData,buffer,SIZE);// 参数:目的数据,源数据,数据大小
- shm->ReadWriteFlag = 1;
- if(strncmp(buffer,"quit",4) == 0) // 最后一个参数为比较字符的数量
- {
- ReadStatusFlag = 0; // 写入共享内存的数据已经被读走
- }
- }
- //4 脱离共享存
- shmdt((const void *)shm);
- return 0;
- }
ReadMemory.c。
- #include <sys/types.h>
- #include <sys/shm.h>
- #include <sys/ipc.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #define SIZE 1024 // 可输入1K字符串
- struct SharedMemoryST
- {
- int ReadWriteFlag; // 标明是读进程还是写进程放入了数据
- char CharData[SIZE]; // 保存用户输入数据
- };
- int main(int argc,char *argv[])
- {
- int shmid;
- int ReadStatusFlag = 1; // 内存中数据是否被读走的标志位,1表示未被读走
- struct SharedMemoryST *shm; // 共享内存结构
- key_t key=ftok("/tmp",12); // 创建共享内存的键值,如果提示创建失败,修改一下数字即可,读写进程都要改成同一数字
- //1 创建/获取共享内存
- shmid = shmget(key,sizeof(struct SharedMemoryST),IPC_CREAT|0777);//分配大小为结构大小,1234是随便给的键值
- //2 映射共享内存
- shm = (struct SharedMemoryST *)shmat(shmid,NULL,0); //内存id,映射的位置,映射的标志(此无特殊要求)
- shm->ReadWriteFlag = 0;
- //3 检查是否收到信息,收到quit退出
- while(ReadStatusFlag)
- {
- //打印共享内存
- if(shm->ReadWriteFlag == 1) // 等于说明有相应的数据
- {
- printf("\nThe write context is: %s\n",shm->CharData);
- shm->ReadWriteFlag = 0;
- if(strncmp(shm->CharData,"quit",3) == 0)
- {
- ReadStatusFlag = 0; // 结束查询,退出
- }
- }
- }
- //4 脱离共享内存
- shmdt((const void *)shm);
- //5 删除共享内存
- shmctl(shmid,IPC_RMID,0);
- }
写共享内存先创建共享内存,写入数据,读共享内存读取数据,通过标志查询方式,退出输入quit。
运行结果如下:
本文转载自微信公众号「嵌入式杂牌军」,可以通过以下二维码关注。转载本文请联系嵌入式杂牌军公众号。