#pragma alloc_text 与 ALLOC_PRAGMA
百度标题中的两部分,可以找到很多文章,现将收集到的其中两篇整理如下:
转载链接:http://hi.baidu.com/billbeggar/item/c378e2ea39a5daeffa42bada
编译时控制分页能力
有时,驱动程序的某些部分必须驻留内存而另一些可以被分页,这就需要一种能控制代码和数据是否分页的方法。通过指导编译器的段分配可以实现这个目的。在运行时,装入器通过检查驱动程序中的段名, 把段放到你指定的内存池中。此外在运行时调用内存管理器的例程也能实现这个目的。
需要注意的是:
--------------------------------------------------------------------------------
(1) Win32 执行文件,包括内核模式驱动程序,在内部都是由一个或多个段组合而成。段可以包含代码或数据,通常还会有诸如可读性、可写性、共享性、执行性,等等附加属性。段是指定分页能力的最小单元。当
装载一个驱动程序映像时,操作系统把以“page”或“.eda(.edata)”为段名开头的段放到分页池中,除非
HKLM/System/CurrentControlSet/Control/Session Manager/Memory
Management中的DisablePagingExecutive值被设置(在这种情况下,驱动程序占用的内存不被分页)。
(2) 在Windows 2000中运行Soft-ICE需要用这种方式禁止内核分页。但这使得把驱动程序代码或数据误放到分页池中所造成的错误特别难以查找。如果你使用这种调试器,我推荐你最好使用PAGED_CODE宏和驱动程序检查器。
(3)
使编译器把代码放到特定段的传统方法是使用alloc_text编译指示。但不是每种编译器都支持这个编译指示,判断DDK中是否定义了
ALLOC_PRAGMA可以帮助决定能否使用alloc_text编译指示。这个编译指示可以把驱动程序的单独例程放到特定段中:
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, AddDevice)
#pragma alloc_text(PAGE, DispatchPnp)
...
#endif
上面语句把AddDevice和DispatchPnp函数的代码放到分页池中。
(4) 如果某些代码在驱动程序完成初始化后不再需要,可以直接把它插入到INIT段。例如:
#pragma alloc_text(INIT, DriverEntry)
加个头无非就是说明该函数是分在 分页内存 非分页内存,初始内存等等...
例如:
#pragma alloc_text(PAGE, a)
#pragma alloc_text(PAGE, b)
表示函数a和b都运行在分页内存中,就是有可能被交换到分页池中,程序中一些高等级,例如dispatch 级别的代码当然不能运行在分页内存,这样往往出现BSOD
另外#pragma alloc_text(INIT, DriverEntry)
像入口函数代码在驱动程序完成初始化后往往不再需要,可以直接把它插入到INIT段。
ps.Microsoft的C/C++编译器在alloc_text的使用上加了两个限制:
1、该编译指示必须跟在函数声明后面而不能在前面。你可以把驱动程序中的所有函数集中到一个头文件中,并在包含该头文件的源文件中,在#include语句的后面使用alloc_text。
2、该编译指示仅能用于有C连接形式的函数。即,它不能用于类成员函数或 C++源文件中未用extern "C"声明的函数。
转载链接:http://laokaddk.blog.51cto.com/368606/318387/
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, DiskPerfCreate)
#pragma alloc_text (PAGE, DiskPerfAddDevice)
#endif
何谓可分页和非分页内存
默认情况下,内核加载器会加载所有的代码部分和全局数据到非分页内存中。而且,加载器是一次加载整个驱动的可执行文件,包括相关的DLL。加载后,内核加载器关闭驱动程序文件,甚至你可以删除当前正在执行的驱动文件。
但是,你可以告诉加载器你希望驱动的哪部分是可分页,所谓可分页,就是可能会被换页出内存(Page out)。可以使用下面的指令来实现:
#define ALLOC_PRAGMA
#pragma alloc_text(PAGE, function_name1)
#pragma alloc_text(PAGE, function_name2)
#endif
由 function_namex 指定的函数代码将被放置于可分页内存中。使数据段可分页,使用下面的编译指令:
#ifdef ALLOC_PRAGMA
#pragma data_seg(PAGE)
// define your pageeble data section module here.
#pragma data_seg()
要注意,绝不能让可能在高的IRQL级别被调用的例程被换出页面。
可以调用MmLockPageableCodeSection 和 MmLockPageableCodeSectionByHandle 来锁定被标志为可分页的代码段。
可以调用MmLockPageableDataSection 和 MmLockPageableDataSectionByHandle 来锁定被标志为可分页的数据段
可以调用MmUnlockPageableImageSection 来解除被上面列出的函数锁定的代码或数据段。
可以调用MmPageEntireDriver 使整个驱动程序可分页,覆盖使用编译指令修饰的段的页面属性。
可以调用MmResetDriverPaging 把页面属性重设回最初描述的属性。
最后,把那些驱动初始化后不再需要的代码自动丢弃可以使用这些编译指令:
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(INIT, function_name) // function called by driverEntry
#endif
驱动程序在执行时可能需要动态分配内存空间,这时你要决定需要的是可分页还是不可分页的内存。如果你的驱动在运行中访问内存的时候能够经受页错误,那么尽量使用可分页内存。
注意:大多数低层磁盘和网络驱动通常不能使用可分页内存,因为他们的代码常常在较高的IRQL等级执行而不允许页错误。但是,文件系统(通常比磁盘驱动占用更大,更多资源)有时候可从可分页池中分配一些内存。
非分页内存在整个系统中是一个有限的资源,其数量依赖于系统使用的类型,和系统可用的物理内存。NT提供下面的例程给内核驱动来分配内存:
ExAllocatePool
ExAllocatePoolWithQuota
ExAllocatePoolWithTag
ExAllocatePoolWithQuotaTag
调用这些函数来请求内存时,必须要指定请求的内存的类型:
NonPagedPool 请求分配一个不可分页的内存
PagedPool 请求分配一个可分页的内存
如果你在分配的内存里有任何同步结构的话,决不要分配分页内存。
当你的应用访问内存时候可以处理页错误的时候,应该指定这个类型。
NonPagedPoolMustSucceed
在其它方式都失败时,而你又必须立即得到内存的时候可以使用这个标志类型。注意这种类型的内存是极度缺乏的资源,可能不足16K。注意,只有在其它途径都
失败的时候才使用,如果分配失败,将会导致系统的bugcheck,错误代码是 MUST_SUCCEED_POOL_EMPTY。
NonPagedPoolCacheAligned
这个标志分配使用数据缓存线的尺寸来在CPU特定的边界对齐的非分页内存。注意这个操作默认是在Intel平台上的 NonPagedPool 分配类型。
PagedPoolCacheAligned
这个标志分配使用数据缓存线的尺寸来在CPU特定的边界对齐的分页内存。
NonPagedPoolCacheAlignedMustSucceed
参考NonPagedPoolMustSucceed 和NonPagedPoolCacheAligned
内存池分配器初始化了一些列表,每个列表包含一种固定大小的块。当你使用上面的函数请求内存时,例程试图分配一个和你请求数量相近的或更大一点的固定大小的块。但是,如果你要求的数量超过一页时,或者超过列表中最大块的大小时,又或者在预先分配的列表中没有可用的块的时候,VMM就会从任何适当类型的系统
可用的内存中分配你请求的数量内存给你。
当预先分配的列表空了的时候,VMM会分配至少一页的内存,切分,然后把剩下的数据放进适当的块列表中。但是,当你请求的非分页内存的数量超过PAGE_SIZE时候,内存池分配例程不会切分未使用的部分,这会浪费宝贵的非分页内存。
也可以使用 MmAllocateNonCachedMemory 或 MmAllocateContiguousMemory
来分配非分页或物理连续内存。它们通常不使用在文件系统或者过滤驱动中,而是用于执行池例程或者其它结构。
内核驱动如果重复的分配和释放小块的内存(小于一个PAGE_SIZE),
可能导致系统的可用物理内存碎片化。这会给系统带来各种问题,包括降低系统的性能等。有一个方法可以避免系统碎片化,就是预先分配一块合理大小的内存,然
后自已管理,在这个预先分配的块中分配和释放小块的内存,但这种方法有可能会浪费核心内存。
用池来管理内存
上面提到用预先分配一块合理大小的内存来自已管理,可以避免系统内存碎片。我们可以用池来管理这块预先分配的内存。必须再次强调,预先分配的内存大小必须足够准确,太大会浪费宝贵的资源。
调用 ExAllocatePool 来分配池使用的内存,你要选择从分页或者非分页的池中分配,注意你的内存片基址必须在8字节的边界对齐。
还要分配和初始化一个自旋锁或者使用其它的同步机制来保护对内存块列表的修改。注意不要在比 DISPATCH_LEVEL 更高的 IRQL 等级使用池操作例程,因为在更高的 IRQL等级不能使用同步结构。
然后定义一个ZONE_HEADER结构的全局变量,用来作为这个池的控制结构,并调用ExInitializeZone来初始化池头部。然后,就可以通过调用ExAllocateFromZone和
ExInterlockedAllocateFromZone 来分配自已管理的内存块。这两个函数的差别在于后者使用了自旋锁用于操作同步。调用ExFreeToZone 和ExInterlockedFreeToZone来释放分配的内存。
虽然池帮助减少系统内存的碎片,但池还是有一些不足:
1、 驱动程序必须预先为池分配内存,这些内存可能会闲置很久造成内存浪费
2、 你对需要的内存的数量必须相当的精确,在很多时候这个很难做到。
3、 当内存需求增大时,可以扩大池的尺寸,但是却不能减小池的尺寸,直到重启系统
lookaside lists
lookaside lists 是NT4.0里新的特性,它突破了池的限制。
当你调用 ExInitializeNPagedLookasideList 和ExInitializePagedlookasideList初始化 lookaside lists 时不用预先分配内存,相反,只有当你有真正需要内存的时候才分配。
在初始化时,你必须指定列表的深度,表示尺寸的最大值。相关的函数有ExAllocateFromN-
PagedLookasideList 和ExAllocateFromPagedLookasideList。我们用一个 NPAGED_
LOOKASIDE_LIST或 PAGED_LOOKASIDE_LIST结构变量来保存lookaside lists的状态,注意这结构一定要从非分页内存中分配。
#pragma alloc_text 与 ALLOC_PRAGMA的更多相关文章
- #pragma alloc_text
#pragma alloc_text 编译时控制分页能力 有时,驱动程序的某些部分必须驻留内存而另一些可以被分页,这就需要一种能控制代码和数据是否分页的方法.通过指导编译器的段分配可以实现这个目的.在 ...
- 解释#ifdef ALLOC_PRAGMA代码段的原理
By default, the kernel loader will load all driver executables and any global data that you may have ...
- #pragma详解
在#Pragma是预处理指令它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个编译器给出了一个方法,在保持与C和C ++语言完全兼容的情况下,给出主机或操作系统专有 ...
- #pragma详细解释(一)
#pragma详细解释 #pragma详细解释(一) 2010-04-18 14:21:00| 分类: 默认分类 | 标签: |字号大中小订阅 在#Pragma是预处理指令它的作用是设定编 ...
- 83.#pragma详解
创建数据段 //创建数据段 #pragma data_seg("fangfangdata") ; #pragma data_seg() 与数据段连接,实现数据通信,分享 //实现数 ...
- #pragma命令详解
每种C和C++的实现支持对其宿主机或操作系统唯一的功能.例如,一些程序需要精确控制超出数据所在的储存空间,或着控制特定函数接受参数的方式.#pragma指示使每个编译程序在保留C和C++语言的整体兼容 ...
- [转]C/C++ 实现文件透明加解密
今日遇见一个开超市的朋友,真没想到在高校开超市一个月可以达到月净利润50K,相比起我们程序员的工资,真是不可同日而语,这个世道啊,真是做程序员不如经商开超市, 我们高科技的从业者,真是造原子弹不如卖茶 ...
- 基于WDF的PCI/PCIe接口卡Windows驱动程序(4)- 驱动程序代码(源文件)
原文出处:http://www.cnblogs.com/jacklu/p/4687325.html 本篇文章将对PCIe驱动程序的源文件代码作详细解释与说明.整个WDF驱动程序工程共包含4个头文件(已 ...
- Writing a device driver for Windows
Writing a device driver for Windows In order to write a device driver for windows, one needs ...
随机推荐
- Mapped Statements collection does not contain value for
这是由pojo的映射文件的命名空间引起的错误. 按照以下格式即可:命名空间中一定要含有mapper.xxMapper这样的格式,否则出现以上错误. <?xml version="1.0 ...
- WCF服务的创建和发布到IIS
一. WCF服务的创建 有两种创建方式: 1.WCF服务库 2.WCF服务应用程序 如下图所示: 这里选择WCF服务库.注意事项: 1.WCF服务库是一个类库项目,这里选择.net 3.5版本(版本高 ...
- 项目图片上传存储的目录部分代码思路Calendar类获取年月日
在项目中将产品图片全部放置在一个文件夹下面的话,如果图片很多的话,加载速度会减慢,可以按照文件夹按年,月,日来分开存放,图片文件名字取系统时间long类型加上5位随机数字码避免重复. Calendar ...
- 用MT.exe将exe中的manifest文件提取出来和将manifest文件放入exe中
前一种方法是将manifest文件放入exe中,但是要记得需要在工程中设置 这样的话exe中就不存在manifest了,在debug目录下就会看到相应的manifest文件.后者是将exe中的man ...
- Ubuntu环境下手动配置openSSH
配置openSSH 1.手动下载压缩文件(.tar.gz) zlib-1.2.7.tar.gz openssl-1.0.1j.tar.gz openssh-6.0p1.tar.gz 2.安装zlib ...
- 123. Best Time to Buy and Sell Stock III
题目: Say you have an array for which the ith element is the price of a given stock on day i. Design a ...
- P154、面试题28:字符串的排列
题目:输入一个字符串,打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a.b.c所能排列出来的所有字符串abc.acb.bac.bca.cab.cba. 测试用例: 1)功能测试( ...
- ActiveMQ可靠性机制
消息的签收(Acknowledgment): 客户端成功接收一条消息的标志是这条消息被签收. 成功接收一条消息一般包括如下三个阶段: (1) 客户端接收消息 (2) 客户端处理消息 (3) 消息 ...
- while ((ch = getchar()) != EOF)中ch定义为char还是int型?cin、scanf等如何结束键盘输入
2013-07-09 18:55:42 EOF是文件的结束符,具体可以作为文本文件的结束符,也可以作为键盘输入char类型数据时的结束符.对于不同的系统,EOF的定义可能不同,一般定义为-1.因为ch ...
- Git教程(5)常用技巧之本地分支
http://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%AE%80%E4%BB%8B 基础 Git 研发组 ...