翼度科技»论坛 云主机 LINUX 查看内容

linux进阶:设备号

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
设计字符设备

文件系统调用系统IO的内核处理过程

在Linux文件系统管理中,当应用程序调用open函数时,内核会根据文件路径找到文件的索引结点(inode),为文件分配文件描述符和文件对象,并根据打开模式和权限等参数进行相应的操作和设置。
 

 
硬件层原理

思路:把底层寄存器配置操作放在文件操作接口里,新建一个文件绑定该文件操作接口,应用程序通过操作指定文件来设置底层寄存器。
基本接口实现:查原理图,数据手册,确定底层需要配置的寄存器。类似于裸机开发。实现一个文件的底层操作接口,这是文件的基本特征。
struct file_operations存放在ebf-buster-linux/include/linux/fs.h。
  1. struct file_operations {
  2.         struct module *owner;
  3.         loff_t (*llseek) (struct file *, loff_t, int);
  4.         ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  5.         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  6.         ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
  7.         ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
  8.         int (*iterate) (struct file *, struct dir_context *);
  9.         int (*iterate_shared) (struct file *, struct dir_context *);
  10.         __poll_t (*poll) (struct file *, struct poll_table_struct *);
  11.         long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  12.         long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
  13.         int (*mmap) (struct file *, struct vm_area_struct *);
  14.         unsigned long mmap_supported_flags;
  15.         int (*open) (struct inode *, struct file *);
  16.         int (*flush) (struct file *, fl_owner_t id);
  17.         int (*release) (struct inode *, struct file *);
  18.         int (*fsync) (struct file *, loff_t, loff_t, int datasync);
  19.         int (*fasync) (int, struct file *, int);
  20.         int (*lock) (struct file *, int, struct file_lock *);
  21.         ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  22.         unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
  23.         int (*check_flags)(int);
  24.         int (*flock) (struct file *, int, struct file_lock *);
  25.         ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
  26.         ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
  27.         int (*setlease)(struct file *, long, struct file_lock **, void **);
  28.         long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);
  29.         void (*show_fdinfo)(struct seq_file *m, struct file *f);
  30. #ifndef CONFIG_MMU
  31.         unsigned (*mmap_capabilities)(struct file *);
  32. #endif
  33.         ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int);
  34.         int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64);
  35.         int (*dedupe_file_range)(struct file *, loff_t, struct file *, loff_t, u64);
  36.         int (*fadvise)(struct file *, loff_t, loff_t, int);
  37. } __randomize_layout;
复制代码
 
驱动层原理

把file_operations文件操作接口注册到内核,内核通过主次设备号来记录它
构造驱动基本对象:struct cdev,里面记录具体的file_operations
  1. cdev_init()  //把用户构建的file_operations结构体记录在内核驱动的基本对象
复制代码
 
两个Hash表(帮助找到cdev结构体)
chrdevs:登记设备号。
  1. __register_chrdev_region()
复制代码
cdev_map->probe:保存驱动基本对象struct cdev。
  1. cdev_add()
复制代码
 
文件系统层原理
  1. mknod + 主次设备号
复制代码
构建一个新的设备文件,通过主次设备号在cdev_map中找到cdev->file_operations,把cdev->file_operations绑定到新的设备文件中。
到这一步,应用程序就可以使用open()、write()、read()等函数来控制设备文件了。
 
 
设备号的组成与哈希表

ebf-buster-linux/include/linux/kdev_t.h描述了设备号的具体构成。
  1. /* 截取部分代码,关于设备号的描述 */
  2. #define MINORBITS    20
  3. #define MINORMASK    ((1U << MINORBITS) -1)
  4. #define MAJOR(dev)   ((unsigned int)((dev) >> MINORBITS))
  5. #define MINOR(dev)   ((unsigned int)((dev) & MINORMASK))
  6. #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
复制代码
上诉函数主设备号相等,判断新旧次设备号三种错误图如下。

保存新注册的设备号到chrdevs哈希表中,防止设备号冲突。
主设备为0时,动态分配设备号(优先使用255~234,其次使用511~384)。主设备号最大为512。
 
 
保存file_operation结构体

关键数据结构:cdev(存放在ebf-buster-linux/include/linux/cdev.h)
  1. static struct char_device_struct {
  2.         struct char_device_struct *next;    //指向下一个链表节点
  3.         unsigned int major;                      //主设备号  
  4.         unsigned int baseminor;               //次设备号
  5.         int minorct;                                 //次设备号的数量
  6.         char name[64];                           //设备名称
  7.         struct cdev *cdev;                       //内核字符对象(已丢弃)
  8. } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
