Windows核心编程_代码段共享_LocalAlloc/GlobalAlloc区别

数据段共享

数据段共享起源于Windows16位的时代,在Win16操作系统时代下,16位windows用一个全局堆和局部堆来管理内存,每一个应用程序或dll装入内存时,代码段被装入全局堆,而系统又为每个实例从全局堆中分配了一个64kb的数据段作为该实例的局部堆,用来存放应用程序的堆栈和所有全局或静态变量。而LocalAlloc/GlobalAlloc就是分别用于在局部堆或全局堆中分配内存。

由于每个进程的局部堆很小,所以在局部堆中分配内存会受到空间的限制。但这个堆是每个进程私有的,相对而言分配数据较安全,数据访问出错不至于影响到整个系统。  而在全局堆中分配的内存是为各个进程共享的,每个进程只要拥有这个内存块的句柄都可以访问这块内存,但是每个全局内存空间需要额外的内存开销,造成分配浪费。而且一旦发生严重错误,可能会影响到整个系统的稳定。  

不过在Win32中,每个进程都只拥有一个缺省的私有堆(默认的私有堆),它只能被当前进程访问。应用程序也不可能直接访问系统内存。所以在Win32中全局堆和局部堆都指向进程的省缺堆。用LocalAlloc/GlobalAlloc分配内存没有任何区别。甚至LocalAlloc分配的内存可以被GlobalFree释放掉。所以在Win32下编程,无需注意Local和Global的区别。

可以说是LocalAlloc分配的堆只能被当前进程下所拥有,GlobalAlloc分配的堆,只要其它进程拥有首地址都均可以访问,GlobalAlloc分配的内存空间区段上拥有的权限一般为:RWS,而LocalAlloc分配的内存区段一般为:RW

S代表特权,任何文件或进程都可以访问这个空间

注意每从堆中分配一次内存,这个内存都会携带一个区段用于表明此内存空间的作用,前40个字节为区段!

综上所述,堆在早期Windows下,可分配内存一律称为堆,没有栈可言,栈的的概念来自于局部变量或全局变量,这些变量的内存空间也是从堆中索取来的,当为这些基本类型比如int char分配的内存一律称为栈,而主动使用malloc或new分配的内存称之为堆,并且栈的大小是有限制的!

这里用dll做个比喻:

dll全局变量数据段是不共享的,我们不能直接对它进行访问和操作,比如有同一个dll,名为sll.dll的16位动态库,里面有一个全局变量

int num;

但是有两个操作函数(这两个函数(代码段)会被放入全局堆):

void setnum(int newnum){

num = newnum;

}

int getnum(void){

return num;

}

 

当两个16位的进程加载这个动态库时

 

当A进程调用setnum设置新值以后:

A:setnum(16);

B进程中调用getnum获取:

B:int b = getnum();

那么此时B进程获取到的是A进程刚刚修改的值:16

双方可以很快完成进程间的通讯!

在win16下每个进程只能被加载到内存一次,但dll不一样,dll也只能被加载到内存一次,但是当其它进程想要在加载这个dll时将会引用这个dll内存,因为早期的dll是有自己的内存的,而不是加载到进程空间下,早期的dll是有自己的实列内存空间的!

但是到了win32以后,这种方法被删除了,但是被保留到编译器命令当中了!

#pragma data_seg

预处理指令

#pragma data_seg的使用方法如下:

#pragma data_seg(“mydate”)

Int num = 0;

#pragma data_seg()

注意必须是全局的,并且变量必须显示初始化,如果不初始化,编译器会自动帮你赋值0然后放到.BSS段里去,这样就不会放到共享内存段中去了!

并且也可以使用#pragma comment预处理指令里的linker来显示规定此共享数据段的连接方法:

#pragma comment(linker,”/SECTION:mydate,RWS")

R可读性

W可写

S任意程序/文件都可以使用此共享数据段

注意前面一定要加上/SECTION:这是来表明区段的,不然编译器不认!

而且此方法还可以用来检测当前程序在同一电脑上运行了多少个!

#pragma data_seg("flag_data")int app_count = 0;#pragma data_seg()#pragma comment(linker,”/SECTION:flag_data,RWS")

BOOL WINAPI DllMain(

  _In_ HINSTANCE hinstDLL, // 指向自身的句柄

  _In_ DWORD fdwReason, // 调用原因

  _In_ LPVOID lpvReserved // 隐式加载和显式加载

){



if(app_count>0) // 如果计数大于0,则退出应用程序。{//MessageBox(NULL, "已经启动一个应用程序", "Warning", MB_OK); //printf("no%d application", app_count);return FALSE;}else{//计数+1app_count++;

}

}

 

有的dll可能不用dllmain函数,所以可以增加两个接口:

 

#pragma data_seg("flag_data")int app_count = 0;#pragma data_seg()#pragma comment(linker,”/SECTION:flag_data,RWS”)

void add(){

 app_count++;

}

int returnapp(){

return app_count;

}

 

然后在应用程序入口使用:

 

int main(){

if(returnapp() > 0){

//MessageBox(NULL, "已经启动一个应用程序", "Warning", MB_OK); //printf("no%d application", app_count);return FALSE;

}else{

add();

}

}

 

或者用来判断当前系统下运行了多个此程序:

 

int main(){

add();

printf(“当前程序运行数量:%d”,returnapp);

}

 

只要你显示的调用dll动态库里的函数,那么根据windows内核规则,动态库一定会被加载进来!

 

 

并且共享的数据段,不属于任何一个进程,Windows会把它独立放在一个内存段里,并维护它,然后有新的进程加载动态库时,Windows会检查dll里的共享数据段名是否已经存在,如果已经存在,则不在开辟,并且此动态库共享此段!那么问题来了,如果有两个新的动态库,并且段名一样怎么办?

答:操作系统是根据动态库名来区分此段属于那个动态库的,所以此问题无需我们关心,Wdindows会严格帮我们区分开!

使用此方法可以很轻松的实现进程间的共享数据!

 

最后值得提一下的是上面这些方法在进程中也是有效的!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

17岁boy想当攻城狮

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值