在Linux中,共享内存是允许两个不相关的进程访问同一个逻辑内存的进程间通信方法,是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。

  不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址。

  就好像它们是由用C语言malloc()分配的内存一样。

  建立一个共享内存大概有五个步骤:

  1.调用shm_open()函数,在指定一个内存的名字,用来创建或者打开一个共享内存

  2.调用mmap()函数,把共享内存映射在进程的空间里

  3.调用fturncate()函数来分配共享内存的大小

  在使用完共享内存之后,还得经过下面两个步骤,将内存关闭并释放

  4.调用munmap()函数,取消共享内存的映射

  5.调用shm_unlink()函数来删除共享内存

  函数介绍

  shm_open()

  这个函数关联的头文件是sys/mman.h,sys/stat.h,fcntl.h

  函数原形如下:fd shm_open(const char *name,int oflag, mode_t mode)

  参数name很好理解,就是指定一个名字,用来标识共享内存

  参数oflag的作用在于指定属性,比如新建,打开,只读,只写等等

  参数mode的作用那就很明显了,是用来指定新建内存的权限,这个参数只有在新建共享内存时才有用

————————————————————————————————————————————————

  新创建或者打开的共享内存的大小是0.必须在指定大小之后才能正常的使用,这里用来指定内存大小的函数是:

  ftruncate()

  这个函数关联的头文件有unistd.h,sys/types.h

  函数原形如下:ftruncate(int fd,off_t length)

  参数fd自然就是内存文件的句柄(Linux中,一切都被当做文件来使用,内存当然也不例外)

  length参数便是用来指定内存的大小了,单位是字节

————————————————————————————————————————————————

  在设置完内存的大小后,依然还是不能直接使用,还需要一个步骤,那就是设置内存映射。

  mmap()

  这个函数关联的头文件有sys/mman.h

  函数原形如下:void * mmap(void *addr, size_t length, int port, int flags, int fd, off_t offset)

  参数addr 设置共享内存的起点,如果将其设置为NULL的话,那就意味是由系统自动分配

  参数length就很好理解了,就是共享内存的大小

  参数port是用来设置共享内存的权限,设置属性和open差不多,但不能超过shm_open所设置的权限

  flag是设置一些特殊的要求,比如MAP_FIXED,要求返回值必须等于addr。MAP_SHARED多个进程对同一文件的映射是共享的,

  其中一个进程对映射做了修改,那么别的进程也能看到(这段话暂时还不理解)。

  参数fd,不多解释,就是内存文件的句柄

  参数offset,要映射字节在文件中的偏移

————————————————————————————————————————————————

  在有了以上三个函数之后,基本上就能在进程之间建立一个共享内存了。至于取消内存和删除内存的函数,那就更加简单,这里不在多说。

  程序实例

  现在实现一个功能,在两个完全不相干的进程中建立一个共享内存,其中一个进程在向一段内存中写入一个数字,另个一进程读取这个数字。

  

