CoreCLR系列随笔 之ClrJit项目之alloc.cpp文件分析(1)
首先声明,自己对CLR了解得不多,只是个人爱好,可能有错误,请指出,文件源码如下(可能不是最新的)

开始吧,alloc.cpp是在ClrJit项目当中的,其中alloc.cpp会引用jitpch.h这个头文件。首先会定义:_MSC_VER 这个活动预处理块。如下图,关于,pragma hdrstop 的介绍可以点击链接去看看,这里就不再赘述。
这里有一个空方法叫做:
我们碰到这种情况怎么去学习呢,很多人就被卡住了,其实这个问题解决方法很简单,可以利用VS的全局搜索功能。
很遗憾,没有地方用到,这只能证明2点,第一,这个函数可能是给外部调用的,第二,这可能是内部机制的一部分,但是我并不了解,从名字上看,是分配器的相关功能,请自行补脑,我不做解释,怕误导你们。
下面继续看代码:关于__cdecl 请看这里的介绍,如果有不懂的人,这个函数顾名思义,是debug调试停止的时候触发的函数,其中带2个参数,第一个是指针类型的char,你可以理解为string 常量(对应C#中的readonly string),第二个参数是任意类型的任意参数,即你可以传入多个参数(类似C#中的params 参数)。
要研究上面的代码,你必须了解va_list这个东西,下面是va_list的定义:下面的翻译成中文就是,如果没有定义_VA_LIST_DEFINED,那么定义_VA_LIST_DEFINED,如果定义了_M_CEE_PURE 把va_list定义为ArgIterator,否则是char类型的指针变量。这里我们是第二种情况,第一种情况你就当空气,因为我也不太懂。另外关于_M_CEE_PURE的解释我在网上并没有找到,只知道在Math.h里面有过类似的定义,有知道的小伙伴可以提示下我。
好吧,现在我们知道了,va_list其实就是一个char指针,类似C#当中的String,简单点理解。下面我们来分析下下面的代码。
其中va_start的源码如下,它来自于stdarg.h文件,这是Visual studio的一个内置文件,:
我们 来看看__crt_va_start的真容:它分为__vcrt_va_start_verify_argument_type和__crt_va_start_a
下面是上面所提到的2种类型的一些源码,可以看到,__vcrt_va_start_verify_argument_type只是做了一个类似“断言”的功能,否就抛出异常;而第二个函数__va_start里的真实代码如下图所示,有四种不同的表现形式,分布在4个.h头文件中,其中4种不同的情况下去调用。分别有_M_X64,_M_ARM64和_M_CEE_PURE || (defined _M_CEE && !defined _M_ARM && !defined _M_ARM64) 和_M_ARM,这其中牵扯到和汇编的相关知识,有兴趣的可以先学学汇编去.
中间的代码我就跳过了,都是一些print语句,打印日志的,其中va_end的真实代码为,其实很好理解,就是清空,因为C++中没有垃圾回收机制。
这个方法中的最后一个方法是:BreakIfDebuggerPresent,它的代码如下,它的意思是至少执行一次,如果有一只处于调试状态,它会进行判断,如果为0,FALSE那么继续循环,
其中IsDebuggerPresent和DebugBreak为windows的API函数,应该类似于WINDOWS进程挂起,其实我也没研究过。
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
WINBASEAPI
VOID
WINAPI
DebugBreak(
VOID
);
#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */
下面有一个有趣的东西,作者问了个问题,这个常量是不是应该更大?欢迎各位来讨论
其中size_t的源码如下,其实就是类似于blockStop的一个定义域吧,如果是64位windows那么就是无符号的long,否则为无符号的int类型。这个很好理解吧。
下面初始化了变量:
然后是初始化nra:

我们先来看看这个方法的声明,其中后2个参数有默认值:
我们先来看看IEEMemoryManager这个东西是何方神圣,先放整体代码,大家先不要看整体代码,怕搞不懂,我一句句来剖析:

首先先决条件是,定义了C++而且没有定义CINTERFACE,CINTERFACE是什么我暂时还不太懂,不过没关系,这并不妨碍我学习源码。
从下面的我们 可以得到一个结论,它是一个类,并且它被IUnknown所继承。
1 | IEEMemoryManager : public IUnknown |
它有几个方法分别为:
- ClrVirtualAlloc
- ClrVirtualFree
- ClrVirtualQuery
- ClrVirtualProtect
- ClrGetProcessHeap
- ClrHeapCreate
- ClrHeapDestroy
- ClrHeapAlloc
- ClrHeapFree
- ClrHeapValidate
- ClrGetProcessExecutableHeap
英语好的同学,理解这些东西应该不是很难,其实你也没必要深入研究,反正认为:这货就是在CLR生命周期中一些必须做的“事情”而已。下面我们再回到nraInit方法里,下面的应该不用我多说了,初始化而已。
如果处于调试模式,还会走如下代码,会从CLR的配置文件中(我也不知道在哪),寻找配置。
下面是INTERNAL_InjectFault的源码。
其中不光是我困惑,看来大大们也困惑,大大还在犹豫是否要“注入”失败”的标志位,具体原因当然我也不知道。
如果参数里面preAlloc为true.
下面我来分析下第一个函数setErrorTrap,其实这只是初始化JIT的一个异常处理机制的部分,从下面的代码中可以窥探下JIT的一些东西。代码没有截全。有兴趣的可以搜索下error.h文件,本文不再分析。
下面让我们看下nraAllocNewPage这个方法:

我只介绍几个重要的地方,第一个为norls_pagdesc这个结构体,它的访问权限是private。个人感觉这个东西是做链表功能用的,具体作用不详。
其中因为传入参数为0,所以真实大小为sizeof(norls_pagdesc)即上面结构体的成员变量的大小之和。
如果出现参数异常,那么执行NOMEM,通知异常发生。
如果是到了链表表尾,其实作者很怀疑是否真正是这样的?
我们先来看看nraFreeNext 的介绍,作者说到:如果不为0,那么永远指向LAST。
好,现在回到alloc.cpp文件中,它的主要作用是为了更新新的使用过的空间大小。
下面的代码是把norls_allocator里的nraPageSize赋值给sizePage以保障空间足够。
如果sizPage小于真实大小,那么把真实大小赋值给sizPage,这是什么原因造成的,其实我觉得这么写不太合理,作者也许做了一个硬编码吧。
下面我们来看看,如果nraDirectAlloc是FALSE,那么执行如下代码,我们先来看看,sizePage是先+了DEFAULT_PAGE_SIZE 个单位,然后按位与了DEFAULT_PAGE_SIZE-1的反码个单位。如果你还不清楚什么是按位计算,那么请你复习下大学基础知识,本文不再做深入讨论。
下面我们再来看看nraDirectAlloc这个方法吧。其实就是返回一个BOOL类型的东西,至于这个方法的整体作用,可以看下面的英文注释。和上面的Config一样,都是做了配置。
下面是分配新的page.
方法如下:如果满足nraDirectAlloc==true,那么执行HeapAlloc方法,其中参数GetProcessHeap和HeapAlloc(堆分配)为调用WINDOW API。有兴趣的可以自己看看源码。
如果不满足nraDirectAlloc,那庅调用DbgNew方法,源码为:
我把ClrAllocInProcessHeap的代码贴一下,有兴趣的可以自己去看看。我就不多做解释了。

下面的代码就是做一些基本的赋值和检查。
下面就是把新的page追加到list的后端。
最后重新设置一下next和last指针,总之这个是个公共方法,只是nraInit里面只用到了为0的情况: pThis->nraAllocNewPage(0);
回到init函数然后执行如下,结束nraInit方法
写完了,知道写得不太好,请指出错误,轻喷。晚安。
__EOF__
作 者:ღKawaii
出 处:https://www.cnblogs.com/kmsfan/p/6938692.html
关于博主:一个普通的小码农,为了梦想奋斗
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!

出处:http://www.cnblogs.com/kmsfan
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
欢迎大家加入KMSFan之家,以及访问我的优酷空间!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?