linux裝置驅動程式設計(17.2)(完)
2023年06月05日
[b]訪問使用者空間[/b][b][/b]
核心的第乙個2.1版引入了一種從核心**訪問使用者空間的新(更好)方法。這個改變修正了乙個長期存在的錯誤行為並增強了系統的效能。
當你位核心2.1編譯**,並需要訪問使用者空間時,你需要包含,而不是。你還必須使用乙個與2.0不同的函式集。不用說,標頭檔案sysdep-2.1.h盡可能地照顧了這些不同,允許你在2.0上編譯時使用2.1的語義。
在使用者訪問中最令人注意的不同時verify_area沒有了,因為多數驗證都由cpu完成了。關於這個主題的細節見本章後面的「處理核心空間錯誤」。
可被用來訪問使用者空間的新的函式集是:
int access_ok(int type, unsigned long addr, unsigned long size);
如果當前程序被允許訪問位址addr處的記憶體,函式返回真(1),否則為假(0)。這個函式取代verify_area,儘管它進行較少的檢查。和老的verify_area接收一樣的引數,但是要快的多。在你復引用乙個使用者空間位址之前,這個函式應該被呼叫對之進行檢查;如果你沒有檢查,使用者有可能會訪問和修改核心記憶體。本章後面的「虛擬記憶體」一節更細緻地解釋了這個問題。幸運的是,下面描述的大多數函式都替你進行了這個檢查,因此你實際上並不需要呼叫access_ok,除非你選擇這樣做。
int get_user(lvalue, address);
在2.1核心中使用的巨集get_user與我們在2.0中使用的並不相同。其返回值在成功時為0,否則為乙個負的錯誤**(總是-efault)。這個函式的淨效果是將從位址address取得的資料賦給lvalue。在通常的c語言含義中,這個巨集的第乙個引數必須是乙個lvalue*。與2.0版中的這個函式類似,資料項的實際大小依賴於address引數型別。這個函式在內部呼叫access_ok。
int __get_user(lvalue, address);
這個函式完全類似get_user,但它不內部呼叫access_ok。當你訪問乙個已經從同一核心函式內部檢查過的使用者位址時,你應該呼叫__get_user。
get_user_ret(lvalue, address, retval);
這個巨集是呼叫get_user的快捷方式,如果函式失敗則返回retval。
int put_user(expression, address);
int __put_user(expression, address);
put_user_ret(expression, address, retval);
這些函式與它們的get_對應者非常類似,只是它們是向使用者空間寫,而不是讀。成功時,值expression被寫到位址address。
unsigned long copy_from_user(unsigned long to, unsigned long from, unsigned long len);
這個函式從使用者空間複製資料到核心空間。它代替舊的memcpy_tofs呼叫。這個函式內部呼叫access_ok。返回值是未能傳送的位元組數。這樣,如果發生錯誤,返回值必然大於0;在那種情況下,驅動程式返回-efault,因為錯誤是由錯誤的記憶體訪問引起的。
unsigned long __copy_from_user(unsigned long to, unsigned long from, unsigned long len);
這個函式與copy_from_user一樣,但它不內部呼叫access_ok。
caopy_from_user_ret(to, from, len, retval);
這個巨集是內部呼叫copy_from_user的快捷方式;如果失敗,則從當前函式返回。
unsigned long copy_to_user(unsigned long to, unsigned long from, unsigned long len);
unsigned long __copy_to_user(unsigned long to, unsigned long from, unsigned long len);
copy_to_user(to, from, len, retval);
這些函式被用來將資料複製到使用者空間,它們的行為非常類似於它們的copy_from的對應者。
2.1版核心還定義了其它訪問使用者空間的函式:clear_user,strncpy_from_user,和strlen_user。我不打算討論它們了,因為linux2.0中沒有這些函式,並且驅動程式的**也很少用到它們。有興趣的讀者可以看看。
[b]使用新的介面[/b][b][/b]
訪問使用者空間的新的函式集初看起來可能有點令人失望,但它們的確使程式設計師的日子好過的多了。在linux2.1上,不再需要顯式地檢查使用者空間;access_ok一般不需要呼叫。使用新介面的**可以直接進行資料傳送。_ret函式在實現系統呼叫時證明是相當有用的,因為乙個使用者空間的失敗通常導致系統呼叫的乙個返回-efault的失敗。
因此,乙個典型的read實現,看起來如下:
long new_read(struct inode *inode, struct file *filp, char *buf, unsigned long count);
注意使用不進行檢查的__copy_to_user是因為呼叫者在把資料傳輸分派到檔案操作之前已經檢查了使用者空間。這就象2.0,read和write不需要呼叫verify_area。
類似地,典型的ioctl實現看起來如下:
int new_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
{/* device-specific checks, if needed */
switch(cmd){
case new_getvalue:
put_user_ret(new_value, (int *)arg, -efault);
break;
case new_setvalue:
get_user_ret(new_value, (int *)arg, -efault);
default:
return
Linux裝置驅動程式設計例項
linux系統中,裝置驅動程式是作業系統核心的重要組成部分,在 與硬體裝置之間 建立了標準的抽象介面。通過這個介面,使用者可以像處理普通檔案一樣,對硬體設 備進行開啟 open 關閉 close 讀寫 read write 等操作。通過分析和設計設 備驅動程式,可以深入理解linux系統和進行系統開...
Linux裝置驅動程式設計例項
linux系統中,裝置驅動程式是作業系統核心的重要組成部分,在 與硬體裝置之間建立了標準的抽象介面。通過這個介面,使用者可以像處理普通檔案一樣,對硬體裝置進行開啟 open 關閉 close 讀寫 read write 等操作。通過分析和設計裝置驅動程式,可以深入理解linux系統和進行系統開發。本...
Linux裝置驅動程式設計例項
linux系統中,裝置驅動程式是作業系統核心的重要組成部分,在與硬體裝置之間建立了標準的抽象介面。通過 這個介面,使用者可以像處理普通檔案一樣,對硬體裝置進行開啟 open 關閉 close 讀寫 read write 等操作。通過分析和設計裝置 驅動程式,可以深入理解linux系統和進行系統開發。...