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


http://www.educity.cn/Linux/1242138.html

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

為什么選擇ioctl

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

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

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

  3.   利用已有的系統調用,比如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能夠工作。

如何實現

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

  1.   在內核模塊初始化代碼中

    1.   用alloc_chrdev_region申請一個設備號

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

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

    4.   用class_create創建一個設備類

    5.   用device_create創建一個虛擬設備

  2.   在內核模塊銷毀代碼中

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

    2.   用device_destroy銷毀設備

    3.   用class_destroy銷毀設備類

    4.   用unregister_chrdev_region釋放設備號。

  3.   在自定義的unlocked_ioctl中

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

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

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM