1、共享内存的数据结构

共享内存就是分配一块能被其他进程访问的内存。每个共享内存段在内核中维护着一个内部结构:

struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};

struct ipc_perm shm_perm; 操作许可,里面包含共享内存的用户ID、组ID
size_t shm_segsz;共享内存段的大小,以单位为字节
time_t shm_atime; 最后一个进程访问共享内存的时间
time_t shm_dtime;最后一个进程离开共享内存的时间
time_t shm_ctime; 最后一次修改共享内存的时间
pid_t shm_cpid; 创建共享内存的进程ID
pid_t shm_lpid; 最后操作共享内存的进程ID
shmatt_t shm_nattch; 当前使用该共享内存段的进程数量

2、共享内存的创建

得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符

int shmget(key_t key, size_t size, int shmflg);

key:此值来源于ftok返回的IPC键值

size:大于0的整数:新建的共享内存大小,以字节为单位;只获取共享内存时指定为0

shmflg:

0:取共享内存标识符,若不存在则函数会报错

IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符

IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个消息队列;如果存在这样的共享内存则报错

3、共享内存的操作

在使用共享内存前,必须通过shmat函数将其附加到进程的地址空间。进程与共享内存就建立了连接。

shmat调用成功就会返回一个指向共享内存区的指针,使用该指针就可以访问共享内存区了。

void *shmat(int shmid, const void *shmaddr, int shmflg);

shmid:共享内存标识符,为shmget的返回值

shmflg:SHM_RDONLY:为只读模式,其他为读写模式

shmaddr:指定共享内存出现在进程内存地址的什么位置。

如果为空,则由内核选择一个空闲的内存区,如果非空,返回地址取决于调用者是否给shmflg参数指定了SHM_RND值,如果没有指定,

则共享内存区附加到shmaddr指定的地址;否则附加地址为shmaddr向下舍入一个共享内存低端边界地址后的地址(SHMLBA,一个常址) 

通常将参数shmaddr设置为NULL

-----------------------------------------------------------------------

当进程结束使用共享内存区时,要通过函数shmdt断开与共享内存区的连接。

int shmdt(const void *shmaddr);

参数shmaddr为shmat函数的返回值。该函数调用成功后,返回0,否则返回-1

进程脱离共享内存区后,数据结构shmid_ds 中的shm_nattch就会减1。但是共享内存依然存在,只有

shm_nattch为0后,即没有任何进程再使用该共享内存,共享内存才会在内核中被删除。

一般来说,当一个进程终止时,它所附加的共享内存都会自动脱离。

fork后子进程继承已连接的共享内存地址。exec后该子进程与已连接的共享内存地址自动脱离(detach)。进程结束后,已连接的共享内存地址会自动脱离(detach)

4、共享内存的控制

int shmctl(int shmid, int cmd, struct shmid_ds *buf)

shmid:共享内去标识符

buf:为指向shmid_ds结构体的指针。

cmd:操作标志位。

IPC_STAT:读取共享内存的shmid_ds结构,并将其存储到buf指定的地址中

IPC_SET:设置共享内存的shmid_ds结构

IPC_RMID:从系统中删除由shmid标识的共享内存

5、共享内存的应用实例

通过读写者问题(不考虑优先级)来演示共享内存和信号量如何配合使用。这里的读者写者问题要求一个进程读共享内存的时候,其他进程不能写内存;当一个进程写共享内存的时候,其他进程不能读

内存。

程序首先定义了一个包含共用函数的头文件 sharemem.h

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h> union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
}; //
int createsem(const char *pathname, int proj_id, int members, int init_val)
{
key_t key;
int semid;
union semun sem_opts;
int index; if ((key=ftok(pathname, proj_id)) == -)
{
perror("ftok error:");
return -;
} if ((semid=semget(key, members, IPC_CREAT|)) == -)
{
perror("semget error:");
return -;
}

//初始化信号量
sem_opts.val = init_val;
for(index=;index<members;index++)
{
semctl(semid, index, SETVAL, sem_opts);
} return semid;
} //
int opensem(const char *pathname, int proj_id)
{
key_t key;
int semid; if ((key=ftok(pathname, proj_id)) == -)
{
perror("ftok error:");
return -;
} if ((semid=semget(key, , IPC_CREAT|)) == -)
{
perror("semget error:");
return -;
} return semid;
} //
int sem_p(int semid, int index)
{
struct sembuf buf = {, -, IPC_NOWAIT};
buf.sem_num = index;
if (semop(semid, &buf, ) == -)
{
perror("semop error:");
return -;
} return ;
} //
int sem_v(int semid, int index)
{
struct sembuf buf = {, , IPC_NOWAIT};
buf.sem_num = index;
if (semop(semid, &buf, ) == -)
{
perror("semop error:");
return -;
} return ;
} //
int sem_delete(int semid)
{
semctl(semid, , IPC_RMID);
} int sem_wait(int semid, int index)
{
while(semctl(semid, index, GETVAL) <= )
{
sleep();
} return ;
} int createshm(const char *pathname, int proj_id, size_t size)
{
key_t key;
int semid; if ((key=ftok(pathname, proj_id)) == -)
{
perror("ftok error:");
return -;
} if ((semid=shmget(key, size, IPC_CREAT|)) == -)
{
perror("shmget error:");
return -;
} return semid;
}

writer和reader程序,两个程序在进入共享内存之前,首先都检查信号量的值是否为1(相当于是否能进入共享内存区),如果不为1,调用sleep进入休眠状态,直到信号量的值变为1.

write程序:

