喜欢美食, 旅行..
分类: WINDOWS
2009-12-17 15:04:25
对进程虚拟地址空间的讨论至此告一段落,本节我们将会讨论另一个重要的话题——数据对齐。相较于操作系统的内存体系结构,数据对齐更多的是CPU体系结构的一部分。
只有当访问已对齐的数据时,CPU的执行效率才最高。把数据的地址模除数据的大小,如果结果为0,那么数据就是对齐的。例如,一个WORD值的起始地址应该能被2整除,一个DWORD值的起始地址应该能被4整除,以此类推。如果CPU要访问的数据没有对齐,那么会有两种可能。第一种可能是CPU会引发一个异常,另一种可能是CPU会通过多次访问已对齐的内存,来取得整个错位数据。
下面的代码访问了错位数据:
VOID SomeFunc(PVOID pvDataBuffer) {
// The first byte in the buffer is some bye of information
char c = * (PBYTE) pvDataBuffer;
// Increment past the first byte in the buffer
pvDataBuffer = (PVOID)((PBYTE) pvDataBuffer + 1);
// Bytes 2-5 contain a double-word value
DWORD dw = * (DWORD *) pvDataBuffer;
// The line above raise a data misalignment exception on some CPUs
…
显然,如果CPU多次访问内存,那么肯定会影响到应用程序的性能。与访问已对齐的数据相比,系统在最好的情况下也需要花两倍的时间来访问错位数据,而且情况可能还会更糟。为了得到最佳的应用程序性能,我们在编写代码时应该尽量让数据对齐。
下面让我们来仔细看一下x86 CPU是如何处理数据对齐的。x86 CPU的EFLAGS寄存器内有一个特殊的标志位,它被称为AC(alignment check,对齐检查)标志。在默认的情况下,这个标志位在第一次给CPU通电时被清零。如果该标志为零,那么CPU会自动执行必要的操作来访问错位数据。但是,如果该标志被设为1,那么一旦程序试图访问错位数据,CPU就会触发INT 17H中断。由于x86版本的Windows从来不会改变这个标志,因此当应用程序在x86处理器上运行时,绝对不会发生数据错位的异常。当应用程序在AMD x86-64处理器上运行时,会得到相同的结果。这是因为在默认的情况下,CPU处理了数据错位的错误。
现在让我们来看一下IA-64 CPU。IA-64 CPU不能自动处理数据错位的错误。当任何代码要访问错位数据时,CPU会通知操作系统。Windows然后决定到底是应该抛出数据错位异常,还是应该没有任何提示地执行额外的指令来修正错误并让代码继续执行。在默认的情况下,IA-64版本的Windows操作系统会自动将数据错位的错误转换成一个EXCEPTION_DATATYPE_MISALIGNMENT异常。但是,我们可以改变这种行为。我们可以通过调用SetErrorMode函数,让操作系统为进程中的所有线程自动修正数据错位的错误:
UINT SetErrorMode(UINT fuErrorMode);
我们这里应该使用SEM_NOALIGNMENTFAULTEXCEPT标志。只要设置了该标志,系统就会自动修正对错位数据的访问。一旦改变了该标志,就无法在进程的生命周期内再次改变它。
注意,改变这个标志会影响到进程中所有的线程。换句话说,改变这个标志不会影响任何其他进程中的线程。另外值得注意的一点是,进程的错误模式标志会被子进程继承。因此,如果不希望让某个错误模式标志影响到子进程的话,可以在调用CreateProcess函数前将该标志临时清零(虽然我们通常不会对SEM_NOALIGNMENTFAULTEXCEPT这样做,因为该标志一旦设置之后就无法被清除)。
当然,我们可以在调用SetErrorMode时不管当前运行的CPU平台,而总是传递SEM_NOALIGNMENTFAULTEXCEPT标志。但是,这样做的结果并不总是相同的。在x86和x64系统上,该标志始终都是开启的而且无法被关闭。我们可以用Windows Reliability and Performance Monitor(可靠性和性能监视器)工具来查看系统每秒修正错位数据的次数。下图显示了如何在Add Counters(添加计数器)对话框中将该计数器加到图表中。
这个计数器实际上显示的是每秒钟内CPU通知操作系统发生错位数据访问的次数。如果在x86机器上观察该计数器,我们会发现它始终都是0。这是因为x86 CPU自己对错位数据的访问进行了修正,所以没有通知操作系统。在x86平台下,由于是CPU而不是操作系统对错误进行修正,因此它对性能的影响要优于以软件的方式(Windows操作系统的代码)来对错误进行修正。我们可以看到,只需调用SetErrorMode就足以让应用程序正确运行了。但这个解决方案的效率显然并不是最好的。
IA-64版本的Microsoft Visual C/C++编译器支持一个叫__unaligned的特殊关键字。除了__unaligned修饰符只能用于指针变量之外,我们可以像使用const或volatile修饰符一样来使用它。当通过一个带__unaligned修饰符的指针访问数据时,编译器会认为数据未经对齐,并生成额外的CPU指令以访问数据。下面的代码是前面的代码经过修改后的版本。新版本使用了__unaligned关键字:
VOID SomeFunc(PVOID pvDataBuffer) {
// The first byte in the buffer is some bye of information
char c = * (PBYTE) pvDataBuffer;
// Increment past the first byte in the buffer
pvDataBuffer = (PVOID)((PBYTE) pvDataBuffer + 1);
// Bytes 2-5 contain a double-word value
DWORD dw = * (__unaligned DWORD *) pvDataBuffer;
// The line above causes the compiler to generate additional
// instructions so that several aligned data accesses are
// performed to read the DWORD.
// Note that a data misalignment exception is not raised.
…
与让CPU捕获对错位数据的访问并由操作系统来修正错误相比,让编译器生成额外代码来修正错误的效率仍然要高得多。实际上,如果我们观察Alignment Fixups/sec计数器,会发现通过未经对齐的指针访问数据并没有在图表中起什么作用。需要注意的是,即使在数据已对齐的情况下,编译器也会生成额外的指令,使得代码的执行效率在这种情况下会有所降低。
最后,x86版本的Microsoft Visual C/C++编译器不支持__unaligned关键字。我猜想可能是因为CPU本身修正错误的速度很快,所以Microsoft认为没有必要再支持__unaligned关键字。但这同时意味着x86版本的编译器在遇到__unaligned关键字时会报错。因此,如果打算在创建应用程序时使用同样的源代码,那么最好不要使用__unaligned关键字,而是使用UNALIGNED和UNALIGNED64宏。UNALIGNED*宏在WinNT.h中的定义如下:
#if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) || defined(_M_IA64) ||
defined(_M_AMD64)
#define ALIGNMENT_MACHINE
#define UNALIGNED __unaligned
#if defined(_WIN64)
#define UNALIGNED64 __unaligned
#else
#define UNALIGNED64
#endif
#else
#undef ALIGNMENT_MACHINE
#define UNALIGNED
#define UNALIGNED64
#endif
文章来自《windows核心编程-第五版》
一睹为快~
chinaunix网友2009-12-18 23:44:35
这位兄弟,最近部门在针对合适人选内部推荐,这次机会较好,真诚希望你或者这里的朋友能关注下: ------------------------------------------------------------------------------------------- 全球TOP3通信公司创新项目招募人才(机会较好,待遇从优) 地点:上海 包括但不限于以下方面:(1)嵌入式技术和linux开发(2)编译环境开发(3)应用和业务软件开发(4)浏览器和互联网技术(5)媒体平台:媒体技术和算法(6)通信协议:传输,组网以及协议开发等 招聘范围:中高级技术专家,技术带头人,3年以上工作经验 待遇:从优,只要您有实力,待遇一切可谈 关于我们:提供一个良好的技术和开发环境,提供优越的个人发展空间通道以及完善的培训制度,个人配股激励计划。 若有任何疑问或者兴趣,欢迎邮件:BlankHt@163.com 静候您或者您的朋友回音。