• 7、字符设备系统


    一、字符设备驱动的结构

    1、cdev 结构体

    struct cdev {
                    struct kobject kobj;    // 内嵌的Kobject对象
                    struct module *owner;  // 所属的模块
                    const struct file_operations *ops;  // 文件操作结构体
                    struct list_head list;  // 内核维护你的链表
                    dev_t dev;  // 设备号,由主次设备号组成
                    unsigned int count;
            };

        在这个结构体中,dev_t dev,是设备的设备号,它由32位的,由两个部分,既主设备号和次设备号组成,12位是主设备号,20位次设备号可以用过下面的宏来获得主次设备号:

    MAJOR(dev_t dev)   // 获得主设备号

    MINOR(dev_t dev)   // 获得次设备号

        如果要将它们合成为一个 dev_t 类型的话:

       MKDEV(int major, int minor);   //获得设备号

    2、cdev 设备的操作

        struct cdev*cdev_alloc(void);     // 申请内存

        void cdev_init(struct cdev *cdev, struct file_operation * fops)   // fops 与 cdev 设备绑定

        int cdev_add(struct cdev *cdev,dev_t devt, unsigned num);   // 字符设备的注册

        void cdev_del(struct cdev *cdev)  // 注销

        cdev_init 函数,建立cdev 与文件操作指针的连接,cdev_add 是将字符设备注册到系统,cdev_add 里面的一个参数是 dev_t ,所以之前必须先获取到设备号。 cdev)_del 注销设备,一般是在字符设备卸载的函数中。

        cdev 注册之前,需要获取到设备号:

        int register_chrdev_region(dev_t from, unsigned long count, const char * name)

        int alloc_chrdev_region(dev_t *dev, unsigned minor, unsigned count, const char * name)

        register_chrdev_region 是在已经确定设备号 from 的时候,count 是申请的个数, name :名字,这些名字,最终是会在 /proc/devices 中显示。

        alloc_chrdev_region ,一般是使用这个,因为一般都是不知道设备号的,minor: 申请最小的次设备号,count 个数,name 名字。

        完成设备号的注册,注销的时候是:

        void unregister_chrdev_region(dev_t from,unsigned count);

     

    3、字符设备操作流程

       struct cdev *mycdev = NULL;   // 1、定义 cdev 设备

        static  int __init xxx_init(void)   // 入口

        {

            mycdev = cdev_alloc();   // 2、分配地址

            cdev_init(mycdev, &fops);   // 3、初始化,将文件操作结构体绑定

            ret = alloc_cdev_region(&xxx_dev_no, 0, 1 , “mycdev”);   // 4、分配设备号

            mycdev->owner = THIS_MODULE;   //5、其余参数的指定

     

           cdev_add(mycdev, xxx_dev_no, 1);  // 6、注册设备;

        }

       static void __exit  xxx_exit(void)

        {

            unregister_chrdev_region(xxx_dev_no, 1);

            cdev_del(mycdev);

        }

        module_init(xxx_init);

        module_exit(xxx_exit);

    二、重要的结构体

    1、file_operation 结构体

        file_operation 的成员函数,是字符设备驱动与内核虚拟文件系统的接口,一般,提供了,read、write、ioctl 等常用函数。

    struct file_operations {

    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);

    unsigned int (*poll) (struct file *, struct poll_table_struct *);

    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

    int (*open) (struct inode *, struct file *);

    int (*release) (struct inode *, struct file *);

    };

    ssize_t(*read) (struct file *filp, char __user * buf, size_t count, loff_t * f_pos);

        file : 结构体,将在后续的位置学习, 

        __user : 充当了特殊的注释,说明 buf 是用户空间的内存地址,这里需要注意的是,用户空间的地址与内核空间的地址,是不能直接进行读写的

        count : 要写入 buf 的字节数,

       f_pos : 文件指针的位置,

        成功,则返回写入的字节数,失败则返回 EINVAL

    ssize_t(*write) (struct file * filp, const char __user * buf, size_t count, loff_t *fpos);

       __user  : 用户空间的buf

       count ; 写入buf 的字节数

        fpos : 文件指针

    而 用户空间与内核空间的数据,只能通过 copy_to_usr 或者 copy_from_usr 完成内核空间与数据空间的交换,

    int (*open) (struct inode *, struct file *);

    inode : 结构体指针

    file : 指针

    2、file 结构体

          当打开一个文件的时候,系统会为每个打开的文件,在内核的空间有一个关联的 struct file。她由内核在打开文件的时候,进行创建,当要对这个文件进行其他操作的时候,会将这个结构体 struct file 传递给任何要对这个文件进行操作的函数。file 结构体 里面保存了关于文件的 N 多信息,

    struct file {
           union {
            struct list_head    fu_list;
            struct rcu_head     fu_rcuhead;
        } f_u;
        struct path        f_path;
    #define f_dentry    f_path.dentry
    #define f_vfsmnt    f_path.mnt
        const struct file_operations    *f_op;                      // 文件管理的操作
        spinlock_t        f_lock;  /* f_ep_links, f_flags, no IRQ */
    #ifdef CONFIG_SMP
        int            f_sb_list_cpu;
    #endif
        atomic_long_t        f_count;
        unsigned int         f_flags;                        // 文件的标志, O_RDONLY,O_NONBLOCK,
        fmode_t            f_mode;                        // 文件的读或者写模式, FMODE_READ,FMODE_WRITE
        loff_t            f_pos;                             // 文件的指针
        struct fown_struct    f_owner;
        const struct cred    *f_cred;
        struct file_ra_state    f_ra;

        u64            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;
    #endif /* #ifdef CONFIG_EPOLL */
        struct address_space    *f_mapping;
    #ifdef CONFIG_DEBUG_WRITECOUNT
        unsigned long f_mnt_write_state;
    #endif
    };

        file 结构体里面信息很多,但是常用的较少,打开文件的时候, file 里面填充的信息,我们可以去使用,比如我们可以判定是不是非阻塞的模式:

        if(file->flags & O_NONBLOCK)

                printf(“NONBLOCK N”);

        else

                printf(“BLOCK N”);

        而 file 里面的私有数据 void            *private_data,这个指针,一般上指向了保存自定义设备结构体的地址,当read 或者 write 的时候,再从这个私有的指针,获取到自动的设备结构体。

    3、 inode 结构体

          当文件保存在磁盘的时候,inode 结构体 保存了文件访问的属性、属主、大小、访问时间、生成时间、最后修改时间等信息。她是 Linux管理文件系统的最基本的单位。

    struct inode {
         umode_t            i_mode;        
        uid_t            i_uid;
        gid_t            i_gid;
         struct mutex        i_mutex;

        dev_t            i_rdev;                       // 文件的设备号
        struct timespec        i_atime;       // access 最后的存取时间
        struct timespec        i_mtime;      // mod 最后的修改时间
        struct timespec        i_ctime;     //  inode 产生时间,create

        union {
            struct pipe_inode_info    *i_pipe;
            struct block_device    *i_bdev;
            struct cdev        *i_cdev;      // 字符设备
        };

        上面的这些信息中,对于编写驱动最为有用的是 dev_t i_rdev; 记录的是文件的设备号,struct cdev *i_cdev;,可以通过:

    unsigned int imonir(struct inode * inode)  // 获取次设备号

    unsigned int imajor(struct inode * inode)  // 获取主设备号

       也可以通过 stat + 文件的命令,查看 一个文件的状态:

    [carlos@localhost 3516c]$ stat gpio.ko
      File: “gpio.ko”
      Size: 7757            Blocks: 16         IO Block: 4096   一般文件
    Device: 801h/2049d      Inode: 358613086   Links: 1
    Access: (0664/-rw-rw-r--)  Uid: (  566/  carlos)   Gid: (  566/  carlos)
    Access: 2016-05-07 15:29:55.000000000 +0800
    Modify: 2016-05-06 13:31:14.000000000 +0800
    Change: 2016-05-06 13:31:14.000000000 +0800

        其实也是获取了 inode 的信息。

  • 相关阅读:
    原子操作--sync/atomic的用法
    基础的排序算法以及查找算法
    (三)MySQL终极篇
    (二)MySQL中级篇
    数据库表添加索引对性能的影响
    事务的四大特性以及事务的隔离级别
    int 和Integer
    数据库三范式
    Java反射
    获取Class实例的三种方式
  • 原文地址:https://www.cnblogs.com/qxj511/p/5468899.html
Copyright © 2020-2023  润新知