《Windows核心編程》讀書筆記(一)

來源:互聯網
上載者:User
    昨天深夜整理了一下Judge的需求和存在的bug,發現自己在Judge這個泥潭裡已經越陷越深,很多問題都不是用一種查閱式的閱讀所能解決,所以決定仔細的讀一讀《Windows核心編程》,來深刻領悟一下Windows相關的編程知識。

一:3.2.2 關閉核心對象

如果將一個無效控制代碼傳遞給CloseHandle,將會出現兩種情況之一。如果進程運行正常,CloseHandle 返回FALSE ,而GetLastError 則返回ERROR_INVALID_HANDLE。如果進程正在排除錯誤,系統將通知偵錯工具,以便能排除它的錯誤。 

這個說明或許可以解釋我Judge中現在存在的某個Bug:那個Bug僅在Debug模式下出現。

二:9.1等待函數

下面這個函數Wa i t F o r M u l t i p l e O b j e c t s與Wa i t F o r S i n g l e O b j e c t函數很相似,區別在於它允許調用線程同時查看若干個核心對象的已通知狀態:


DWORD WaitForMultipleObjects(DWORD dwCount,
   CONST HANDLE* phObjects, 
   BOOL fWaitAll, 
   DWORD dwMilliseconds);
d w C o u n t參數用於指明想要讓函數查看的核心對象的數量。這個值必須在1與M A X I M U M _WA I T _ O B J E C T S(在Wi n d o w s標頭檔中定義為6 4)之間。p h O b j e c t s參數是指向核心物件控點的數組的指標。
可以以兩種不同的方式來使用Wa i t F o r M u l t i p l e O b j e c t s函數。一種方式是讓線程進入等待狀態,直到指定核心對象中的任何一個變為已通知狀態。另一種方式是讓線程進入等待狀態,直到所有指定的核心對象都變為已通知狀態。f Wa i tAl l參數告訴該函數,你想要讓它使用何種方式。如果為該參數傳遞T R U E,那麼在所有對象變為已通知狀態之前,該函數將不允許調用線程運行。

d w M i l l i s e c o n d s參數的作用與它在Wa i t F o r S i n g l e O b j e c t中的作用完全相同。如果在等待的時候規定的時間到了,那麼該函數無論如何都會返回。同樣,通常為該參數傳遞I N F I N I T E,但是在編寫代碼時應該小心,以避免出現死結情況。

Wa i t F o r M u l t i p l e O b j e c t s函數的傳回值告訴調用線程,為什麼它會被重新調度。可能的傳回值是WA I T _ FA I L E D和WA I T _ T I M E O U T,這兩個值的作用是很清楚的。如果為f Wa i tAl l參數傳遞T R U E,同時所有對象均變為已通知狀態,那麼傳回值是WA I T _ O B J E C T _ 0。如果為f Wa i t A l l傳遞FA L S E,那麼一旦任何一個對象變為已通知狀態,該函數便返回。在這種情況下,你可能想要知道哪個對象變為已通知狀態。傳回值是WA I T _ O B J E C T _ 0與(WA I T _ O B J E C T _ 0 + d w C o u n t - 1)之間的一個值。換句話說,如果傳回值不是WA I T _ T I M E O U T,也不是WA I T _ FA I L E D,那麼應該從傳回值中減去WA I T _ O B J E C T _ 0。產生的數字是作為第二個參數傳遞給Wa i t F o r M u l t i p l e O b j e c t s的控制代碼數組中的索引。該索引說明哪個對象變為已通知狀態。下面是說明這一情況的一些範例程式碼:


HANDLE h[3];
h[0] = hProcess1;
h[1] = hProcess2;
h[2] = hProcess3;
DWORD dw = WaitForMultipleObjects(3, h, FALSE, 5000);
switch(dw) 
{
   case WAIT_FAILED:
      // Bad call to function (invalid handle?)
      break;

   case WAIT_TIMEOUT:
      // None of the objects became signaled within 5000 milliseconds.
      break;

   case WAIT_OBJECT_0 + 0:
      // The process identified by h[0] (hProcess1) terminated.
      break;

   case WAIT_OBJECT_0 + 1:
      // The process identified by h[1] (hProcess2) terminated.
      break;

   case WAIT_OBJECT_0 + 2:
      // The process identified by h[2] (hProcess3) terminated.
      break;
}

如果為f Wa i t A l l參數傳遞FA L S E,Wa i t F o r M u l t i p l e O b j e c t s就從索引0開始向上對控制代碼數組進行掃描,同時已通知的第一個對象終止等待狀態。這可能產生一些你不希望有的結果。例如,通過將3個進程控制代碼傳遞給該函數,你的線程就會等待3個子進程終止運行。如果數組中索引為0的進程終止運行,Wa i t F o r M u l t i p l e O b j e c t s就會返回。這時該線程就可以做它需要的任何事情,然後迴圈反覆,等待另一個進程終止運行。如果該線程傳遞相同的3個控制代碼,該函數立即再次返回WA I T _ O B J E C T _ 0。除非刪除已經收到通知的控制代碼,否則代碼就無法正確地運行。用WaitForMultipleObjects改寫我的Judge,使得中止線程時,可以讓線程自己中止,而不是由其他線程殺。這樣可以解決另一Bug。

