dev下无法生成节点的分析思路和解决方法及原理

前言:本篇没有解决什么大问题,不神秘也不高深莫测,单纯记录,同时为其他碰到该问题的同仁提供思路,重点在于分析思路的整理,并使用流水帐的形式,试图讲清/dev下节点的生成原理

1.背景

为了调试方便,将之前直接编译进内核的驱动调整为module模式加载,即将驱动编译为.ko文件,通过insmod动态加载驱动。在未调整为module加载方式之前,系统启动后可以正常在dev下正常生成驱动节点 driver_test0,driver_test1
在这里插入图片描述
将驱动编译为module后,在系统完全启动后,使用insmod 加载单编的ko文件后,驱动加载未报错,init和porobe打印均有显示,整个驱动加载也未报错,驱动执行动作一切正常,只是dev目录下没有生成暴露给用户态使用的节点。而且驱动源码未动,只是单纯修改了Makefile使其编译为ko文件

2.差异分析

出现问题了,就先对比下差异项,从差异项入手,既然没有编译为模块的时候,节点正常生成,那么就有了一个好的场景,有了对比对象,这就好办了
相同点:源码相同
差异点:
(1).编译方式不同,即Makefile不同
在这里插入图片描述

(2).加载方式和时机不同
有节点的是将驱动直接编译为内核,在系统启动过程中将驱动加载
无节点的是将驱动单独编译为ko文件,打包到文件系统中,等系统起来后,再通过insmod命令将ko文件注册到系统中,驱动被加载

3.分析

从2中可以看到2个差异项。makefile只是负责编译为不同的镜像形式,感觉和加载方式的不同有很大关联。但是insmod只是单纯的加载,之前也一直在用,一时半会找不到为何使用insmod加载后就没有节点的线索。
既然差异项无法突破,那么就从正面突破吧,先来搞清楚dev下节点生成的原理

4.生成dev节点的原理

dev目录下的节点不是由驱动本身生成的,它是由文件系统中的工具生成的,即busybox中的程序mdev。
具体流程是,当系统启动后,加载完内核再去加载文件系统,执行文件系统中的脚本,脚本会执行mdev -s命令。其实mdev就是一个上层可执行程序,mdev -s执行后,会去遍历/sys/class下的所有文件,寻找所有的名为dev的文件,dev文件保存了每个驱动的主设备号,次设备号,以及驱动名。mdev根据找到的dev,在/dev目录下根据dev文件中的驱动名创建节点(其实就是新建了一个文件)此时,驱动的节点就生成了。
(mdev的源码在busybox源码路径下:./util-linux/mdev.c中,接口为mdev_main,感兴趣可研究下)
根据上述原理,其关键点就2个:
(1).sys/class 下必须有驱动的节点信息,即dev文件
(2).必须执行mdev -s创建/dev下的节点
根据上述结论查询系统下sys/class目录发现驱动的class节点
在这里插入图片描述
进入class_test节点
在这里插入图片描述
发现驱动的节点也已经创建好了,再进入
在这里插入图片描述
可以看到dev文件也是有的,cat dev文件可以看到该驱动的主设备号为249,次设备号为0。所以第一个要素已经具备了,此时就剩下mdev -s的执行了。查看系统脚本:
在这里插入图片描述
在这里插入图片描述
发现mdev -s在脚本中是有的,而且确定执行了。也是就说第二个要素mdev -s的执行也满足了。
定位到这里我确实也迷糊了,2个条件都满足为何/dev目录下不生成节点,难道mdev -s执行的时候出问题了?于是我尝试重新执行下mdev -s,然后再ls下dev节点
在这里插入图片描述
发现driver_test0和driver_test1节点已经出来了。。。难道真的是因为mdev -s没有执行吗,没道理啊,系统起来后会顺序执行rc脚本,这句命令会执行的啊
到这里想了会,就重新卸载再加载了几遍,突然发现,2中的差异之一就是加载时机的不同,出问题的时候是系统已经起来了,此时mdev -s已经执行过了,但是我的驱动还没有被加载,所以class下是没有启动节点节点的。所以不会创建。这下清楚了。
所以无法生成/dev下节点的原因是:
在mdev -s执行之后加载的驱动,所以无法创建/dev下的节点

5.新问题

无法创建/dev下节点的原因找到了,但是又有一个新问题,难道对于insmod加载驱动这种方式,必须在insmod以后,再执行一遍mdev -s吗,这个命令谁执行,每次都手动执行吗,显然对于牛逼的linux这不太可能,肯定有更好的方式。
我想了一下,目的其实就是在insmod以后,系统必须通知一下让某个“人”去执行下mdev -s,从而达成一直自动化的效果,我相信一定是这样的,是个人都要这样设计。。。。
所以这里的问题变为了谁去通知呢?我也不会,百度了下,看了几篇帖子,上报通知的动作在device_create操作中。
device_create 操作就是创建class下节点的函数接口
代码中是这样用的:
在这里插入图片描述
执行device_create后,在sys/class中就可以看到class_test的calss类
在这里插入图片描述
查看class_create的源码

