Linux字符设备驱动剖析
以下內容轉載于博客http://blog.csdn.net/yueqian_scut/article/details/45938557。有刪改和格式調整,如有侵權,請告知刪除 。
一、應用層的程序
- 很簡單,open設備文件,read、write、ioctl,最后close退出。
[cpp]?view plaincopy
二、/dev目錄、文件系統
三、設備文件的創建
(1)/dev目錄下的設備文件基本上都是通過mdev來動態創建的。mdev是一個用戶態的應用程序,位于busybox工具箱中。其創建過程包括:
- 驅動初始化或者總線匹配后,會調用驅動的probe接口。該接口會調用device_create(設備類, 設備號, 設備名),在“/sys/class/設備類”目錄生成唯一的設備屬性文件(包括設備號和設備名等信息),并且發送uvent事件(KOBJ_ADD和環境變量,如路徑等信息)到用戶空間(通過socket方式)。
- mdev是一個work_thread線程,收到事件后會分析“/sys/class/設備類”的對應文件,最終調用mknod動態來創建設備文件。
- 設備文件內容主要是設備號(這個設備文件對應的inode,會記錄文件的屬性是一個設備(其他屬性還包括目錄,一般文件,符號鏈接等))。
- 應用程序open最重要的一步就是(通過文件系統接口)獲得該設備文件的內容,即設備號。
(2)如果初始化過程中沒有調用device_create接口來創建設備文件,則需要手動通過命令行調用mknod接口來創建設備文件。
(3)mknod接口分析
四、open設備文件
(1)open設備文件,是為了獲取(該設備驅動的)file_operations操作集。
- 該接口集是struct file的成員,open返回file數據結構指針:
(2)以下是led設備驅動的操作接口。open("/dev/LED",O_RDWR)就是為了獲得led_fops。
[cpp]?view plaincopy
- 仔細看應用程序int fd =open("/dev/LED",O_RDWR),open的返回值是int,并不是file,其實是為了操作系統和安全考慮。
- fd位于應用層,而file位于內核層,它們都同屬進程相關概念。
- 在linux中,同一個文件(對應于唯一的inode)可以被不同的進程打開多次,而每次打開都會獲得file數據結構。
- 每個進程都會維護一個已經打開的file數組,fd就是對應file結構的數組下標。因此,file和fd在進程范圍內是一一對應的關系。
(3)open接口分析
通過系統調用后對應調用sys_open,其是vfs層的接口Sys_open(/dev/led)
- SYSCALL_DEFINE3(open,const char __user *, filename, int, flags, int, mode)
- ? do_sys_open(AT_FDCWD,/dev/tty, flags, mode);
- ? ? fd = get_unused_fd_flags(flags);
- ? ? struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
- ? ? ? path_init(dfd, pathname, LOOKUP_PARENT, &nd);//path_init返回時nd->dentry即為搜索路徑文件名的起點
- ? ? ? link_path_walk(pathname, &nd);//link_path_walk一步步建立打開路徑的各個目錄的dentry和inode
- ? ? ? do_last(&nd, &path, open_flag, acc_mode, mode, pathname);
- ? ? ? ??filp = nameidata_to_filp(nd);//通過inode節點創建file
- ? ? ? ? ? ?__dentry_open()
- ? ? ? ? ? ? ?f->f_op =fops_get(inode->i_fop);
在__dentry_open()函數中有?
[cpp]?view plaincopy
- 其中inode->i_fop在mknod的init_special_inode調用中被賦值為def_chr_fops。
- open(inode, f)即調用到chrdev_open。其可以看出是字符設備所對應的文件系統接口,我們姑且稱其為字符設備文件系統。
[cpp]?view plaincopy
- Kobj_lookup(cdev_map,inode->i_rdev, &idx)即是通過設備的設備號(inode->i_rdev)在cdev_map中查找設備對應的操作集file_operations。
- 關于如何查找,我們在理解字符設備驅動如何注冊自己的file_operations后再回頭來分析這個問題。
五、字符設備驅動的注冊
(1)字符設備對應的結構體cdev
[cpp]?view plaincopy
- cdev_init是初始化cdev結構體,并將led_fops填入該結構。
- cdev_add函數
- kobj_map函數
- (1)kobj_map函數使用hash散列表來存儲cdev數據結構(即存儲設備的信息)。
- (2)通過(注冊設備的主設備號major)來獲得cdev_map->probes數組的索引值i(i = major % 255);
- (3)然后把一個類型為struct probe的節點對象加入到probes[i]所管理的鏈表中;
- (4)probes[i]->data即是cdev數據結構,而probes[i]->dev和range代表字符設備號和范圍。
- (5)其中參數cdev_map的類型如下:
六、再述open設備文件
? ?通過第五步的字符設備的注冊過程,應該很容易理解Kobj_lookup查找led_ops的過程(這里不寫)。
? ?至此,獲得led設備驅動的led_ops。
(1)接著調用file->f_ops->open來調用led_open
- 該函數中對led用到的GPIO進行ioremap,并設置GPIO方向、上下拉等硬件初始化。?
(2)最后chrdev_open一步步返回
- 最后到do_sys_open函數中的“struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);”返回。
- fd_install(fd, f),是在當前進程中將存有led_ops的file指針填入進程的file數組中,下標是fd。最后將fd返回給用戶空間。而用戶空間只要傳入fd即可找到對應的file數據結構。
七、設備操作
? ?這里以設備寫為例,主要是控制led的亮和滅。
? ?write(fd,val,1)系統調用后對應sys_write,其對應所有的文件寫,包括目錄、一般文件和設備文件。
? ?一般文件有位置偏移的概念,即讀寫之后,當前位置會發生變化,所以如要跳著讀寫,就需要fseek。對于字符設備文件,沒有位置的概念。因此重點跟蹤vfs_write的過程。
- fget_light,在當前進程中通過fd來獲得file指針;
- vfs_write
- 對于led設備,file->f_op->write即是led_write。在該接口中實現對led設備的控制。
八、再論字符設備驅動的初始化
綜上所述,字符設備的初始化包括兩個主要環節:
(1)字符設備驅動的注冊
- 即通過cdev_add向系統注冊cdev數據結構,提供file_operations操作集和設備號等信息,最終file_operations存放在全局指針變量cdev_map指向的Hash表中,其可以通過設備號索引并遍歷得到。
(2)創建屬性文件、設備文件
- 通過device_create(設備類,設備號,設備名)在“sys/class/設備類”中創建設備屬性文件并發送uevent事件,而mdev利用該信息自動調用mknod在/dev目錄下創建對應的設備文件,以便應用程序訪問。
總結
以上是生活随笔為你收集整理的Linux字符设备驱动剖析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux高性能服务器编程:进程池和线程
- 下一篇: 缠论入门到精通理论到实战