记一次 .NET某网络边缘计算系统 卡死分析
一:背景
1. 讲故事
早就听说过有什么 网络边缘计算,这次还真给遇到了,有点意思,问了下 chatgpt 这是干嘛的 ?
网络边缘计算是一种计算模型,它将计算能力和数据存储位置从传统的集中式数据中心向网络边缘的用户设备、传感器和其他物联网设备移动。这种模型的目的是在接近数据生成源头的地方提供更快速的计算和数据处理能力,从而减少数据传输延迟并提高服务质量。网络边缘计算使得在设备本地进行数据处理和决策成为可能,同时也有助于减轻对中心数据中心的网络流量和负载。
看到.NET还有这样的应用场景还是挺欣慰的,接下来就来分析下这个dump到底是怎么回事?
二:WinDbg 分析
1. 为什么会卡死
不同程序的卡死有不同的分析方式,所以要先鉴别下程序的类型以及主线程的调用栈即可,参考如下:
0:000> !eeversion
5.0.721.25508
5.0.721.25508 @Commit: 556582d964cc21b82a88d7154e915076f6f9008e
Server mode with 64 gc heaps
SOS Version: 8.0.10.10501 retail build
0:000> k
# Child-SP RetAddr Call Site
00 0000ffff`e0dddac0 0000fffd`c194c30c libpthread_2_28!pthread_cond_wait+0x238
...
18 (Inline Function) --------`-------- libcoreclr!RunMain::$_0::operator()::{lambda(Param *)#1}::operator()+0x14c [/__w/1/s/src/coreclr/src/vm/assembly.cpp @ 1536]
19 (Inline Function) --------`-------- libcoreclr!RunMain::$_0::operator()+0x188 [/__w/1/s/src/coreclr/src/vm/assembly.cpp @ 1538]
1a 0000ffff`e0dde600 0000fffd`c153e860 libcoreclr!RunMain+0x298 [/__w/1/s/src/coreclr/src/vm/assembly.cpp @ 1538]
...
20 0000ffff`e0dded10 0000fffd`c1bf7800 libhostpolicy!corehost_main+0xc0 [/root/runtime/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp @ 409]
21 (Inline Function) --------`-------- libhostfxr!execute_app+0x2c0 [/root/runtime/src/installer/corehost/cli/fxr/fx_muxer.cpp @ 146]
22 (Inline Function) --------`-------- libhostfxr!<unnamed-namespace>::read_config_and_execute+0x3b4 [/root/runtime/src/installer/corehost/cli/fxr/fx_muxer.cpp @ 520]
23 0000ffff`e0ddeeb0 0000fffd`c1bf6840 libhostfxr!fx_muxer_t::handle_exec_host_command+0x57c [/root/runtime/src/installer/corehost/cli/fxr/fx_muxer.cpp @ 1001]
24 0000ffff`e0ddf000 0000fffd`c1bf4090 libhostfxr!fx_muxer_t::execute+0x2ec
25 0000ffff`e0ddf130 0000aaad`c9e1d22c libhostfxr!hostfxr_main_startupinfo+0xa0 [/root/runtime/src/installer/corehost/cli/fxr/hostfxr.cpp @ 50]
26 0000ffff`e0ddf200 0000aaad`c9e1d468 dotnet!exe_start+0x36c [/root/runtime/src/installer/corehost/corehost.cpp @ 239]
27 0000ffff`e0ddf370 0000fffd`c1c63fe0 dotnet!main+0x90 [/root/runtime/src/installer/corehost/corehost.cpp @ 302]
28 0000ffff`e0ddf3b0 0000aaad`c9e13adc libc_2_28!_libc_start_main+0xe0
29 0000ffff`e0ddf4e0 00000000`00000000 dotnet!start+0x34
从卦中的指标来看,这是一个 Linux 上部署的 Web网站,既然是网站的卡死,那就要关注各个线程都在做什么。
2. 线程都在干嘛
以我多年的分析经验,绝大多数都是由于 线程饥饿 或者说 线程池耗尽 导致的,首先我们看下线程池的情况。
0:000> !t
ThreadCount: 365
UnstartedThread: 0
BackgroundThread: 354
PendingThread: 0
DeadThread: 10
Hosted Runtime: no
Lock
DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 31eaf 0000AAADF267C600 2020020 Preemptive 0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn
...
423 363 36d30 0000FFDDB4000B20 1020220 Preemptive 0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn (Threadpool Worker)
424 364 36d31 0000FFDDA8000B20 1020220 Preemptive 0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn (Threadpool Worker)
425 365 36d32 0000FFDDAC000B20 1020220 Preemptive 0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn (Threadpool Worker)
0:000> !tp
Using the Portable thread pool.
CPU utilization: 9%
Workers Total: 252
Workers Running: 236
Workers Idle: 13
Worker Min Limit: 64
Worker Max Limit: 32767
Completion Total: 0
Completion Free: 0
Completion MaxFree: 128
Completion Current Limit: 0
Completion Min Limit: 64
Completion Max Limit: 1000
从卦中看当前有 365 个托管线程,这个算多吗?对于64core 来说,这个线程其实算是正常,训练营里的朋友都知道,server版的gc仅gc线程就有 64*2=128 个,接下来再看一个指标就是当前是否存在任务积压? 可以使用 !ext tpq 命令,参考输出如下:
0:000> !ext tpq
global work item queue________________________________
local per thread work items_____________________________________
从卦中看当前没有任务积压,这就有点反经验了。
3. 真的不是线程饥饿吗
最后一招比较彻底,就是看各个线程栈都在做什么,可以使用 ~*e !clrstack 命令。