#include <sharemem.h>
#include <string.h> #define SHM_SIZE 256 int main()
{
int shmid;
int semid;
char *shmaddr;
char write_str[SHM_SIZE]; shmid = createshm(".", 'm', SHM_SIZE); shmaddr = (char*)shmat(shmid, (char*), ); semid = createsem(".", 's', , ); while()
{ sem_wait(semid, ); sem_p(semid, ); printf("writer: ");
fgets(write_str, SHM_SIZE, stdin); int len = strlen(write_str) - ; write_str[len] = '\0'; strcpy(shmaddr, write_str);
sleep(); sem_v(semid, ); sleep();
} }

reader程序:

#include <sharemem.h>

#define SHM_SIZE 256

int main()
{
int shmid;
int semid;
char *shmaddr;
char write_str[SHM_SIZE]; shmid = createshm(".", 'm', SHM_SIZE); shmaddr = (char*)shmat(shmid, (char*), ); semid = opensem(".", 's'); while()
{ printf("reader: ");
sem_wait(semid, ); sem_p(semid, ); printf("%s\n", shmaddr);
sleep(); sem_v(semid, ); sleep(); } }

共享内存创建shmget控制操作shmat,shmctl的更多相关文章

  1. 『Numpy』内存分析_利用共享内存创建数组

    引.内存探究常用函数 id(),查询对象标识,通常返回的是对象的地址 sys.getsizeof(),返回的是 这个对象所占用的空间大小,对于数组来说,除了数组中每个值占用空间外,数组对象还会存储数组 ...

  2. Linux进程间通信(六):共享内存 shmget()、shmat()、shmdt()、shmctl()

    下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式 ...

  3. 【转载】Linux进程间通信(六):共享内存 shmget()、shmat()、shmdt()、shmctl()

    来源:https://www.cnblogs.com/52php/p/5861372.html 下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相 ...

  4. linux 进程间通信 共享内存 shmat

    系统调用mmap()通过映射一个普通文件实现共享内存.系统V则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信.也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件(这是通过shm ...

  5. JNI创建共享内存导致JVM terminated的问题解决(segfault,shared memory,内存越界,内存泄漏,共享内存)

    此问题研究了将近一个月,最终发现由于JNI不支持C中创建共享内存而导致虚拟机无法识别这块共享内存,造成内存冲突,最终虚拟机崩溃. 注意:JNI的C部分所使用的内存也是由JVM创建并管理的,所以C创建了 ...

  6. 共享内存同行,王明学learn

    共享内存同行 一.共享内存概念 共享内存是IPC机制中的一种,它允许两个不相关的进程访问同一段内存, 这是传递数据的一种非常有效的方式. 二.函数学习 这里主要有创建共享内存.映射共享内存.分离共享内 ...

  7. Linux进程通信之System V共享内存

    前面已经介绍过了POSIX共享内存区,System V共享内存区在概念上类似POSIX共享内存区,POSIX共享内存区的使用是调用shm_open创建共享内存区后调用mmap进行内存区的映射,而Sys ...

  8. Linux环境进程间通信(五): 共享内存(下)

    linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

  9. System V IPC 之共享内存

    IPC 是进程间通信(Interprocess Communication)的缩写,通常指允许用户态进程执行系列操作的一组机制: 通过信号量与其他进程进行同步 向其他进程发送消息或者从其他进程接收消息 ...

随机推荐

  1. 03: KindEditor (HTML可视化编辑器)

    目录: 1.1 kindEditor常用配置参数 1.2 kindEditor下载与文件说明 1.3 kindEditor实现上传图片.文件.及文件空间管理 1.1 kindEditor常用配置参数返 ...

  2. 03: Django Model数据库操作

    目录:Django其他篇 01:Django基础篇 02:Django进阶篇 03:Django数据库操作--->Model 04: Form 验证用户数据 & 生成html 05:Mo ...

  3. go learning

    1. vim-go https://github.com/fatih/vim-go-tutorial curl -fLo ~/.vim/autoload/plug.vim --create-dirs ...

  4. 汽车OBD接口定义

    汽车上的OBD-II接口(母):  ELM327用到的引脚: 2: SAE-J1850 PWM和SAE-1850 VPW总线(+) 4. 车身地 5. 信号地 6. CAN high (ISO 157 ...

  5. mac OSX 实用快捷键

    Command + shift + G. 前往文件夹 按键 效果 Shift + option + 音量+/- 以四分之一的刻度加 / 减音量 Shift. + option + 9 ······

  6. codevs & vijos 爱在心中 - Tarjan

    描述 “每个人都拥有一个梦,即使彼此不相同,能够与你分享,无论失败成功都会感动.爱因为在心中,平凡而不平庸,世界就像迷宫,却又让我们此刻相逢Our Home.” 在爱的国度里有N个人,在他们的心中都有 ...

  7. ReadResolve方法与序列化

    使用枚举实现的单例模式,不但可以防止利用反射强行构建单例对象,而且可以在枚举类对象被反序列化的时候,保证反序列的返回结果是同一对象. 对于其他方式实现的单例模式,如果既想要做到可序列化,又想要反序列化 ...

  8. Android Studio下载新的AVD映像把C盘给占满了

    不管你的Android SDK/Studio安装哪儿, 默认总是下载到 C:\Users\Administrator\.android ... 可以设置ANDROID_SDK_HOME 指定到新的目录 ...

  9. Hadoop MapReduce编程 API入门系列之Crime数据分析(二十五)(未完)

    不多说,直接上代码. 一共12列,我们只需提取有用的列:第二列(犯罪类型).第四列(一周的哪一天).第五列(具体时间)和第七列(犯罪场所). 思路分析 基于项目的需求,我们通过以下几步完成: 1.首先 ...

  10. codevs 1082 线段树练习 3 区间更新+延迟标记

    题目描述 Description 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[a,b]的数的和. 输入描述 Input Description 第一行一个正整数n,接下 ...