最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解析C# Console 控制臺為什么也會卡死(原因分析)

 更新時間:2023年10月23日 14:25:30   投稿:mrr  
在分析旅程中,總會有幾例控制臺的意外卡死導致的生產(chǎn)事故,有經(jīng)驗的朋友都知道,控制臺卡死一般是動了快速編輯窗口的緣故,雖然知道緣由,但一直沒有時間探究底層原理,市面上也沒有對這塊的底層原理介紹,昨天花了點時間簡單探究了下,感興趣的朋友一起看看吧

一:背景

1. 講故事

在分析旅程中,總會有幾例控制臺的意外卡死導致的生產(chǎn)事故,有經(jīng)驗的朋友都知道,控制臺卡死一般是動了 快速編輯窗口 的緣故,截圖如下:

雖然知道緣由,但一直沒有時間探究底層原理,市面上也沒有對這塊的底層原理介紹,昨天花了點時間簡單探究了下,算是記錄分享吧。

二:幾個疑問解答

1. 界面為什么會卡死

相信有很多朋友會有這么一個疑問?控制臺程序明明沒有 message loop 機制,為什么還能響應 窗口事件 呢?

說實話這是一個好問題,其實 Console 之所以能響應 窗口事件,是因為它開了一個配套的 conhost 窗口子進程,用它來承接 UI 事件,為了方便闡述,上一段定時向控制臺輸出的測試代碼。

        static void Main(string[] args)
        {
            for (int i = 0; i < int.MaxValue; i++)
            {
                Console.WriteLine($"i={i}");
                Thread.Sleep(1000);
            }
        }

將程序跑起來,再用 process explorer 觀察進程樹即可。

接下來用 windbg 附加到 conshost 進程上,觀察下有沒有 GetMessageW。

0:005> ~* k
   0  Id: 3ec8.2c20 Suspend: 1 Teb: 000000d2`92014000 Unfrozen
 # Child-SP          RetAddr               Call Site
00 000000d2`922ff798 00007fff`a3e45746     ntdll!NtWaitForSingleObject+0x14
01 000000d2`922ff7a0 00007fff`a60b5bf1     KERNELBASE!DeviceIoControl+0x86
02 000000d2`922ff810 00007ff6`9087a790     KERNEL32!DeviceIoControlImplementation+0x81
03 000000d2`922ff860 00007fff`a60b7614     conhost!ConsoleIoThread+0xd0
04 000000d2`922ff9e0 00007fff`a66a26a1     KERNEL32!BaseThreadInitThunk+0x14
05 000000d2`922ffa10 00000000`00000000     ntdll!RtlUserThreadStart+0x21
...
   2  Id: 3ec8.1b70 Suspend: 1 Teb: 000000d2`9201c000 Unfrozen
 # Child-SP          RetAddr               Call Site
00 000000d2`9227f858 00007fff`a4891b9e     win32u!NtUserGetMessage+0x14
01 000000d2`9227f860 00007ff6`908735c5     user32!GetMessageW+0x2e
02 000000d2`9227f8c0 00007fff`a60b7614     conhost!ConsoleInputThreadProcWin32+0x75
03 000000d2`9227f920 00007fff`a66a26a1     KERNEL32!BaseThreadInitThunk+0x14
04 000000d2`9227f950 00000000`00000000     ntdll!RtlUserThreadStart+0x21
...

2. 進程間如何通訊

這個問題再細化一點就是Client 端通過 Console.WriteLine($"i={i}"); 寫入的內(nèi)容是如何被 Server 端的conhost!ConsoleIoThread 方法接收到的。

熟悉 Windows 編程的朋友都知道:Console.WriteLine 的底層調(diào)用邏輯是 ntdll!NtWriteFile -> nt!IopSynchronousServiceTail ,前者是用戶態(tài)進入到內(nèi)核態(tài)的網(wǎng)關函數(shù),后者是用戶將irp丟到線程的請求包隊列后進入休眠(KeWaitForSingleObject),直到驅(qū)動提取并處理完之后喚醒。

說了這么多,怎么去驗證呢?

客戶端下斷點

0: kd> !process 0 0 ConsoleApp2.exe
PROCESS ffffe001b5e51840
    SessionId: 1  Cid: 0e8c    Peb: 7ff7ab226000  ParentCid: 09d4
    DirBase: 18079000  ObjectTable: ffffc00036965200  HandleCount: <Data Not Accessible>
    Image: ConsoleApp2.exe
