2017年7月31日 星期一

windows device and driver -- WDM, KMDF and UMDF

WDM

Windowd Driver Model 是 win2000 之後微軟提出的一種 driver model,在那時微軟也提出了 pnp device 的概念,基本上目前所有的 kernel mode windows driver 都屬於 WDM driver

KMDF

Kernel Mode Driver Framework 是微軟開發的一套 framework 來幫助開發者可以更快的開發 kernel driver,它幫忙做好了一些 OS 相關,driver 開發者必須要面對但是可能跟他的 driver business logic 無關的部分,例如 pnp 跟 power managerment

UMDF

User Mode Driver Framework 跟 KMDF 合起來就成了一套微軟所謂的 WDF(windows driver framework),它可以讓開發者在 user mode 開發 driver,但是應該還是需要有一部分程式是跑在 kernel mode 的,不是所有的程式都在 user mode

2017年7月29日 星期六

windows device and driver -- SetupAPI

微軟提供的 SetupAPI 是 user mode 的一套 API, 可以做安裝 device driver 或是查詢系統上的 device 資料,刪除 device 等等

在使用 SetupAPI 前,必需要先了解 device setup class,device interface class,device information set 這些跟 windows 上裝置管理相關的概念

Device Setup Class

微軟將以相同方式安裝和設定的 device 歸類成一個個的 device setup class,例如 IDE 硬碟,usb 隨身碟,都被歸類在 disk drives device setup class

微軟為幾乎所有的裝置都定義了 device setup class,每一個 device setup class 都有對應的 GUID,可以在 Devguid.h 裡面找到其 GUID並在程式中使用,系統上的 device 也可以在 ..\CurrentControlSet\Control\Class\ClassGuid 找到該 device 的資料

Device Interface Class

Device interface class 是一組接口的定義,可以讓 device driver 將它的功能讓其他的 driver,系統元件或是 user mode 程式來使用,可以想成若是一個 driver 註冊了某個 device interface class,它就必定支援某些功能

每個 device interface class 都有對應的 GUID,微軟定義了大部分通用的 device interface class

不同類型的裝置可能會屬於同一個 device interface class,例如 usb 滑鼠或 PS/2 接口的滑鼠都屬於 GUID_DEVINTERFACE_MOUSE

drivers 通常只註冊一個 device interface class,不過也有例外,例如一個可以被 mounted 的 disk 應該註冊 GUID_DEVINTERFACE_DISK 和 MOUNTDEV_MOUNTED_DEVICE_GUID

Device Information Set

Windows 中屬於 device setup class 或 device interface class 的 device 必須要透過 device information set 和 device information element 來操作

device information set 中包含 device information element,device information element 又包含了 device devnode 的 handle 和指向 device interface list 的 pointer

我們的程式實際上看不到這些資料結構內含哪些資料,因為這是 Windows 內部使用的資料結構,我們只會拿到 device information set 的 handle 並且透過 handle 來呼叫 SetupAPI

通常是用 SetupDiGetClassDevs API 來取得 device information set 的 handle,而且 device information set 裡面的元素不是固定的,而是動態的根據不同的條件系統幫忙 create 出來的一個結構,例如用不同的 disk type 的 device setup class 參數,SetupDiGetClassDevs API 會回傳只包含 disk type 的 device information set 回來

https://docs.microsoft.com/en-us/windows-hardware/drivers/install/images/devinfosets.png

要怎麼使用 SetupAPI

了解了這些概念後,就可以使用 SetupAPI 開始對 device 做一些操作,例如想知道系統上有多少磁碟機,可以用 GUID_DEVCLASS_DISKDRIVE device setup class 當參數傳給 SetupDiGetClassDevs

SetupDiGetClassDevs 有提供 enumerator 參數可以讓 caller 做進一步篩選想選取的 device,例如可以傳 "USBSTOR" 參數,只取得 usb 磁碟機的 device information set

取得 device information set 之後,用SetupDiEnumDeviceInfo 取得每一個 device 的 SP_DEVINFO_DATA 資料結構,再用 SetupDiGetDeviceRegistryProperty 取得 device 的資訊例如 friendly name

除了取得 device 的資訊,還可以對這些 device 做更多的操作,例如想要 uninstall 某些 device,一樣取得 device information set 與各個 device 的 SP_DEVINFO_DATA 之後,可以呼叫 DiUninstallDevice,就可以將 device 移除

2017年7月24日 星期一

word 自動生成目錄

