第五章 走得太遠,別忘了回家的路(2) ——《箴言》第三章 Windows運行機理之讀書筆記之一

 

第五章 走得太遠,別忘了回家的路(2)
——《箴言》第三章 Windows運行機理之讀書筆記之一


二、“Message Deadlocks”、大師Charles Petzold與Jeffrey Richter的差異

其實,如果真正明白了消息的處理過程,消息還是有些很不簡單的。在一定意義上(如果實事求是之程度與勇氣等無可懷疑的話),我想,恐怕MS最有資格發言了,如MS自己一再告誡的“Message Deadlocks”問題:“A thread that calls the SendMessage function to send a message to another thread cannot continue executing until the window procedure that receives the message returns. If the receiving thread yields control while processing the message, the sending thread cannot continue executing, because it is waiting for SendMessage to return. If the receiving thread is attached to the same queue as the sender, it can cause an application deadlock to occur. (Note that journal hooks attach threads to the same queue.)”(Platform SDK: Windows User Interface MSDN2001.10)。一個用SendMessage給其他線程的窗口發送消息的線程需要依賴接收線程的正確處理,如果接受線程不能正確處理這種情形時那麼發送線程就將掛起了,這樣系統就不是健壯的。

爲此MS提供了幾種辦法來處理這種情形。一方面,MS提供其他一些發送消息的辦法來避免這種消息“死鎖”的情形,如用SendMessageCallback、SendMessageTimeout和SendNotifyMessage等來給其他線程發送消息。

另一方面,還允許那種因用SendMessage向其他線程發送消息而阻塞的線程,在收到incoming nonqueued messages時可以解除這種阻塞狀態,即可以先轉去處理這些消息而不用等待接收線程處理完成。

關於SendMessage,我們來看看MSDN(2001.10)上的說法:“If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed”(適用於Windows NT 3.1 and later, Windows 95 and later)。現在一個阻塞於向其他線程發送消息之SendMessage的發送線程,可以徑直處理其他線程發給自己的Incoming nonqueue messages(即當一個線程既是接收線程又是發送線程的時候,亦即在“however”之後再“however”一次,這個發送線程同時成了接收線程之時),就立即給了剛剛說過的話“Messages sent between threads are processed only when the receiving thread executes message retrieval code”一記響亮的耳光,剛剛說過的這話也就立即失去了它的效力,成了一句空話了。而進一步,如果這個SendMessage本身就是在一個Incoming nonqueue message 的消息處理過程中發送的呢,那麼就更成了一句空話了。這時將恐怕又要上映一幕矛和盾的喜劇了。

【測試程序 存在性 Incoming nonqueue message之SendMessage至別線程再受理Incoming nonqueue message】
【下面是兩個測試程序,先看第一個:
我們再來看第二個:】



雖說這樣解除了阻塞狀態,但破壞了SendMessage的同步語義,會引來了其他的問題:這種屬於強行奪取Windows事件驅動系統之控制權的行爲,破壞了正常的Windows事件驅動系統之控制權的轉移機制,發送線程如果正在對Posted消息的處理過程之中,那麼此時解除阻塞轉而去處理incoming nonqueued messages就可能會引起比阻塞更嚴重的問題(一種混亂之源:顛覆應用程序的控制邏輯);如果發送線程是在處理其他線程(且假定不是接收線程的較簡單情形)發送來的消息(即incoming nonqueued message【s】)的過程中用SendMessage給另外的線程發送消息而阻塞的,那麼此時系統將又轉而去處理新到來的incoming nonqueued messages麼?【Win95 NT3.51 XPsp2上試驗正是如此】(這又是另一種混亂之源:顛覆事件驅動系統之控制邏輯)。


我們先來看看Petzold的說法。我沒有找到《Programming Windows》的1988年的版本,但先來看看1990年第二版的中文版本《Windows編程》的第一章《何謂Windows程序》,在講了排隊消息和非排隊消息的處理之後,Petzold接着說,“這個過程雖然比較複雜,但最幸運的是該複雜性在Windows內部而不在程序中。從窗口過程的角度看,消息以一種有序和同步的方式交往。窗口過程可以處理這些消息也可不予理睬。因此,窗口過程象消息的最終匯合點。幾乎所有對窗口有影響的操作都有消息發送到窗口過程。……消息與硬件中斷不同。當處理窗口過程中的一個消息時,程序不能被另一個消息中斷。僅當窗口過程調用一個函數產生一個新消息時,消息過程在函數返回前處理這個消息。”

再來看看第五版本,其中思想是一貫的。在講了排隊消息和非排隊消息的處理之後,Petzold也同樣接着說,“This process is obviously complex, but fortunately most of the complexity is Windows' problem rather than our program's. From the perspective of the window procedure, these messages come through in an orderly and synchronized manner. The window procedure can do something with these messages or ignore them.
When I say that messages come through in an orderly and synchronized manner, I mean first that messages are not like hardware interrupts. While processing one message in a window procedure, the program will not be suddenly interrupted by another message.  Although Windows programs can have multiple threads of execution, each thread's message queue handles messages for only the windows whose window procedures are executed in that thread. In other words, the message loop and the window procedure do not run concurrently. When a message loop retrieves a message from its message queue and calls DispatchMessage to send the message off to the window procedure, DispatchMessage does not return until the window procedure has returned control back to Windows.
However, the window procedure could call a function that sends the window procedure another message, in which case the window procedure must finish processing the second message before the function call returns, at which time the window procedure proceeds with the original message. For example, when a window procedure calls UpdateWindow, Windows calls the window procedure with a WM_PAINT message. When the window procedure finishes processing the WM_PAINT message, the UpdateWindow call will return controls back to the window procedure. ”

