應用程序訪問設備驅動程序的原理

/************************************************************************************html

*本文爲我的學習記錄,若有錯誤,歡迎指正。node

*本文參考資料: 
linux

*        http://www.169it.com/tech-qa-linux/article-5682294992603241339.html安全

*        http://www.javashuo.com/article/p-rywmihdl-bm.html
數據結構

*        https://www.cnblogs.com/wanghetao/archive/2012/05/28/2521675.htmlapp

*        http://www.cnblogs.com/xiaojiang1025/p/6196198.html框架

*        http://www.javashuo.com/article/p-dlfrucxn-bw.htmlide

*        http://www.cnblogs.com/xiaojiang1025/p/6363626.html函數

************************************************************************************/oop

1.相關數據結構

1.1 struct inode

VFS inode包含文件訪問權限、屬主、組、大小、生成時間、訪問時間、最後修改時間等信息。它是linux管理文件系統的最基本單位,也是文件系統鏈接任何子目錄、文件的橋樑。

在Linux內核中,當建立一個文件時,就會在相應的文件系統建立一個inode與之對應,文件實體和文件的inode是一 一對應的,建立好一個inode會存在存儲器中,第一次open就會將inode在內存中有一個備份,同一個文件被屢次打開並不會產生多個inode,當全部被打開的文件都被close以後,inode在內存中的實例纔會被釋放。

當建立一個設備文件(mknod或udev)時,也會在文件系統中建立一個inode,該inode用來存儲關於這個文件的靜態信息,其中包括該設備文件對應的設備號、文件路徑以及對應的驅動對象等。

inode結構體中包含了設備文件的大量信息,着重關心如下結構體成員便可:

(1)dev_t i_rdev:表示設備文件對應的字符設備的設備號。

(2)struct cdev *i_cdev:指向字符設備對應的cdev結構體。

(3)const struct file_operations *i_fop:文件的操做方法集,建立設備文件的時候i_fops填充的是def_chr_fops,blk_blk_fops,def_fifo_fops,bad_sock_fops之一。

//linux/fs.h
struct inode {
        struct hlist_node       i_hash;              /* 哈希表 */
        struct list_head        i_list;              /* 索引節點鏈表 */
        struct list_head        i_dentry;            /* 目錄項鍊表 */
        unsigned long           i_ino;               /* 節點號 */
        atomic_t                i_count;             /* 引用記數 */
        umode_t                 i_mode;              /* 訪問權限控制 */
        unsigned int            i_nlink;             /* 硬連接數 */
        uid_t                   i_uid;               /* 使用者id */
        gid_t                   i_gid;               /* 使用者id組 */
        kdev_t                  i_rdev;              /* 實設備標識符 */
        loff_t                  i_size;              /* 以字節爲單位的文件大小 */
        struct timespec         i_atime;             /* 最後訪問時間 */
        struct timespec         i_mtime;             /* 最後修改(modify)時間 */
        struct timespec         i_ctime;             /* 最後改變(change)時間 */
        unsigned int            i_blkbits;           /* 以位爲單位的塊大小 */
        unsigned long           i_blksize;           /* 以字節爲單位的塊大小 */
        unsigned long           i_version;           /* 版本號 */
        unsigned long           i_blocks;            /* 文件的塊數 */
        unsigned short          i_bytes;             /* 使用的字節數 */
        spinlock_t              i_lock;              /* 自旋鎖 */
        struct rw_semaphore     i_alloc_sem;         /* 索引節點信號量 */
        struct inode_operations *i_op;               /* 索引節點操做表 */
        struct file_operations  *i_fop;              /* 默認的索引節點操做 */
        struct super_block      *i_sb;               /* 相關的超級塊 */
        struct file_lock        *i_flock;            /* 文件鎖鏈表 */
        struct address_space    *i_mapping;          /* 相關的地址映射 */
        struct address_space    i_data;              /* 設備地址映射 */
        struct dquot            *i_dquot[MAXQUOTAS]; /* 節點的磁盤限額 */
        struct list_head        i_devices;           /* 塊設備鏈表 */
        struct pipe_inode_info  *i_pipe;             /* 管道信息 */
        struct block_device     *i_bdev;             /* 塊設備 */
        struct cdev *i_cdev;                             /* 字符設備 */
        unsigned long           i_dnotify_mask;      /* 目錄通知掩碼 */
        struct dnotify_struct   *i_dnotify;          /* 目錄通知 */
        unsigned long           i_state;             /* 狀態標誌 */
        unsigned long           dirtied_when;        /* 首次修改時間 */
        unsigned int            i_flags;             /* 文件系統標誌 */
        unsigned char           i_sock;              /* 多是個套接字吧 */
        atomic_t                i_writecount;        /* 寫者記數 */
        void                    *i_security;         /* 安全模塊 */
        __u32                   i_generation;        /* 索引節點版本號 */
        union {
                void            *generic_ip;         /* 文件特殊信息 */
        } u;
};
struct inode

 

1.2 struct file

 Linux內核中,使用 file結構體描述一個已經打開的文件(設備對應於設備文件),系統中的每一個打開的文件在內核空間都有一個相應的struct file結構體,它由內核在打開文件時建立,並傳遞給在文件上進行操做的任何函數,直至文件被關閉。若是文件被關閉,內核就會釋放相應的數據結構。

