【內核&驅動】字符設備驅動程序【2】

一些重要的數據結構(VFS核心結構體)
1.super_block
和mount一一對應,這也意味着如果mount操作,在內核中分配新的結構體,存儲分區信息
umount時,VFS就把該結構體釋放掉
mount幾次內核中就有幾個super_block結構體

2.inode
和打開文件一一對應,記錄文件信息,關閉文件時,如果內存不緊張,不會釋放,
如果多個人同時打開一個文件,VFS只需創建一個inode

3.file <linux/fs.h>
和用戶態對文件操作open一一對應,每次open生成一個file結構體,
內核中有多個file結構體和打開的文件對應
該結構與用戶空間的FILE沒有任何關聯,他代表一個打開的文件,它由內核在open時創建
直到最後的close函數,在文件的所有實例都被關閉之後,內核會釋放這個數據結構
指向struct file的指針稱爲文件指針


  1. struct file {
  2.     union {
  3.         struct list_head    fu_list;
  4.         struct rcu_head     fu_rcuhead;
  5.     } f_u;
  6.     struct path        f_path;
  7. #define f_dentry    f_path.dentry
  8.     //文件對應的目錄項結構
  9. #define f_vfsmnt    f_path.mnt
  10.     const struct file_operations    *f_op;
  11.     atomic_long_t        f_count;
  12.     unsigned int         f_flags;
  13.         //文件標誌, 如O_RDONLY, O_NONBLOCK, O_SYNC
  14.     fmode_t            f_mode;
  15.         //文件模式
  16.     loff_t            f_pos;
  17.         //文件當前讀/寫位置
  18.     struct fown_struct    f_owner;
  19.     unsigned int        f_uid, f_gid;
  20.     struct file_ra_state    f_ra;

  21.     u64            f_version;
  22. #ifdef CONFIG_SECURITY
  23.     void            *f_security;
  24. #endif
  25.     /* needed for tty driver, and maybe others */
  26.     void            *private_data;
  27.         

  28. #ifdef CONFIG_EPOLL
  29.     /* Used by fs/eventpoll.c to link all the hooks to this file */
  30.     struct list_head    f_ep_links;
  31.     spinlock_t        f_ep_lock;
  32. #endif /* #ifdef CONFIG_EPOLL */
  33.     struct address_space    *f_mapping;
  34. #ifdef CONFIG_DEBUG_WRITECOUNT
  35.     unsigned long f_mnt_write_state;
  36. #endif
  37. };
4.file_operations <linux/fs.h>
其中包括了一組函數指針,每個打開的文件和一組函數關聯起來;這些操作用於實現系統調用
file_operations和指向這類結構的指針叫做fops


  1. struct file_operations {
  2.     struct module *owner;
  3.         指向擁有該結構的模塊的指針,防止模塊的操作在被使用的時候卸載該模塊
  4.         .onwer = THIS_MODULE
  5.     loff_t (*llseek) (struct file *, loff_t, int);
  6.         修改文件的當前讀寫位置,返回新位置
  7.         struct file*    當前文件
  8.         loff_t         長偏移量
  9.         int    
  10.     ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  11.         從設備中讀數據
  12.     ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  13.         向設備發送數據
  14.     ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  15.         初始化一個異步讀取操作
  16.     ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  17.         初始化設備上的異步寫入操作    
  18.     int (*readdir) (struct file *, void *, filldir_t);
  19.         對設備文件來說,應該置null,用於讀取目錄,只對文件系統有用
  20.     unsigned int (*poll) (struct file *, struct poll_table_struct *);
  21.         是poll,epoll,select的後端實現
  22.     int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  23.         提供了一種執行設備特定命令的方法
  24.     long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  25.     long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
  26.     int (*mmap) (struct file *, struct vm_area_struct *);
  27.         用於請求將設備內存映射到進程地址空間
  28.     int (*open) (struct inode *, struct file *);
  29.         對設備文件執行的第一個操作,但不要求驅動程序一定聲明該方法,
  30.         如果置爲NULL,設備的打開永遠有效,但系統不會通知驅動程序
  31.     int (*flush) (struct file *, fl_owner_t id);
  32.         發生在進程關閉設備文件描述符副本的時候,用來執行設備上尚未完成的操作
  33.     int (*release) (struct inode *, struct file *);
  34.         當file被釋放時, 將調用這個操作,與open相仿,也可設置爲NULL
  35.     int (*fsync) (struct file *, struct dentry *, int datasync);
  36.         刷新待處理的數據
  37.     int (*aio_fsync) (struct kiocb *, int datasync);
  38.         fsync的異步方法
  39.     int (*fasync) (int, struct file *, int);
  40.         通知設備其FASYNC狀態發生了變化
  41.     int (*lock) (struct file *, int, struct file_lock *);
  42.         實現文件鎖
  43.     ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  44.         發送數據,每次一頁
  45.     unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
  46.         將底層設備中的內存段映射到進程空間的合適的位置
  47.     int (*check_flags)(int);
  48.         檢查傳遞給fcntl(F_SETFL...)調用的標誌
  49.     int (*dir_notify)(struct file *filp, unsigned long arg);
  50.     int (*flock) (struct file *, int, struct file_lock *);
  51.     ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
  52.     ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
  53.     int (*setlease)(struct file *, long, struct file_lock **);
  54. };