在Petzold看來,消息不像硬件中斷;當窗口過程正在處理一個消息時,程序不能被另一個消息突然中斷(當然窗口過程自己調用函數發送另一消息給自己(窗口過程)不在此列的,相當於普通的函數調用,當然在此過程中遵循同樣的Petzold規則)。

我們再來看Jeffrey的說法,他的說法,也是一貫的,不同的是,這裏是他的一貫性,就是用SendMessage發送消息到其他線程而阻塞時可以被中斷的,即可以立即處理Incomming nonqueue messages而不用等待接受線程處理完成。先看《Advanced Windows NT》1993年,Jeffrey雲,
“While the receiving thread is processing the message, the thread that called SendMessage is sitting idle. After the message has been processed, the result of the processing is returned and the thread that called SendMessage is resumed so that it can continue execution.
While a thread is waiting for SendMessage to return, it sits basically idle. It is allowed to do one thing, however: If another thread in the system sends a message to a window created by a thread that is waiting for SendMessage to return, the system will process the sent message immediately. The system doesn't have to wait for the thread to call GetMessage or PeekMessage in this case.”

肯定了Incoming nonqueue messages機制,肯定了它正是可以“突然”中斷消息處理(系統強行中斷正在處理的消息而另外插入其他消息的處理,不同於線程內用SendMessage發送消息的情形)。但立即轉到怎樣防止發送線程被掛起的辦法,不對這個機制贊一詞。

“Because the Win32 subsystem uses this method to handle the sending of interthread messages,your thread could possibly hang. Let's say that the thread processing the sent message has a bug and enters an infinite loop. What happens to the thread that called SendMessage? Will it ever be resumed? Does this mean that a bug in one application has the ability to hang another application? The answer is yes!
Four functions allow you to defensively write code to protect yourself from this situation.
The first function is SendMessageTimeout:…”。

再看其第四版(1999年)也基本一樣,“While a thread is waiting for SendMessage to return, it basically sits idle. It is, however, allowed to perform one task: if another thread in the system sends a message to a window created by a thread that is waiting for SendMessage to return, the system will process the sent message immediately. The system doesn't have to wait for the thread to call GetMessage,PeekMessage, or WaitMessage in this case.
Because Windows uses this method to handle the sending of interthread messages, it's possible that your thread could hang.For example, let's say that the thread processing the sent message has a bug and enters an infinite loop. What happens to the thread that called SendMessage? Will it ever be resumed? Does this mean that a bug in one application can cause another application to hang? The answer is yes!
Four functions—SendMessageTimeout, SendMessageCallback, SendNotifyMessage, and ReplyMessage—allow you to write code defensively to protect yourself from this situation. The first function is SendMessageTimeout:…”

儘管Jeffrey只講Incoming nonqueue messages機制可以爲發送線程解除阻塞,卻絕口不提這一機制的引入對發送線程、整個窗口系統意味着什麼,不提及它對窗口系統帶來的嚴重危害【後果】,但肯定了Incoming nonqueue messages機制,肯定了它正是可以“突然”(系統強行中斷正在處理的消息而另外插入其他消息的處理,不同於線程內用SendMessage發送消息的情形)中斷消息處理,這點與Petzold是不一樣的。看前面的MSDN中有關說明及Jeffrey的成書介紹,Jeffrey在這點上正是與MS同步的。我想Petzold不會不知道MS的這些說明,而自始自終堅持自己的觀點(我尚未發現Petzold的有關這個問題的其它觀點,讀者如有知情或發現,敬請告我,謝謝!),或許倒有點耐人尋味吧。

【不知什麼原因,到第五版(《Windows via C/C++》2008年)時,在書的Introduction中雖然說“In addition to the new organization and greater depth, I added a ton of new content.”,也對重寫部分加以說明,對未觸及部分如COM和.NET加以說明,但就是絕口不提這個版本中已對窗口系統這部分剪除的事實,而在第四版中窗口內容整整佔了一個部分即第六部分。這大抵不會是無意的疏忽,而倒有點像是刻意的迴避。窗口系統已經沒有什麼(重要的)新玩意可說?Windows XP,Windows 2003,Windows Vista,Windows Server 2008等,我想,它們的窗口系統也不至於(重要的)沒有新東西可說罷。此時Jeffrey認爲不再屬於系統編程範疇?或者不宜再在他的這個系統編程書籍中講述窗口部分?不得而知。【《Windows System Programming》2004年的第3版和2010年的第4版均無窗口系統內容,早期版本我沒有找到。作爲MS操作系統內幕的官方代表著作系列,從《Inside Windows NT》(第一版)到《Windows Internals》(第五版)都從未將窗口系統作爲一個核心部件(如進程管理、內存管理、IO系統、文件系統等部件)來正式講述其內部機制(即使把網絡部分納入後也沒有窗口系統的一席之地,第二版無網絡部分),基本上是在介紹系統體系結構時由於完整性而帶上一筆,還有就是第二版至第四版爲Windows NT 4.0及其以後的版本將GUI部分移入核心態對內核穩定性等沒什麼影響(降低)而進行的冗長的辯護(這種聲音終於在第五版消停了)等而有所觸及。】

【Incoming nonqueue message機制對線程正常執行的影響:如何防止不經意、惡意的干擾線程的正常執行(不談對SendMessage()被其他線程阻塞的“拯救”)】


三、Window Procedure 與“Reentrant”及魚死網破

 

(2011.4.1 1214)

發佈了48 篇原創文章 · 獲贊 7 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章