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. 一个远程启动windows c++程序引发的技术决策现象

    还是因为那个8点半前要启动近百套报盘程序的问题,差不多两周前表示自己会抽空给解决掉,一次性启动,直到昨天才差不多能够抽点时间出来开始想怎么解决的问题. 这个问题的复杂点在于除了启动exe外,还需要鼠标 ...

  2. 算法之路 level 01 problem set

    2992.357000 1000 A+B Problem1214.840000 1002 487-32791070.603000 1004 Financial Management880.192000 ...

  3. 函数对象与仿函数(function object and functor)

    part 1. 仿函数在STL组件中的关系 如下图: # 仿函数配合算法完成不同的策略变化. # 适配器套接仿函数. part 2. 仿函数介绍 传递给算法的“函数型实参”不一定得是函数,可以是行为类 ...

  4. PHP微信公众号后台开发(Yii2实现)

    本文内容较多,包括微信接入.获取微信用户信息.微信支付.JSSDK配置参数获取等部分.如果读者对微信开发没有一个主观上的认识,那么建议读者先研读微信公众平台开发者文档,然后再阅读本文,效果更佳!另外本 ...

  5. 20165310 java_blog_week4

    2165310 <Java程序设计>第4周学习总结 教材学习内容总结 继承(extends) 同一个包内:继承除了private修饰的变量与方法 不同包内:不继承private和友好,继承 ...

  6. bzoj 2427 软件安装 - Tarjan - 树形动态规划

    题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大). 但是 ...

  7. VC++ 利用CreateFile、ReadFile和WriteFile实现CopyFile

    1. CreateFile:这是一个多功能的函数,可打开或创建以下对象,并返回可访问的句柄:控制台,通信资源,目录(只读打开),磁盘驱动器,文件,邮槽,管道. 参照:http://www.cppblo ...

  8. mac下的一些操作

    mac 下修改Hosts文件 : http://www.cnblogs.com/zhangqs008/p/3773623.html mac下装Tomcat服务器: 在苹果系统安装Tomcat:首先下载 ...

  9. Type.Missing和System.Reflection.Missing.Value

    Type.Missing https://msdn.microsoft.com/en-us/library/system.type.missing(v=vs.110).aspx Missing.Val ...

  10. hash入门

    如果你已经确保自己的hash技巧已经入门,那么请左转这篇博文 首先介绍一下hash? 事实上是一种叫做蛤丝的病毒 以下讲到的hash都是OI中最常用到的hash方法:进制哈希 做法: 首先设一个进制数 ...