这不看不知道,一看吓一跳,有 193 个线程在 Task.Result 上等待,这玩意太经典了,然后从上面的调用栈 UIUpdateTimer_Elapsed 来看,貌似是一个定时器导致的,接下来我就好奇这代码是怎么写的?

分析上面的代码之后,我发现它是和 Linux Shell 窗口进行命令交互,不知道为何 Shell 没有响应导致代码在这里卡死。
4. 为什么线程池没有积压
相信有很多朋友对这个反经验的东西很好奇为什么请求没有积压在线程池,其实这个考验的是你对 PortableThreadPool 的底层了解,这里我就简单说一下吧。
- 在 ThreadPool 中有一个 GateThread 线程是专门给线程池动态注入线程的,参考代码如下:
private static class GateThread
{
private static void GateThreadStart()
{
while (true)
{
bool wasSignaledToWake = DelayEvent.WaitOne((int)delayHelper.GetNextDelay(tickCount));
WorkerThread.MaybeAddWorkingWorker(threadPoolInstance);
}
}
}
- 一旦有人调用了 Task.Result 代码,内部会主动唤醒 DelayEvent 事件,告诉 GateThread 赶紧通过 MaybeAddWorkingWorker 方法给我注入新的线程,参考代码如下:
private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken)
{
bool flag3 = ThreadPool.NotifyThreadBlocked();
}
internal static bool NotifyThreadBlocked()
{
if (UsePortableThreadPool)
{
return PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked();
}
return false;
}
public bool NotifyThreadBlocked()
{
GateThread.Wake(this);
}
上面这种主动唤醒的机制是 C# 版 PortableThreadPool 做的优化来缓解线程饥饿的,这里有一个重点就是它只能缓解,换句话说如果上游太猛了还是会有请求积压的,但为什么这里没有积压呢? 很显然上游不猛呗,那如何眼见为实呢? 这就需要看 timer 的周期数即可,到当前的线程栈上给扒出来。
0:417> !DumpObj /d 0000ffee380757f8
Name: System.Timers.Timer
MethodTable: 0000fffd4ab24030
EEClass: 0000fffd4ad6e140
Size: 88(0x58) bytes
File: /home/user/env/dotnet/shared/Microsoft.NETCore.App/5.0.7/System.ComponentModel.TypeConverter.dll
Fields:
MT Field Offset Type VT Attr Value Name
0000fffd4c947498 400001c 8 ...ponentModel.ISite 0 instance 0000000000000000 _site
0000000000000000 400001d 10 ....EventHandlerList 0 instance 0000000000000000 _events
0000fffd479195d8 400001b 98 System.Object 0 static 0000000000000000 s_eventDisposed
0000fffd47926f60 400000e 40 System.Double 1 instance 3000.000000 _interval
0000fffd4791fb10 400000f 48 System.Boolean 1 instance 1 _enabled
0000fffd4791fb10 4000010 49 System.Boolean 1 instance 0 _initializing
0000fffd4791fb10 4000011 4a System.Boolean 1 instance 0 _delayedEnable
0000fffd4ab241d8 4000012 18 ...apsedEventHandler 0 instance 0000ffee3807aae8 _onIntervalElapsed
0000fffd4791fb10 4000013 4b System.Boolean 1 instance 1 _autoReset
0000fffd4c944ea0 4000014 20 ...SynchronizeInvoke 0 instance 0000000000000000 _synchronizingObject
0000fffd4791fb10 4000015 4c System.Boolean 1 instance 0 _disposed
0000fffd49963e28 4000016 28 ...m.Threading.Timer 0 instance 0000ffee38098dc8 _timer
0000fffd48b90a30 4000017 30 ...ing.TimerCallback 0 instance 0000ffee3807aaa8 _callback
0000fffd479195d8 4000018 38 System.Object 0 instance 0000ffee38098db0 _cookie
从卦中看当前是 3s 为一个周期,这就能解释为什么线程池没有积压的底层原因了。
三:总结
这个卡死事故还是蛮好解决的,如果有一些经验直接用dotnet-counter也是能搞定的,重点在于这是一个 Linux的dump,同时又是 .NET上的一个很好玩的场景,故此分享出来。

