日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux字符设备开发

發布時間:2024/4/13 linux 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux字符设备开发 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


本篇基于ldd3中第三章,原書自帶的源碼隨著內核版本更新已經不能運行,代碼需要進行升級,文章參考代碼能在內核版本4.17.2運行。

1.?? 分配設備編號

建立一個字符驅動時,需要做的第一件事是獲取一個或多個設備編號來使用.此目的必要的函數是 register_chrdev_region.

注冊字符設備函數執行后會出現在/proc/devices和sysfs中:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

  first是要分配的起始設備編號. first 的次編號部分常常是 0, 但是沒有要求是那個效果. count 是請求的連續設備編號的總數. 注意, 如果 count 太大,要求的范圍可能溢出到下一個次編號; 但是只要要求的編號范圍可用, 一切都仍然會正確工作. name 是應當連接到這個編號范圍 的設備的名子; 它會出現在 /proc/devices 和 sysfs 中.

  一些主設備編號是靜態分派給最普通的設備的. 一個這些設備的列表在內核源碼樹的 Documentation/devices.txt 中.

  對于新驅動,建議使用動態分配來獲取你的主設備編號, 而不是隨機選取一個當前空閑的編號.使用 alloc_chrdev_region, 不是 register_chrdev_region.

這個alloc_chrdev_region是動態分配主設備號的,因為你可能不知道系統中哪些主設備號可以給你的驅動程序使用,動態分配的一個缺點就是不能提前分配設備節點(注:通過MKNOD來創建節點的):

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

??????????? 兩個函數從名字上也可以區分,一個是注冊,表示我有了主設備號只是想內核注冊,而alloc_則是分配,意味著讓內核來幫著allocate一下。

??????????? 注冊對應的是注銷,這個是驅動程序卸載時候必須做的,從哪里來還那里去,對應的函數是:

void unregister_chrdev_region(dev_t from, unsigned count)

另外,獲取設備主設備號和次設備號,使用如下宏:

MAJOR(dev_t dev);

MINOR(dev_t dev);

將主、次設備號轉換成一個設備號,如下:

MKDEV(int major, int minor);

2.?? 基礎性的驅動操作

基礎性的驅動操作包括 3 個重要的內核數 據結構, 稱為 file_operations, file, 和 inode.

2.1???? file_operation

  傳統上, 一個 file_operation 結構或者其一個指針稱為 fops( 或者它的一些變體). 結構中的每個成員 必須指向驅動中的函數, 這些函數實現一個特別的操作

??????????? 其中定義的操作函數并不是需要全部實現,根據具體驅動實現針對的函數功能即可。字符設備主要有一下函數需要實現:

owner,llseek,read,write,ioctl,open,release.

2.2???? file

file結構表示一個打開的文件。在內核中指向file的指針經常叫做filp,就是file pointer,以免是file搞混。

2.3???? inode

inode結構由內核在內部用來表示文件,代表打開文件描述符的文件結構是不同的。多個打開的描述符可能指向一個單個inode結構。

相對于字符設備驅動程序,我們先使用i_rdev和i_cdev。

dev_t i_rdev;//實際設備的節點

Struct cdev *i_cdev; //指向字符設備驅動程序指針

??????????? 現在可以通過宏如下,來獲取節點的主、次設備號:

unsigned int iminor(struct inode *inode);

unsigned int imajor(struct inode *inode);

3.?? 字符設備注冊

內核中使用cdev結構體來表示字符設備。可以通過cdev_alloc來分配。

然后使用cdev_init來初始化。

?????? 我們可以將cdev結構體嵌入到我們自己的設備結構體中,這也正是例子所使用的方法。

??????????? 最后告訴內核添加進去,如果不告訴內核就是空有一身資源而無施展之處,通過函數cdev_add。一旦添加,那么內核就可能來騷擾設備,所以要確保所有都準備好的時候調用cdev_add函數。

??????????? 去除設備調用函數cdev_del.

4.?? 設備布局

設備由內存來模擬,其設備中的布局如下圖。


數據結構如下,scull_qset結構體非常簡單,其實現一個鏈表的同時,每個元素同時指向塊內存:

struct scull_qset {

??????? void **data;

??????? struct scull_qset *next;

};

