​ --详细概念见《unix环境高级编程》15.9小节

​ 共享内存可以说是Linux 下最快速、最有效的进程间通信方式。两个不同进程A 、B 共享内存的意思是,同一块物理内存被映射到进程A 、B 各自的进程地址空间,进程A 可以即时看到进程B 对共享内存中数据的更新;反之,进程B 也可以即时看到进程A对共享内存中数据的更新。

​ 这里简单说下映射的概念:

​ Linux系统会为每个进程分配 4GB 的虚拟地址空间,一定情况下,需要将虚拟内存转换成物理内存,这就需要内存映射。为什么我们需要使用虚拟地址呢?最主要的原因是不同PC的物理内存会不一样,如果直接使用物理地址,会造成程序的移植性很差,另外虚拟地址访问内存有以下优势:

1、程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。

2、程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。

3、不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。

​ 进程可用的虚拟地址范围称为该进程的“虚拟地址空间”。每个用户模式进程都有其各自的专用虚拟地址空间。对于 32 位进程,虚拟地址空间通常为 4 GB,范围从 0x00000000 至 0xFFFFFFFF。

1. 共享内存的概念

​ 共享内存从字面意义解释就是多个进程可以把一段内存映射到自己的进程空间,以此来实现数据的共享及传输,这也是所有进程间通信方式最快的一种,共享内存是存在于内核级别的一种资源。

​ 在Shell 中可以使用ipcs 命令查看当前系统IPC中的状态,在文件系统中/proc目录下有对其描述的相应文件

root@jonathan-pc:~/test/shm# ipcs -m

------ Shared Memory Segments --------

key shmid owner perms bytes nattch status

0x01011d17 0 root 666 2176 1

0x02011d17 32769 root 666 2176 1

0xffffffff 65538 root 666 1024 0

ipcs -m ,其中 -m 是 memory 的意思 。

​ 在系统内核为一个进程分配内存地址时,通过分页机制可以让一个进程的物理地址不连续,同时也可以让一段内存同时分配给不同的进程。共享内存机制就是通过该原理实现的,共享内存机制只是提供数据的传送,如何控制服务器端和客户端的读写操作互斥,这就需要一些其他的辅助工具,例如信号量。

​ 采用共享内存通信的一个显而易见的好处就是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户控件进行四次数据的拷贝,而共享内存只拷贝两次数据:一次从输入文件到共享区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,知道通信完毕为止,这样,数据内同一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在接触映射时才写回文件的。因此,采用共享内存的通信方式效率是最高的。

​ 共享内存最大不足之处在意,由于多个进程对同一块内存区域具有访问的权限,各个进程之间的同步问题显得尤为重要。必须控制同一时刻只有一个进程对共享内存区域写入数据,否则会造成数据的混乱。同步控制问题可以由信号量来解决;

2.相关函数

#include

#include

#include

/* 创建或打开共享内存

Key:IPC_PRIVATE 或 ftok 的返回值

size:共享内存区大小

shmflag :同open函数的权限位,也可用8进制表示法

返回值:成功:共享内存段标识符 出错:-1

*/

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

/* 当一个共享内存创建或打开后,某个进程如果要使用该共享内存则必须将此内存区附加到它的地址空间

shmid :要映射的共享内存区标示符

shmaddr:将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)

shmflg:默认0:共享内存只读

返回值:成功:映射后的地址 出错:-1

*/

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

/*当进程对共享内存段的操作完成后,应调用 shmdt 函数,作用是将指定的共享内存段从当前进程空间中脱离出去

shmaddr:共享内存映射后的地址

返回值:0 出错:-1

*/

int shmdt(const void *shmaddr);

/* 共享内存的控制

shmid:要操作的共享内存标示符

cmd: IPC_STAT (获取对象属性)

IPC_SET(设置对象属性)

IPC_RMID(删除对象)

buf:指定IPC_STAT/ IPC_SET 时用以保存/设置属性

返回值:0 出错:-1

*/

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

示例

无关进程之间使用共享内存

shm.h、client.c、server.c

//shm.h

#ifndef __SHM_H__

#define __SHM_H__

struct people {

char name[10];

int age;

};