Linux中同一個文件可被多個進程打開,該文件每被打開一次,內核就會在該進程中建立一個struct file來描述該文件。由此可知,一個文件在內核中可能對應多個struct file,可是該文件只有惟一一個struct inode與之對應。

struct file {
union {
    struct list_head fu_list;       //文件對象鏈表指針linux/include/linux/list.h
    struct rcu_head fu_rcuhead;     //RCU(Read-Copy Update)是Linux 2.6內核中新的鎖機制
} f_u;
struct path f_path;                 //包含dentry和mnt兩個成員,用於肯定文件路徑
#define f_dentry  f_path.dentry     //f_path的成員之一,當前文件的dentry結構
#define f_vfsmnt  f_path.mnt        //表示當前文件所在文件系統的掛載根目錄
const struct file_operations *f_op; //與該文件相關聯的操做函數
atomic_t  f_count;                  //文件的引用計數(有多少進程打開該文件)
unsigned int  f_flags;              //對應於open時指定的flag
mode_t  f_mode;                     //讀寫模式:open的mod_t mode參數
off_t  f_pos;                       //該文件在當前進程中的文件偏移量
struct fown_struct f_owner;         //該結構的做用是經過信號進行I/O時間通知的數據。
unsigned int  f_uid, f_gid;         //文件全部者id,全部者組id
struct file_ra_state f_ra;          //在linux/include/linux/fs.h中定義,文件預讀相關
unsigned long f_version;
#ifdef CONFIG_SECURITY
void  *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
struct file

  着重關注如下結構體成員:

(1)struct inode *f_inode:指向該文件對應的inode。

(2)const struct file_operations *f_op:驅動提供的file_operations對象,這個對象應該在第一次open()的時候被填充(調用char_open實現),直至該文件被close。

1.3 字符設備管理框架

詳見Linux字符設備:字符設備管理框架

2. 具體訪問流程

 

(1)當一個字符設備文件被建立的時候,內核會構造相應的inode,並對其進行初始化操做。

//fs/char_dev.c
const struct file_operations def_chr_fops = {                                                       
         .open = chrdev_open,
         .llseek = noop_llseek,
};

//fs/inode.c
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
    inode->i_mode = mode;
    if (S_ISCHR(mode)) {
        inode->i_fop = &def_chr_fops;
        inode->i_rdev = rdev;
    } else if (S_ISBLK(mode)) {
        inode->i_fop = &def_blk_fops;
        inode->i_rdev = rdev;
    } else if (S_ISFIFO(mode))
        inode->i_fop = &def_fifo_fops;
    else if (S_ISSOCK(mode))
        inode->i_fop = &bad_sock_fops;
    else
        printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
                  " inode %s:%lu\n", mode, inode->i_sb->s_id,
                  inode->i_ino);
}

(2)chrdev_open()
--359-->嘗試將inode->i_cdev(一個cdev結構指針)保存在局部變量p中;
--360-->若是p爲空,即inode->i_cdev爲空;
--364-->咱們就根據inode->i_rdev(設備號)經過kobj_lookup()搜索cdev_map,並返回與之對應kobj
--367-->因爲kobject是cdev的父類,咱們根據container_of很容易找到相應的cdev結構並將其保存在inode->i_cdev中;
--374-->找到了cdev,咱們就能夠將inode->devices掛接到inode->i_cdev的管理鏈表中,這樣下次就不用從新搜索;
--378-->直接cdev_get()便可;
--386-->找到了咱們的cdev結構,咱們就能夠將其中的操做方法集inode->i_cdev->ops傳遞給filp->f_ops(386-390);
--392-->這樣,咱們就能夠回調咱們的設備打開字符設備fops中的char_opena函數。若是咱們沒有實現本身的open接口,就什麼都不作,也不是錯。

351 static int chrdev_open(struct inode *inode, struct file *filp)
352 {
353         const struct file_operations *fops;
354         struct cdev *p;
355         struct cdev *new = NULL;
356         int ret = 0;
            ...
359         p = inode->i_cdev; 360         if (!p) {
361                 struct kobject *kobj;
362                 int idx;
                    ...
364                 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
                    ...
367                 new = container_of(kobj, struct cdev, kobj); 369                 /* Check i_cdev again in case somebody beat us to it while
370                    we dropped the lock. */
371                 p = inode->i_cdev;
372                 if (!p) {
373                         inode->i_cdev = p = new;
374                         list_add(&inode->i_devices, &p->list);
375                         new = NULL;
376                 } else if (!cdev_get(p))
377                         ret = -ENXIO;
378         } else if (!cdev_get(p)) 379                 ret = -ENXIO;
            ...
386 fops = fops_get(p->ops); ... 390  replace_fops(filp, fops); 391         if (filp->f_op->open) {
392                 ret = filp->f_op->open(inode, filp);
                    ...
395         }
396 
397         return 0;
398 
399  out_cdev_put:
400         cdev_put(p);
401         return ret;
402 }
相關文章
相關標籤/搜索