大家好 在本文中,我想分享创建简单Linux内核模块的经验。 对于那些想了解如何编写内核模块但又不知道从哪里开始的人来说,本文将是有用的。
我一直想了解这个主题,但是直到最近我才知道该如何处理它。 我希望该模块足够简单,但要比消息“ Hello world!”更复杂。 在日志文件中输出。 最后,我决定尝试使LED闪烁。 另一个目标是推导引起sysfs闪烁频率的参数。
在实验中,我使用了Orange Pi One开发板和Ubuntu Linux(内核版本3.4.113)。 为了构建内核模块,您需要gcc编译器,make实用程序和内核头文件。 要安装头文件,请运行以下命令:
sudo apt-get install linux-headers-$(uname -r)
接下来,我将分析模块中最有趣的部分。 为了节省空间,我不会在这里给出所有代码,它与make文件一起在
github上可用。
在模块中,我使用了头文件:
#include <linux/kernel.h> #include <linux/module.h> #include <linux/gpio.h> #include <linux/hrtimer.h> #include <linux/moduleparam.h>
编写内核模块时,应始终启用kernel.h和module.h,gpio.h实际上负责使用GPIO,hrtimer.h(高分辨率计时器)是计时器的头文件,需要moduleparam.h来显示sysfs中的参数。
为了不使它们的变量和功能在系统内核中发光,它们都应描述为静态的。 以防万一,我注意到内核是用C和静态语言编写的,这与C ++不同,这意味着该对象仅在可执行文件内部可用。
入口点是:
static int blink_module_init(void)
在这里,我将初始化以后将使用的变量,包括:
gpio_timer_interval = ktime_set(gpio_blink_interval_s, 0);
ktime_set通过为其提供所需的秒数(gpio_blink_interval_s)和纳秒(0)来初始化ktime_t数据类型。 将来,计时器将使用此变量。
以下是使用GPIO的请求:
err = gpio_request(BLINK_PIN_NR, "blink_led");
如果成功,此函数将返回0,因此以后我会检查它是否返回。 接下来,必须将选定的引脚设置为信号输出并指定默认值。
err = gpio_direction_output(BLINK_PIN_NR, GPIOF_INIT_LOW);
如果没有错误,则初始化并启动计时器
hrtimer_init(&gpio_blink_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); gpio_blink_timer.function = &gpio_blink_timer_callback; hrtimer_start(&gpio_blink_timer, gpio_timer_interval, HRTIMER_MODE_REL);
计时器回调函数将为gpio_blink_timer_callback。 在此功能中,我将引脚值更改为相反的值
gpio_value ^= 0x01; gpio_set_value(BLINK_PIN_NR, gpio_value);
我问定时器什么时候下一次工作
hrtimer_forward_now(&gpio_blink_timer, gpio_timer_interval);
并返回HRTIMER_RESTART。
现在,让我们看一下如何在sysfs中显示某种变量。 为此,我使用一个宏
module_param_cb(gpio_blink_interval_s, &kp_ops, &gpio_blink_interval_s, 0660);
该宏的第一个参数是sysfs中的文件名。 第二个是包含回调函数的数据结构。 第三个参数是指向实变量的指针,并且是sysfs中的第四个文件权限。
当用户更改sysfs文件的值或读取其值时,将调用kp_ops中的函数。 这是我初始化它们的方式:
static const struct kernel_param_ops kp_ops = { .set = &set_blink_interval, .get = &get_blink_interval };
在这种情况下,由于set会设置一个新值gpio_timer_interval,因此很有趣。
gpio_timer_interval = ktime_set(gpio_blink_interval_s, 0);
在出口处,我清除所有使用的资源
static void blink_module_exit(void) { hrtimer_cancel(&gpio_blink_timer); gpio_set_value(BLINK_PIN_NR, 0); gpio_free(BLINK_PIN_NR); printk(KERN_ALERT "Blink module unloaded\n"); }
入口和出口点必须在相应的宏中指示
module_init(blink_module_init); module_exit(blink_module_exit);
它似乎描述了所有要点。 如果读者有任何疑问,我将很乐意在评论中回答。