当前位置: 首页 > news >正文

Linux 内核——字符设备驱动框架详解

0. Linux 进程控制块与文件对象
在 linux 操作系统中,进程对应的 PCB 中维护一个已打开文件描述符表,如下代码片段这个表本质是一个数组,其下标就是文件句柄(我们都知道 Windows 中是指针、而 linux 是整数)。每打开一个文件,就在该数组中放入这个文件对应的文件对象的指针(struct file*),并且返回数组的下标。在使用 open() 打开设备节点时传入的 flags、mode 等参数都会被记录在对应中。在读写文件时,文件的当前偏移地址则会保存在 f_pos 成员中。

// ~/linux-4.4.232/include/linux/sched.h
struct task_struct {struct fs_struct    *fs; /* filesystem information */struct files_struct *files; /* open file information */
};// ~/linux-4.4.232/include/linux/fdtable.h
struct files_struct {struct file* fd_array[32]; // 32 or 64
};// ~/linux-4.4.232/include/linux/fs.h
struct file {struct path                     f_path;struct inode*                   f_inode; /* cached value */const struct file_operations*   f_op;unsigned int                    f_flags;fmode_t                         f_mode;struct mutex                    f_pos_lock;loff_t                          f_pos;
} __attribute__((aligned(4)));	/* lest something weird decides that 2 is OK */

2. cdev object 管理
cdev 其实是对驱动的抽象(struct cdev),它实现了文件接口(struct file_operations)、继承了 struct kobject。char_dev.c 源文件中定义了一个存储 cdev 的名为 cdev_map 的表,当cdev_add() 被调用时, 函数体调用 kobj_map() 把 cdev 对象添加到其中。struct kobj_map 本质是一个哈希表, 它用主设备号作为哈希桶的下标,通过它进行字符设备的管理(增、删、查)。

// ~/linux-4.4.232/include/linux/cdev.h
struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops;struct list_head list;dev_t dev;unsigned int count;
};// ~/linux-4.4.232/fs/char_dev.c
static struct kobj_map *cdev_map;// ~/linux-4.4.232/drivers/base/map.c
struct kobj_map {struct probe {struct probe *next;dev_t dev;unsigned long range;struct module *owner;kobj_probe_t *get;int (*lock)(dev_t, void *);void *data;} *probes[255];struct mutex *lock;
};

在这里插入图片描述

2. 在根文件系统创建字符设备节点
有了字符设备驱动后用户层并不能直接使用它们,用户层只有通过字符设备节点才能访问到驱动,所以必须为字符设备生成对应的设备节点。之前基于 Qemu 搭建内核开发环境时使用命令 sudo mknod -m 666 node_name c major minor 创建设备节点,然后将它们写入 ext3 格式的文件系统中,制作出了根文件系统,最后系统启动后可以看到我们创建的设备节点。同样调用 device_create() 函数会触发 kobject_uevent(KOBJ_ADD) 调用,进而生成设备节点。这两种调用都是触发了 mknod() 系统调用。

device_create()device_add() kobject_uevent(KOBJ_ADD) mknod()                          // 在根文件系统创建 /dev/ledSYSCALL_DEFINE3(mknod)sys_mknodat()do_mknodat()user_path_create()               // 路径解析may_mknod()                      // 权限检查vfs_mknod()may_create()                 // 创建权限检查security_inode_mknod()       // 安全模块检查dir->i_op->mknod()           // 文件系统特定实现ext4_mknod()             // ext4 实现ext4_new_inode()     // 创建新 inodeinit_special_inode() // 初始化设备 inodeencode_dev_to_disk() // 编码设备号到磁盘 inodeext4_add_nondir()    // 关联 dentry 和 inodefsnotify_create()            // 文件创建通知

