linux使用mmap共享内存封装动态库,实现全局变量对外只读接口

想要实现的功能:动态库中定义结构体类型全局变量(一个结构体中嵌套多个结构体),该变量在库内可读写,但主程序(外部程序)只可读,不可写。

方便说明,简化成一个如下的结构体变量:

typedef struct
{
    int a;
    int b;
    int c;
} Test_STU;

思路:

  1. 结构体改成类,public成员,通过get()方法获取——不喜欢,直接采用 . 结构体方式访问最好;
  2. 结构体改成类,public成员,添加const修饰——不喜欢,变量很多,初始化列表不方便,且类内以及其它类内无法修改变量,而且const并非完全保证变量不会被修改,比如const_cast;
  3. 对于让外部程序可读可写的全局变量,库内定义好一个对该变量的引用,如:Test_STU&Test,外部程序可通过extern &Test声明后使用;那么类似,针对只读的变量,库内定义时候使用const &修饰,如const Test_STU & Test,外部程序也通过extern &Test声明后使用——测试不行,const属性只在本编译模块有效,外部程序依然可以通过Test.a改写;

思考:本质上是想获取一个对某一片内存区域只有访问权限的指针!

思路:
联想多进程通信中的共享内存,在同一进程中使用shm_open打开同一文件,通过mmap建立两块内存映射区,获得操作该内存区域的指针,在建立映射区时,一个权限设为可读可写,一个权限设为只读……好像可以,试试……

动态库部分代码:

test.h

#ifndef TEST_H
#define TEST_H

#include "test_global.h"
#include <iostream>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

typedef struct{
    int a;
    int b;
    int c;
} Test_STU;

class test
{
public:
    test();
    Test_STU *getSTUAddress();	//提供给外部程序获取只读内存映射区的地址
};
#endif // TEST_H

test.cpp

#include "test.h"

Test_STU *APP_RO = nullptr;	
test::test()
{
	//创建共享内存过程
    int fd = shm_open("shmfile", O_CREAT|O_RDWR|O_TRUNC, 0666);
    if (fd < 0)
        printf("shm_open failed!\n");

    ftruncate(fd, sizeof(Test_STU));
	//该映射区,库程序可读可写
    Test_STU *BGD_RW;
    BGD_RW = (Test_STU *) mmap(NULL, sizeof(Test_STU), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (BGD_RW == MAP_FAILED) {
        perror("mmap failed!\n");
    }
    close(fd);

	//再次打开同一个文件,创建第二个内存映射区,该映射区只读
    int fd2 = shm_open("shmfile", O_RDWR, 0666);
    if (fd2 < 0)
        printf("shm_open failed!\n");
    APP_RO = (Test_STU *) mmap(NULL, sizeof(Test_STU), PROT_READ, MAP_PRIVATE, fd2, 0);
    if (APP_RO == MAP_FAILED) {
        perror("mmap failed!\n");
    }
    close(fd2);
	//库程序先随便赋值
    BGD_RW->a = 100;
    BGD_RW->b = 200;
    BGD_RW->c = 300;
   
//     munmap(APP_RO, sizeof(Test_STU));
//     munmap(BGD_RW, sizeof(Test_STU));
//     shm_unlink("shmfile");
}

Test_STU *test::getSTUAddress()
{
    return APP_RO;
}

OK,生成动态库文件libtest.so

注意:编译生成动态库时需要添加链接选项:-lrt!(如果是用qt,则在.pro文件中添加:LIBS +=-lrt)

外部测试程序:

#include <iostream>
#include "test.h"
using namespace std;

int main()
{
    test test1;
    Test_STU *APP_RO;	//我自己都没注意,变量名和库中定义的全局变量重名了…
    APP_RO = test1.getSTUAddress();
    cout<<APP_RO->a <<" "<<APP_RO->b<<" "<<APP_RO->c<<" ";	//输出100 200 300
    cout<<endl;
//    APP_RO->a = 999;	//报错,segment fault!,只读不可写
    getchar();
    return 0;
}

目的达到,至于效率,除了浪费些内存,也没啥大问题……嗯,还不在小白的研究层次……

另外两点:

  1. 如上代码,外部程序指针变量与库内定义的全局指针变量重名,对于只读没影响,特意用BGD_RW测试了可读可写的情况,发现也没影响……好像解释的通,不管重不重名,不管是库内还是库外指针,操作的都是同一块内存区域;
  2. mmap存在一个也许可以忽略也许不能忽略的问题:并不是每次写入内存,都会被立马同步到文件。对于上例,理论上来说,库程序修改了Test中的值,shmfile文件可能不会立即改变,此时,如果外部程序读取的话,值可能还没变化。这个时间究竟多久,没去研究,保险起见似乎要用msync()函数强制同步。

最后,本文纯属好奇测试,权当学习记录~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值