• 【linux API 分析】register_chrdev


    linux kernel:4.19
    在注册字符设备的时候,可使用register_chrdev()函数,其对应的注销函数是unregister_chrdev(),其定义是在include\linux\fs.h文件

    register_chrdev()

    首先分析register_chrdev()函数
    其定义如下

    static inline int register_chrdev(unsigned int major, const char *name,
    				  const struct file_operations *fops)
    {
    	return __register_chrdev(major, 0, 256, name, fops);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    该函数会调用__register_chrdev()函数,其定义位于fs\char_dev.c文件

    /**
     * __register_chrdev() - create and register a cdev occupying a range of minors
     * @major: major device number or 0 for dynamic allocation
     * @baseminor: first of the requested range of minor numbers
     * @count: the number of minor numbers required
     * @name: name of this range of devices
     * @fops: file operations associated with this devices
     *
     * If @major == 0 this functions will dynamically allocate a major and return
     * its number.
     *
     * If @major > 0 this function will attempt to reserve a device with the given
     * major number and will return zero on success.
     *
     * Returns a -ve errno on failure.
     *
     * The name of this device has nothing to do with the name of the device in
     * /dev. It only helps to keep track of the different owners of devices. If
     * your module name has only one type of devices it's ok to use e.g. the name
     * of the module here.
     */
    int __register_chrdev(unsigned int major, unsigned int baseminor,
    		      unsigned int count, const char *name,
    		      const struct file_operations *fops)
    {
    	struct char_device_struct *cd;
    	struct cdev *cdev;
    	int err = -ENOMEM;
    
    	cd = __register_chrdev_region(major, baseminor, count, name);
    	if (IS_ERR(cd))
    		return PTR_ERR(cd);
    
    	cdev = cdev_alloc();
    	if (!cdev)
    		goto out2;
    
    	cdev->owner = fops->owner;
    	cdev->ops = fops;
    	kobject_set_name(&cdev->kobj, "%s", name);
    
    	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
    	if (err)
    		goto out;
    
    	cd->cdev = cdev;
    
    	return major ? 0 : cd->major;
    out:
    	kobject_put(&cdev->kobj);
    out2:
    	kfree(__unregister_chrdev_region(cd->major, baseminor, count));
    	return err;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    __register_chrdev()函数首先调用__register_chrdev_region()函数

    /*
     * Register a single major with a specified minor range.
     *
     * If major == 0 this functions will dynamically allocate a major and return
     * its number.
     *
     * If major > 0 this function will attempt to reserve the passed range of
     * minors and will return zero on success.
     *
     * Returns a -ve errno on failure.
     */
    static struct char_device_struct *
    __register_chrdev_region(unsigned int major, unsigned int baseminor,
    			   int minorct, const char *name)
    {
    	struct char_device_struct *cd, **cp;
    	int ret = 0;
    	int i;
    
    	cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
    	if (cd == NULL)
    		return ERR_PTR(-ENOMEM);
    
    	mutex_lock(&chrdevs_lock);
    
    	if (major == 0) {
    		ret = find_dynamic_major();
    		if (ret < 0) {
    			pr_err("CHRDEV \"%s\" dynamic allocation region is full\n",
    			       name);
    			goto out;
    		}
    		major = ret;
    	}
    
    	if (major >= CHRDEV_MAJOR_MAX) {
    		pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n",
    		       name, major, CHRDEV_MAJOR_MAX-1);
    		ret = -EINVAL;
    		goto out;
    	}
    
    	cd->major = major;
    	cd->baseminor = baseminor;
    	cd->minorct = minorct;
    	strlcpy(cd->name, name, sizeof(cd->name));
    
    	i = major_to_index(major);
    
    	for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
    		if ((*cp)->major > major ||
    		    ((*cp)->major == major &&
    		     (((*cp)->baseminor >= baseminor) ||
    		      ((*cp)->baseminor + (*cp)->minorct > baseminor))))
    			break;
    
    	/* Check for overlapping minor ranges.  */
    	if (*cp && (*cp)->major == major) {
    		int old_min = (*cp)->baseminor;
    		int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
    		int new_min = baseminor;
    		int new_max = baseminor + minorct - 1;
    
    		/* New driver overlaps from the left.  */
    		if (new_max >= old_min && new_max <= old_max) {
    			ret = -EBUSY;
    			goto out;
    		}
    
    		/* New driver overlaps from the right.  */
    		if (new_min <= old_max && new_min >= old_min) {
    			ret = -EBUSY;
    			goto out;
    		}
    
    		if (new_min < old_min && new_max > old_max) {
    			ret = -EBUSY;
    			goto out;
    		}
    
    	}
    
    	cd->next = *cp;
    	*cp = cd;
    	mutex_unlock(&chrdevs_lock);
    	return cd;
    out:
    	mutex_unlock(&chrdevs_lock);
    	kfree(cd);
    	return ERR_PTR(ret);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    其次调用cdev_alloc()函数

    struct cdev *cdev_alloc(void)
    {
    	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    	if (p) {
    		INIT_LIST_HEAD(&p->list);
    		kobject_init(&p->kobj, &ktype_cdev_dynamic);
    	}
    	return p;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后调用cdev_add()函数

    /**
     * cdev_add() - add a char device to the system
     * @p: the cdev structure for the device
     * @dev: the first device number for which this device is responsible
     * @count: the number of consecutive minor numbers corresponding to this
     *         device
     *
     * cdev_add() adds the device represented by @p to the system, making it
     * live immediately.  A negative error code is returned on failure.
     */
    int cdev_add(struct cdev *p, dev_t dev, unsigned count)
    {
    	int error;
    
    	p->dev = dev;
    	p->count = count;
    
    	error = kobj_map(cdev_map, dev, count, NULL,
    			 exact_match, exact_lock, p);
    	if (error)
    		return error;
    
    	kobject_get(p->kobj.parent);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    综上register_chrdev函数调用流程如下

    register_chrdev()
    	-->__register_chrdev()
    		-->__register_chrdev_region()
    		-->cdev_alloc()
    		-->cdev_add()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    从__register_chrdev_region()函数可以看出,该函数会根据传入的参数,去完善结构体 char_device_struct(除cdev结构体),并返回该结构体地址

    static struct char_device_struct {
    	struct char_device_struct *next;
    	unsigned int major;
    	unsigned int baseminor;
    	int minorct;
    	char name[64];
    	struct cdev *cdev;		/* will die */
    } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    该结构体中,存在一个链表,链表的作用分析如下:

    #define CHRDEV_MAJOR_HASH_SIZE 255
    
    • 1

    可以看出分配的结构体指针数组有255个元素,由于有的主设备号大于254,故需要除以255取余,这样,当主设备号为256时,除以255取余为1,故存在数组chrdev[1]中,但chrdev也可存主设备号为1的元素,故使用链表,使主设备号为1的char_device_struct的next成员指向主设备号为256的char_device_struct

    在这里插入图片描述

    对于cdev结构体,其内容如下

    struct cdev {
    	struct kobject kobj;
    	struct module *owner;
    	const struct file_operations *ops;
    	struct list_head list;
    	dev_t dev;
    	unsigned int count;
    } __randomize_layout;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    cdev_alloc()函数,该函数为cdev结构体分配内存空间,并初始化list以及kobj成员
    对于kobj目前我没搞清楚,在这个时候看到了一篇文章,写得不错,有兴趣的去看这篇文章吧
    链接: link

  • 相关阅读:
    东极岛 需要提早买门票
    蓝桥杯嵌入式STM32G431RBT6竞赛指南与模板——最后的绝唱
    高项_第十一章项目风险管理
    OpenCV官方教程中文版 —— 图像修复
    可堆叠的残差注意力模块用于图像分类(Residual Attention Network for Image Classification——代码复现与解读)
    「废话少说,放码过来」:博客园2024夏季T恤上架预售
    Springboot毕设项目博恒人力资源规划系统671c9(java+VUE+Mybatis+Maven+Mysql)
    基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码
    【Cherno的OpenGL视频】How to make your uniform faster in OpenGL
    2024智慧农场系统微信小程序前端如何上传以及配置
  • 原文地址:https://blog.csdn.net/weixin_42963900/article/details/133897956