嵌入式linux学习笔记--IPC(inter-process-commuction,进程间通讯)三种方式之一---- linux下的共享内存的使用方法
发布日期:2021-06-30 13:42:22 浏览次数:2 分类:技术文章

本文共 6690 字,大约阅读时间需要 22 分钟。

最近学习了共享内存,做笔记记录一下。 预计会补全 信号量和消息队列。

我理解的共享内存: 共享内存就是同一台主机上的多个进程共同将自己的程序运行中的内存的一段区域映射到相同的真实物理地址。
在linux内 每一个程序都有自己独立的寻址空间,这个寻址空间可能是4G大小,(当然一般程序肯定不会用这么大的空间的) 通过PCB(Processing Control Block) 可以决定将进程内的虚拟地址映射到真实的硬件上的什么位置。(这个过程应该是有MMU(内存管理单元)参与的,具体的实现机理忘了,貌似内存保护也是用这个实现的) 共享内存的速度是最快的,只需要拷贝两次的数据即可!
直接分析函数:

/******************需要包含的头文件**********************/#include 
#include
#include
/*****************提供的函数***************************///ftok(把一个已存在的路径名和一个整数标识符转换成IPC键值)// fname : 是一个路径,可以随意设置,但是必须保证路径一定存在且要注意文末提到的陷阱。// id : 一个约定好的数据,多个线程之间保证这个数据相同即可// return: 返回一个唯一的IPC 键值,错误时返回-1 ,错误信息保存在error中key_t ftok( char * fname, int id )//demo:key_t key = ftok("/file/path",12345)/*****************************************************************************///shmget 是根据 key 值 创建共享内存。// key : 是一个长整形变量,建议由内核产生// size : 是共享内存的大小,单位是字节// shmflg : 是一组标志位,通常为0,可以和下面的标志位进行或运算,低三位是存取权限,剩下的是创建模式// 作用与open函数的mode参数//#define IPC_CREAT 01000 /* Create key if key does not exist. *///#define IPC_EXCL 02000 /* Fail if key exists. *///return : 成功返回共享内存的ID,出错返回-1 int shmget(key_t key,size_t size,int shmflg); /*****************************************************************************///description : attach memory to this process/ 把 共享内存链接到当前的程序空间中// shm_id : IPC 's key,get from ftok.// shm_addr : 指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址// shmflg : 读写权限,为了保护程序可以限制单一的权限。void *shmat(int shm_id,const void *shm_addr,int shmflg);// attach/*****************************************************************************/// detach memory from this process/ 将共享内存从当前进程中分离,不会删除共享内存!// shmaddr : 共享内存的虚拟地址(或者说当前进程空间中的地址)int shmdt(const void *shmaddr); // detach/*****************************************************************************/// shmctl()完成对共享内存的控制-----相关信息引自百度百科//shmid : 共享内存标识符// cmd : 命令,大致就是获得当前的状态,修改状态,删除这片共享内存。 //IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中//IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内//IPC_RMID:删除这片共享内存//buf 共享内存管理结构体。具体说明参见共享内存内核结构定义部分// 返回错误码// EACCESS:参数cmd为IPC_STAT,确无权限读取该共享内存// EFAULT:参数buf指向无效的内存地址// EIDRM:标识符为shmid的共享内存已被删除// EINVAL:无效的参数cmd或shmid// EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行int shmctl(int shm_id,int command,struct shmin_ds *buf);// control/*****************************************************************************/

分析完函数之后贴一个之前自己测试的demo

my_shem.h
这是自己编写的一个头文件,有点想模仿 c++的标准库

#ifndef __MY_SHEM__H_#define __MY_SHEM__H_#include 
#include
#include
#include
#include
#include
#define PATHNAME "/etc"#define bolock_size 8192class my_sharemem{ public: ~my_sharemem(void); static my_sharemem* Instance(){ static my_sharemem instance; return &instance; } void* get_blocks(int blocks_id);private: my_sharemem(void);// 创建函数 static void* get_pointer_get(int blocks_id); static bool is_blocks_exist(int blocks_id); static std::map
blocks;};inline my_sharemem::my_sharemem(){ printf("inline void my_sharemem::my_sharemem()\n");}inline void* my_sharemem::get_blocks(int blocks_id){ if(is_blocks_exist(blocks_id)) { return get_pointer_get(blocks_id); }else { printf("get and create faild!!!!!!! \n"); return NULL; } }inline void* my_sharemem::get_pointer_get(int blocks_id){ return blocks[blocks_id];}// 这个函数会检测块是否存在,不存在就会创建!inline bool my_sharemem::is_blocks_exist(int blocks_id){ if( blocks.find(blocks_id) != blocks.end()){ return true; }else { // 创建块 key_t key = ftok(PATHNAME,blocks_id); if(key < 0) { perror("ftok"); return false; } int shmid = 0; if((shmid = shmget(key,bolock_size,IPC_CREAT | 0666)) < 0) { perror("shmget"); printf(" create faild ,try to open\n"); if((shmid = shmget(key,bolock_size,IPC_EXCL | 0666)) < 0) { perror("shmget"); return false; }else { printf("create success\n"); return true; } }else{ printf(" open success! \n"); } void *addr = shmat(shmid,NULL,0); if(addr != (void*)0xffffffff) { blocks[blocks_id] = addr; return true; }else { } return false; }}inline my_sharemem::~my_sharemem(void){ // detach all shared memory std::map
::iterator it; for(it=blocks.begin();it!=blocks.end();++it) { printf("%p was free!\n",it->second); shmdt(it->second); delete it->second; blocks.erase(it); }}#endif

