Inside GAC
--浅析GAC目录架构在不同CPU平台上的工作原理
By Alva Chien
File Version: 1.0
Released: 2007.5.30
GAC是用来存放可以被大部分Assembly引用的Assembly的地方,它的地址是:%SystemRoot@/Assembly,GAC中只能存放Strongly named assembly。.NET Framework SDK提供了一个GACUtil.exe的工具来操作GAC。
GAC 的大致结构
一个比较完整的.NET Framework 2.0的GAC下子目录的结构如下所示:
GAC
GAC_32
GAC_MSIL
GAC_64
NativeImages1_v1.1.4322
NativeImages_v2.0.50727_32
NativeImages_v2.0.50727_64
其中:
GAC是用于存放基于.NET Framework 1.0/1.1创建的Assemblies,这些Assemblies只能运行在32-bit 地址空间。
GAC_MSIL用于存放基于.NET Framework 2.0上的无特定CPU指定的Assemblies,这些assemblies可以运行在32-bit地址空间或者64-bit地址空间。
GAC_32也是用于存放基于.NET Framework 2.0创建的基于x86架构的Assemblies,因为这些Assemblies可以拥有32-bit的native CPU代码,所以只能运行在32-bit空间,可以直接在32-bit系统上运行或者使用WOW64技术在64-bit的OS系统上运行。
GAC_64用于存放.NET Framework 2.0上创建的基于x64或者IA64的Assemblies,这些Assemblies可能含有x64或者IA64的Native Code,所以他们只能运行在64-bit地址空间上。这个目录在32-bit的OS上不存在。值得注意的是,GAC_64中的Assemblies是根据系统架构决定,在x64系统上,该目录中的Assemblies就必须是基于x64的,IA64的Assemblies无法被装入,反之亦然。
以NativeImages开头的目录存放的通过NGen.exe编译assembly生成的基于当前平台的非managed代码,这个目录不需要Strongly named Assemblies,后面的数字对应了版本号。而基于.NET Framework 2.0的两个NativeImages目录名称最后的两个字符’64’和’ 32’则代表了对应的平台。
.NET Framework SDK安装的两份系统Assemblies
众所周知,.NET Framework SDK在安装时,会安装两份系统自带的Assemblies,一份存在GAC中,另外一份则存放于编译器和CLR所在的目录(%SystemRoot%/Microsoft.NET/Framework/versio_nnumber)。安装在CLR目录的那一份是跟当前系统相关的,也就是说,x64位的系统上,CLR目录中的Assemblies都是基于平台无关或者基于x64的。
编译一个引用了其他Assembly的Assembly时,编译器查找引用assembly的顺序是:
1) 当前工作目录
2) 编译器 (CSC.exe) 所在的目录
3) 编译器的/lib编译开关指定的目录
4) 当前系统环境变量LIB指定的目录
之所以会存在两份系统Assembly以及这样的查找顺序的根本原因就在于GAC内部的复杂结构,这样引用系统Assemblies,编译器可以直接从CLR所在目录读取对应的Assembly。
一台机器容许存在同一个系统Assembly的多个版本 ,但是仅限于可以运行的多个版本,例如,x64平台上完全存在同一个系统Assembly的x86和x64两个版本(其他的属性:比如名称,版本,Culture, Public key token都完全一致,GAC将这两个Assembly分别放在GAC_32和GAC_64中),但是x64平台不会存在IA64的版本,同样x86平台无法存在x64或者IA64版本。
因为CLR所在的目录只包含跟机器平台相吻合的系统Assembly版本,所以编译器而不管需要编译什么平台的Assembly时都将读取CLR中的版本 (如x64版本),这样做的原因在于编译器只需要Assembly的Metadata部分 (包括Visual Studio的Intelligent也只需要Metadata) ,而同一个Assembly的不同平台版本之间只有IL不同且Metadata完全相同,所以完全不影响编译,真正运行或调试时CLR会从GAC中装载正确的平台版本。
GAC详细结构
.NET Framework安装时会同时安装一个Windows Explorer扩展, SHFusion.dll,用于在Explorer中打开%SystemRoot%Assembly文件夹时显示一个可理解并User-kind的界面。
最简单而有效查看GAC内部文件结构的方法是使用cmd.exe。以GAC_MSIL为例子,其下面包含了大量的子目录:
.
..
Accessibility
AspNetMMCExt
CppCodeProvider
Cscompmgd
IEExecRemote
IEHost
IIEHost
Microsoft.AnalysisServices
Microsoft.AnalysisServices.AdomdClient
Microsoft.AnalysisServices.DeploymentEngine
……
其实这里子目录就是Assembly的名字,以System.Xml为例,其下只有一个子目录:
2.0.0.0_b77a5c561934e009
这个子目录的命名方式就是“版本号_PublicKeyToken”。这样即使两个Assembly不幸同名的话,也因为其版本号和PublicKeyToken(肯定不同)的不同而存放在不同的文件夹下。
总结
下表是.NET Framework 2.0安装的示意:
CPU |
OS |
详细 |
x86 |
32-bit OS |
主要的Assembly安装在GAC_MSIL中,另外一部分会安装在GAC_32中。在CLR目录上安装一份CPU无关和x86平台的Assemblies。 |
x64 |
32-bit OS (事实上这里的x64就是x86) |
主要的Assembly安装在GAC_MSIL中,另外一部分会安装在GAC_32中。在CLR目录上安装一份CPU无关和x64平台的Assemblies。 |
64-bit OS |
主要的Assembly安装在GAC_MSIL中,另外一部分会安装在GAC_64中,GAC_64中只能存放x64结构的Assemblies。GAC_32可能会被安装。在CLR目录上安装一份CPU无关和x64平台的Assemblies。 |
|
IA64 |
64-bit OS |
主要的Assembly安装在GAC_MSIL中,另外一部分会安装在GAC_64中,GAC_64中只能存放IA64结构的Assemblies。GAC_32可能会被安装。在CLR目录上安装一份CPU无关和IA64平台的Assemblies。 |