• 应用程序访问设备驱动程序的原理


    /************************************************************************************

    *本文为个人学习记录,如有错误,欢迎指正。

    *本文参考资料: 

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

    *        https://www.cnblogs.com/yanghong-hnu/p/5699528.html

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

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

    *        https://www.cnblogs.com/chen-farsight/p/6177870.html

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

    ************************************************************************************/

    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
    ", 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 }
  • 相关阅读:
    hdu 1087(LIS变形)
    poj 1088(记忆化搜索)
    hdu 1505(最大子矩阵)
    hdu 1506(好题+DP或者RMQ)
    poj 2593&&poj2479(最大两子段和)
    hdu 1003(最大子段和)
    hdu 2881(LIS变形)
    poj 1692(动态规划)
    CodeForces 626C Block Towers
    CodeForces 626B Cards
  • 原文地址:https://www.cnblogs.com/linfeng-learning/p/9309833.html
Copyright © 2020-2023  润新知