前言
在 Linux Kernel 中,根据 Makefile 和 Kconfig,可以快速地了解一个小的内核子系统。所以我将这两个文件称之为 Kernel 地图。
Kernel 地图
基本上,Linux 内核中,每一个目录下面都有一个 Makefile 和一个 Kconfig 文件。这两个文件就像一座城市的地图,地图带领我们去认识一座城市,而这两个文件则可以让我们了解这个目录下面的结构以及大致内容。我们每次浏览 Kernel 寻找属于自己的那段代码时,都应该首先看看目录下的这两个文件。
就像利用地图寻找目的地一样,我们需要利用 Makefile 和 Kconfig 来寻找所要研究的目标代码。
毫不夸张地说,Makefile 和 Kconfig 是我们浏览内核代码时最为依仗的两个文件。对于一个希望能够在 Linux 内核的汪洋代码里看到一丝曙光的人来说,将它们放在怎样重要的地位都不过分。
实例运用
对象:Kernel led trigger 子系统
首先进入 driver/led/ 目录,列出目录内容
看到有上百个文件,如果心中无地图,基本上会迷失在代码的海洋中。
不过,我们心中有地图,不慌。
Makefile
先看 Makefile
# SPDX-License-Identifier: GPL-2.0# LED Core
obj-$(CONFIG_NEW_LEDS) += led-core.o
obj-$(CONFIG_LEDS_CLASS) += led-class.o
obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o
obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += led-class-multicolor.o
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o# LED Platform Drivers (keep this sorted, M-| sort)
obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o
obj-$(CONFIG_LEDS_ACER_A500) += leds-acer-a500.o
obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o
obj-$(CONFIG_LEDS_APU) += leds-apu.o
obj-$(CONFIG_LEDS_ARIEL) += leds-ariel.o
obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o
obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
obj-$(CONFIG_LEDS_AW2013) += leds-aw2013.o
obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o
obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
obj-$(CONFIG_LEDS_CPCAP) += leds-cpcap.o
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o
obj-$(CONFIG_LEDS_IP30) += leds-ip30.o
obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o
obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
obj-$(CONFIG_LEDS_LM3532) += leds-lm3532.o
obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o
obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o
obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o
obj-$(CONFIG_LEDS_LM3692X) += leds-lm3692x.o
obj-$(CONFIG_LEDS_LM3697) += leds-lm3697.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
obj-$(CONFIG_LEDS_LP3952) += leds-lp3952.o
obj-$(CONFIG_LEDS_LP50XX) += leds-lp50xx.o
obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o
obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o
obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o
obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o
obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
obj-$(CONFIG_LEDS_OT200) += leds-ot200.o
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
obj-$(CONFIG_LEDS_SGM3140) += leds-sgm3140.o
obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o
obj-$(CONFIG_LEDS_TLC591XX) += leds-tlc591xx.o
obj-$(CONFIG_LEDS_TPS6105X) += leds-tps6105x.o
obj-$(CONFIG_LEDS_TURRIS_OMNIA) += leds-turris-omnia.o
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o# LED SPI Drivers
obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
obj-$(CONFIG_LEDS_EL15203000) += leds-el15203000.o
obj-$(CONFIG_LEDS_SPI_BYTE) += leds-spi-byte.o# LED Userspace Drivers
obj-$(CONFIG_LEDS_USER) += uleds.o# LED Triggers
obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
这个文件很长,不过不必担心,有用的就前面几行,也就是 LED Core 部分。
- led-core.c 是整个 led 子系统的核心,我们需要使用该子系统,定要使能 NEW_LEDS 选项
- led-class.c 是创建 /sys/class/led/ 目录相关信息
- led-triggers 就是我们想要研究的 led trigger 功能
其余的选项,我们都可以不关心。不过,如果你不放心的话,可以去查看每一项的解释。去哪去看每一项的解释呢?那就是到 Kconfig
Kconfig
# SPDX-License-Identifier: GPL-2.0-only
config LEDS_GPIO_REGISTERboolhelpThis option provides the function gpio_led_register_device.As this function is used by arch code it must not be compiled as amodule.menuconfig NEW_LEDSbool "LED Support"helpSay Y to enable Linux LED support. This allows control of supportedLEDs from both userspace and optionally, by kernel events (triggers).if NEW_LEDSconfig LEDS_CLASStristate "LED Class Support"helpThis option enables the LED sysfs class in /sys/class/leds. You'llneed this to do anything useful with LEDs. If unsure, say N.
。。。
内容太长了,我们只贴开头一段,找两项来看下
- NEW_LEDS:Say Y to enable Linux LED support.
- LEDS_CLASS:This option enables the LED sysfs class in /sys/class/leds.
解释地清晰明了,任你选择是否开启该功能。
如果你想知道当前系统开了哪些选项,除了查看内核顶层目录的 .config 文件外,还有一个更直观的办法,那就是 make menuconfig
打 “*” 或 “M” 的选项表示启用了该功能,空格表示未启用该功能。
这样,我们在了解一个子系统的功能实现时,只需阅读开启的代码就可以了,通常一个开启的选项对应一个 .c 文件。
PS:当然你也可以根据编译后是否生成对应的 .o 文件来判断某个 .c 文件是否被编译到了。但是根据 Makefile 和 Kconfig,我们不仅可以知道哪些文件被编译到了,还可以知道它们的包含关系、依赖关系、简要描述等。这将有利于帮助我们在大脑中构建一个子系统的结构关系图。
Help 选项可以查看当前选项的详细含义
可以看到,其描述内容和我们在 Kconfig 文件中看到的一致。
除此之外,还可以直观地看出其所依赖的选项,以及它被依赖的选项。这些信息在 Kconfig 中是以文本的形式出现的。比如
if NEW_LEDSconfig LEDS_CLASStristate "LED Class Support"helpThis option enables the LED sysfs class in /sys/class/leds. You'llneed this to do anything useful with LEDs. If unsure, say N.endif # NEW_LEDS
LEDS_CLASS 是被 if NEW_LEDS ... endif
限定的,说明 LEDS_CLASS 依赖 NEW_LEDS;
config LEDS_CLASS_FLASHtristate "LED Flash Class Support"depends on LEDS_CLASShelpThis option enables the flash LED sysfs class in /sys/class/leds.It wraps LED Class and adds flash LEDs specific sysfs attributesand kernel internal API to it. You'll need this to provide supportfor the flash related features of a LED device. It can be builtas a module.
LEDS_CLASS_FLASH 有 depends on LEDS_CLASS
,说明 LEDS_CLASS 被该功能所依赖。
根据这份地图,我们很快能够明晰哪些文件需要我们关心,哪些可以无视它。很显然,我们需要关心 trigger 这个选项。
发现这个选项对应一个目录,那就继续进入该目录,看该目录下的地图。如同下了高速,开启城市导航。
trigger
根据地图,我们很容易明白,trigger 目录下存放的是各种类型的 trigger 方式,有 heartbeat(心跳),有 mtd(NOR/NAND Flash),
以 mtd 举例,使能 TRIGGER_MTD 选项后,Kernel 启动过程中,就会安装该模块,导出其 activity() 函数,在需要闪烁 mtd 灯的内核代码或驱动模块中,只要调用该 activity() 函数就行了,
上面这处就是每当从 Flash 读取数据时,led 闪烁一下。
具体想让哪个灯作为 mtd 灯呢?只要在 /sys/class/led/ 对应灯的目录中绑定一下即可,如
# cd /sys/class/leds/orangepi:
orangepi:green:pwr/ orangepi:red:status/
# cd /sys/class/leds/orangepi:red:status/
# ls
brightness max_brightness subsystem uevent
device power trigger
# cat trigger
[none] rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock mtd nand-disk heartbeat default-on mmc0
# echo mtd > trigger
# cat trigger
none rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock [mtd] nand-disk heartbeat default-on mmc0
这样,每当有读写 Flash 的操作,OrangePi PC 上面的这颗 red 灯就会闪烁。
这里用到的 /sys/class/led/ 目录就是我们开始时介绍的 LEDS_CLASS 选项。
总结
看内核代码,先看 Makefile 和 Kconfig。