三:17.1.2 在可執行檔或DLL的多個執行個體之間共用待用資料

我可以建立一個稱為“S h a r e d”的節,它包含單個L O N G值,如下所示:

#pragma data_seg("Shared")
LONG g_lInstanceCount = 0;
#pragma data_seg()
當編譯器對這個代碼進行編譯時間,它建立一個新節,稱為S h a r e d,並將它在編譯指示後面看到的所有已經初始化(i n i t i a l i z e d)的資料變數放入這個新節中。在上面這個例子中,變數放入S h a r e d節中。該變數後面的#pragma dataseg()一行告訴編譯器停止將已經初始化的變數放入S h a r e d節,並且開始將它們放回到預設資料節中。需要記住的是,編譯器只將已經初始化的變數放入新節中。例如,如果我從前面的程式碼片段中刪除初始設定變數(如下面的代碼所示),那麼編譯器將把該變數放入S h a r e d節以外的節中。

#pragma data_seg("Shared")
LONG g_lInstanceCount;
#pragma data_seg()
Microsoft 的Visual C++編譯器提供了一個A l l o c a t e說明符,使你可以將未經初始化的資料放入你希望的任何節中。請看下面的代碼:

// Create Shared section & have compiler place initialized data in it.
#pragma data_seg("Shared")

// Initialized, in Shared section
int a = 0;

// Uninitialized, not in Shared section
int b;

// Have compiler stop placing initialized data in Shared section.
#pragma data_seg()

// Initialized, in Shared section
__declspec(allocate("Shared")) int c = 0;

// Uninitialized, in Shared section
__declspec(allocate("Shared")) int d;

// Initialized, not in Shared section
int e = 0;

// Uninitialized, not in Shared section
int f;        
上面的注釋清楚地指明了指定的變數將被放入哪一節。若要使A l l o c a t e聲明的規則正確地起作用,那麼首先必須建立節。如果刪除前面這個代碼中的第一行#pragma data_seg,上面的代碼將不進行編譯。
之所以將變數放入它們自己的節中,最常見的原因也許是要在. e x e或D L L檔案的多個映像之間共用這些變數。按照預設設定, . e x e或D L L檔案的每個映像都有它自己的一組變數。然而,可以將你想在該模組的所有映像之間共用的任何變數組合到它自己的節中去。當給變數分組時,系統並不為. e x e或D L L檔案的每個映像建立新執行個體。

僅僅告訴編譯器將某些變數放入它們自己的節中,是不足以實現對這些變數的共用的。還必須告訴連結程式,某個節中的變數是需要加以共用的。若要進行這項操作,可以使用連結程式的命令列上的/ S E C T I O N開關:


/SECTION:name,attributes
在冒號的後面,放入你想要改變其屬性的節的名字。在我們的例子中,我們想要改變S h a r e d節的屬性。因此應該建立下面的連結程式開關:

/SECTION:Shared,RWS
在逗號的後面,我們設定了需要的屬性。用R代表R E A D ,W代表W E I T E,E代表E X E C U T E,S代表S H A R E D。上面的開關用於指明位於S h a r e d節中的資料是可以讀取、寫入和共用的資料。如果想要改變多個節的屬性,必須多次設定/ S E C T I O N開關,也就是為你要改變屬性的每個節設定一個/ S E C T I O N開關。
也可以使用下面的句法將連結程式開關嵌入你的原始碼中:


#pragma comment(linker, "/SECTION:Shared,RWS")
這一行代碼告訴編譯器將上面的字串嵌入名字為“ . d r e c t v e”的節。當連結程式將所有的. o b j模組組合在一起時,連結程式就要查看每個. o b j模組的“ . d r e c t v e”節,並且規定所有的字串均作為命令列參數傳遞給該連結程式。我一直使用這種方法,因為它非常方便。如果將原始碼檔案移植到一個新項目中,不必記住在Visual C++的Project Settings(項目設定)對話方塊中設定連結程式開關。
雖然可以建立共用節,但是,由於兩個原因, M i c r o s o f t並不鼓勵你使用共用節。第一,用這種方法共用記憶體有可能破壞系統的安全。第二,共用變數意味著一個應用程式中的錯誤可能影響另一個應用程式的運行,因為它沒有辦法防止某個應用程式將資料隨機寫入一個資料區塊。

用這個方法可以在兩個進程間共用記憶體資料。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.