复制代码
 
关键数据结构:kobj_map(与哈希表有关,存放在ebf-buster-linux/drivers/base/map.c)
  1. static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name)
  2. {
  3.         struct char_device_struct *cd, **cp;
  4.         int ret = 0;
  5.         int i;
  6. <br>     /* 动态申请内存 */
  7.         cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
  8.         if (cd == NULL)  return ERR_PTR(-ENOMEM);
  9.      /* 加互斥锁保护资源 */
  10.         mutex_lock(&chrdevs_lock);
  11.         if (major == 0) {  <br>          /* 主设备号为0,从chadevs哈希表中查找一个空闲位置 */
  12.                 ret = find_dynamic_major();
  13.                 if (ret < 0) {
  14.                         pr_err("CHRDEV "%s" dynamic allocation region is full\n", name);
  15.                         goto out;
  16.                 }<br>          /* 返回主设备号 */
  17.                 major = ret;  
  18.      }
  19.         if (major >= CHRDEV_MAJOR_MAX) {
  20.                 pr_err("CHRDEV "%s" major requested (%u) is greater than the maximum (%u)\n", name, major, CHRDEV_MAJOR_MAX-1);
  21.                 ret = -EINVAL;
  22.                 goto out;
  23.         }
  24. <br>     /* 保存参数 */
  25.         cd->major = major;
  26.         cd->baseminor = baseminor;
  27.         cd->minorct = minorct;
  28.         strlcpy(cd->name, name, sizeof(cd->name));
  29. <br>     /* 哈希函数,计算哈希表的位置 */
  30.         i = major_to_index(major);<br>     /* 链表排序,按主设备号从小到大排序。如果主设备号相等,按次设备号从小到大排序,要考虑次设备号的最大值 */
  31.         for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
  32.                 if ( (*cp)->major > major || ( (*cp)->major == major && ( ( (*cp)->baseminor >= baseminor ) || ( (*cp)->baseminor + (*cp)->minorct > baseminor) ) ) )  break;
  33.         /* 如果主设备号相等,检查次设备号是否存在冲突  */
  34.         if (*cp && (*cp)->major == major) {<br>          /* 获取链表节点的次设备号范围 */
  35.                 int old_min = (*cp)->baseminor;
  36.                 int old_max = (*cp)->baseminor + (*cp)->minorct - 1;<br>          /* 获取新设备的次设备号范围 */
  37.                 int new_min = baseminor;
  38.                 int new_max = baseminor + minorct - 1;
  39.                 /* 判断新设备的次设备号最大值是否位于链表节点的次设备号范围  */
  40.                 if (new_max >= old_min && new_max <= old_max) {<br>               /* 确定冲突,返回错误 */
  41.                         ret = -EBUSY;
  42.                         goto out;
  43.                 }
  44.                 /* 判断新设备的次设备号最小值是否位于链表节点的次设备号范围  */
  45.                 if (new_min <= old_max && new_min >= old_min) {<br>               /* 确定冲突,返回错误 */
  46.                         ret = -EBUSY;
  47.                         goto out;
  48.                 }
  49. <br>          /* 判断新设备的次设备号是否跨越链表节点的次设备号范围 */
  50.                 if (new_min < old_min && new_max > old_max) {<br>               /* 确定冲突,返回错误 */
  51.                         ret = -EBUSY;
  52.                         goto out;
  53.                 }
  54.         }
  55. <br>     /* 插入新设备的链表节点 */
  56.         cd->next = *cp;
  57.         *cp = cd;
  58.         mutex_unlock(&chrdevs_lock);
  59.         return cd;
  60. out:
  61.         mutex_unlock(&chrdevs_lock);
  62.         kfree(cd);
  63.         return ERR_PTR(ret);
  64. }
复制代码
 
关键函数:cdev_init(存放在ebf-buster-linux/fs/char_dev.c)

作用:保存file_operation到cdev中。
 
关键函数:cdev_add(存放在ebf-buster-linux/fs/char_dev.c)

作用:根据哈希函数保存cdev到probes哈希表中,方便内核查找file_operation使用。
 

来源:https://www.cnblogs.com/couvrir/p/17626647.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具