Linux学习笔记(14)-进程通信|共享内存
在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)-进程通信|共享内存的更多相关文章
- Linux学习笔记(六) 进程管理
1.进程基础 当输入一个命令时,shell 会同时启动一个进程,这种任务与进程分离的方式是 Linux 系统上重要的概念 每个执行的任务都称为进程,在每个进程启动时,系统都会给它指定一个唯一的 ID, ...
- linux io 学习笔记(03)---共享内存,信号灯,消息队列
system V IPC 1)消息队列 2)共享内存 3)信号灯(信号量集) 1.消息队列. ipcs -q 查看系统中使用消息队列的情况 ipcrm -q +msqid 删除消息队列 消息队列工作原 ...
- Linux学习笔记14——使用fcntl实现文件锁定
期末考试快要来了,Linux学习进度一下拉下来许多.今天学习的是文件锁定,在Linux中,实现文件锁定的方法很多,例如fcntl和lockf.下面主要是fcntl的调用. fcntl函数的原型是:in ...
- Windows进程通信 -- 共享内存(1)
共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 W ...
- Windows进程通信 -- 共享内存
享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 Wi ...
- Windows进程通信-共享内存空间
三个模块 1,game.exe,三个方法,控制台输入指令('A','B','R')分别控制三个方法的调用: 2,WGDll.dll,要注入到game进程中的dll文件: 3,myconsole.exe ...
- linux学习笔记之进程
一.基础知识 1:进程. 1,进程ID: 非负整数,具有唯一性. 1)ID=0的进程:调度进程/交换进程.内核的一部分.不执行任何磁盘上的程序. 2)ID=1的进程:init进程. 1-自举结束时,由 ...
- Linux学习笔记24——进程管道
一 管道的作用 通常把一个进程的输出通过管道连接到另一个进程的输入. 二 popen和pclose函数 #include <stdio.h> FILE *popen(const char ...
- linux学习笔记-13.进程控制
1.查看用户最近登录情况 lastlastlog 2.查看硬盘使用情况 df 3.查看文件大小 du 4.查看内存使用情况 free 5.查看文件系统 /proc 6.查看日志 ls /var/log ...
随机推荐
- HDU 1859
#include <iostream> #include <cstdio> #include <algorithm> #include <vector> ...
- awk命令和grep命令的使用
1.遇到需求:用ping命令去检测系统网络延迟 跑 ping baidu.com -c 3,想要直接得到平均延迟. ping baidu.com -c 3 | grep rtt | awk -F \/ ...
- C++基础知识(5)---类和对象
终于把C++中的基础在前面的几篇博客中总结完了,可能还有一些语法还没有总结到,没关系,以后用到了再查资料就好.类是C++中的一个非常重要的概念,这是区别你使用的C++到底是面向过程还是面向对象的一个重 ...
- c++虚析构函数
虚析构函数的作用主要是当通过基类指针删除派生类对象时,调用派生类的析构函数(如果没有将不会调用派生类析构函数) #include <iostream> using namespace st ...
- 变量声明---let,const,解构
let在很多方面与var是相似的,但是可以帮助大家避免在JavaScript里常见一些问题. const是对let的一个增强,它能阻止对一个变量再次赋值. 块作用域 当用let声明一个变量,它使用的是 ...
- swing中JTable的使用方法
public static void main(String[] args) { Student s1 = new Student("张三", "001", 0 ...
- Android JNI总结
@Dlive 0x01 JNI介绍 JNI是Java Native Interface的缩写,JNI不是Android专有的东西,它是从Java继承而来,但是在Android中,JNI的作用和重要性大 ...
- windows+ant+git+tomcat中ant直接获取git项目部署注意点
最近项目搬迁到公司的"GitHub"上面原来的SVN的ant发布脚本要改下,于是百度ant获取git的方法太少了,windows平台上更是没有所以搞了两天,今天终于有点成果分享给大 ...
- PowerDisner15 关于生成表带双""号问题
我们可以尝试在DBMS配置文件中修改相应的格式来解决. 在PowerDesigner中 选择 Database->Edit current database->Script->Sql ...
- js中的斐波那契数列法
//斐波那契数列:1,2,3,5,8,13…… //从第3个起的第n个等于前两个之和 //解法1: var n1 = 1,n2 = 2; for(var i=3;i<101;i++){ var ...