設備的結構體如下:

struct scull_dev {

??????? struct scull_qset *data;? /* Pointer to first quantum set */

??????? int quantum;? ????????????/* the current quantum size */

??????? int qset;???????????????? /* the current array size */

??????? unsigned long size;?????? /* amount of data stored here */

??????? unsigned int access_key;? /* used by sculluid and scullpriv */

??????? struct semaphore sem;???? /* 互斥所*/

??????? struct cdev cdev;???????? /* Char device structure????????????? */

};

設備的大小為quantum*qset。

?

5.?? 代碼解析

5.1???? 初始化

初始化函數為scull_init_module

如果指定了主設備號,調用register_chrdev_region否則調用alloc_chrdev_region,并獲取主設備號。

然后分配設備scull_dev結構體數組scull_devices,數量為SCULL_NR_DEVS子設備號數量,并初始化為0。接著根據需要分配的內存空間大小正式初始化scull_devices,其中會調用scull_setup_cdev函數(該函數中會使用cdev_init,cdev_add函數,初始化設備結構中嵌入的cdev,同時綁定scull_fops),scull_fops結構如下。

struct file_operations scull_fops = {

??????? .owner =??? THIS_MODULE,

??????? .llseek =?? scull_llseek,

?? ?????.read =???? scull_read,

??????? .write =??? scull_write,

??????? .unlocked_ioctl = scull_ioctl,

??????? .open =???? scull_open,

??????? .release =? scull_release,

};

??????????? 然后調用

初始化時候分配了指定數量的設備數據結構,并初始化后增加到了內核,可以在/proc/devices中看到,此時其實并沒有分配設備的內存空間,因為還不需要。

?

5.2???? 退出

退出函數是scull_cleanup_module,該函數先獲取設備號。然后根據設備數量循環調用scull_trim,cdev_del函數來刪除cdev設備,最后調用kfree釋放在初始化中分配的結構體數據。

??????????? 其中scull_trim函數負責釋放分配的內存空間。

5.3???? 操作函數集合

驅動的open函數

通過inode獲取設備結構體的指針,這個通過內核中的container_of函數來實現,并將其保存到文件對象的private_data中以備后用。

如果是寫模式打開,則將之前該設備分配的空間使用scull_trim函數清空。

?

llseek

返回當前讀寫位置。

?

release

直接返回0,并不做操作。

read

先從filp->private_data中獲取設備地址。獲取設備總空間大小。

如果要讀的位置大于空間大小則退出。否則計算要讀取的正確位置,因為每個scull_qset結構體指向的item空間大小是固定的,其相互之間是鏈表方式連接的。

然后會調用scull_follow函數,該函數中會通過kmalloc函數動態分配scull_qset結構體(如果沒有被分配過),直到包含的item累計理論空間能包含要讀取的地址。然后返回最后一個scull_qset結構體,如果返回null,說明系統內存空間不夠了。

??????????? 最后調用copy_to_user函數,將內容復制到用戶的buf中。然后更新文件讀取位置,并返回所讀取字節大小。

write

獲取設備結構體的指針,以及對應的設備空間相關大小。如item,quantum。

計算文件讀取位置,調用scull_follow,返回位置所在的那個item的scull_qset結構體,如果該結構體對應的數據指針為NULL,說明之前沒有給其分配內存空間,則調用kmalloc分配qset指向quantum的指針數數組。

然后根據讀取位置,通過函數kmalloc函數分配quantum的內存空間。

最后調用用copy_from_user函數將數據復制到內存中的quantum片段。

?

unlocked_ioctl

對ioctl的實現。

6.?? 使用測試

加載驅動后,執行如下,其中247是主設備號,在/proc/devices中可以查看到:

mknod /dev/scull0 c 247 0

mknod /dev/scull1 c 247 1

mknod /dev/scull2 c 247 2

mknod /dev/scull3 c 247 3

然后可以使用dd命令或者cp命令復制內容到設備中。

# echo "hello" > scull0

# cat scull0

hello

7.?? 代碼

https://github.com/kernel-z/ldd3/tree/master/scull

?

?

?

?

?

總結

以上是生活随笔為你收集整理的linux字符设备开发的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。