union semun {

int val; // value for SETVAL

struct semid_ds *semid_dsbuf; // buffer for IPC_STAT, IPC_SET

unsigned short *array; // array foror GETALL, SETALL

struct seminfo *__buf; // buffer for IPC_INFO

};

#define SHM_KEY_FILE "./client.c"

#define SEM_KEY_FILE "./server.c"

#endif

//server.c

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "shm.h"

int semid, shmid;

int p(int semid, int semnum)

{

struct sembuf sops = {semnum, -1, SEM_UNDO};

return semop(semid, &sops, 1);

}

int v(int semid, int semnum)

{

struct sembuf sops = {semnum, +1, SEM_UNDO};

return semop(semid, &sops, 1);

}

void * signal_set(int signo, void (*func)(int))

{

int ret;

struct sigaction sig;

struct sigaction osig;

sig.sa_handler = func;

sigemptyset(&sig.sa_mask);

sig.sa_flags = 0;

#ifdef SA_RESTART

sig.sa_flags |= SA_RESTART;

#endif

ret = sigaction(signo, &sig, &osig);

if (ret < 0)

return SIG_ERR;

else

return osig.sa_handler;

}

void sigint(int sig)

{

union semun arg;

if (shmctl(shmid, IPC_RMID, NULL) == -1) {

fprintf(stderr, "shmctl delete error");

}

if (semctl(semid, 0, IPC_RMID, 0) == -1) {

fprintf(stderr, "semctl delete error");

}

exit(sig);

}

int main(int argc, const char *argv[])

{

key_t semkey;

key_t shmkey;

struct people *addr = NULL;

union semun arg;

signal_set(SIGINT, sigint);

signal_set(SIGTERM, sigint);

semkey = ftok(SEM_KEY_FILE, 0);

if (semkey < 0) {

fprintf(stderr, "ftok semkey error");

return -1;

}

shmkey = ftok(SHM_KEY_FILE, 0);

if (shmkey < 0) {

fprintf(stderr, "ftok shmkey error");

return -1;

}

semid = semget(semkey, 1, 0666 | IPC_CREAT);

if (semid < 0) {

fprintf(stderr, "semget error");

return -1;

}

shmid = shmget(shmkey, sizeof(struct people), 0666 | IPC_CREAT);

if (shmid < 0) {

fprintf(stderr, "shmget error");

goto err1;

}

arg.val = 0;

if (semctl(semid, 0, SETVAL, arg) < 0) {

fprintf(stderr, "ctl sem error");

goto err2;

}

addr = (struct people *)shmat(shmid, NULL, 0);

if (addr == (struct people *)-1) {

fprintf(stderr, "shmat error");

goto err2;

}

p(semid, 0);

strncpy(addr->name, "xiaoming", 10);

addr->age = 10;

v(semid, 0);

if (shmdt(addr) == -1) {

fprintf(stderr, "shmdt is error");

goto err2;

}

p(semid, 0);

err2:

if (shmctl(shmid, IPC_RMID, NULL) == -1) {

fprintf(stderr, "shmctl delete error");

}

err1:

if (semctl(semid, 0, IPC_RMID, 0) == -1) {

fprintf(stderr, "semctl delete error");

}

return 0;

}

//client.c

#include

#include

#include

#include

#include

#include

#include "shm.h"

int p(int semid, int semnum)

{

struct sembuf sops = {semnum, -1, SEM_UNDO};

return semop(semid, &sops, 1);

}

int v(int semid, int semnum)

{

struct sembuf sops = {semnum, +1, SEM_UNDO};

return semop(semid, &sops, 1);

}

int main(int argc, const char *argv[])

