锁定非分页内存分页是什么意思

后使用快捷导航没有帐号?
查看: 37283|回复: 143
释放你内存的潜力,系统内存潜力最大化全攻略
该用户从未签到
本帖最后由 老灰机 于
21:40 编辑
& && & 现在不少的童鞋已经把自己的内存加到了4G以及以上(经常来本友会的童鞋想必也是如此),可是我们会发现,在把内存提升到4G以上的时候,对于我们电脑速度的提升却没有我们想象中的那么强大,反而会认为内存对于系统的提升可有与无,其实一般来说,无论32位还是64位系统,内存达到4G以上的时候,对于系统性能的提升就相对较小了,除非你要运行大型的工程软件另说。对于家用娱乐来说,我们要想用“多余'的内存为我们达到效益最大化,虚拟硬盘(ramdisk)就是一个不错的选择,并且在现在傻瓜型内存盘软件很多,也为一些初学者创造了很好的条件。如果你认为自己的系统还是不够快,如果你希望在上网娱乐的时候能够得到类似于SSD的速度,如果你希望保护你的SSD,延长它的寿命,ramdisk就是你的不二选择。现在的电脑的标配应该就是以4G单条起步,我们可以尝试下进行改装,让我们的系统运行更加顺畅,同时可以存放系统临时文件以及自己临时保存的文件,让系统更加干净。
& && &&&现在我们开始进行安装,在安装之前首先要确定你的系统内存要≥4G才可以进行,2G的请不要尝试,因为2G还在使用系统分页文件,有可能导致系统显示内存不足。ramdisk虚拟化软件有很多,其实都是大同小异,现在我主要以国产品牌”魔方“为代表进行说明,当然,对于16G乃至32~64GB内存的超级玩家来说,魔方显然不能满足你的需求,对于需要构建4GB以及以上的用户,请使用AMD出品的AMD Radeon RAMDisk汉化版(免费版最大4GB)以及SuperSpeed Ramdisk Plus 10.0.1(任意大小),当然,软件的使用难易程度也随着内存盘的增加而略有增加,对于这两个软件我会在文末附上,由于本文还要说明除了内存盘以外的其它要点,具体教程请自行百度~另请不要插楼,我会把你”和谐”掉的,十八大,促和谐~~有什么问题请在文末提问,谢谢配合~
备注:如果你用的IE的话就请不要更改缓存了,重启又回去了,估计是魔方内存盘的加载机制导致的,建议用一下AMD Ramdisk,然后按照步骤来,具体操作请百度,很简单~
很详细,加分奖励
恭喜,获得本币奖励
恭喜,获得本币奖励
有点创意~~
该用户从未签到
本帖最后由 老灰机 于
14:42 编辑
首先我们要做的是下载魔方,建议选择绿色版。下载地址可以百度或者将此地址在新窗口打开~
下载之后解压的一套不必赘言,说句题外话,其实魔方就是原来的win7优化大师,我认为这个软件更像是中国的瑞士军刀类软件,对于进阶水准的用户来说带来了极大的方便,不过也是越来越臃肿,添加了许多无用的功能(如魔方守护),绿色版在一定程度上解决了某些问题,希望魔方多多改进吧~言归正传,初次打开之后请仔细查看初次运行向导,以防魔方做出了你不愿看到的设置,有可能会导致越优化越卡的窘境,如果对于自己的水平有信心完全不用顾及那些设置,关掉就好。
打开魔方主界面的“应用”中的“魔方内存盘”
02.png (255.09 KB, 下载次数: 7)
14:29 上传
对于初次使用的本友来说,你要点击“添加内存盘”选项,我由于已经创建了内存盘,因此下面会有选项。
03.png (105.76 KB, 下载次数: 7)
14:41 上传
对于初次使用内存盘的本友来说,我们要选择“创建新内存盘”的选项,内存盘的大小看自己的内存大小,无论是windows7/xp/vista的32位或者64位用户,4G内存请选择512MB~600MB为宜,6G用户的选择比较多样,如果只是做系统缓存的话请选择1G左右为宜,如果希望添加我以下要说明的更改作为IE/chrome浏览器的缓存的话,请维持在1.5~2G上,建议选择2G,8G的用户选择2G,请看好上面的“MB”和“GB”选项。盘符可以自己选择,,其它默认,然后点击“创建”。之后建议关闭程序重启系统以完成内存盘加载。
04.png (57.29 KB, 下载次数: 5)
14:39 上传
该用户从未签到
好了,重启系统之后我们就能看到我们已经创建好的我内存盘啦~
05.png (4.7 KB, 下载次数: 4)
14:43 上传
持续I/O性能恐怖
img328.jpg.thumb.jpg (64.38 KB, 下载次数: 15)
14:49 上传
随机存取性能
img329.jpg.thumb.jpg (65.76 KB, 下载次数: 4)
14:50 上传
具体如何强悍我就不说了,下面即将说的是关于系统分页文件以及IE/chrome的缓存修改,可以提高系统性能和打开网页速度,如果本友已经知道的可以离开,或者使用opera的童鞋也可以离开~
该用户从未签到
俗话说的好,巧妇难为无米之炊,我们有了巧妇,我们也需要有米,才能完美的做出一顿饭~也就是说,我们要想提升系统性能还需要另外的方法,才能够拥有完美的速度~以上步骤只是形成了一个免费的垃圾回收站,减少系统垃圾以及提高文件的传输速度,要想实现系统性能的提升请往下看~
改变系统分页文件位置,提高系统缓存速度以及系统响应速度
这点对于机械硬盘系统的提升格外大,对于SSD系统的提升反而比较小,但是本着保护SSD的原则,我们还是需要把分页文件转移到内存盘来减少SSD的擦写次数的不是~
在桌面“计算机”上面右键,点击“属性”然后点击“高级系统设置”
09.png (53.26 KB, 下载次数: 6)
15:01 上传
点击“高级”里面的第一个“设置”
10.png (38.44 KB, 下载次数: 4)
15:02 上传
点击“高级”选项卡里面的“更改”选项
12.png (25.63 KB, 下载次数: 4)
15:05 上传
在出现的“虚拟内存”对话框按照以下的箭头步骤更改:去掉“自动管理分页大小”的对号,点击磁盘对话框,选中C盘,选择“无分页文件”,点击“设置”出现如下对话框选择“是”,然后点击“确定”。
20.png (48.4 KB, 下载次数: 6)
15:13 上传
16.png (23.37 KB, 下载次数: 6)
15:13 上传
该用户从未签到
接下来,在框内选中你的虚拟硬盘,选择“自定义大小”,最小值选择512,最大不超过虚拟总空间,需要更改chrome/IE缓存位置的童鞋请至少留出500M的缓存空间。
21.png (22.83 KB, 下载次数: 7)
15:23 上传
紧接着的对话框,想立即生效,请重启~
上面已经把虚拟内存设置到了虚拟硬盘,现在我们要将临时文件设置到虚拟硬盘~点击高级系统设置中的“环境变量“
22.png (133.67 KB, 下载次数: 6)
15:27 上传
然后分别更改上下两个对话框中的”TMP“和”TEMP“的变量值,上下两个变量值都要更改,总共更改4处,其它不动。变量值为你的虚拟硬盘盘符,最后确定。看图,按照步骤更改。
63.png (101.25 KB, 下载次数: 6)
15:34 上传
该用户从未签到
好了,经过上面的更改,你已经把系统分页和缓存文件设置到了虚拟磁盘,这时你可以打开看一下你的虚拟磁盘,有500M左右已被占用。说明已经成功,下面是更改浏览器缓存,让你的浏览器加载更快,同时尽可能减少磁盘碎片的设置~~
64.png (7.34 KB, 下载次数: 7)
15:39 上传
该用户从未签到
本帖最后由 老灰机 于
15:52 编辑
IE修改的设置:
打开IE,点击设置,选择”internet“选项
65.png (24.83 KB, 下载次数: 4)
15:43 上传
在打开的选项对话框看图设置,将文件夹设置在虚拟盘里面,点击”确定“。可能会出现需要重启对话框,根据自己情况,不重启的话下次开机会进行设置。
68.png (114.63 KB, 下载次数: 7)
15:48 上传
69.png (17.09 KB, 下载次数: 5)
15:52 上传
至此,IE的缓存设置就已经OK,需要说明的是,只要在IE中进行了设置,无论你用的什么IE内核浏览器(360,KR,马桶)都会使用这个设置,不会更改啦~~
chrome设置稍微复杂,有需求的用户请看下楼~~
该用户从未签到
chrome浏览器是我比较喜欢的一个浏览器,也是现在和IE不分伯仲的一款浏览器,特点是打开网页快,拓展性强,开源,简洁快速的特点收到了不少用户的喜爱,由webkit内核开源项目衍生的各种国内”极速“浏览器都是由此而生,各种优化仁者见仁智者见智吧,反正我还是认为简洁的chrome比较和我的口味,各位请自行判断~
需要说明的是,这种更改稍显复杂,不适合喜欢频繁安装各种测试版的童鞋使用,建议一般的稳定版用户使用,测试版童鞋不嫌麻烦也可以使用~
好,现在开始。由于chrome官方版的可定制性不强,因此推荐最新的chrome22稳定中文绿色版(来自独木成林,感谢~)请在新窗口打开以下地址(度娘网盘):
下载之后解压到自己喜欢的盘,最好不是C盘,容易产生磁盘碎片,降低系统性能,双击”chrome-bin“文件夹里面的”chrome“可以打开浏览器,绿色版浏览器默认缓存位置在D盘,可以右键在桌面创建快捷方式备用~然后请看以下说明~~
(1)用户数据文件:
首先创建Chrome.exe的桌面快捷方式(改好以后将此快捷方法锁定到任务栏同样有效),&&右键——属性——快捷方式——目标在chrome.exe后面添加 --user-data-dir=X文件夹(此文件夹指的是你要存放用户数据文件、上网缓存文件的地方 例如我的是&R:&)添加好以后是这样的:DProgram FilesChromechrome.exe --user-data-dir=&R:&(英文状态标点,注意空格,将”R“换成你虚拟盘的位置)
70.png (12.08 KB, 下载次数: 5)
16:12 上传
(2)上网缓存文件:
方法同上 创建Chrome.exe快捷方式, 右键——属性——快捷方式——目标在chrome.exe后面添加 --disk-cache-dir=Y文件夹 添加好以后是这样的:DProgram FilesChromechrome.exe --disk-cache-dir=Y文件夹(英文状态标点,注意空格截图同上)
两个一块定义的方法:
创建Chrome.exe快捷方式,&&右键——属性——快捷方式——目标
在chrome.exe后面添加 --user-data-dir=X文件夹 --disk-cache-dir=Y文件夹(英文状态标点,注意空格,定义的两个每一个都有一个空格)
75.png (12.27 KB, 下载次数: 7)
16:20 上传
改好这些后,还只是成功了一半,这只能从快捷健启动有效,要使外部调用(其他程序,如从QQ、迅雷等软件处打开浏览器)生效,还要做如下修改:
开始——运行 regedit
分别找到[HKEY_CLASSES_ROOT下的ChromeHTML,ftp,http,https,
在shellopencommand的右边数值上右键修改Program FilesChromechrome.exe -- %1的值,
在 -- %1的前面chrome.exe的后面添加 --user-data-dir=X文件夹 --disk-cache-dir=Y文件夹(注意空格)其他方法相同[HKEY_CLASSES_ROOTChromeHTMLshellopencommand][HKEY_CLASSES_ROOTftpshellopencommand][HKEY_CLASSES_ROOThttpshellopencommand][HKEY_CLASSES_ROOThttpsshellopencommand]全部弄好后,不管点击Chrome快捷方式,还是QQ、迅雷等软件外部调用,都会成功定义文件夹。
细节考量:
注意修改注册表的时候的每一个空格,我都有标明,当然不排除有的童鞋确实不会修改的情况,这个绿色版的chrome好处是缓存在D盘,即使你修改失败也有一半的缓存会在虚拟盘,其实只要心细,一定会成功啦~不排除有童鞋不会修改注册表的情况,那就不要折腾啦,注册表的不当修改会导致系统性能下降乃至无法启动哦~要是确实不懂注册表请不要尝试!!
通过以上的设置,我们就完成了chrome的缓存文件设置,重新打开chrome看看,是不是启动速度和载入速度有提高呢~还不产生垃圾哦~
该用户从未签到
本帖最后由 老灰机 于
16:32 编辑
总结:& && & 通过以上的设置,我们基本上就完成了内存效能最大化的设置,今后内存就充当了临时的高速仓库,临时保存的系统文件以及自己临时的文件都可以存储在里面,最主要的好处是提高了系统的响应速度,这一点确实很好,当然缺点也是有的,那就是系统启动速度回稍显慢,我的SSD在加载ramdisk之前是16S,加载之后是20S的启动速度,这不是按照百分比来的,机械硬盘的加载速度应该也会延迟4~5S,不过为了系统的速度和健康,以及浏览器的速度,我们为什么不用呢?毕竟启动速度只是一个没用的参数,我们需要的是我们使用系统的快感不是么~
& && & 好啦,以上就是我的系统内存设置方法,文中的软件我会在楼下放出,欢迎本友提问,同时我也长期经营此贴,不过够详细,相信基本上都会了吧~欢迎留名哦~
我的似乎比以往快了,,,呵呵。
该用户从未签到
本帖最后由 老灰机 于
21:39 编辑
占楼更新~顺便AMD Ramdisk汉化版下载地址(来自卡吧,感谢~)
SuperSpeed Ramdisk Plus下载失效,请本友自行查找~
附带修改opera浏览器临时文件的方法:
修改临时文件夹位置
Opera的临时文件夹也就是硬盘缓存,默认位置是profile下的cache4目录。修改方法:
在地址栏输入opera:config,确定后进入UserPrefs下Cache Directory4,自己设置Cache Directory4里面的路径就行了。比如设置为C:\\Documents and Settings\\你的用户名\\Local Settings\\Temp或者C:\\Documents and Settings\\你的用户名\\Local Settings\\Temporary Internet Files。
方法来自网络,请有需要的童鞋试用下,谢谢啦~
另:如果你用的IE的话就请不要更改缓存了,重启又回去了,估计是魔方内存盘的加载机制导致的,建议用一下AMD Ramdisk~
该用户从未签到
本帖最后由 老灰机 于
16:51 编辑
感谢导师crazy_panda的指正,请各位本友在创建虚拟盘的时候不要勾选”关机保存内存盘数据“的选项,也不要安装软件在里面哦,重启会直接清除的哦~当然,要是只是安装一次性的程序是可以的哦~
该用户从未签到
给力。。。
研究下,,
支持了。。。
该用户从未签到
LOVE梦1314 发表于
给力。。。
研究下,,
支持了。。。
该用户从未签到
悲剧的opera党路过了。。。
该用户从未签到
代武 发表于
悲剧的opera党路过了。。。
.........opera貌似也可以改,你可以说搜一下哦~
该用户从未签到
代武 发表于
悲剧的opera党路过了。。。
已更新opera,请试用下~如果有需要的话~
该用户从未签到
小白我只是来插楼的。。。。。。
该用户从未签到
用搜狗浏览器这种非主流的呢。。。可以么
该用户从未签到
6G内存 弄了2G缓存&&开机变慢了。。。这有影响的么
该用户从未签到
IE设置完后 重启 又变回原来那个了。。。怎么办
17年8月精华宗师
17年8月精华大师
关注本友会
本友会微信公众号
VR微信公众号
benyouhui2012
Powered bywindows驱动开发——锁、死锁、以及同步
表格中描述的是常用的同步机制,这些机制的相关描述以及他们在windows系统下面的实现。
Windows下的机制
Interlocked operations
提供原子的算术,逻辑,和列表操作,不仅是多线程安全的同时也是多处理器安全的
InterlockedXxx and ExInterlockedXxx routines
提供内存的互斥访问权限
Spin locks, fast mutexes, kernel mutexes, synchronization events
Shared/exclusive lock
运行一个线程专享写多个线程共享读
Executive resources
Counted semaphore
允许固定大小的获取权限
Semaphores
原子锁的实现需要处理器汇编的支持,因而可以在所有的IRQL级别上面使用。InterlockedXxx例程能够运行在可换页数据当中。
一个互斥体保证共享资源的专有访问权限,任意能够保证互斥专有权限的锁都能被认为是互斥体。比如,自旋锁和同步事件都可以被认为是互斥体,因为当一个同步事件被唤醒的时候,仅有一个线程能够获取访问权限,同样自旋锁在并发条件下最多只能有一个线程能够被选中执行。互斥体的类型依赖于互斥体的使用环境,选择互斥体需要遵循下列规则:
1 互斥体可以运行的IRQL级别,也就是互斥体可以在哪一级IRQL当中获取和释放
2 获取互斥体是否会提升当前的?如果得到提升,那么原来的被保存在哪里?
3 是否互斥体的释放和获取必须在同一个线程环境上下文当中?
4 互斥体是否能够被递归获取,也就是说一个线程能否在不释放互斥体的条件下,再次获取该互斥体?
5 当一个持有互斥体的线程被终止而没有释放互斥体会出现什么情况?
互斥体类型
递归和线程方面的细节
Interrupt spin lock
获取的时候提升IRQL到并且返回之前的给调用者
不能递归获取,获取和释放在同一个线程上下文
获取的时候提升IRQL到并且返回之前的给调用者
不能递归获取,获取和释放在同一个线程上下文
Queued spin lock
获取的时候提升IRQL到并且保存之前的在一个锁持有者句柄当中
不能递归获取,获取和释放在同一个线程上下文
Fast mutex
获取的时候提升IRQL到并且保存之前的到锁当中
不能递归获取,获取和释放在同一个线程上下文
Kernel mutex (a kernel dispatcher object)
在获取的时候进入代码互斥区域,在释放的时候离开代码互斥区域
可以递归获取,获取和释放在同一个线程上下文
Synchronization event (a kernel dispatcher object)
获取并不会改变IRQL,在小于等于级别等待,而在小于等于级别激发
不能递归获取,获取和释放不需要在同一个线程上下文
Unsafe fast mutex
获取的时候不改变IRQL并且在小于等于级别下面释放
不能递归获取,释放和获取在同一个线程上下文
共享互斥锁也被称为读写锁,允许一个线程进行专享的写操作,而其他的线程进行共享的读操作。计数信号量和互斥体一样,只不过可以出现多个线程同时获取信号量。
Windows同步机制
InterlockedXxx routines
在换页内存上执行原子的算术和逻辑运算
能够在任何IRQL当中获取
Spin locks
在非换页内存上提供专享的内存访问
在IRQL &= DISPATCH_LEVEL级别上获取
ExInterlockedXxx routines
执行算术、逻辑和列表控制的原子操作,不仅是多线程安全的同样也是多处理器安全的
在IRQL &= DISPATCH_LEVEL级别上访问 SList 历程; 其他历程可以在任意IRQL上面访问
Fast mutexes
在APC_LEVEL级别保护数据,同时防止线程被打断
在IRQL &= APC_LEVEL级别上获取
Executive resources
允许一个线程专享写而其他的线程共享读
在IRQL&=APC_LEVEL级别上获取
Kernel dispatcher objects (events, kernel mutexes, semaphores, timers, files, threads, processes)
在IRQL&=APC_LEVEL级别上提供不同形式的同步,能够和用户模式下的应用程序同步
在 IRQL &= APC_LEVEL下等待; 在IRQL &= DISPATCH_LEVEL下激发
Callback objects
在IRQL&=DISPATCH_LEVEL内核模式下面提供代码同步,能够用于驱动之间同步
在 IRQL &= DISPATCH_LEVEL被通知; 回调例程运行在激发线程的上下文当中,和激发线程的IRQL一样
自旋锁和它的名称所暗示的一样:当一个线程拥有自旋锁的时候,其他的线程在内存的某一个位置上通过忙等待自旋,直到锁是可用的。也就是说线程并不会阻塞,而是继续保持的控制权,以防止执行相同或者更低中的代码。自旋锁是一个没有完全公开的结构体,他们必须从非分页内存当中分配,比如设备扩展结构体,或者由用户调用的非分页内存分配操作获得的内存。
自旋锁类型
Ordinary spin lock
保护DISPATCH_LEVEL级别或者更高级的共享数据
Queued spin lock
保护DISPATCH_LEVEL级别或者更高级的共享数据,排队自旋锁可以在或者以后的系统版本上面使用
Interrupt spin lock
保护DIRQL级别的共享数据,在和例程当中使用
所有的自旋锁提升IRQL到DISPATCH_LEVEL或者更高,自旋锁是唯一一个能够被用于高于DISPATCH_LEVEL的同步机制。也就是说系统线程不能够切换,并且当前线程也不能被打断。所有持有自旋锁的代码都需要遵从高于DISPATCH_LEVEL级别的IRQL的执行规则。一个自旋锁的单处理器实现很简单,只需要提升IRQL就可以了,但是多处理器的实现则需要两步,第一步提升IRQL,第二步通过一个原子操作测试并设置相应的数值。
普通自旋锁工作在DISPATCH_LEVEL,为了创建一个普通自旋锁,驱动程序在非换页内存当中分配一个KSPIN_LOCK结构体,并且调用KeInitializeSpinLock进行初始化。运行在低于DISPATCH_LEVEL的代码必须通过KeAcquireSpinLock和KeReleaseSpinLock进行自旋锁获取和释放。而已经运行在DISPATCH_LEVEL的代码应该调用KeAcquireSpinLockAtDpcLevel和KeReleaseSpinLockFromDpcLevel进行自旋锁的获取和释放,这两个例程不会引起IRQL的改变。
不论何时多个线程请求队列自旋锁,都需要在自旋锁的队列上面排队。另外,队列自旋锁仅仅测试和设置本地CPU,因此总线开销更小,对NUMA体系架构尤其高效。一个排队自旋锁需要一个KLOCK_QUEUE_HANDLE结构体来辅助KSPIN_LOCK——前者为后者提供一个队列存储的句柄。这个结构体能够在堆栈当中分配,为了初始化排队自旋锁需要调用KeInitializeSpinLock例程。同样的,运行在低于DISPATCH_LEVEL的驱动历程应该调用KeAcquireInStackQueuedSpinLock和KeReleaseInStackQueuedSpinLock来获取和释放自旋锁。而运行在DISPATCH_LEVEL大的驱动历程则需要调用KeAcquireInStackQueuedSpinLocktDpcLevel和KeReleaseInStackQueuedSpinLockFromDpcLevel来获取和释放自旋锁。
一个中断自旋锁保护设备寄存器和驱动的InterruptService例程以及可以在DIRQL访问的SynchCritSection例程。当一个设备驱动与中断对象关联的时候,操作系统为中断对象创建一个中断自旋锁。当InterruptService例程运行在DIRQL,并且持有相关的中断自旋锁。当InterruptService退出的时候,操作系统释放自旋锁并且降低IRQL。
当一个驱动调用KeSynchronizeExecution来运行一个SynchCritSection例程的时候,同样会申请默认的中断自旋锁。操作系统提升IRQL到DIRQL,获取自旋锁,并且调用SynchCritSection例程。其他的驱动需要访问中断自旋锁应该调用KeAcquireInterruptSpinLock来对共享数据进行访问。然而,一个设备可能在不同的IRQL上面产生多个中断。在这种情况下,驱动必须创建一个中断能够到达的最高的IRQL的自旋锁。当驱动连接中断对象的时候,传递一个KSPIN_LOCK结构体指针。这个结构体和一个中断能够达到的最高的DIRQL关联。系统会将这个自旋锁与中断对象关联起来。
ExInterLockedXxx例程由汇编代码进行编码并且通常禁止相关的处理器的中断。实际上ExInterLockedXxx例程的代码运行在HIGH_LEVEL。为了保护SMP系统上的数据,在操作之前,操作系统提升IRQL并且获取自旋锁。当例程完成操作的时候,系统释放自旋锁并且返回到原来的IRQL。
另外,ExInterLockedXxx例程能够管理下列三种类型的列表:
非InterLocked例程
Interlocked例程
Insert entry at front of singly linked list.
PushEntryList
ExInterlockedPushEntryList
Remove entry from front of singly linked list.
PopEntryList
ExInterlockedPopEntryList
Insert entry at front of doubly linked list.
InsertHeadList
ExInterlockedInsertHeadList
Remove entry from front of doubly linked list.
RemoveHeadList
ExInterlockedRemoveHeadList
Insert entry at end of doubly linked list.
InsertTailList
ExInterlockedInsertTailList
Remove entry from end of doubly linked list.
RemoveTailList
Initialize doubly linked list.
InitializeListHead
Check whether list has entries.
IsListEmpty
Remove entry from doubly linked list.
RemoveListEntry
Initialize S-list.
ExInitializeSListHead
Insert entry at front of Slist.
ExInterlockedPushEntrySList
Remove entry from end of S-list.
ExInterlockedPopEntrySList
Remove all entries from an S-list.
ExInterlockedFlushSList
ExInterLockedXxxList例程使用了一个为驱动分配的自旋锁,这些例程能够在任何IRQL进行调用,而S-链表只能在低于DISPATCH_LEVEL运行。windows内部使用S链表实现lookaside链表。
一个快速互斥体是一个不完全公开的FAST_MUTEX结构体,这个结构体必须在非换页内存当中分配。FAST_MUTEX运行在APC_LEVEL级别,因此可以阻止所有的APC提交,也不会被其他的线程打断。
ExAcquireFastMutex
在获取快速互斥体之前提升IRQL到APC_LEVEL ,阻塞直到快速互斥体可用
ExAcquireFastMutexUnsafe
在当前的IRQL获取快速互斥体,阻塞直到互斥体可用
ExTryToAcquireFastMutex
在获取快速互斥体之前提升IRQL到APC_LEVEL ,不可用时不阻塞
ExAcquireFastMutex和ExAcquireFastMutexUnsafe引起线程阻塞直到互斥体可用。而ExTryToAcquireFastMutex如果互斥体不可用立刻返回FALSE。当出现下面两种情况之一的时候,使用ExAcquireFastMutexUnsafe获取互斥体:
· 线程已经运行在APC_LEVEL
· 线程在获取互斥体之前通过调用KeEnterCriticalRegion和FsRtlEnterFileSysytem进入了关键代码段
在上面两种情况下的任意一种,用户模式和内核模式的APC提交已经被禁止了。
一个驱动程序应该按照下面的步骤使用快速互斥体:
1 从非换页内存当中分配一个结构体FAST_MUTEX
2 初始化这个快速互斥体的结构体
3 在进入保护区域之前,调用ExAcquireFastMutex,ExAcquireFastUnsafe或者ExTryToAcquireFastMutex
4 执行受保护的操作
5 通过调用ExReleaseFastMutex或者ExReleaseFastMutexUnsafe释放快速互斥体
快速互斥体具有下列局限性:
1 快速互斥体不能够递归获取,这样做的结果是导致一个死锁
2 持有快速互斥体的驱动代码运行在APC_LEVEL级别。因此,在持有快速互斥体的代码中不能调用只能在PASSIVE_LEVEL级别可调用的例程,比如IoBuildDeviceIoControlRequest
3 快速互斥体不是系统内核分发对象。因此,一个驱动不能够利用KeWaitForMultipleObjects例程来等待快速互斥体。
操纵系统定义了一些内核分发对象,这些对象提供不同类型的同步机制。内核调度机制相对简单,并且能够在PASSIVE_LEVEL级别进行申请。
Kernel mutex
提供在PASSIVE_LEVEL或者APC_LEVEL专享访问
在 IRQL &=APC_LEVEL级别等待
提供驱动程序下的同步,能够用于和用户模式的应用程序同步can be used to synchronize with user-mode applications.
在IRQL&=APC_LEVEL级别等待,在IRQL&=DISPATCH_LEVEL设置
保护一组同类的数据
在IRQL&=APC_LEVEL等待,在IRQL&=DISPATCH_LEVEL.
在一个绝对或者绝对的定时时间间隔内提供通知或者同步定时器
在IRQL &=APC_LEVEL等待,在IRQL &=DISPATCH_LEVEL.
Threads, processes, and files
在文件、线程和进程创建或者文件IO结束,线程和进程终止的时候进行同步
在IRQL &=APC_LEVEL等待
一个驱动程序将内核调度对象作为参数传递给KeWaitForSingleObject或者KeWaiForMultipleObject例程。通这两个例程,驱动程序能够等待超时或者直到内核分发对象被设置。
内核分发数据库通过内核分发对象的名称管理所有内核调度对象。为了访问这些对象,系统必须提升IRQL到DISPATCH_LEVEL,并且获取系统范围内的分发数据库的自旋锁。分发锁的获取很频繁,大多数情况下系统需要等待锁就绪,因此,驱动代码应该优先考虑使用快速互斥体而不是分发对象。
内核分发对象有一个公有头部(DISPATCHER_HEADER),但是每一种类型的分发对象都有自己特有的初始化和释放例程。内核分发对象必须在非换页内存当中分配。驱动代码能够通过句柄或者指针管理内核分发对象。如果驱动通过句柄管理内核分发对象,但是这个驱动程序在任何线程上下文运行,那么驱动必须设置OBJ_KERNEL_HANDLE属性来防止用户模式下的访问。驱动代码通常利用内核分发对象来等待一个同步IO操作的结果。最顶层的驱动创建并且发送IRP给底层的驱动,然后在发出IO请求的线程上下文等待。更底层的驱动有可能需要在PASSIVE_LEVEL和APC_LEVEL级别的任意线程上下文等待同步执行。
内核分发对象有两种状态,激发状态和非激发状态。激发状态表明当前分发对象能够被获取,一个激发状态的对象还没有被任何线程锁获取,而一个非激发状态的对象则被一个或多个线程所获取。分发对象的初始激发状态和分发对象的类型有关,比如内核互斥体在初始化的时候立刻就被激发,而事件对象则需要手动调用KeSetEvent例程来激发。
一个线程能够在低于等于DISPATCH_LEVEL级别激发内核分发对象,但是仅仅能够在低于等于APC_LEVEL级别的情况下等待这个内核分发对象。也就是说驱动程序不能在IoCompletion,StartIo或者其他的延迟调用例程当中等待分发对象。最高层的驱动程序在他的读写例程当中等待分发对象,而底层的驱动程序不能在读写例程当中等待分发对象,因为他们的读写例程能够在DISPATCH_LEVEL被调用。
然而,如果一个线程不要等待分发对象的话,能够在DISPATCH_LEVEL级别获取一个内核分发对象。这一功能通过给KeWaitForSingleObject或者KeWaitMultipleObjects传递超时值为0。这种特性可用于测试一个对象是否被激发。比如,一个DPC例程和其他例程同步的时候,可能需要测试对象是否被激发。如果对象被激发则DPC处理这个任务,否则做其他的任务,然后将这个原始任务进行排队。因为work item是在PASSIVE_LEVEL当中执行,可以在work item当中等待相应的分发对象。
KeWaitForSingleObject和KeWaitForMultipleObjects函数的参数Alterts和WaitMode决定在线程等待的时候系统怎样处理用户模式下的APC。
Value of Alertable and WaitMode parameters
kernel-mode APC
kernel-mode APC
User-mode APC
Terminate wait?
Deliver and run APC?
Terminate wait?
Deliver and run APC?
Terminate wait?
Deliver and run APC?
Alertable = TRUE
WaitMode = UserMode
If (A*), then Yes
If (B**), then Yes
Yes, after thread returns to user mode
Alertable = TRUE
WaitMode = KernelMode
If (A), then Yes
If (B), then Yes
Alertable = FALSE
WaitMode = UserMode
If (A), then Yes
If (B), then Yes
No (with exceptions, such as CTRL+C to terminate)
Alertable = FALSE
WaitMode = KernelMode
If (A), then Yes
If (B), then Yes
*A: IRQL & APC_LEVEL.
**B: IRQL & APC_LEVEL,线程既不在APC_LEVEL也不在关键代码段
当一个线程从系统模式的切换到用户模式的时候,系统会提交大多数用户模式的APC。用户模式APC不会中断用户模式下的代码,在一个应用程序为一个线程排队用户模式的APC之后,这个应用程序通过调用等待函数并传递参数Alterable为TRUE会引起系统提交APC。
当驱动程序调用KeWaitForSingleObject或者KeWaitForMultipleObjects,并且设置传参数Alterable为TRUE同时WaitMode为UserMode。那么等待将会返回STATUS_USER_APC或者STATUS_ALTERTED,只要存在悬挂的用户模式APC。
驱动程序调用KeWaitForXxx例程不应该传递Alterable参数为TRUE并同时设置WaitMode为UserMode。除非应用程序明确要求驱动程序在等待期间提交用户模式下的APC。
当一个驱动程序调用KeWaitForSingleObject或者KeWaitForMultipleObjects,参数WaitMode被设为UserMode,而Alterable设为FALSE。那么等待将会在线程被终止的时候返回STATUS_USER_APC。然而驱动程序必须在PASSIVE_LEVEL级别而不能在关键代码区域等待。
WaitMode同样也可以决定线程的内核堆栈在等待的时候是否能够被换页出去。当等待模式是UserMode的时候,系统将内核模式的堆栈换页出去。当正在等待的驱动是堆栈上的唯一的驱动,在UserMode下等待才是安全的。如果一个或者多个驱动在堆栈上,这些驱动当中的更新堆栈变量操作可能导致缺页错误。
windows在\\kernelObject对象目录当中定义了一些标准的事件对象。KeSetEvent函数有三个参数:第一个参数是一个将要被激发的事件指针,第二个是事件激发之后想要获取的优先级提升,第三个参数是一个Wait布尔值。当Wait布尔值为TRUE的时候表示线程在KeSetEvenet之后立刻就会调用KeWaitXxx例程。
一般情况下,驱动程序调用KeSetEvent——设置Wait为FALSE,当Wait设置为FALSE的时候,KeSetEvent提升IRQL到DISPATCH_LEVEL,获取分发锁,修改事件对象的激发状态,激活任意等待的线程,解锁分发对象数据库,降低IRQL到原来的数值,然后返回。然而,当Wait为TRUE的时候,KeSetEvnet不释放分发锁或者降低IRQL。这个优化能够避免不必要的上下文切换,因为调用者已经在一个原子操作当中激发了这个事件。如果一个驱动程序使用了这个特性,那么它必须在小于DISPATCH_LEVEL级别下调用KeSetEvent,并且保证不在任意线程上下文当中。一个类似生产者和消费者场景下的驱动历程可能使用这个特性。这种驱动通常以下列方式和事件进行交互,生产者驱动历程激发第一个事件通知应可以发送数据。然后立刻等待第二个事件被另一个线程激发,第二个线程设置第二个事件通知数据已经收到,并且做好接收更多数据的准备。驱动应该使用这些特性仅仅在请求IO的线程的上下文当中,一个驱动应该避免阻塞一个不相关的线程。
一个通知事件唤醒任何在等待的线程,并且保持激发状态,直到显式调用KeResetEvent。在win32 API当中通知事件被称作手工重置事件。驱动程序通常用通知事件等待IRP的完成。比如,一个驱动可能通过IoBuildDeviceIoControlRequest发送IO控制代码给设备对战当中的更底层的驱动。这个例程的一个参数就是一个事件对象的指针。在驱动例程创建并且发送IRP之后,驱动在这个事件对象上面等待。当IRP完成,IO管理器激发这个事件,这个事件保持激发状态直到调用KeResetEvent。
同步事件也被称为自动重置事件,在唤醒线之后立刻返回到非激发状态,驱动程序较少使用同步事件。一个需要长时间初始化的设备驱动程序可能在StartDevice例程上等待同步事件来确保设备被完整初始化。在设备被中断并且任何在DISPATCH_LEVEL级别的处理都结束时,驱动的DpcForIsr例程激发这个事件。控制权然后转移到StartDevice例程,在这里可能继续初始化驱动和设备。同样的,一个驱动能够在同步事件上等待DispatchPnp例程来确保IO已经在停止或者移除设备之前完成。
一种在内核驱动和用户模式应用程序下协作的方式。在驱动当中:
1 定义一个私有的IO控制代码,应用程序可以通过这个IO控制代码传递一个事件
2 提供一个DispatchDeviceControl例程来处理私有的IRP_MJ_DEVICE_CONTROL下面的IOCTL请求
3 通过调用ObReferenceObjectByHandle来获取有效的事件指针,在DesiredAccess参数当中,指定SYNCHRONIZE权限,而在ObjectType参数当中,指定*ExEventObjectType
4 通过KeSetEvent激发事件,通过调用KeResetEvent来重置通知事件
5 调用ObDereferenceObject来释放句柄,当句柄值不再需要的时候
在应用程序当中:
1 通过调用CreateEvent函数创建一个有名事件
2 通过调用DeviceIoControl函数,将句柄传递给驱动程序
3 通过调用调用WaitForSingleObject或者WaitForMultipleObjects等待内核模式下的驱动程序激发这个事件
4 在退出之前调用CloseHandle删除这个事件句柄
内核互斥体是一种可以用于分页内存的同步技术,同时它还可以用于需要相对较长的执行时间的情况下。驱动程序能够在小于等于APC_LEVEL的情况下使用。
内核互斥体依赖于线程上下文,通常是运行在请求线程上下文的最高层次的驱动例程使用内核互斥体。一个获取互斥体的线程应该在同一个线程上下文当中释放互斥体。
内核互斥体和快速互斥体的不同之处表现在以下几个方面:
1 内核互斥体能够被递归获取,而快速互斥体不行
2 内核互斥体通过调用KeWaitForSingleObject,KeWaitForMultipleObjects以及KeWaitForMutexObject。而快速互斥体通过ExAcquireFastMutex、ExTryToAcquireFastMutex,和ExAcquireFastMutexUnsafe
3 内核互斥体的获取需要使用系统范围内的锁。因此,他们效率相对快速互斥体低
为了使用内核互斥体,驱动程序必须按照下列步骤来执行:
1 从一个非换页内存当中分配一个KMUTEX数据结构
2 传递之前分配的数据结构指针给KeInitializeMutex函数进行互斥体初始化
3 通过KeWaitForSingleObject、KeWaitForMultipleObjects或者KeWaitForMutexObject等待互斥体
4 执行保护的操作
5 通过KeReleaseMutex释放互斥体
系统在初始化内核互斥体的时候已经让这个互斥体处于激发状态,第一个等待互斥体的线程将获取互斥体。驱动历程应该总是定义KernelMode,当他们等待内核互斥体的时候。在内核模式下等待将阻止内核模式堆栈被换页出去,并且禁止用户模式下的APC和普通内核模式的APC提交,因此可以阻止线程被终止和被打断。而内核模式下的特殊APC仍然可以提交。在北部,获取一个内核模式的互斥体需要调用KeEnterCriticalRegion。如果这个获取互斥体的线程运行在PASSIVE_LEVEL,会禁止普通内核模式的APC提交直到内核释放互斥体。当线程已经在APC_LEVEL的时候,进入关键代码段不起作用,因为此时普通内核模式APC已经禁止了。
一个持有互斥体的线程在转入到用户模式之前必须释放互斥体。如果再切换到用户模式过程当中线程持有互斥体,那么系统将崩溃。
KeReleaseMutex和KeSetEvent有同样意义的Wait参数。一个线程递归获取互斥体必须释放互斥体同样的次数,操作系统不会使得互斥体处于激发状态或者调用KeLeaveCriticalRegion直到所有的申请都释放。
信号量的处理和互斥体类似,但是当释放次数超过信号量的上限的时候,系统会跳出异常,这一点和事件不同——激发状态的事件可以进行设置。另外信号量的释放参数Wait和互斥体的处理一样,线程能够通过KeReadStateSemphore来测试信号量是否被激发。
定时器有通知和同步两种,一个驱动程序通过KeInitializeTimer创建一个通知定时器,或者通过调用KeInitializeTimer创建两种定时器之一。相对时间通过计算机器运行时间并且不受系统时钟的影响。相对时间包括系统休眠的时间,当系统被唤醒,系统调整机器时间来包含计算机休眠的时间。结果是一旦系统唤醒许多定时器同时超时。
当一个通知定时器超时的时候,所有的等待线程都被激发。定时器保持激发状态直到一个线程调用KeSetTimer显式重置定时器。当一个同步定时器超时,仅仅一个线程被激发。同时系统立刻重置定时器到非激发状态。
驱动能够在小于等于APC_LEVEL级别上等待IRQL,或定义一个CustomTimerDpc例程被调用当超时的时候。这个历程能够取代驱动程序创建的协助线程来执行相应的操作。这个历程同样可以用于超时来自DISPATCH_LEVEL级别的请求。
为了使用定时器,驱动应该遵从下面的步骤:
1 从非换页内存当中分配一个结构体
2 调用或者创建并且初始化这个定时器
3 为了绑定一个到定时器上,还需要额外的调用初始化一个对象,并注册
4 通过或者设置定时器,指定超时时限。超时的时候排队例程,包括可选的参数
5 调用或者来等待定时器
6 如果需要在定时器超时之前取消定时器,可以调用
7 为了在超时之后重置定时器,可以调用
线程、进程以及文件也是内核调度对象。驱动程序能够用来同步进程、线程或者文件。另外,驱动程序能够监听到新线程或者进程的创建。为了在线程、进程或者文件对象上面等待,内核模式的驱动必须定义等待模式。当线程或者进程终止、文件操作完成的时候,等待会被唤醒。
一个文件IO操作完成的时候系统会自动激发一个内置在文件对象当中的事件。这个事件是同步事件,也就是说这个事件会在等待线程被通知之后自动重置。特定线程的同步只有在驱动创建一个辅助线程的时候才非常有用。大多数驱动例程,除了最高层次的驱动分发例程,都是在任意线程的环境上下文当中调用。因此,与当前线程环境上下文的同步是没有意义的。,当一个系统范围的线程或者进程被创建或者删除的时候,驱动能够获得通知。为了得到通知,驱动可以通过函数PsSetCreateProcessNotifyRoutine或者设置一个回调例程。设置回调例程的驱动在系统关闭之前不能退出。
通过使用可执行资源,驱动能够实现读写锁。可执行资源设计用于专有写而可以共享读。执行资源不需要获取系统分发数据库的自旋锁,因此速度比较快。一个运行在小雨等于APC_LEVEL级别的线程代码可以使用执行资源。一个执行资源是一个结构体,这个结构体必须从非分页内存当中分配。一个必须是自动对齐的,不过结构体是不完全公开的。
Table 9. Executive Resource Acquisition Routines
ExAcquireResourceSharedLite
如果资源没有被专享访问并且没有任何线程在等待专享访问,或者请求的线程已经包含共享或者独享访问权限
ExAcquireResourceExclusiveLite
如果资源没有被独享或者共享访问
ExAcquireSharedStarveExclusive
如果资源没有被专享访问并且没有任何线程在等待专享访问,或者请求的线程已经包含共享或者独享访问权限,等待独享权限的线程将继续等待
ExAcquireSharedWaitForExclusive
如果资源没有被专享访问并且没有任何线程在等待专享访问,如果请求线程有权限访问这个资源但是其他线程正在等待的时候,递归获取权限需要先释放权限然后和其他的线程竞争
线程可以将独享特权变换到共享特权,而不能将共享特权变换为专有特权。ExConvertExclusiveToSharedLite例程可以将线程的专享特权转换为共享特权。一个线程可以代替其他线程来释放资源通过调用ExReleaseResourceForThread。文件系统驱动使用这个例程,当一个线程获取了资源然后发送这个请求给其他线程的时候。在这种情况下,线程完成请求可以调用这个例程代替第一个线程来释放资源。
驱动程序能够通过,和来判断资源是否被任何线程占用。另外,驱动还能够通过或者来获取等待的线程数目。中断一个拥有互斥资源所的线程可能引起死锁。
使用执行资源的要点:
1 从非换页内存当中分配一个结构体
2 在或者例程当中调用初始化资源
3 在获取资源之前禁止普通内核模式的,驱动程序通过调用,文件系统调用。如果驱动例程运行在系统线程,可以不必要禁止,因为系统线程不会被中断。
4 通过调用表当中的线程来获取资源
5 执行代码
6 通过调用释放资源
7 重新使能普通内核模式的提交,通过调用或者
所有的资源获取例程返回一个指示获取成功与否的布尔值。执行资源能够被递归获取。比如,一个文件系统可能通过映射文件到虚拟内存当中的保留区域来实现。如果这个过程引起页错误,那么操作系统将产生额外的请求。这些请求同样会送到这个文件系统驱动并且打断驱动处理的过程。为了处理额外的请求,文件系统必须递归的获取在过程当中使用的锁。
回调对象仅仅能够在内核模式下使用,他们不能与用户模式下的应用程序共享。驱动程序通过调用ExCreateCallBack来创建一个回调对象。用户通过来注册一个回调例程。当驱动程序指定的回调条件发生的时候,驱动程序调用来通知回调例程运行。能够在小于等于级别上被调用。而回调例程在在通知线程的环境上下文当中和例程相同的上面运行。如果驱动注册一个回调例程,确定你知道通知发生的,并且适合回调例程运行。
当使用用户自定义的锁的时候,需要牢记编译器可能会对指令进行位置调整,这就需要我们使用内存栅栏来防止这种优化。内存栅栏是一个处理器指令防止读写操作的指令顺序调整。和例程和或例程都可以插入内存栅栏防止重排序。
多种同步机制的使用
比如,一个驱动程序可能包含两个需要在级别保护的列表,当驱动例程需要讲一个列表当中的数据转移到另一个列表的时候,仅仅只是用一个自旋锁是不够的。但代码需要访问两个链表的时候,就需要两个锁。获取锁的顺序可以通过简历锁层次得到。锁的层次通过他们的递增形式排列。首先列出需要最低的锁,然后列出第二低的锁…当代码需要一次获取多个锁的时候,它应该以递增的方式获取。当存在相同的时候,优先获取访问次数比较频繁的锁。
一下步骤可以防止死锁:
1 绝对不要在任何大于等于级别被调用的驱动例程当中等待一个内核分发对象,在大于小于级别调用的例程包括例程和存储驱动的分发例程以及的驱动
2 禁止正常内核模式的调用,在任何可执行资源获取之前,或者调用等待事件、信号量、定时器、线程、文件对象或者进程之前
3 使用驱动验证程序的死锁检测选项来发现潜在的死锁
4 总是通过锁层次来编码
在级别的关键代码段外使用锁有可能导致攻击,当一个持有锁的驱动被中断,这是因为排队一个普通内核模式的来中断线程,即使驱动定义了等待,普通内核模式仍然会提交,当下面所有条件成立的时候:
1 目标线程运行在
2 目标线程没有运行
3 目标线程不在关键代码段
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?}

我要回帖

更多关于 内存分页机制 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信