0: kd> bp /p ffffe001b5e51840 nt!IopSynchronousServiceTail
0: kd> g
Breakpoint 0 hit
nt!IopSynchronousServiceTail:
fffff802`a94f3410 48895c2420      mov     qword ptr [rsp+20h],rbx
3: kd> k
 # Child-SP          RetAddr               Call Site
00 ffffd000`f6477988 fffff802`a94f2e80     nt!IopSynchronousServiceTail
01 ffffd000`f6477990 fffff802`a916db63     nt!NtWriteFile+0x680
02 ffffd000`f6477a90 00007ffc`2fed38aa     nt!KiSystemServiceCopyEnd+0x13
03 0000009f`0743dbd8 00007ffc`2cd1d478     ntdll!NtWriteFile+0xa
04 0000009f`0743dbe0 00000000`00000005     0x00007ffc`2cd1d478
05 0000009f`0743dbe8 0000009f`0743dcf0     0x5
06 0000009f`0743dbf0 0000009f`0978c9b8     0x0000009f`0743dcf0
07 0000009f`0743dbf8 00007ffc`2986e442     0x0000009f`0978c9b8
08 0000009f`0743dc00 0000009f`0743dc30     0x00007ffc`2986e442
09 0000009f`0743dc08 0000009f`0743de00     0x0000009f`0743dc30
0a 0000009f`0743dc10 00000000`00000005     0x0000009f`0743de00
0b 0000009f`0743dc18 00000000`00000000     0x5
3: kd> tc
nt!IopSynchronousServiceTail+0x70:
fffff802`a94f3480 e8ebf1b5ff      call    nt!IopQueueThreadIrp (fffff802`a9052670)

服務端下斷點

conhost端的提取邏輯是在 conhost!ConsoleIoThread 方法中,它的內(nèi)部調(diào)用的是 kernelbase!DeviceIoControl 函數(shù),這個方法挺有意思,可以直接給驅(qū)動程序下達命令,方法簽名如下:

BOOL DeviceIoControl(
  HANDLE       hDevice,
  DWORD        dwIoControlCode,
  LPVOID       lpInBuffer,
  DWORD        nInBufferSize,
  LPVOID       lpOutBuffer,
  DWORD        nOutBufferSize,
  LPDWORD      lpBytesReturned,
  LPOVERLAPPED lpOverlapped
);

提取完了之后會通過 conhost!DoWriteConsole 向控制臺輸出,接下來可以下個斷點驗證下。

0:000> bp conhost!DoWriteConsole
0:000> g
Breakpoint 0 hit
conhost!DoWriteConsole:
00007ff6`90876ec0 48895c2410      mov     qword ptr [rsp+10h],rbx ss:00000095`d627f738=0000000000000000
0:000> r
rax=000000000000000c rbx=00000095d627f7b0 rcx=000002370df76cc0
rdx=00000095d627f768 rsi=00000095d627f7c0 rdi=00000095d627f7f0
rip=00007ff690876ec0 rsp=00000095d627f728 rbp=00000095d627f8f9
 r8=000002370bedf010  r9=00000095d627f7b0 r10=000002370df76cc0
r11=000002370e0c9d00 r12=00000095d627f970 r13=000002370bedf010
r14=000002370bedf010 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
conhost!DoWriteConsole:
00007ff6`90876ec0 48895c2410      mov     qword ptr [rsp+10h],rbx ss:00000095`d627f738=0000000000000000
0:000> du 000002370df76cc0
00000237`0df76cc0  "i=18.."

可以看到果然有一個 i=18,這里要提醒一下,要想看方法的順序邏輯,可以借助 perfview。

3. 為什么快捷編輯之后就卡死

  • conhost 的源碼不是公開的,不過可以感官上推測出來。
  • 快速編輯窗口 被用戶啟用后, GetMessage 會感知到這個自定義的 MSG 消息。

這個消息的邏輯會讓 server 處理Client消息的流程一直處于等待中,導致 Client 的 IopSynchronousServiceTail 不能被喚醒,導致一直處于阻塞中,類似 Task 的完成狀態(tài)一直不被設置。

接下來可以驗證下 快速編輯窗口 的處理消息碼是多少,只要在控制臺點一下鼠標。參考腳本如下:

0:004> bp win32u!NtUserGetMessage "dp ebp-30 L2 ; g"
0:004> g
00000095`d61ffae0  00000000`00130e6e 00000000`00000404
00000095`d61ffae0  00000000`00130e6e 00000000`00000404
00000095`d61ffae0  00000000`00130e6e 00000000`00000201
00000095`d61ffae0  00000000`00130e6e 00000000`00000405
00000095`d61ffae0  00000000`00130e6e 00000000`00000202
00000095`d61ffae0  00000000`00130e6e 00000000`00000200

從 chaggpt 中對每個消息碼的介紹,可以看到會有一個 405 的自定義消息,這個就是和 快速編輯窗口 有關的。

三:總結

這篇就是我個人對窗口卡死的推測和記錄,高級調(diào)試不易,如果大家感興趣,歡迎補充細節(jié)。

到此這篇關于C# Console 控制臺為什么也會卡死的文章就介紹到這了,更多相關C# Console 控制臺內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C#Url操作類封裝、仿Node.Js中的Url模塊實例

    C#Url操作類封裝、仿Node.Js中的Url模塊實例

    這篇文章主要介紹了C#Url操作類封裝、仿Node.Js中的Url模塊,實例分析了C#Url操作類封裝的技巧,非常具有實用價值,需要的朋友可以參考下。
    2016-10-10
  • C#自動生成漂亮的水晶效果頭像的實現(xiàn)代碼

    C#自動生成漂亮的水晶效果頭像的實現(xiàn)代碼

    這篇文章主要介紹了C#自動生成漂亮的水晶效果頭像的實現(xiàn)代碼,有需要的朋友可以參考一下
    2013-12-12
  • C#基礎知識系列八const和readonly關鍵字詳細介紹

    C#基礎知識系列八const和readonly關鍵字詳細介紹

    這篇文章主要介紹了C#中的const和readonly關鍵字,有需要的朋友可以參考一下
    2014-01-01
  • c#根據(jù)文件類型獲取相關類型圖標的方法代碼

    c#根據(jù)文件類型獲取相關類型圖標的方法代碼

    c#根據(jù)文件類型獲取相關類型圖標的方法代碼,需要的朋友可以參考一下
    2013-05-05
  • asp.net中調(diào)用oracle存儲過程的方法

    asp.net中調(diào)用oracle存儲過程的方法

    存儲過程是在大型數(shù)據(jù)庫系統(tǒng)中,一組為了完成特定功能的SQL 語句集,存儲在數(shù)據(jù)庫中經(jīng)過第一次編譯后再次調(diào)用不需要再次編譯,用戶通過指定存儲過程的名字并給出參數(shù)來執(zhí)行它,下面給大家介紹下asp.net中調(diào)用oracle存儲過程的方法,需要的朋友可以參考下
    2015-08-08
  • C#中關于序列化與反序列化的三種方法

    C#中關于序列化與反序列化的三種方法

    序列化是將對象的狀態(tài)信息轉(zhuǎn)換為可以存儲或傳輸?shù)男问降倪^程,本文主要介紹了C#中關于序列化與反序列化的三種方法,文章具有一定的參考價值,感興趣的可以了解一下
    2022-03-03
  • 使用C#代碼進行Word文檔中的郵件合并功能

    使用C#代碼進行Word文檔中的郵件合并功能

    郵件合并是 Microsoft Word 中的一個強大功能,它允許您通過單一模板文檔和數(shù)據(jù)源創(chuàng)建多份文檔,對于需要向大量收件人發(fā)送個性化信件的任務,郵件合并尤其有用,無需逐一編寫每封信件,在本文中,您將學習如何使用C#在 Word 文檔中執(zhí)行郵件合并,需要的朋友可以參考下
    2025-12-12
  • C#設置輸入法實例分析

    C#設置輸入法實例分析

    這篇文章主要介紹了C#設置輸入法的方法,實例分析了C#獲取系統(tǒng)輸入法及設置輸入法的相關技巧,需要的朋友可以參考下
    2015-05-05
  • C#從控制臺讀取字符串的方法

    C#從控制臺讀取字符串的方法

    這篇文章主要介紹了C#從控制臺讀取字符串的方法,實例分析了ReadLine方法的基本使用技巧,需要的朋友可以參考下
    2015-06-06
  • C#實現(xiàn)根據(jù)年份計算生肖屬相的方法

    C#實現(xiàn)根據(jù)年份計算生肖屬相的方法

    這篇文章主要介紹了C#實現(xiàn)根據(jù)年份計算生肖屬相的方法,涉及C#數(shù)組與字符串的操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-03-03

最新評論

扎赉特旗| 涡阳县| 理塘县| 嘉义县| 崇左市| 商丘市| 翼城县| 镇坪县| 长治市| 介休市| 台州市| 密山市| 扎兰屯市| 嵩明县| 铁岭县| 木兰县| 宝兴县| 德阳市| 安平县| 曲麻莱县| 祥云县| 永平县| 博客| 金沙县| 乾安县| 镇康县| 达尔| 贞丰县| 简阳市| 抚松县| 舟曲县| 南召县| 绥芬河市| 边坝县| 琼海市| 日照市| 莱芜市| 潮州市| 通州区| 晋州市| 江阴市|