亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

一:背景

1. 講故事

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

雖然知道緣由,但一直沒(méi)有時(shí)間探究底層原理,市面上也沒(méi)有對(duì)這塊的底層原理介紹,昨天花了點(diǎn)時(shí)間簡(jiǎn)單探究了下,算是記錄分享吧。

二:幾個(gè)疑問(wèn)解答

1. 界面為什么會(huì)卡死

相信有很多朋友會(huì)有這么一個(gè)疑問(wèn)?控制臺(tái)程序明明沒(méi)有 message loop 機(jī)制,為什么還能響應(yīng) 窗口事件 呢?

說(shuō)實(shí)話這是一個(gè)好問(wèn)題,其實(shí) Console 之所以能響應(yīng) 窗口事件,是因?yàn)樗_(kāi)了一個(gè)配套的 conhost 窗口子進(jìn)程,用它來(lái)承接 UI 事件,為了方便闡述,上一段定時(shí)向控制臺(tái)輸出的測(cè)試代碼。

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

將程序跑起來(lái),再用 process explorer 觀察進(jìn)程樹(shù)即可。

接下來(lái)用 windbg 附加到 conshost 進(jìn)程上,觀察下有沒(méi)有 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. 進(jìn)程間如何通訊

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

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

說(shuō)了這么多,怎么去驗(yàn)證呢?

客戶(hù)端下斷點(diǎn)

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)

服務(wù)端下斷點(diǎn)

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

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

提取完了之后會(huì)通過(guò) conhost!DoWriteConsole 向控制臺(tái)輸出,接下來(lái)可以下個(gè)斷點(diǎn)驗(yàn)證下。

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.."

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

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

  • conhost 的源碼不是公開(kāi)的,不過(guò)可以感官上推測(cè)出來(lái)。
  • 快速編輯窗口 被用戶(hù)啟用后, GetMessage 會(huì)感知到這個(gè)自定義的 MSG 消息。

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

接下來(lái)可以驗(yàn)證下 快速編輯窗口 的處理消息碼是多少,只要在控制臺(tái)點(diǎn)一下鼠標(biāo)。參考腳本如下:

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 中對(duì)每個(gè)消息碼的介紹,可以看到會(huì)有一個(gè) 405 的自定義消息,這個(gè)就是和 快速編輯窗口 有關(guān)的。

三:總結(jié)

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

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

相關(guān)文章

  • C#讀取CSV文件的方法總結(jié)

    C#讀取CSV文件的方法總結(jié)

    CSV文件是一種簡(jiǎn)單的文本文件格式,用于存儲(chǔ)表格數(shù)據(jù),在C#中,有多種方法可以用于讀取CSV文件,本文將介紹幾種常見(jiàn)的讀取CSV文件的方法,包括使用System.IO命名空間中的類(lèi)、使用CsvHelper庫(kù)以及使用LINQ,需要的朋友可以參考下
    2024-05-05
  • C# 位運(yùn)算符整理

    C# 位運(yùn)算符整理

    在C#中可以對(duì)整型運(yùn)算對(duì)象按位進(jìn)行邏輯運(yùn)算。按位進(jìn)行邏輯運(yùn)算的意義是:依次取被運(yùn)算對(duì)象的每個(gè)位,進(jìn)行邏輯運(yùn)算,每個(gè)位的邏輯運(yùn)算結(jié)果是結(jié)果值的每個(gè)位。
    2008-10-10
  • 輕松學(xué)習(xí)C#的預(yù)定義數(shù)據(jù)類(lèi)型

    輕松學(xué)習(xí)C#的預(yù)定義數(shù)據(jù)類(lèi)型

    輕松學(xué)習(xí)C#的預(yù)定義數(shù)據(jù)類(lèi)型,C#的預(yù)定義數(shù)據(jù)類(lèi)型包括兩種,一種是值類(lèi)型,一種是引用類(lèi)型,需要的朋友可以參考下
    2015-11-11
  • C# dump系統(tǒng)lsass內(nèi)存和sam注冊(cè)表詳細(xì)

    C# dump系統(tǒng)lsass內(nèi)存和sam注冊(cè)表詳細(xì)

    這篇文章主要介紹了C# dump系統(tǒng)lsass內(nèi)存和sam注冊(cè)表,在這里選擇 C# 的好處是體積小,結(jié)合 loadAssembly 方便免殺,希望對(duì)讀者們有所幫助
    2021-09-09
  • C# Mqtt 斷線重連的實(shí)現(xiàn)代碼

    C# Mqtt 斷線重連的實(shí)現(xiàn)代碼

    這篇文章主要介紹了C# Mqtt 斷線重連,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • C#使用Dns類(lèi)實(shí)現(xiàn)查詢(xún)主機(jī)名對(duì)應(yīng)IP地址

    C#使用Dns類(lèi)實(shí)現(xiàn)查詢(xún)主機(jī)名對(duì)應(yīng)IP地址

    C#中的Dns類(lèi)能夠與默認(rèn)的DNS服務(wù)器進(jìn)行通信,以檢索IP地址,這篇文章主要介紹了C#如何使用Dns類(lèi)解析出主機(jī)對(duì)應(yīng)的IP地址信息,需要的可以參考下
    2024-02-02
  • C#程序集的主版本號(hào)和次版本號(hào)的實(shí)現(xiàn)

    C#程序集的主版本號(hào)和次版本號(hào)的實(shí)現(xiàn)

    C# 程序集的版本號(hào)和次版本號(hào)是程序集的一部分,用于標(biāo)識(shí)程序集的不同版,本本文主要介紹了C#程序集的主版本號(hào)和次版本號(hào)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-04-04
  • C#如何使用Bogus創(chuàng)建模擬數(shù)據(jù)示例代碼

    C#如何使用Bogus創(chuàng)建模擬數(shù)據(jù)示例代碼

    這篇文章主要給大家介紹了關(guān)于C#如何使用Bogus創(chuàng)建模擬數(shù)據(jù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C#具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • c# 獲取字符串的字節(jié)數(shù)的方法

    c# 獲取字符串的字節(jié)數(shù)的方法

    本篇文章主要是對(duì)c#中獲取字符串的字節(jié)數(shù)的方法進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助
    2014-01-01
  • 深入線程安全容器的實(shí)現(xiàn)方法

    深入線程安全容器的實(shí)現(xiàn)方法

    本篇文章是對(duì)線程安全容器的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05

最新評(píng)論