首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

GC终结标记 SuspendEE 是怎么回事

编程知识
2024年08月21日 10:17

一:背景

1. 讲故事

写这篇是起源于训练营里有位朋友提到了一个问题,在 !t -special 输出中有一个 SuspendEE 字样,这个字样在 coreclr 中怎么弄的?输出如下:


0:000> !t -special
ThreadCount:      3
UnstartedThread:  0
BackgroundThread: 2
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                                                            Lock  
 DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1     4ab0 000001CC44E5C490    2a020 Cooperative 0000000000000000:0000000000000000 000001cc44e520d0 -00001 MTA (GC) 
  11    2     19d8 000001CC44E84700    21220 Preemptive  0000000000000000:0000000000000000 000001cc44e520d0 -00001 Ukn (Finalizer) 
  12    3     6668 000001CC44ED4520    2b220 Preemptive  0000000000000000:0000000000000000 000001cc44e520d0 -00001 MTA 

          OSID Special thread type
        0 4ab0 SuspendEE 
       10 3b6c DbgHelper 
       11 19d8 Finalizer 

哈哈,其实我特别能理解,很多人学了高级调试之后好奇心会爆棚,看啥都想探究底层,有一种技术上的重生,这篇我们就好好聊一聊。

二:WinDbg 分析

1. SuspendEE 标记是什么

这个单词全称为 Suspend Engine Execution, 即 冻结执行引擎 ,那冻结执行引擎的入口方法在哪里呢?这个考验着你对GC运作骨架图的认识,在 coreclr 源码中有一个骨架图,简化后如下:


     GarbageCollectGeneration()
     {
         SuspendEE();
         garbage_collect();
         RestartEE();
     }
     
     garbage_collect()
     {
         generation_to_condemn();
         gc1();
     }

上面的 SuspendEE() 即 SOS 中的 SuspendEE 标记的入口函数,接下来我们深入探究下这个方法。

2. SuspendEE 到底做了什么

如果你仔细阅读过 SuspendEE() 方法的源代码,你会发现核心枚举变量是 ThreadType_DynamicSuspendEE,它起到了定乾坤的作用,参考代码如下:


thread_local size_t t_ThreadType;

void ThreadSuspend::SuspendEE(SUSPEND_REASON reason)
{
    // set tls flags for compat with SOS
    ClrFlsSetThreadType(ThreadType_DynamicSuspendEE);
}

void ClrFlsSetThreadType(TlsThreadTypeFlag flag)
{
    t_ThreadType |= flag;

    gCurrentThreadInfo.m_EETlsData = (void**)&t_ThreadType - TlsIdx_ThreadType;
}

enum PredefinedTlsSlots
{
    TlsIdx_ThreadType = 11 // bit flags to indicate special thread's type
};

enum TlsThreadTypeFlag // flag used for thread type in Tls data
{
    ThreadType_DynamicSuspendEE = 0x00000020,
}

从上面的代码中可以看到 t_ThreadType 是一个 C++ 级的线程本地存储,意味着每一个线程都有其备份,同时它也是 SuspendEE 标记的核心来源,如果 m_EETlsData 的第 11号 槽位为 0x20 的时候, SuspendEE 标记就会被成功打下,并且可以通过 gCurrentThreadInfo.m_EETlsData 变量去跟踪来源,有了这么多信息之后,接下来就可以代码验证了。

三:案例验证

1. 一段测试代码

代码非常简单,就是一个简单的手工 GC触发。


    internal class Program
    {
        static void Main(string[] args)
        {
            Debugger.Break();

            GC.Collect();

            Console.ReadLine();
        }
    }

接下来使用 windbg 在入口的 SuspendEE 方法上下断点 bp coreclr!ThreadSuspend::SuspendEE 观察,截图如下:

一旦将 ThreadType_DynamicSuspendEE=0x20 赋值之后,接下来用 windbg 去做个验证。