struct device *device_create_vargs(struct class *class, struct device *parent,
				   dev_t devt, void *drvdata, const char *fmt,
				   va_list args)
{
	return device_create_groups_vargs(class, parent, devt, drvdata, NULL,
					  fmt, args);
}

=> device_add
在device中看到了熟悉的这个操作:kobject_uevent,熟悉linux代码知道这就是上报的接口了,继续跟进
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
	return kobject_uevent_env(kobj, action, NULL);
}
kobject_uevent_env 中有如下的操作:
#ifdef CONFIG_UEVENT_HELPER
	/* call uevent_helper, usually only enabled during early boot */
	if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
		struct subprocess_info *info;

		retval = add_uevent_var(env, "HOME=/");
		if (retval)
			goto exit;
		retval = add_uevent_var(env,
					"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
		if (retval)
			goto exit;
		retval = init_uevent_argv(env, subsystem);
		if (retval)
			goto exit;

		retval = -ENOMEM;
		info = call_usermodehelper_setup(env->argv[0], env->argv,
						 env->envp, GFP_KERNEL,
						 NULL, cleanup_uevent_env, env);这句操作是重点,
						 env->argv[0]保存的是一段路径,该调用的作用是
						 通知用户态调用env->argv[0]中的路径存放的应用程序,
						 env->envp是给该程序带的参数,注意这句调用是重点和精髓。
		if (info) {
			retval = call_usermodehelper_exec(info, UMH_NO_WAIT);
			env = NULL;	/* freed by cleanup_uevent_env */
		}
	}
#endif

所以重点是env->argv[0]中的赋值,代码中为:

static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)
{
	env->argv[0] = uevent_helper;
	env->argv[1] = &env->buf[env->buflen];
	env->argv[2] = NULL;
uevent_helper 定义在:
#ifdef CONFIG_UEVENT_HELPER
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
#endif

那么CONFIG_UEVENT_HELPER_PATH又是啥?不知道就搜么,反正内核代码都看得见,大不了就全局搜,只要电脑扛得住。。
在这里插入图片描述
按照搜索的结果,.config中已经默认指定了为/sbin/hotplug,所以应该就是调用sbin/hotplug这个命令或者可执行程序了。
但是在sbin目录下并没有找到hotplug这个命令或者可执行程序
在这里插入图片描述
而且CONFIG_UEVENT_HELPER_PATH这个值不应该市"sbin/mdev "么,如果是"sbin/mdev"那么就可以串起来了,整个过程就很合理,先验证下这个猜测,既然可以在config中可以配置,那么就简单了,重新配置config
在这里插入图片描述
在这里插入图片描述

生成新配置
在这里插入图片描述

重新编译内核,启动看下效果
在这里插入图片描述
在这里插入图片描述
可以看到节点出来了。。。到这里一切就都串起来了,串起来了。。
所以解决方案是:
修改config中的CONFIG_UEVENT_HELPER_PATH配置为/sbin/mdev

6.总结

总结下:dev下自动需要生成节点的必要条件:
条件1:驱动需要调用device_create接口生成sys/class下的class类和dev文件,供mdev程序扫描生成/dev下的节点
在这里插入图片描述
条件2:需要执行/sbin/mdev -s 去扫描1中创建的节点,谁来执行呢,答案在条件3中
2.1前提你的busybox中也要有mdev这个可执行程序

条件3:需要使能CONFIG_UEVENT_HELPER 机制,使能后,在条件1中,调用device_create创建好sys/class下的节点文件后,会通知用户态调用CONFIG_UEVENT_HELPER_PATH中的应用程序。这个应用程序就是/sbin/mdev

条件4:需要配置CONFIG_UEVENT_HELPER_PATH为sbin/mdev

上述4个条件满足后,当你的驱动调用device_create后,/dev目录下就会创建暴露给用户态的驱动节点。

所以dev下生成节点的流程为:

1.驱动调用device_create 创建sys/class/xxx节点
2.接着device_create中会调用call_usermodehelper_exec去执行CONFIG_UEVENT_HELPER_PATH路径中的用户态程序,传入参数s
3.该程序为/sbin/mdev,mdev -s 会去遍历/sys/class下的所有文件,寻找所有的名为dev的文件,
dev文件保存了每个驱动的主设备号,次设备号,以及驱动名。
mdev根据找到的dev,在/dev目录下根据dev文件中的驱动名创建节点(其实就是新建了一个文件)
此时,驱动的节点就生成了

7.后记

还有一种修改CONFIG_UEVENT_HELPER_PATH的方法:
在脚本中执行 echo /sbin/mdev > /proc/sys/kernel/hotplug即可将CONFIG_UEVENT_HELPER_PATH中的内容重写为/sbin/mdev
在这里插入图片描述

  • 13
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值