{

int semid, shmid;

key_t semkey;

key_t shmkey;

struct people *addr = NULL;

//获取ipc key

semkey = ftok(SEM_KEY_FILE, 0);

if (semkey < 0) {

fprintf(stderr, "ftok semkey error");

return -1;

}

shmkey = ftok(SHM_KEY_FILE, 0);

if (shmkey < 0) {

fprintf(stderr, "ftok shmkey error");

return -1;

}

//获取semid和shmid

semid = semget(semkey, 0, 0666);

if (semid < 0) {

fprintf(stderr, "semget error");

return -1;

}

shmid = shmget(shmkey, 0, 0666);

if (shmid < 0) {

fprintf(stderr, "shmget error");

return -1;

}

//将共享内存链接到进程内存

addr = (struct people*)shmat(shmid, 0, 0);

if (addr == (struct people *)-1) {

fprintf(stderr, "shmat error");

return -1;

}

v(semid, 0);//取消server的阻塞

sleep(1);

p(semid, 0);//获取共享内存内容

printf("name is : %s\n", addr->name);

printf("age is : %d\n", addr->age);

v(semid, 0);

if (shmdt(addr) == -1) {//断开共享内存

fprintf(stderr, "shmdt is error");

return -1;

}

return 0;

}

运行结果

先运行server,再运行client

root@jonathan-pc:~/test/shm# ls

client.c server.c shm.h

root@jonathan-pc:~/test/shm# gcc client.c -o c

root@jonathan-pc:~/test/shm# gcc server.c -o s

root@jonathan-pc:~/test/shm# ./s

root@jonathan-pc:~/test/shm# ./c

name is : xiaoming

age is : 10

相关进程的共享内存

TELL_WAIT、WAIT_PARENT、err_sys等相关函数见《unix环境高级编程》的代码

/dev/zero的存储映射

相关联进程之间的共享内存,可以对/dev/zero使用mmap,创建未命名的存储区

#include "apue.h"

#include

#include

#define NLOOPS 1000

#define SIZE sizeof(long) /* size of shared memory area */

static int

update(long *ptr)

{

return((*ptr)++); /* return value before increment */

}

int

main(void)

{

int fd, i, counter;

pid_t pid;

void *area;

if ((fd = open("/dev/zero", O_RDWR)) < 0)

err_sys("open error");

if ((area = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,

fd, 0)) == MAP_FAILED)

err_sys("mmap error");

close(fd); /* can close /dev/zero now that it's mapped */

TELL_WAIT();

if ((pid = fork()) < 0) {

err_sys("fork error");

} else if (pid > 0) { /* parent */

for (i = 0; i < NLOOPS; i += 2) {

if ((counter = update((long *)area)) != i)

err_quit("parent: expected %d, got %d", i, counter);

TELL_CHILD(pid);

WAIT_CHILD();

}

} else { /* child */

for (i = 1; i < NLOOPS + 1; i += 2) {

WAIT_PARENT();

if ((counter = update((long *)area)) != i)

err_quit("child: expected %d, got %d", i, counter);

TELL_PARENT(getppid());

}

}

exit(0);

}

匿名存储映射

相关联进程之间的共享内存,可以对文件描述符-1使用mmap,创建未命名的存储区

#include "apue.h"

#include

#include

#define NLOOPS 1000

#define SIZE sizeof(long) /* size of shared memory area */

static int

update(long *ptr)

{

return((*ptr)++); /* return value before increment */

}

int

main(void)

{

int i, counter;

pid_t pid;

void *area;

if ((area = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED,

-1, 0)) == MAP_FAILED)

err_sys("mmap error");

TELL_WAIT();

if ((pid = fork()) < 0) {

err_sys("fork error");

} else if (pid > 0) { /* parent */

for (i = 0; i < NLOOPS; i += 2) {

if ((counter = update((long *)area)) != i)

err_quit("parent: expected %d, got %d", i, counter);

TELL_CHILD(pid);

WAIT_CHILD();

}

} else { /* child */

for (i = 1; i < NLOOPS + 1; i += 2) {

WAIT_PARENT();

if ((counter = update((long *)area)) != i)

err_quit("child: expected %d, got %d", i, counter);

TELL_PARENT(getppid());

}

}

exit(0);

}