0:000> x coreclr!*gCurrentThreadInfo*
000001a1`668ee8c0 coreclr!gCurrentThreadInfo = struct ThreadLocalInfo

0:000> dx -id 0,0 -r1 (*((coreclr!ThreadLocalInfo *)0x1a1668ee8c0))
(*((coreclr!ThreadLocalInfo *)0x1a1668ee8c0))                 [Type: ThreadLocalInfo]
    [+0x000] m_pThread        : 0x1a166902e50 [Type: Thread *]
    [+0x008] m_pAppDomain     : 0x1a166948b40 [Type: AppDomain *]
    [+0x010] m_EETlsData      : 0x1a1668ee880 [Type: void * *]

0:000> dp 0x1a1668ee880
000001a1`668ee880  00000000`00000000 00000000`00000000
000001a1`668ee890  00000000`00000000 00000000`00000000
000001a1`668ee8a0  00000000`00000000 00000000`00000000
000001a1`668ee8b0  00000000`00000000 00000000`00000000
000001a1`668ee8c0  000001a1`66902e50 000001a1`66948b40
000001a1`668ee8d0  000001a1`668ee880 00000000`00000020

从上面输出可以看到 000001a1668ee8d0+0x8 地址的内容已经被成功种下,相信这时候 !t -special 也能拿到标记了。


0:000> !t -special
                                                                                                            Lock  
 DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1     640c 000001A166902E50    2a020 Preemptive  000001A16B0094A8:000001A16B00A5B8 000001a166948b40 -00001 MTA (GC) 
  11    2     3e50 000001A16692B2D0    21220 Preemptive  0000000000000000:0000000000000000 000001a166948b40 -00001 Ukn (Finalizer) 
  12    3     6a24 000001A16699F8F0    2b220 Preemptive  0000000000000000:0000000000000000 000001a166948b40 -00001 MTA 

    OSID Special thread type
        0 640c SuspendEE 
       10 76b0 DbgHelper 
       11 3e50 Finalizer 

那这个 0x20 什么时候被拿掉呢? 这个在源码中也能找到相应的答案,继续 go 运行,输出如下:


void ClrFlsClearThreadType(TlsThreadTypeFlag flag)
{
    t_ThreadType &= ~flag;
}

0:012> dp 0x1a1668ee880
000001a1`668ee880  00000000`00000000 00000000`00000000
000001a1`668ee890  00000000`00000000 00000000`00000000
000001a1`668ee8a0  00000000`00000000 00000000`00000000
000001a1`668ee8b0  00000000`00000000 00000000`00000000
000001a1`668ee8c0  000001a1`66902e50 000001a1`66948b40
000001a1`668ee8d0  000001a1`668ee880 00000000`00000000

当然如果你去寻找 sos 的源码实现,也会找到相应的答案。


HRESULT PrintSpecialThreads()
{
    ...
    if (ThreadType & ThreadType_DynamicSuspendEE)
    {
        type += "SuspendEE ";
    }
    ...
    return Status;
}

四:总结

挖掘这个标记的前世今生回头看其实还是挺有意思的,coreclr 居然新增了 m_EETlsData 字段来给 sos 做妥协,哈哈,这彰显了 sos 一等公民的地位。
图片名称

From:https://www.cnblogs.com/huangxincheng/p/18371228
本文地址: http://www.shuzixingkong.net/article/1296
0评论
提交 加载更多评论
其他文章 【2】Kaggle 医学影像数据读取
赛题名称:RSNA 2024 Lumbar Spine Degenerative Classification 中文:腰椎退行性病变分类 kaggle官网赛题链接:https://www.kaggle.com/competitions/rsna-2024-lumbar-spine-degenerat
【2】Kaggle 医学影像数据读取 【2】Kaggle 医学影像数据读取 【2】Kaggle 医学影像数据读取
Python 开发中,使用bcrypt 或 Passlib 对系统用户密码进行哈希和验证处理
在设计一个系统的时候,肯定都有会有用户身份认证的问题,一般对用户校验的时候,都是对用户存在数据库总的密码哈希值进行判断,从而避免密码泄露和反向解密,那么在Python 开发中,我们可以引入bcrypt 或 Passlib 对系统用户密码进行哈希和验证处理,以及介绍使用其他类库实现常规加解密处理操作。
零基础学习人工智能—Python—Pytorch学习(六)
前言 本文主要讲神经网络的上半部分。 另外,我发现我前面文章写的有歧义的地方还是挺多,虽然,已经改了一部分,但,可能还有没发现的,大家看的时候尽量多理解着看吧。 本着目的是学会使用神经网络的开发,至于数学的部分,就能过就过吧。 神经网络 先学个例子 先结合以前的知识理解一个例子,理解了这个例子,后面
零基础学习人工智能—Python—Pytorch学习(六) 零基础学习人工智能—Python—Pytorch学习(六) 零基础学习人工智能—Python—Pytorch学习(六)
SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧
SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧 SearXNG 是一个免费的互联网元搜索引擎,整合了各种搜索服务的结果。用户不会被跟踪,也不会被分析。 github地址:https://github.com/searxng/searxng 项目地址:https
SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧 SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧 SearXNG与LLM强强联合:打造用户隐私保护的智能搜索解答流程,隐私无忧,搜索无忧
Electron初体验
为什么使用electron 最近准备开发一个国产麒麟系统上的桌面程序,主要完成Linux命令的可视化,而electron作为目前最活跃的跨平台桌面应用程序的框架之一,它可以使用网页技术(如 HTML、CSS 和 JavaScript)来创建桌面应用程序,同时利用 Node.js 的强大能力来访问操作
Electron初体验 Electron初体验
源码解析之为何要用ConcurrentHashMap
为什么要用ConcurrentHashMap? ConcurrentHashMap是JUC包下的一个线程安全的HashMap类,我们都知道多线程的场景下要用ConcurrentHashMap来代替HashMap使用,有没有想过为什么不能用HashMap,为什么能用ConcurrentHashMap呢
源码解析之为何要用ConcurrentHashMap 源码解析之为何要用ConcurrentHashMap 源码解析之为何要用ConcurrentHashMap
12米空间分辨率DEM数据申请下载:TanDEM-X数据集
本文介绍全球12米与30米高空间分辨率的数字高程模型(DEM)数据——TanDEM-X数据的下载申请方法~
12米空间分辨率DEM数据申请下载:TanDEM-X数据集 12米空间分辨率DEM数据申请下载:TanDEM-X数据集 12米空间分辨率DEM数据申请下载:TanDEM-X数据集
Echarts 5 动态按需引入图表
官网提供的按需引入方法为全量按需引入,在打包分离中,仍旧存在使用不到的图表被打包进去。 例如:组件A使用了折线图、柱状图,组件B只用到了折线图,但是打包组件B的时候,柱状图也会被打包进去。 本文提供一种动态按需引入的思路,使得只用到折线图的组件B,打包的时候只打包折线图,不会将组件A用到的柱状图也打
Echarts 5 动态按需引入图表