process_a.cpp

进程A 负责将共享内存中的数据进行自增操作

#include "my_shem.h"std::map
my_sharemem::blocks;int main(){
uint32_t id = 0; char* test = (char*)my_sharemem::Instance()->get_blocks(1); //char* test2 = (char*)my_sharemem::Instance()->get_blocks(2); for(int i=0;i<100;i++) {
test[i] = i; // test2[i] = i*2; } while(1) {
for(int i=0;i<100;i++) {
test[i] ++; // test2[i] += 2; } usleep(100000); //printf("%p\n",my_sharemem::Instance()->get_blocks(id )); // printf("%p\n",my_sharemem::Instance()->get_blocks(id++)); } return 0;}

process_b.cpp

process_b 负责打印数据

#include "my_shem.h"std::map
my_sharemem::blocks;int main(){
uint32_t id = 0; char* test = (char*)my_sharemem::Instance()->get_blocks(1);// char* test2 = (char*)my_sharemem::Instance()->get_blocks(2); while(1) {
for(int i=0;i<20;i++) {
printf("%3d ",test[i] ); } printf("\n"); usleep(100000); } return 0;}

以上只是一个简单的测试demo 证实了两个进程之间确实可以 访问到一块的内存

在linux 下 我们可以使用ipcs -m 指令查看共享内存信息,但是以上的程序还存在一个问题,目前的共享内存demo 没有添加共享内存的删除函数,所以创建出来的共享内存最后需要手动释放,这一部分在实际的应用中可以考虑放到某一个监控进程中。

root@jeason:~# ipcs -m------ Shared Memory Segments --------key        shmid      owner      perms      bytes      nattch     status0x01010001 2260992    root       666        8192       20x02010001 2293761    root       666        8192       2root@jeason:~# ipcrm -m 2260992root@jeason:~# ipcrm -m 2293761root@jeason:~# ipcs -m   #  测试失误,此时还没停下来进程就删掉了共享内存,此时共享内存没被释放------ Shared Memory Segments --------key        shmid      owner      perms      bytes      nattch     status0x00000000 2260992    root       666        8192       2          dest0x00000000 2293761    root       666        8192       2          dest

关于ftok()函数的一个陷阱

在使用ftok()函数时,里面有两个参数,即fname和id,fname为指定的文件名,而id为子序列号,这个函数的返回值就是key,它与指定的文件的索引节点号和子序列号id有关,这样就会给我们一个误解,即只要文件的路径,名称和子序列号不变,那么得到的key值永远就不会变。
事实上,这种认识是错误的,想想一下,假如存在这样一种情况:在访问同一共享内存的多个进程先后调用ftok()时间段中,如果fname指向的文件或者目录被删除而且又重新创建,那么文件系统会赋予这个同名文件新的i节点信息,于是这些进程调用的ftok()都能正常返回,但键值key却不一定相同了。由此可能造成的后果是,原本这些进程意图访问一个相同的共享内存对象,然而由于它们各自得到的键值不同,实际上进程指向的共享内存不再一致;如果这些共享内存都得到创建,则在整个应用运行的过程中表面上不会报出任何错误,然而通过一个共享内存对象进行数据传输的目 的将无法实现。
这是一个很重要的问题,希望能谨记!!!
所以要确保key值不变,要么确保ftok()的文件不被删除,要么不用ftok(),指定一个固定的key值。

摘录自《深入浅出Linux工具与编程》

转载地址:https://jeason.blog.csdn.net/article/details/113062425 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:嵌入式学习笔记--jlink 工具RTT使用笔记
下一篇:嵌入式linux学习笔记-- ubuntu欢迎信息分析

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年04月30日 04时43分22秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章