#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<unistd.h> #define SHM_SIZE 10
#define SHM_NAME "shmname" int main(void)
{
int fd;
char * prt; fd = shm_open(SHM_NAME,O_RDWR|O_CREAT,);
if (fd < )
{
printf("创建共享内存失败!\n");
exit(-);
} fturncate(fd,SHM_SIZE);//设置共享内存的大小为10
prt = mmap(NULL, SHM_SIZE, 0x777, MAP_SHARED, fd,);
if (prt == MAP_FAILED)
{
printf("创建共享内存映射失败!\n");
exit(-);
} *prt = ;
munmap(prt,SHM_SIZE);
shm_unlink(SHM_NAME); return ;

  以上便是代码,现在写完了Makefile之后进行编译,但是在编译中出现了很奇怪的问题。

  

  难道是我把这三个函数的名字打错了吗?

  仔细检查之下,还真有打错的!!!(ftruncate函数打成了fturncate,真是粗心啊!!!)

  把这个函数修改之后,依然不通过,这是什么原因?

  经过我的分析,从编译结果上来看,应该不在是语法上出的问题了……

  开始调查……

  在一个大神的资料中看见这么一句话,我仿佛是看见了救星:

  

好吧,原来是这个问题!那么开始修正Makefile。

  

  编译还是没有通过,这是怎么回事?

  继续调查……

  半小时后,终于发现了愿意,原来Linux在编译中,首先是从最后面开始进行连接,所以把-lrt放在编译指令的中间是不可以的,一定要放在最后面。

  (不要问我为什么,我也不知道)

  等我把它放在最后,就能编译通过了!

  

  现在写数据的进程已经写完了,开始写读数据的进程。

  

#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h> #define SHM_SIZE 10
#define SHM_NAME "shmname" int main(void)
{
int fd;
char * prt; fd = shm_open(SHM_NAME, O_CREAT|O_TRUNC|O_RDWR, );
if (fd < )
{
printf("进程2创建共享内存失败!%d\n",errno);
exit(-);
} ftruncate(fd,SHM_SIZE);//设置共享内存的大小为10
prt = mmap(NULL, SHM_SIZE, 0x777, MAP_SHARED, fd,);
if (prt == MAP_FAILED)
{
printf("创建共享内存映射失败!\n");
exit(-);
}
while(*prt != )
{
sleep();
}
printf("读出数据prt为=%d.\n",*prt);
munmap(prt,SHM_SIZE); return ; }

  两个代码都做完了,现在开始编译……完美!

  开始运……啊!!!

  这又怎么啦?

  

  怎么会创建内存失败呢?

  创建共享内存,为什么回返回errno 13呢?

  找了半天,终于找到了问题的原因,原来是因为我在之前代码还不完善的时候,创建了一个名为shmname的内存,

  

  然后又没能把他删掉,而且它的权限还高的吓人,导致它一直存在于/dev/shm路径下,所以等代码完善之后,在运行程序就会发现因为没有权限而运行失败了。

  手动把那个名为shmname的东西删掉后,再次运行程序……OK!!

  

  ————————————————————————————————————————————————————

  本节重点:

  1.在编译共享内存的代码时,要在编译命令的最后面加上-lrt,用来连接库

  2.如果在运行创建共享内存程序时发生errno = 13的错误,可能就是我刚才遇见的错误,在/dev/shm路径下已经有一段同名的共享内存了,

    需要手动删除,或者换一个名字。

  

  

    

Linux学习笔记(14)-进程通信|共享内存的更多相关文章

  1. Linux学习笔记(六) 进程管理

    1.进程基础 当输入一个命令时,shell 会同时启动一个进程,这种任务与进程分离的方式是 Linux 系统上重要的概念 每个执行的任务都称为进程,在每个进程启动时,系统都会给它指定一个唯一的 ID, ...

  2. linux io 学习笔记(03)---共享内存,信号灯,消息队列

    system V IPC 1)消息队列 2)共享内存 3)信号灯(信号量集) 1.消息队列. ipcs -q 查看系统中使用消息队列的情况 ipcrm -q +msqid 删除消息队列 消息队列工作原 ...

  3. Linux学习笔记14——使用fcntl实现文件锁定

    期末考试快要来了,Linux学习进度一下拉下来许多.今天学习的是文件锁定,在Linux中,实现文件锁定的方法很多,例如fcntl和lockf.下面主要是fcntl的调用. fcntl函数的原型是:in ...

  4. Windows进程通信 -- 共享内存(1)

    共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 W ...

  5. Windows进程通信 -- 共享内存

    享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 Wi ...

  6. Windows进程通信-共享内存空间

    三个模块 1,game.exe,三个方法,控制台输入指令('A','B','R')分别控制三个方法的调用: 2,WGDll.dll,要注入到game进程中的dll文件: 3,myconsole.exe ...

  7. linux学习笔记之进程

    一.基础知识 1:进程. 1,进程ID: 非负整数,具有唯一性. 1)ID=0的进程:调度进程/交换进程.内核的一部分.不执行任何磁盘上的程序. 2)ID=1的进程:init进程. 1-自举结束时,由 ...

  8. Linux学习笔记24——进程管道

    一 管道的作用 通常把一个进程的输出通过管道连接到另一个进程的输入. 二 popen和pclose函数 #include <stdio.h> FILE *popen(const char ...

  9. linux学习笔记-13.进程控制

    1.查看用户最近登录情况 lastlastlog 2.查看硬盘使用情况 df 3.查看文件大小 du 4.查看内存使用情况 free 5.查看文件系统 /proc 6.查看日志 ls /var/log ...

随机推荐

  1. ASP.NET vNext on CentOS 7

    第一步是在Linux上安装.Net的运行时Mono VNext要求Mono最小版本3.4.1,可怜的centos连低版本的mono都不含.我们只能通过编译来安装.目前最新的版本为3.12 源码下载:h ...

  2. 调用WebServices超时

    1. 服务器端设置超时 在 web.config 的 system.web 里添加如下配置项: < httpRuntimeexecutionTimeout="300000"/ ...

  3. Java动态加载JAR包

    参考代码: package org; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import ...

  4. ZOJ Problem Set - 3329(概率DP)

    One Person Game Time Limit: 1 Second      Memory Limit: 32768 KB      Special Judge There is a very ...

  5. 谈谈__proto__和prototype的区别

    我想javascript中的原型链一直想很多初学javascript的同学感到非常的困惑,今天看了一些文章,结合自己的理解,把原型链这个东西从新来整理一下,如有不对之处,望大家帮忙指出. 首先,我们应 ...

  6. 仅用aspx文件实现Ajax调用后台cs程序。(实例)

    仅用aspx文件实现Ajax调用后台cs无刷新程序.(实例) 两个文件:aaa.aspx 和aaa.aspx.cs 一.aaa.aspx <script type="text/java ...

  7. robotframework,selenium启动不了打不开浏览器的问题访问不了网页

    由于最近发现咨询火狐浏览器打不开的问题比较多,现罗列几点解决办法. 1,由于selenium更新3.0的原因导致不在默认支持火狐浏览器,且支持的火狐浏览器大概在45以上的版本,所以很多都由于这个原因导 ...

  8. HTML 接收本地文件

    HTML代码请把文件拖到下面的框里触发drop事件读取拖放的文件常用情况:结合XMLHttpRequest和拖放文件实现上传查看和管理本地文件和图片 <!DOCTYPE HTML> < ...

  9. zabbix安装unixODBC配置完之后报错

    zabbix安装unixODBC配置完之后报错 libmysqlclient_16 not defined in file libmysqlclient_r.so.16 分析 我没有使用centos6 ...

  10. Java 动态代理机制详解

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...