4. 调用 open() 函数打开字符设备文件
有了根文件提供的设备节点信息,用户便可以按需读写它们。应用层调用库函数 open() 会触发软中断,进入内核空间调用 sys_open(), 然后调用 vfs_open(),最后驱动层 drv_open() 被调用。当 open 一个字符设备时会发生如下操作:

  1. VFS 会找到与之对应的节点,并在内存中建立与之对应的 inode 对象(struct inode);
// ~/linux-4.4.232/include/linux/fs.h
struct inode {umode_t			i_mode;dev_t			i_rdev;union {struct pipe_inode_info	*i_pipe;struct block_device	*i_bdev;struct cdev		*i_cdev;char			*i_link;};
};
  1. 内核还会创建一个 file 对象(struct file),将其保存在 PCB 的已打开文件描述符表中;
  2. inode 对象中 i_mode 成员会指示它是一个字符设备,然后依据
    inode 对象成员 i_rdev 去 cdev_map 里检索对应的 cdev 对象;
  3. inode 对象的 i_cdev成员(struct cdev*) 指向上一步检索到的 cdev 对象;
  4. 用刚才检索到的 cdev 对象的ops指针去替换第2步创建的 file 对象的 f_op 指针的指向;

如下详细过程:

open("/dev/led", mode, flag) // [0] 库函数调用SYSCALL_DEFINE3(open)    // [1] 系统调用 sys_opendo_sys_open()do_sys_open()do_filp_open()path_openat()link_path_walk()                 // 路径解析do_last()vfs_open()                   // [2] vfs 调用do_dentry_open()chrdev_open()        // 字符设备特殊处理 kobj_lookup()    // [注]查找设备驱动replace_fops()   // 替换文件操作led_fops->open() // [3] 驱动open方法

5. 字符设备的读(写)、关闭 操作

字符设备文件被打开之后就可以对其进行读、写、关闭等操作。这些操作接口都由 struct file 的 ops 提供(源头是驱动)。
struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);int (*iterate) (struct file *, struct dir_context *);unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);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 (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **, void **);long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMUunsigned (*mmap_capabilities)(struct file *);
#endif
};

6. 参考文献

  1. Linux驱动最基本的驱动框架 LED驱动
  2. Linux 字符设备驱动结构(一)——cdev 结构体、设备号相关知识解析
  3. Linux 字符设备驱动结构(二)—— 自动创建设备节点
http://www.dtcms.com/a/575327.html

相关文章:

  • 毕业设计做网站还是系统湛江市手机网站建设企业
  • 做网站是否要备案网站建站网站
  • 莱芜做网站站酷网站
  • 上海加盟网网站建设如何做内网站的宣传栏
  • 如何设计公司标志图案江苏企业网站排名优化
  • 想要做一个网站关于政务网站建设工作情况的总结
  • 上海建网站工作室flash网站引导页面制作
  • 【Janet】语法与解析器
  • 异构比较查找
  • 网站价位无法访问服务器上网站
  • 服装购物网站排名icp备案证书
  • 网站对公司的作用是什么意思免费外贸网站制作
  • 一级a做爰片免费网站体验nginx进wordpress不能进目录
  • 湛江市建设局网站成功的软文营销案例
  • 对于网站开发有什么要求冉冉科技网站建设
  • 汝州网站建设网站建设方案数
  • 跨境电商平台网站建设制作h5用什么软件比较好
  • 网站构建设计思路设计参考网站推荐
  • 两台电脑一台做服务器 网站电商付费推广方式
  • 编写网站 支付宝做爰的最好看的视频的网站
  • 金华企业网站建设富阳seo关键词优化
  • 青岛网上房地产官网查网签苏州seo关键词优化
  • 网站建设行业发展方向企业百度网站怎么做
  • 做西装的网站金融保险网站模板
  • 网站做长连接用手机怎么制作软件
  • 购物网站建设教程全面的客户管理系统
  • 菏泽 兼职做网站阿里云网站建设教程
  • evince魔改记
  • 做内贸的网站小说网站怎么做用户画像
  • Spring AI Advisors API与 Ollama 的结合及实战示例