应群里几位聊的好的哥们的邀请,要我分析一个内核模块。我后面就选了LED模块分析,LED模块分析不算难,但
要说清楚其实还是很挑战的。今天俺的文章被推荐到首页了。挺有成就感的。我的文章虽然不登大雅之堂,但只
要能给到大家一起指点,哪怕就一点点我就心满意足了。好了,闲话不多说了,开始我们的linux内核之旅吧。
这一节是应群里几位兄弟的要求讲LED模块,我稍微看了一下,就挑了一个最软的柿子来捏。怎么样挑到一个最软
的柿子呢?首先我们在分析这个模块之前第一件事就是想办法缩小范围,如果不缩小范围,你我的精力都是不够
的,可能会搞的非常痛苦,怎么缩小。看法宝:Kconfig Makefile。我们首先进入/drivers/leds的目录,然后查
看Makefile,可以看到:
# LED Core
obj-$(CONFIG_NEW_LEDS) += led-core.o
obj-$(CONFIG_LEDS_CLASS) += led-class.o
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
。。。。
很明显,前三个文件是逃不过的,这个玩意叫core。在内核中凡是叫core的东西基本是属于必看的,没事,才三
个文件,哥们不怕(这都怕的就别玩了^_^)。哥几个说要分析led的trigger,OK没问题,俺就开始挑一个容易明
白点的模块了。ledtrig-heartbeat.c。哥为什么要挑这个模块讲呢?在说原因之前我还是强调,有的东西只能见
招拆招,要多动脑子,多找规律。我是这么找的:打开Kconfig查看一下对应的信息。Kconfig给了我们什么信息
呢?首先就是注释,其次是它本身依赖的模块。(又见潜规则了,我们的计算机之所以能跑起来就是一大堆的潜
规则。)这是Kconfig对trigger_heartbeat的描述:
config LEDS_TRIGGER_HEARTBEAT
tristate "LED Heartbeat Trigger"
depends on LEDS_TRIGGERS
help
This allows LEDs to be controlled by a CPU load average.
The flash frequency is a hyperbolic function of the 1-minute
load average.
If unsure, say Y.
我们可以发现这么一句: depends on LEDS_TRIGGERS,这玩意就是我们前面看到的makefile中的那个core宏所包
含的,哥心中暗爽了一把。捡了个现成的便宜。在对应的makefile中办理入LEDS_TRIGGER_HEARTBEAT查找后得:
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
现在找准了我们要分析的对象后,就要打场硬仗了。
首先我们要有一个意识:像读写锁等这些用于同步互斥等的手段对于我们理解模块的体系结构的影响是非常小的
,我们要先避重就轻,我现在在分析模块的时候已经形成了一种语感,一眼扫过去,这种东西根本进不了我的脑
袋中,可以较为快速的扫到重要的东西,从而加快自已分析代码的速度,在实际写或者改一个内核模块的时候,
可以再仔细分析这些同步或互斥的手段。
很明显,哥一眼扫过去后,发现这个函数做的事其实挺少的:
int led_trigger_register(struct led_trigger *trigger)
{
struct led_classdev *led_cdev;
struct led_trigger *trig;
rwlock_init(&trigger->leddev_list_lock);
INIT_LIST_HEAD(&trigger->led_cdevs);
down_write(&triggers_list_lock);
/* Make sure the trigger's name isn't already in use */
list_for_each_entry(trig, &trigger_list, next_trig) {
if (!strcmp(trig->name, trigger->name)) {
up_write(&triggers_list_lock);
return -EEXIST;
}
}
/* Add to the list of led triggers */
list_add_tail(&trigger->next_trig, &trigger_list);
up_write(&triggers_list_lock);
/* Register with any LEDs that have this as a default trigger */
down_read(&leds_list_lock);
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
if (!led_cdev->trigger && led_cdev->default_trigger &&
!strcmp(led_cdev->default_trigger, trigger->name))
led_tr