《Linux Device Drivers》第三章 字符設備驅動程序——note

  • 主設備號和次設備號
    • 那些名稱被稱爲特殊文件、設備文件,或者簡單稱之爲文件系統樹的節點,它們通常位於/dev目錄
    • 通常而言,主設備號標識設備對應的驅動程序
    • 一個主設備號對應一個驅動程序
    • 設備編號的內部表達
      • dev_t(<linux/types.h>)
      • dev_t是一個32位的數,12位表示主設備號,其餘20位表示次設備號
      • <linux/kdev_t.h>
        • MAJOR(dev_t dev);
        • MINOR(dev_t dev);
        • MKDEV(int major, int minor);
    • 分配和釋放設備編號
      • <linux/fs.h>
        • int register_chrdev_region(dev_t first, unsigned int count, char *name);
        • int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
        • void u nregister_chrdev_resion(dev_t first, unsigned int count);
    • 動態分配主設備號
      • 驅動程序應該始終使用alloc_chrdev_region而不是register_chrdev_region函數
      • 缺點是:由於分配的主設備號不能保證始終一致,所以無法預先創建設備節點
      • /proc/devices
      • 分配主設備號的最佳方式
        • 默認採用動態分配,同時保留在加載甚至是編譯時指定主設備號的餘地

  • 一些重要的數據結構
    • 三個重要的內核數據結構
      • file_operations
      • file
      • inode
    • 文件操作
      • file_operations結構用來將驅動程序操作連接到設備編號
      • <linux/fs.h>
      • file_operations結構或者指向這類結構的指針稱爲fops
        • 每個字段必須指向驅動程序中實現特定操作的函數
      • struct module *owner
      • loff_t (*llseek) (struct file *, loff_t, int);
      • ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
      • ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
      • ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
      • ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t *);
      • int (*readdir) (struct file *, ,void *, filldir_t);
      • unsigned int (*poll) (struct file *, struct poll_table_struct *);
      • int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
      • int (*mmap) (struct file *, struct vm_area_struct *);
      • int (*open) (struct inode *, struct file *);
      • int (*flush) (struct file *);
      • int (*release) (struct inode *, struct file *);
      • int (*fsync) (struct file *, struct dentry *, int);
      • int (*aio_fsync) (struct kiocb *, int);
      • int (*fasync) (int, struct file *, int);
      • int (*lock) (struct file *, int, struct file_lock *);
      • ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
      • ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
      • ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
      • ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
      • unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
      • int (*check_flags) (int)
      • int (*dir_notify) (struct file *, unsigned long);
    • file結構
      • file結構代表一個打開的文件
      • <linux/fs.h>
      • 與用戶空間程序中的FILE沒有任何關聯
      • 指向struct file的指針通常被稱爲file或filp
      • 字段
        • mode_t f_mode
        • loff_t f_pos
        • unsigned int f_flags
        • struct file_operations *f_op
        • void *private_data
        • struct dentry *f_dentry
    • inode結構
      • 內核用inode結構在內部表示文件
      • 對單個文件,可能會有許多個表示打開的文件描述符的file結構
      • 字段
        • dev_t i_rdev
        • struct cdev *i_cdev
  • 字符設備的註冊
    • <linux/cdev.h>
    • 獲取一個獨立的cdev結構
      • struct cdev *my_cdev = cdev_alloc();
      • my_cdev->ops = &my_fops;
      • owner
    • struct cdev *cdev_alloc(void);
    • void cdev_init(struct cdev *cdev, struct file_operations *fops);
    • int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
    • void cdev_del(struct cdev *dev);
  • open和release
    • open方法
      • 主要工作
        • 檢查設備特定的錯誤
        • 如果設備是首次打開,則對其進行初始化
        • 如有必要,更新f_op指針
        • 分配並填寫置於flip->private_data裏的數據結構
      • int (*open)(struct inode *inode, struct file *filp);
      • <linux/kernel.h>
        • container_of(pointer, continer_type, container_field);
    • release方法
      • 主要工作
        • 釋放由open分配的、保存在filp->private_data中的所有內容
        • 在最後一次關閉操作時關閉設備
  • scull的內存使用
    • <linux/slab.h>
      • void *kmalloc(size_t size, int flags);
      • void kfree(void *ptr);
      • 不應該將非kmalloc返回的指針傳遞給kfree
      • 將NULL指針傳遞給kfree是合法的
  • read和write
    • ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
    • ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
    • 內核代碼不能直接引用用戶空間的指針
      • 隨着驅動程序所運行的架構的不同或者內核配置的不同,在內核模式中運行時,用戶空間的指針可能是無效的
      • 即使該指針在內核空間中代表相同的東西,但用戶空間的內存是分頁的,而在系統調用被調用時,涉及到的內存可能根本不在RAM中
      • 用戶空間的指針由用戶程序提供,該程序可能存在缺陷或者是個惡意程序
    • 訪問用戶空間的緩衝區應始終通過內核提供的專用函數完成
      • <asm/uaccess.h>
      • unsigned long copy_to_user(void __user *to, const void * from, unsigned long count)
      • unsigned long copy_from_user(void *to, const void __user *from, unsigned long count);
    • 訪問用戶空間的任何函數都必須是可重入的,必須能和其他驅動程序函數併發執行,必須處於能夠合法休眠的狀態
    • read方法
      • 如果返回值等於傳遞read系統調用的count參數,則說明所請求的字節數傳輸成功完成了
      • 如果返回值是正的,但比count小,說明部分數據傳輸成功
      • 如果返回值爲,則表示已經到達了文件尾
      • 負值意味着發生了錯誤,該值指明瞭發生了什麼錯誤,錯誤碼在<linux/error.h>中定義
      • 現在還沒有數據,但以後可能會有
    • write方法
    • readv和writev
      • ssize_t (*readv) (struct file * filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
      • ssize_t (*writev) (struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
      • iovec結構
        • void __user *iov_base;
        • __kernel_size_t iov_len
發佈了48 篇原創文章 · 獲贊 12 · 訪問量 1141萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章