file_operations的初始化方式

  1. struct file_operations scull_fops = {
  2.     .owner = THIS_MODULE,
  3.     .llseek = scull_llseek,
  4.     .read = scull_read,
  5.     .write = scull_write,
  6.     .ioctl = scull_ioctl,
  7.     .open = scull_open,
  8.     .release = scull_release,
  9. };
字符設備的註冊
在運行的時候獲得一個獨立的cdev結構:

  1. struct cdev *my_cdev = cdev_alloc();
  2. my_cdev->ops = &my_fops;
此時就可以將cdev結構嵌入到自己的設備的特定結構中
以下代碼用來初始化已分配到的結構:

  1. void cdev_init(struct cdev *cdev, struct file_operations *fops);
通過以下代碼告知內核結構的信息:

  1. int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

dev     是cdev結構
num     是該設備對應的第一設備編號
count    是應該和該設備關聯的設備號的數量

scull中的設備註冊
在scull內部, 他通過struct scull_dev的結構來表示每個設備


  1. struct scull_dev {
  2.     struct scull_qset *data;    /* 指向第一個量子集的指針 */
  3.     int quantum;            /* 當前量子的大小 */
  4.     int qset;            /* 當前數組的大小 */
  5.     unsigned long size;        /* 保存在其中的數據總量 */
  6.     unsigned int access_key;    /* 由sculluid和scullpriv使用 */
  7.     struct semaphore sem;        /* 互斥信號量 */
  8.     struct cdev cdev        /* 字符設備結構 */
  9. };
量子:我們把每一個內存區域稱之爲量子,一個指向這些量子的指針數組稱之爲量子集

  1. static void scull_setup_cdev(struct scull_dev *dev, int index)
  2. {
  3.     int err, devno;
  4.     devno = MKDEV(scull_major, scull_minor index);
  5.     
  6.     cdev_init(&dev->sdev, &scull_fops);
  7.     dev->cdev.owner = THIS_MODULE;
  8.     dev->cdev.ops = &scull_fops;

  9.     err = cdev_add(&dev->cdev, devno, 1);
  10.     if (err) {
  11.         printk(KERN_NOTICE "Error %d adding scull%d", err, index);
  12.     }
  13. }

早期辦法(內核2.6之前的版本的設備註冊的方法)

註冊:

  1. int register_chrdev(unsigned int major,
  2.                 const char * name,
  3.                 struct file_operations *fops);


釋放:

  1. int unregister_chrdev(unsigned int major, const char * name);



   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章