1 / 41

Thread Synchronization with Kernel object

Thread Synchronization with Kernel object. 井民全. 為何要使用 Kernel Object 來做同步 ?. User Mode Synchronization 的好處 速度快 User Mode 的限制是 只能處理單一 Process 內的 threads 之間同步 在單一 CPU 情況下 , 你不能設定等待時間 Interlocked 家族函式不能讓 thread 進入 wait 狀態. 不能處理 其他 Process 之間的同步. EnerCriticalSection(…) 飢餓狀態.

nero
Download Presentation

Thread Synchronization with Kernel object

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Thread Synchronization with Kernel object 井民全

  2. 為何要使用 Kernel Object 來做同步? • User Mode Synchronization的好處 • 速度快 • User Mode 的限制是 • 只能處理單一 Process內的 threads之間同步 • 在單一CPU情況下, 你不能設定等待時間 • Interlocked 家族函式不能讓 thread 進入 wait 狀態 不能處理 其他 Process之間的同步 EnerCriticalSection(…) 飢餓狀態 讓 CPU 空轉

  3. Kernel Object 的性質 • 每一個核心物件都有紀錄狀態 • Process object 被建立  process handle non-signal • 當 Process 終止時  process handle signal • 希望等待某個 Process terminated, 怎麼辦? • 只要檢查該Process是否 signal即可 等待的過程中,你的 thread 不會佔住 CPU time

  4. 最多只能64 (MAXIMUM_ WAIT_OBJECTS) 有哪些 Wait functions 呢? DWORD WaitForSingleObject( HANDLE hObject, DWORD dwMilliseconds); 等待某一個 kernel object signal • 等待時間限制: • 一般 INFINITE OS 會幫你監控 Kernel Object 的狀態, 在 Signal 之前. 你的 Thread 不會被安排進入排程 DWORD WaitForMultipleObjects( DWORD dwCount, CONST HANDLE* phObjects, BOOL fWaitAll, DWORD dwMilliseconds); 數量 Object 陣列 把要等待的 Object 放到CONST HANDLE陣列中

  5. DWORD dw = WaitForSingleObject(hProcess, 5000); switch (dw) { case WAIT_OBJECT_0: break; case WAIT_TIMEOUT: break; case WAIT_FAILED: // Bad call to function (invalid handle?) break; } 等待的 Process terminate 等待的 Process 並沒有在 5000 millisecond內 terminate 等待時間設定為 INFINITE是危險的 • 若等待的 Object 永遠不會 Signal • 則會造成 dead-lock(永遠不會醒來) 比較好的策略是這樣

  6. 如果我要等待多個物件怎麼辦? • 使用 WaitForMultiObject(…) • 最多只能等待 64個物件 DWORD WaitForMultipleObjects( DWORD dwCount, CONST HANDLE* phObjects, BOOL fWaitAll, DWORD dwMilliseconds); TRUE 必須等待所有的 object 都 signal FALSE  只要有一個 object signal 即可

  7. 關鍵片段 由 WAIT_OBJECT_0 決定 如何得知是那一個 Object Signal ? 一個簡單的範例 HANDLE h[3]; h[0] = hProcess1; h[1] = hProcess2; h[2] = hProcess3; DWORD dw = WaitForMultipleObject(3,h, FALSE, 5000); switch (dw) { case WAIT_TIMEOUT: break; case WAIT_FAILED: // Bad call to function (invalid handle?) break; case WAIT_OBJECT_0 + 0: break; case WAIT_OBJECT_0 + 1: break; case WAIT_OBJECT_0 + 2: break; } 現在設定為只要一個 Object signal 就跳離 h[0] terminated h[1] terminated h[2] terminated

  8. 有哪些 Kernel Object 支援 signal? • Processes • Thread • Jobs • Files • Console input • File change notifications • Events • Waitable timers • Semaphores • Mutexes

  9. 現在來討論 多個 Threads 同時 Wait 一個 object 的情況 • 當成功的呼叫 WaitForSingleObject 等functions • 通常會改變 kernel object 的狀態 Side effect • 不同的object 有不同的 Side effect • Auto-reset object • 建立 non-signal • Terminated  signal 通知其他等待的thread • 當有人成功的呼叫 Wait function  自動設回 non-signal 有些 Wait function不會改變 這些 object 的狀態 例如: Thread , Process Object 就沒有 Side effect

  10. 考慮下面的情況 • 若有兩條threads, 同時執行下面的程式碼 HANDLE h[2]; h[0] = hAutoResetEvent1; h[1] = hAutoResetEvent2; WaitForMultipleObjects(2, h, TRUE, INFINITE); 等待的是兩個 Auto-reset objects 兩個 Object 必須都是 signal 才會跳出來

  11. 統一設定 Signal 統一設定為 non-Signal BOOL SetEvent (HANDLE hEvent); BOOL ResetEvent(HANDLE hEvent); 利用 Event Object 作行程間的同步 • 策略是 • 主程式建立 Event Object • Child Process等待該 Event Signal後才做事 TRUE: 設定為必須手動,才會 Reset False: Auto-reset 建立一個 Global 等級 Event HANDLE CreateEvent( PSECURITY_ATTRIBUTES psa, BOOL fManualReset, BOOL fInitialState, PCTSTR pszName); TRUE: 剛建立時,這個 Object 就是 signal 狀態 False: non-signal 狀態 決定你的 childe process 是否可以存取這個 Object HANDLE OpenEvent( DWORD fdwAccess, BOOL fInherit, PCTSTR pszName); 你想要對該 Object 的動作 目前我們使用 SYNCHRONIZE 另外的 Process 則用 OpenEvent 開啟

  12. Event 名稱為 Fired 設定為手動reset HANDLE hEvent=CreateEvent(NULL,TRUE,FALSE,"Fired"); if(hEvent == NULL) ErrorExit( "建立 Event 發生錯誤" ); 初值為 non-signal 只希望用在同步上 HANDLE hEvent=OpenEvent(SYNCHRONIZE, FALSE,"Fired"); 不希望讓其他 衍生process 使用 WaitForSingleObject(hEvent,INFINITE); 簡單同步範例 建立一個 Global 等級 Event 建立兩個 ChildProcess 等待工頭的命令 CreateEventDemo

  13. 1 兩個 Processes 同時收到 Signal 2 只有一個 Process 收到 Signal 補充 設定為手動reset HANDLE hEvent=CreateEvent(NULL,TRUE,FALSE,"Fired"); if(hEvent == NULL) ErrorExit( "建立 Event 發生錯誤" ); • 如果: Manual 設定為 TRUE, 則當有人 • SetEvent hEvent 時, hEvent 會 Signal • 造成 Process A 與 Process B 成功的 wait 到這項訊號 • 如果: Manual 設定為 FALSE, 則當有人 • SetEvent hEvent 時, hEvent 會 Signal • 造成 Process A 或 Process B 只有一個會成功的 wait到這項訊號 Process A Process B 因為其中一個 成功 wait hEvent 後, hEvent 會自動設定為 non-signal 造成另一個 process 繼續的等待

  14. 我們現在討論 Waitable Timer來做同步化的動作 • 固定時間啟動工作 • 週期的啟動工作

  15. 最簡單的範例: CreateWaitableTimer 主控程式 我們的Timer 名稱 HANDLE hTimer; hTimer =CreateWaitableTimer(NULL, FALSE, "MyControlTimer"); const int nTimerUnitsPerSecond = 10000000; LARGE_INTEGER li; li.QuadPart= -1 * (10 * nTimerUnitsPerSecond); SetWaitableTimer(hTimer,&li, 0, \ // period =0 NULL, NULL, \ FALSE); Auto-reset // 1 秒 = 10,000,000 (100 nanosecond) 負數表示 相對啟動時間 設定 10 秒鐘後,這個 object 會 signal complete routine 參數 不使用 complete routine TRUE: 當 signal 後, 將系統從 suspended power conservation restore 回來

  16. 最簡單的範例: OpenWaitableTimer 當 Timer Signal 才會執行的 Process HANDLE hTimer=NULL; while(hTimer==NULL) hTimer=OpenWaitableTimer(SYNCHRONIZE, FALSE, "MyControlTimer"); // 當 Timer signal才能執行工作 WaitForSingleObject(hTimer,INFINITE); 我們等待的Timer 名稱 這個Timer不希望能被繼承 這個Timer指能用在 wait function 注意: Timer 一定要先建立,也就是說,主控程式要先執行

  17. 最簡單的範例: 程式架構 設定 Timer 啟動時間 五秒後,啟動 拉動 slider 功能 啟動時,就直接 Create Timer 物件 WaitableTimerDemo

  18. 那麼要如何設定時間呢? 我希望能在西元2005年1月1日下午1時整,然後每隔6小時啟動一次信號 我該如何指定啟動時間? 週期? SetWaitableTimer(hTimer,&li, 0, NULL, NULL, FALSE);

  19. 我們使用 SYSTEMTIME 指定年月日 西元2005年1月1日下午1時整 SYSTEMTIME st; st.wYear = 2005; // Year st.wMonth = 1; // January st.wDayOfWeek = 0; // Ignored st.wDay = 1; // The first of the month st.wHour = 13; // 1PM st.wMinute = 0; // 0 minutes into the hour st.wSecond = 0; // 0 seconds into the minute st.wMilliseconds = 0; // 0 milliseconds into the second

  20. 將 System Time 轉成適當的參數 FILETIME(從 1601年 1 月 1 號 12:00 AM 到現在經過多少100 ns單位) 將SystemTime轉成以100 ns為單位 Local FileTime 1 FILETIME ftLocal, ftUTC; SystemTimeToFileTime(&st, &ftLocal); 前面算出來的 SystemTime 2 將 Local FileTime  Coordinated Universal Time(英國格林威治當地目前時間) LocalFileTimeToFileTime(&ftLocal, &ftUTC); 主要是處理位址對齊的問題  LARGE_INTEGER 是一個 union 可以把 LowPart 與 HiPart 組合成一個 64-bit 大數 3 FileTime  LARGE_INTEGER 表示式 LARGE_INTEGER liUTC; liUTC.LowPart = ftUTC.dwLowDateTime; liUTC.HighPart = ftUTC.dwHighDateTime; 設定時間 週期為 6 hours = 6*60*60*1000 milliseconds SetWaitableTimer(hTimer,& liUTC, 6 * 60 * 60 * 1000, NULL, NULL, FALSE); 要求使用 UTC-Based absolute time

  21. Windows 中的 Time 觀念 人看的懂得 機器看的 本地時間 (相對於 UTC) MS-DOS 16-bit 應用程式記錄時間的格式 若使用 GetFileTime 讀取 MS-DOS 應用程式建立的檔案時間, 則系統會自動幫你把 MS-DOS Time 轉換成 UTC-based FileTime 傳回來 因為 System Time 可以往前或往後 更改, 所以若要記錄計算時間可以使用 Windows Time

  22. 補充 FileTime 基本觀念 • The system records file times whenever applications create, access, and write to files • 例外: • NT FAT create time , write time and access time 使用不同的 time resolution • NTFS access time 的 resolution = 1 hour • 如何取得指定檔案的 file time ? • 呼叫 GetFileTime函式: 他會將資料複製到 FILETIME 的結構中 • FindFirstFile , FindNextFile: 會將資料複製到 WIN32_FIND_DATA 結構中

  23. 補充 FileTime 基本觀念 • 設定 file time? • 呼叫 SetFileTime 函式 • 比較不同檔案的 time? • 呼叫 CompareFileTime 函式 • 注意事項: 呼叫前,應該使用 SystemTimeToFileTime 先把日期等 SystemTime 資訊轉成 FileTime * 當然, 你也可以呼叫 FileTimeToSystemTime 將 FileTime 資料轉成看的懂得 SystemFile 然後把 FileTime 餵給 SetFileTime 傳回 0 表示時間一樣

  24. 補充 日光節約時間概念(Daylight Saving Time ) • 也叫做夏令時(Summer Time ) • 將標準時撥快一小時,分秒不變,恢復時再撥慢一小時。 • 首先提倡者1908年英國議會 夏天時天亮的比較早 理由為:若將時刻改早,則學校工廠機關同樣提早上課上班,而一般人民勢必早睡早起,早睡可節省燈火. 早起可呼吸新鮮空氣,而且接觸日光機會較多,足以增進一般國民的健康。 當時英國學者多數反對,因而英國國會未通過此議案 西元1916年歐戰期問,德國因經濟上關係,首先實行「經濟時」,也就是所謂「日光節約時」,而奧地利、荷蘭、丹麥相繼採用。英國自感驚異,不久亦採用。 我國民國六十九年因民間對於日光節約時反應不佳,停止使用 參考資料: http://e-service.cwb.gov.tw/docs/V3.0/astronomy/calendar/summert.htm

  25. Data Alignment 觀念 • 當你要存取的資料所在位址與資料大小相除餘數 != 0,就會出現這個問題! • char 型態絕不會出現 data alignment 問題 • 考慮下面 DWORD情況 x0007 x0006 x0005 DWORD 以4個 byte 為單位 x0007 x0004 資料發生 Alignment x0006 x0003 x0005 x0002 x0004 x0001 x0003 Buffer x0000 x0002 會產生 alignment 的程式片段 x0001 DWORD data; data=*(DWORD*)((char*) Buffer+1) x0000

  26. Data Alignment 觀念 • 如 32-bit CPU一次讀資料從位址 0 ~ 3到暫存器中, 下次讀資料從位址 4~7 • 絕不會一次由位址2~5 把資料放到暫存器中, 因為 byte 2~5 分別在兩個 word 中 • 如果要讀位址 2~5的資料,必須 • 讀取 0 ~ 3 與 3~7 • 並且做 shift 與 extract 的動作 讀取位址 0 ~3 的資料 取出位址 2 ~3 的資料 讀取位址 4 ~7 的資料 組合 2~3 與 4~5  2 ~ 5 資料

  27. Data Alignment 所造成的問題 • CPU必須進行數次記憶體存取 • 程式效能降低 • 移植程式時會遇到問題 • X86 會自動修正這個問題, • Alpha CPU會丟出 INT 17h 中斷通知 OS EXCEPTION_ DATATYPE_MISALIGNMENT 例外 DWORD data; data=*(DWORD*)((char*) Buffer+1) 利用 Compiler 修正 Data Alignment的問題 DWORD data; data=*(UNALIGNED DWORD*)((char*) Buffer+1)

  28. 討論資源的問題 • 當我們可存取的資源有限時,有甚麼好方法進行個 thread 間的管理呢? 我們可以使用 Semaphore管理資源 目前可用的資源數目 設定最大資源數目 HANDLE CreateSemaphore( PSECURITY_ATTRIBUTE psa, LONG lInitialCount, LONG lMaximumCount, PCTSTR pszName);

  29. 程式設計的流程 目前可用的資源數目 設定目前資源總數,可用資源 // 目前有2個名額,總共有2個資源 hsem = CreateSemaphore(NULL, 2, 2, NULL); 接受 Request 設定最大資源數目 DWORD dwWaitResult; dwWaitResult=WaitForSingleObject(hsem,0); 判斷資源是否足夠 通知使用者 建立新的 thread 處理 request 釋放資源 ReleaseSemaphore(hsem,1,NULL ) 不需要傳回 previous count

  30. 視窗架構 等待時間 = 0 ReleaseSemaphore(hsem,1,NULL ) WaitForSingleObject(hsem,0) 釋放一筆資源 傳回上一次的 可用資源數目 資源不足 SemaphoreDemo

  31. Mutex Kernel Object

  32. Introduction to Mutex • 保證只有一條 thread可以獨佔資源 • 具有擁有性 (thread ownership ) • Mutex 物件會紀錄擁有者的 thread ID • 當正在等待的 mutex 已經擁有了,則會直接遞增 recursion counter,並繼續執行 • V.S.CriticalSection object • Mutex 比較慢 (因為 mutex 是 kernel object) • 內容: Mutex 的資料內容 Usage counter Thread ID Recursion counter Kernel Object 被索引的次數 ? 擁有者的 thread id 擁有者擁有目前物件的次數

  33. 最簡單的 Mutex 範例 Writer 123 123 123 連續寫入資料 連續讀取資料 Reader SimpleMutex

  34. Mutex VS EventMutex ‘s thread ownership InitialOwner: TRUE calling thread 擁有這個mutex Security Attribute Name HANDLE hMutex=CreateMutex(NULL, FALSE, NULL); WaitForSingleObject(hMutex, INFINITE); // 再次進入 WaitForSingleObject(hMutex,INFINITE); cout << "Mutex 使用次數遞增" << endl; RelaseMutex(hMutex); ReleaseMutex(hMutex); 1 對於已經擁有的 Mutex 可以立即的進入 schedulable 階段 2 使用時機: 1. 所以當你的程式要求資料寫完後,必須立即 讀取,則請使用 Event. 2. 當你希望, Reader 與 Writer 能夠獨立楚 理, 則你可以使用 Mutex 不能再繼續寫資料了,會被 Event 擋住 誰先搶到誰就可以繼續進入 請看 Mutex_VS_Event

  35. 重要的是: mutex 會記得是誰搶到權杖. 對於 搶到的人有特別的待遇. Event 不管是誰, 只要有人搶到權杖, 別人就要等 補充: 我把 Mutex VS. Event 程式碼列出來 我們現在是討論: 是否可以再進入 Critical Section 的問題 void main(){ HANDLE hMutex=CreateMutex(NULL, FALSE, NULL); WaitForSingleObject(hMutex, INFINITE); // 再次進入 WaitForSingleObject(hMutex,INFINITE); cout << "Mutex 使用次數遞增" << endl; RelaseMutex(hMutex); ReleaseMutex(hMutex); } /* // ====== 比較一下 Event ======== HANDLE hEvent=CreateEvent(NULL,FALSE,TRUE,NULL); WaitForSingleObject(hMutex,INFINITE); // 無法進入 WaitForSingleObject(hEvent,INFINITE); cout << "無法進入" << endl; // ==== End =========== */

  36. 放棄 Abandonment Issues (ExitThread, TerminateThread, ExitProcess, TerminateProcess ) • 當一個擁有該 mutex 的 thread 停止執行時, 將沒有任何人可以 release mutex.  Abandoned mutex OS 隨時監視系統中的物件, 發現 abandoned mutex 1. Reset the mutex 2. 公平的挑選一個正在等待的 thread 3. Wait function 傳回 WAIT_ABANDONED

  37. 與 CriticalSection 比較 接下頁

  38. 各種 Kernel Object 的比較

  39. 有人使用 semorphore 有人使用 mutex 有人在 critical section 中

  40. End

More Related