歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> 使用ioctl“實現”自定義的系統調用

使用ioctl“實現”自定義的系統調用

日期:2017/3/3 16:32:09   编辑:關於Linux

最近做的項目跟Linux內核的關系比較大,我們的項目需要在用戶態觸發一些 內核態的代碼運行。眾所周知,內核態的代碼是不能直接被用戶態代碼調用的, 用戶態代碼觸發內核態代碼的必須要經過系統調用。

為什麼選擇 ioctl

那麼該如何實現我們的需求呢?有幾種方法:

改寫內核,擴大系統調用表,添加新的系統調用

利用內核模塊,覆蓋沒被使用或這使用頻率很低的一個系統調用的處理函數

利用已有的系統調用,比如ioctl,來“實現”自定義的系統調用。

第一種方法需要修改內核,適用面比較窄;第二種方法hack意味很濃,沒有 被使用的系統調用號有限,不同模塊可能都使用這種機制,可能會產生沖突。最 終我們選擇了第三種方法。下面將一一道來。

ioctl系統調用是用戶態控 制設備的接口,其用戶態原型為

int ioctl(int d, int request, 

...)

第一個參數是打開的設備文件的文件描述符,通常是 open系統調用的返回值;第二個參數request是可以自定義的請求號;第三個參 數可以是一個指針,指向一段用戶態內存,用來傳遞參數,也可以是一個整形數 據。函數原型中的'...'並非表示ioctl是可變參數函數,只是為了告訴 編譯器不要檢查第三個參數。

在較新內核中,ioctl的內核態原型為 unlock_ioctl

long unlocked_ioctl(struct file *file, 

unsigned int request, unsigned long arg);

這個原型可以 在struct file_operation的定義中找到,還有一個compat_ioctl,用於內核為 64位,用戶空間為32位的情形,跟我們的需求關系不大。

傳入的request 和arg就來自於ioctl系統調用的第二個和第三個參數。在內核態中,可以根據 request的值,來調用約定的函數,“實現”自定義的系統調用。

需要注 意的是,request的值並不是可以隨隨便便自定義的,需要遵循一些規則,可以 參考《選擇ioctl命令》(注意它用的ioctl的原型是老內核的)。

ioctl 是用來操作設備的,因此我們需要一個虛擬的設備,以便ioctl能夠工作。

如何實現

實現虛擬設備需要通過內核模塊來實現。這篇文章寫了 如何寫一個入門的內核模塊。

在內核模塊初始化代碼中

用alloc_chrdev_region申請一個設備號

初始化一個struct file_operations類型的全局變量,將open、close、 unlocked_ioctl等成員賦值為我們實現的函數。

利用cdev_add將設備號與file_operations關聯起來

用class_create創建一個設備類

用device_create創建一個虛擬設備

在內核模塊銷毀代碼中

用cdev_del解除設備號與設備操作之間的關聯

用device_destroy銷毀設備

用class_destroy銷毀設備類

用unregister_chrdev_region釋放設備號。

在自定義的unlocked_ioctl中

通過switch-case,根據request號進入某個case

如果目標函數沒有參數,那麼直接調用即可

如果要傳遞給目標函數的參數直接存儲在arg中,則直接讀取arg再調用即 可。

如果要傳遞給目標函數的參數是arg所指向的一段用戶態內存,則需要從用 戶態拷貝到內核態。較少的數據可以用get_user和put_user來讀寫,較多的數據 可以用copy_from_user和copy_to_user來讀寫。准備好參數之後,調用目標函數 。

編寫字符設備驅動可以參考《Linux Device Driver》,網絡上也有大量的 教程,在此不再贅述。

Copyright © Linux教程網 All Rights Reserved