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. Linux下Tomcat端口、进程以及防火墙设置

     Linux下Tomcat端口.进程以及防火墙设置 1,查看tomcat进程: #ps -aux | grep tomcat(或者ps -ef | grep tomcat都行) 可以看到现在运行着两个 ...

  2. java service wrapper日志参数设置及优化

    一般在容器比如tomcat/weblogic中运行时,我们都是通过log4j控制日志输出的,因为我们现在很多服务端使用java service wrapper(至于为什么使用jsw,原先是比较排斥使用 ...

  3. 使用 Vue.js 结合bootstrap 实现的分页控件

    原文链接:http://blog.csdn.net/qiuhaotc/article/details/53031884 源码下载: http://pan.baidu.com/s/1i4XgH6H 密码 ...

  4. C#调用非托管dll

    以C#开发周立功CAN举例,在官网下载了周立功的demo 一.C++头文件样子 //接口卡类型定义#define VCI_PCI5121 1 //一些结构体定义 typedef struct tagR ...

  5. DDMS files not found

    在eclipse中启动新建的android项目的时候,控制台提示如图: 方法1.cmd中adb kill-server,然后adb -startserver 方法2.方法1不管用,那么在任务管理器中杀 ...

  6. linux下kermit工具的使用

    1.环境: ubuntu16.04 2.背景: 想更换下位机内核 3.使用kermit进行串口传输 举例:传输文件到下位机 2.1首先进入下位机的uboot 2.2 使用uboot自带的命令从串口接收 ...

  7. ActiveMQ 集群配置 高可用

    自从activemq5.9.0开始,activemq的集群实现方式取消了传统的Pure Master Slave方式,增加了基于zookeeper+leveldb的实现方式,其他两种方式:目录共享和数 ...

  8. BZOJ5188: [Usaco2018 Jan]MooTube 并查集+离线处理

    BZOJ又不给题面... Luogu的翻译看不下去... 题意简述 有一个$n$个节点的树,边有权值,定义两个节点之间的距离为两点之间的路径上的最小边权 给你$Q$个询问,问你与点$v$的距离超过$k ...

  9. 【Coursera】Third Week(1)

    The Early World-Wide-Web 关于CERN 欧洲核子研究组织,除了它为世界物理学所作出的卓越贡献,它还是世界上第一个网站,第一个网络服务器,第一个浏览器的诞生地. Robert C ...

  10. HDU 2460 Network(桥+LCA)

    http://acm.hdu.edu.cn/showproblem.php?pid=2460 题意:给出图,求每次增加一条边后图中桥的数量. 思路: 先用tarjan算法找出图中所有的桥,如果lowv ...