markdown Word 可以將各個段落之標題設定成大綱,並且可以自動生成目錄(Table of content),可[參考](http://blog.cnyes.com/my/txgkfvsts/article1114948)。在生成目錄時還可以設定要生成幾層目錄以及是否要做成超連結。 這樣寫完文件後,就不用煩惱目錄到底要怎麼做了。

2017年7月20日 星期四

裝置管理員顯示隱藏裝置

簡單兩個 command 就可以讓裝置管理員顯示隱藏的裝置, 例如未連接的 USB device

set devmgr_show_nonpresent_devices=1
start devmgmt.msc 

visual studio Ctrl + F5 無法繼承系統環境變數

在寫 C# 時遇到一個問題, 修改了系統環境變數 PATH 之後, visual studio Ctrl + F5 重新開啟程式後卻發現程式裡面 PATH 環境變數一直都沒有更新, 但是直接雙擊 build 好的 exe 檔卻可以讀到更新後的 PATH 變數, 後來才想到 Ctrl + F5 後程式是由 visual studio 也就是 xxx.vshost.exe 叫起來, 所以他繼承的一直是 xxx.vshost.exe 的環境變數, 當然一直是舊的囉

所以, 只要把 visual studio 重開, 讓他重新讀取新的環境變數就可以了, 環境變數造成的問題真是有一些小眉角需要注意

2017年7月13日 星期四

32bits windows 打開 PAE 後無法使用超過 4GB 記憶體


雖然有 PAE 這個技術可以讓 32bits windows 用到 4GB 以上的 memory, 但是 PAE 只能在 windows XP 上 work, 準確說來, XP SP2 之後的 32bits windows client 版本, 即使你打開 PAE, 還是無法突破 4GB 的限制

到底是誰限制了 4GB 記憶體


既然有 PAE, 那就不會是 hardware 的限制, 而是軟體, 也就是 windows 作業系統強制你無法使用到 4GB 的記憶體
Windows:我知道你裝了超過 4GB 記憶體, 我也的確抓得到那些記憶體, 但我就是不讓你用, 打我阿笨蛋

微軟為什麼要這麼做


官方說明是因為很多 driver 在開發時並沒有預期到系統會使用到超過 4GB 記憶體, 所以系統使用超過 4GB 記憶體的話這些 driver 就會無法正常工作導致系統當掉

那為什麼 Server 版本 32bits windows 可以用超過 4GB


官方說明是因為 server 版本的 driver 比較穩定, 所以 server 版就讓你用拉, 還不跪下謝恩

Reference
http://www.geoffchappell.com/notes/windows/license/memory.htm
https://superuser.com/questions/52275/how-can-i-enable-pae-on-windows-7-32-bit-to-support-more-than-3-5-gb-of-ram
https://blogs.technet.microsoft.com/markrussinovich/2008/07/21/pushing-the-limits-of-windows-physical-memory/

2017年7月12日 星期三

python 的 logging 機制

log 是寫程式最常用到的功能之一, C# 有好用的 log4net, java 有 log4j, 而 python 不需要第三方函式庫, 內建就有 logging 模組可使用

如果只是想做個小工具或 prototype, logging 模組有提供 module level 的函式, 直接呼叫 logging.info 或 logging.error 就可以在 console 印出 log 來, 不過預設設定是會濾掉 info 以下 level 的 log, 這之後可以用 Logger.setLevel 修改

import logging
logging.warning('Watch out!')  # will print a message to the console
logging.info('I told you so')  # will not print anything 

若是想將 log 寫入到檔案中, 只需用 logging.basicConfig 做些設定即可 

import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

但如果我有更複雜的需求, 比如想同時寫到 console 跟 file, 或是希望自訂 log 印出的格式應該怎麼做?這時需要先理解 logging 模組的架構, 可以先看下面的 log 架構圖

https://docs.python.org/2/_images/logging_flow.png

Logger 

Logger 是用來產生 log message 的類別, 他有 info, error, debug, warn 跟 critical 函式可以產生不同 level 的 log, client 必須先取得 logger 才能寫入 log message

程式中可以有多個 logger, 每個 logger 有不同的 name, name 還可以決定 logger 間的繼承關係, 例如有個 logger 叫 foo, 另一個 looger 叫 foo.bar, 則 foo.bar 就會繼承 foo, logging 模組內建有一個 root logger, 所有的 logger 都會繼承它, 而前面所說的 logging.info 等模組層級函式內部就是使用 root logger

Handler

logger 中可以 attach 上多個 handler, logger 產生 log 後, handler 負責將其送到目的地去, 前面所說若想同時將 log 寫到 console 和 file 中, 只需要將 logging 模組內建的 StreamHandler 和 FileHandler 都 attach 到同一個 looger 中, 就可以將 log 同時寫到 console 跟 file

Formatter

logging 中有 Formatter 可以讓使用者客製化想印出的 log 格式, 只要將 formatter 設定好並且 attach 到 handler 中就可以

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)


繼承關係

在 create logger 時你可以不指定這些 handler, formatter, 那新創建的 logger 的 handler 與 formatter 等設定就會繼承它的 parent, 例如 root logger


以上這些都可以用 code 設定好, 但是一般建議的方式還是把這些關於 log 設定的部分寫在額外的 log config 中方便管理維護, python logging 模組提供了兩種 config 方式, ini 式的 config 和 dictionary 式的 config,

dictionary 式的 config 看起來更直覺一點, 不過需要使用到 yaml package 來做讀取

logging.config.dictConfig(yaml.load(open('logging.conf', 'r')))

2017年7月9日 星期日

Linux IPC 機制 -- named pipe 與 unix domain socket

named pipe 與 unix domain socket 都是 Linux 上的 IPC 機制, 兩個機制都會在檔案系統上建立一個特殊的文件, 有時候可能會不確定這兩種機制的區別, 但其實兩者是有很多不同的地方

names pipe 是單向的傳輸資料, 但是 unix domain socket 可以雙向的傳輸資料, 如果想用 names pipe 雙向傳輸資料, 則需要兩個 names pipe

一個 unix domain socket 可以用來在很多個 process 中做資料傳輸, 一個 server process 可以在一個 unix domain socket 上同時接收很多的 client process, 若是 names pipe 則 server 跟每個 client 之間需要各自的 named pipe file

大致來說 unix domain socket 比 names pipe 擴充性更好, 但是若是簡單的場景中使用 named pipe 會更容易, 因為 unix domain socket 需要用 socket 系列 API 存取, 但存取 named pipe 就跟存取一般檔案一樣沒有分別