linux c 文件映射,linuxc-共享内存相关推荐

  1. linux c 文件映射,linuxc试题

    一.选择题(20题,共44分) 1.常见的Linux发行版本有很多,下面不是Linux发行版本的是( ). [A] Red Hat Linux [B] Ubuntu Linux [C] X Windo ...

  2. Linux进程间通信一 System V 共享内存简介与示例

    目录 1. System V共享内存简介 2. API介绍 2.0 key_t和标识符 2.1  创建system v共享内存 2.2 映射共享内存并使用 2.3 取消共享内存映射 2.4 控制共享内 ...

  3. linux进程间通信:system V 共享内存

    文章目录 思维导图如下 通信原理 优势 运行流程 编程接口 编程实例 思维导图如下 通信原理 多个进程共享物理内存的同一块区域(通常称之为"段":segment) 抛弃了内核态消息 ...

  4. linux shared,从 0 开始学习 Linux 系列之「22.共享内存 Shared Memory」

    共享内存 版权声明:本文为 cdeveloper 原创文章,可以随意转载,但必须在明确位置注明出处! 共享内存 Shared Memory 这次我们来学习在 Linux 中最快的一种 IPC 方式:共 ...

  5. 【Linux Program】信号量、共享内存和消息队列

    系列文章: 文件操作 数据管理 进程和信号 POSIX 线程 进程间通信:管道 信号量共享内存和消息队列 套接字 文章目录 1. 信号量 1.1 信号量的定义 1.2 Linux 的信号量机制 1.3 ...

  6. 【Linux系统编程】进程间通信--共享内存

    概述 共享内存是进程间通信中最简单的方式之一.共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针.当一个进程改变了这块地址中的内容的时 ...

  7. Linux 进程间通信:管道、共享内存、消息队列、信号量

    进程间通信 管道 共享内存 消息队列 信号量 进程间通信 https://blog.csdn.net/qq_35423154/article/details/105294963 在之前的一篇博客中讲过 ...

  8. Linux IPC实践(10) --Posix共享内存

    1. 创建/获取一个共享内存 #include <sys/mman.h> #include <sys/stat.h> /* For mode constants */ #inc ...

  9. linux如何看分配固定共享内存段,Linux共享内存的查看和删除

    在使用共享内存的程序异常退出时,由于没有释放掉共享内存,在调试时会出现错误.您可以使用shell命令来查看与释放已经分配的共享内存,下面将详细说明如何进行查看和释放分配的共享内存的方法. 预备知识 L ...

最新文章

  1. 《树莓派Python编程入门与实战(第2版)》——1.7 排除树莓派的故障
  2. 抽象类实例化 使用原功能 c++_java 学习笔记 day08 final/static关键字、抽象类和接口...
  3. listen()和accept()函数:让套接字进入监听状态并响应客户端请求
  4. replaceAll的坑
  5. java 气泡聊天消息_CSS3 巧妙实现聊天气泡
  6. bean 属性设置默认值_activiti7源码分析之引擎初始化与Bean注入
  7. 解析Servlet/JSP会话跟踪机制
  8. 共阳极管的代码_共阳极数码管-共阳极数码管显示
  9. 详解python主函数
  10. 利用简单电阻分压原理自动测量电阻
  11. java数据库驱动加载失败_Java连接数据库,成功加载SQL驱动程序,但数据库连接失败...
  12. 手把手教你如何用VBA统计问卷调查表
  13. Linux系统之安装mariadb方法
  14. Tkinter单选框
  15. 直播预约 | 如何通过MLOps解放和提升AI生产力?
  16. [推荐系统]推荐系统实践Reference
  17. 加密解密验签概念理解
  18. iOS逆向工程-工具篇
  19. 【Vscode】解决报错 An SSH installation couldn‘t be found
  20. 杭州2.0之新挑战、新梦想

热门文章

  1. python range函数范围_Python range函数深入解析
  2. mac pycharm 卸载_Mac上Virtual Box虚拟机Linux系统安装
  3. php添加语句,有什么办法能把一条php语句插入到方法里呢?
  4. c语言学习-编程实现以下功能,读入两个数(d1,d2)和一个运算符(o),计算d1 o d2的值
  5. caffe.net matlab,windows-matlab环境下,生成的caffe模型无法运行
  6. OpenShift 4 - 查看Node上的日志
  7. Model和ViewModel之间的通用MVVM数据交换
  8. python登录验证程序_python – 测试Flask登录和身份验证?
  9. 佛罗里达大学计算机专业世界排名,2020年佛罗里达大学排名TFE Times美国最佳计算机科学硕士专业排名第55...
  10. 基础的c语言题目,几个c语言的基础题目