记一次 .NET某网络边缘计算系统 卡死分析的更多相关文章
- 记一次 .NET 某药品仓储管理系统 卡死分析
一:背景 1. 讲故事 这个月初,有位朋友wx上找到我,说他的api过一段时间后,就会出现只有请求,没有响应的情况,截图如下: 从朋友的描述中看样子程序是被什么东西卡住了,这种卡死的问题解决起来相对简 ...
- 记一次 .NET 某金融企业 WPF 程序卡死分析
一:背景 1. 讲故事 前段时间遇到了一个难度比较高的 dump,经过几个小时的探索,终于给找出来了,在这里做一下整理,希望对大家有所帮助,对自己也是一个总结,好了,老规矩,上 WinDBG 说话. ...
- 记一次 .NET 某数控机床控制程序 卡死分析
一:背景 1. 讲故事 前段时间有位朋友微信上找到我,说它的程序出现了卡死,让我帮忙看下是怎么回事? 说来也奇怪,那段时间求助卡死类的dump特别多,被迫训练了一下对这类问题的洞察力 ,再次声明一下, ...
- [转帖]记一次KUBERNETES/DOCKER网络排障
记一次KUBERNETES/DOCKER网络排障 https://coolshell.cn/articles/18654.html 记得之前在一个公众号里面看过这个文章 讲的挺好的.. 物理机直接跑d ...
- VMware vRealize Network Insight 6.2 发布 - 网络和安全可视化分析
发现.优化应用安全性和网络连接解决方案并对其进行故障排除 VMware vRealize Network Insight 可帮助您跨混合和多云环境构建经过优化且高度可用的安全网络基础架构.它提供了网络 ...
- Cacti 是一套基于PHP,MySQL,SNMP及RRDTool开发的网络流量监测图形分析工具
Cacti 是一套基于PHP,MySQL,SNMP及RRDTool开发的网络流量监测图形分析工具. mysqlreport是mysql性能监测时最常用的工具,对了解mysql运行状态和配置调整都有很大 ...
- Android-蓝牙的网络共享与连接分析
一.概述 本次分析是基于android7.0的源码,主要是介绍如何通过反射来打开蓝牙的网络共享以及互联网的连接. 二.蓝牙的网络共享 1. 网络共享部分源码分析 关于packages/apps/Set ...
- 开源网络抓包与分析框架学习-Packetbeat篇
开源简介packbeat是一个开源的实时网络抓包与分析框架,内置了很多常见的协议捕获及解析,如HTTP.MySQL.Redis等.在实际使用中,通常和Elasticsearch以及kibana联合使用 ...
- 网络协议图形化分析工具EtherApe
网络协议图形化分析工具EtherApe 在对网络数据分析的时候,渗透测试人员往往只关心数据流向以及协议类型,而不关心具体数据包的内容.因为这样可以快速找到网络的关键节点或者重要的协议类型. Kal ...
- # Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析#
Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析 Volley源码一共40多个类和接口.除去一些工具类的实现,核心代码只有20多个类.所以相对来说分析起来没有那么吃力.但是要想分析透 ...
随机推荐
- 坐标轴调控大揭秘:Matplotlib坐标轴设置全攻略+顺口溜,一文掌握!
在数据可视化的世界里,Matplotlib是那把魔法棒,让枯燥的数据跃然纸上,而掌控这把魔法棒的核心,就是对坐标轴的精妙操作.今天,就让我们一起揭开Matplotlib坐标轴设置的神秘面纱,配上易记的 ...
- 01 elasticsearch学习笔记-环境安装
docker-compose安装EFK git clone https://github.com/haimait/docker_compose_efk docker-compose up -d Flu ...
- SpringBoot-mybatis-plus 分页
前言: 想必数据分页对于每一个程序员并不陌生,针对分页查询功能代码实现上:肯定是代码简洁明了且能达到分页的效果会更好! 现在我将基于SpringBoot - mybatisPlus分页查询的方法总结如 ...
- Ubuntu更新源文件报错:E: 仓库 “http://ppa.launchpad.net/chris-lea/node.js/ubuntu bionic Release” 没有 Release 文件。
E: 仓库 "http://ppa.launchpad.net/chris-lea/node.js/ubuntu bionic Release" 没有 Release 文件. 一条 ...
- 2022最新的Centos6.10安装mysql8.0
一.系统源替换 1.备份系统源 (1)进入源的默认路径 cd /etc/yum.repos.d (2)查看一下 (3)备份 cp CentOS-Base.repo CentOS-Base-Back.r ...
- vmware迁移虚拟机
迁移 1.打开"VMware",点击"虚拟机详细信息"可以看到虚拟机的储存路径. 2. 按照储存路径找到虚拟机文件位置,将整个虚拟机文件复制,粘贴到需要转移的路 ...
- Biwen.Settings添加对IConfiguration&IOptions的集成支持
Biwen.Settings 是一个简易的配置项管理模块,主要的作用就是可以校验并持久化配置项,比如将自己的配置存储到数据库中,JSON文件中等 使用上也是很简单,只需要在服务中注入配置即可, 比如我 ...
- linux下srpm源码包的使用和安装
目录 一.关于srpm包 二.srpm包和rpm包的区别 三.不对srpm包做修改,直接安装srpm包 四.对srpm包的源码进行修改,然后安装srpm包 一.关于srpm包 SRPM包是Sour ...
- django多表关联实战
定义模型类: from django.db import models from django.contrib.auth.models import User ''' ---------- Djang ...
- 最好的在线PDF转换工具服务
工作中有时候会碰到需要转换PDF文件的情况,现在网上就要很多免费的在线工具,可以进行PDF文件的转换,下面就来介绍一些可以直接在浏览器中将文档.电子表格.和图片转换为PDF或者互相转换的服务工具. ...