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

歡迎訪問 生活随笔!

生活随笔

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

linux

Linux字符设备驱动剖析

發布時間:2023/12/20 linux 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux字符设备驱动剖析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

以下內容轉載于博客http://blog.csdn.net/yueqian_scut/article/details/45938557。有刪改和格式調整,如有侵權,請告知刪除 。


一、應用層的程序

  • 很簡單,open設備文件,read、write、ioctl,最后close退出。

[cpp]?view plaincopy
  • int?main(int?argc?,char?*argv[])??
  • {??
  • ???unsigned?char?val[1]?=?1;??
  • ???int?fd?=open("/dev/LED",O_RDWR);//打開設備??
  • ???write(fd,val,1);//寫入設備,這里代表LED全亮??
  • ???close(fd);//關閉設備??
  • ???return?0;??
  • }??
  • 二、/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數據結構指針:
    [cpp]?view plaincopy
  • struct?file???
  • {??
  • ??const?struct?file_operations?*f_op;??
  • ??unsigned?int?f_flags;//可讀,可寫等??
  • ??…??
  • };??
  • (2)以下是led設備驅動的操作接口。open("/dev/LED",O_RDWR)就是為了獲得led_fops。

    [cpp]?view plaincopy
  • static?const?struct?file_operations?led_fops?=?{??
  • ??.owner?=THIS_MODULE,??
  • ??.open?=led_open,??
  • ??.write?=?led_write,??
  • };??
    • 仔細看應用程序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
  • if?(!open?&&?f->f_op)??
  • ????open?=?f->f_op->open;??
  • if?(open)?{??
  • ????error?=?open(inode,?f);??
  • ????if?(error)??
  • ????????goto?cleanup_all;??
  • }??
    • 其中inode->i_fop在mknod的init_special_inode調用中被賦值為def_chr_fops。
    • open(inode, f)即調用到chrdev_open。其可以看出是字符設備所對應的文件系統接口,我們姑且稱其為字符設備文件系統。

    [cpp]?view plaincopy
  • const?struct?file_operations?def_chr_fops?=?{??
  • ??.open?=?chrdev_open,??
  • };??
  • (4) 繼續分析chrdev_open


    • Kobj_lookup(cdev_map,inode->i_rdev, &idx)即是通過設備的設備號(inode->i_rdev)在cdev_map中查找設備對應的操作集file_operations。
    • 關于如何查找,我們在理解字符設備驅動如何注冊自己的file_operations后再回頭來分析這個問題。

    五、字符設備驅動的注冊

    (1)字符設備對應的結構體cdev

    [cpp]?view plaincopy
  • struct?cdev???
  • {??
  • ??struct?kobject?kobj;?//?每個?cdev?都是一個?kobject??
  • ??struct?module*?owner;?//?指向實現驅動的模塊??
  • ??const?struct?file_operations?*ops;?//?操縱這個字符設備文件的方法??
  • ??struct?list_head?list;?//對應的字符設備文件的inode->i_devices?的鏈表頭??
  • ??dev_t?dev;?//?起始設備編號??
  • ??unsigned?int?count;?//?設備范圍號大小??
  • };??
  • (2)led設備驅動初始化和設備驅動注冊


    • cdev_init是初始化cdev結構體,并將led_fops填入該結構。
    • cdev_add函數
    [cpp]?view plaincopy
  • int?cdev_add(struct?cdev?*p,?dev_t?dev,?unsigned?count)??
  • {??
  • ??p->dev?=?dev;??
  • ??p->count?=?count;??
  • ??return?kobj_map(cdev_map,?dev,?count,?NULL,?exact_match,?exact_lock,?p);??
  • }??
    • 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字符设备驱动剖析的全部內容,希望文章能夠幫你解決所遇到的問題。

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