共享内存创建shmget控制操作shmat,shmctl
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的更多相关文章
- 『Numpy』内存分析_利用共享内存创建数组
引.内存探究常用函数 id(),查询对象标识,通常返回的是对象的地址 sys.getsizeof(),返回的是 这个对象所占用的空间大小,对于数组来说,除了数组中每个值占用空间外,数组对象还会存储数组 ...
- Linux进程间通信(六):共享内存 shmget()、shmat()、shmdt()、shmctl()
下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存.共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式 ...
- 【转载】Linux进程间通信(六):共享内存 shmget()、shmat()、shmdt()、shmctl()
来源:https://www.cnblogs.com/52php/p/5861372.html 下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相 ...
- linux 进程间通信 共享内存 shmat
系统调用mmap()通过映射一个普通文件实现共享内存.系统V则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信.也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件(这是通过shm ...
- JNI创建共享内存导致JVM terminated的问题解决(segfault,shared memory,内存越界,内存泄漏,共享内存)
此问题研究了将近一个月,最终发现由于JNI不支持C中创建共享内存而导致虚拟机无法识别这块共享内存,造成内存冲突,最终虚拟机崩溃. 注意:JNI的C部分所使用的内存也是由JVM创建并管理的,所以C创建了 ...
- 共享内存同行,王明学learn
共享内存同行 一.共享内存概念 共享内存是IPC机制中的一种,它允许两个不相关的进程访问同一段内存, 这是传递数据的一种非常有效的方式. 二.函数学习 这里主要有创建共享内存.映射共享内存.分离共享内 ...
- Linux进程通信之System V共享内存
前面已经介绍过了POSIX共享内存区,System V共享内存区在概念上类似POSIX共享内存区,POSIX共享内存区的使用是调用shm_open创建共享内存区后调用mmap进行内存区的映射,而Sys ...
- Linux环境进程间通信(五): 共享内存(下)
linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...
- System V IPC 之共享内存
IPC 是进程间通信(Interprocess Communication)的缩写,通常指允许用户态进程执行系列操作的一组机制: 通过信号量与其他进程进行同步 向其他进程发送消息或者从其他进程接收消息 ...
随机推荐
- 20145206邹京儒《网络对抗技术》 PC平台逆向破解
20145206邹京儒<网络对抗技术> PC平台逆向破解 注入shellcode并执行 一.准备一段shellcode 二.设置环境 具体在终端中输入如下: apt-cache searc ...
- 20145327 《网络对抗》逆向及BOF基础实践
20145327 <网络对抗>逆向及BOF基础实践 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任 ...
- Python3基础 函数 递归 阶乘与斐波那契数列
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- 有时候shell中某些变量总是不能被改变是什么原因
答:在子shell执行,那么变量的值总是不能如愿以偿的改变,示例如下: #!/bin/sh var="jello" cat "jello.txt" | whil ...
- CF620E New Year Tree 线段树 dfs序
luogu链接 题目大意: 有一个节点有颜色的树 操作1.修改子树的颜色 操作2.查询子树颜色的种类 注意,颜色种类小于60种 只有子树的操作,dfs序当然是最好的选择 dfs序列是什么,懒得讲了,自 ...
- Visual Studio 项目模板制作(三)
前面,我们已经制作好了模板,然后放到相应的Template目录就可以在Visual Studio中使用 本篇,我们采用安装VSIX扩展的方式来安装模板,这种方式需要安装Visual Studio SD ...
- 返回json格式数据乱码
本文为博主原创,未经允许不得转载: 原本返回json格式数据的代码: @ResponseBody @RequestMapping(value = "getListByTime", ...
- 【TCP/IP详解 卷一:协议】第十章 动态选路协议
更为详细的RIP博客解析: RIP理论 距离向量算法的简介: RIP协议V-D算法的介绍 10.1 引言 静态选路修改路由表的三种方法 (1)主机设置时,默认的路由表项 (2)ICMP重定向报文(默认 ...
- UVa 1347 旅行
https://vjudge.net/problem/UVA-1347 思路:用d(i,j)表示第一个人走到i,第二个人走到j,还需要走多长的距离.在这里强制定义i>j,并且每次只能走到i+1. ...
- ubuntu14.04(server amd64)免密码sudo
vi /etc/sudoers.d/nopasswd4sudo 加入以下内容 用户名 ALL=(ALL) NOPASSWD : ALL