From b0c7a463f70e3ea683aa64c1ffaacfa89564e70d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=BB=E8=A7=85?= Date: Thu, 5 Sep 2024 12:55:06 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=E4=BC=98=E5=8C=96=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 35 ++++--- 引言.md | 29 ++++++ 示例/8-调度与中断/Makefile | 3 +- 示例/8-调度与中断/bh_thread.c | 151 +++++++++++++++++++++++++++++ 示例/8-调度与中断/bottomhalf.c | 171 +++++++++++++++++++++++++++++++++ 示例/8-调度与中断/intrpt.c | 2 +- 6 files changed, 371 insertions(+), 20 deletions(-) create mode 100644 引言.md create mode 100644 示例/8-调度与中断/bh_thread.c create mode 100644 示例/8-调度与中断/bottomhalf.c diff --git a/README.md b/README.md index eac4c1a..7867bd3 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,27 @@ -本项目是[lkmpg](https://github.com/sysprog21/lkmpg)项目的翻译副本 - # Linux 内核模块编程指南 -该项目是不断更新《Linux 内核模块编程指南》的中文译本, 但需要注意的是此项目和原版在使用的中央处理器架构上有所不同, 原版是使用的主流X86架构, 本版本会将部分涉及到架构相关的内容修改为龙架构,目前本译本已支持到 6.9.x 版本的内核, 相关[示例](示例/)。 +本项目可以看作为[lkmpg](https://github.com/sysprog21/lkmpg)项目的中文译本, 但并添加了部分译者注, 需要注意的是此项目和原版在使用的中央处理器架构上有所不同, 原版是使用的主流X86架构, 本版本会将部分涉及到架构相关的内容修改为龙架构,目前本译本已支持到 6.9.x 版本的内核, 相关[示例](示例/)。 -本项目原版是使用TeXLie格式编写, 在译者对此格式较为陌生, 已改为更为主流的 `markdown` 格式重写, 并且在翻译途中为了方便读者理解一些不常见的英文缩写, 添加了部分注释 +原作者与本书的一些介绍和致谢说明请看[引言](./引言.md) + +本书是由寻觅(樊旭东)翻译为中文 + +本项目原版是使用TeXLie格式编写, 在译者对此格式较为陌生, 已改为更为主流的 `markdown` 格式重写 ## 开始使用 在线文档: http://223.76.216.188:50201/books/linux/chapter/6c4b7 > 目录: -> * [0 准备工作](文档/0-准备工作) -> * [1 编写驱动](文档/1-编写驱动) -> * [2 驱动相关文件](文档/2-驱动相关文件) -> * [3 ioctl](文档/3-ioctl) -> * [4 系统调用](文档/4-系统调用) -> * [5 阻塞进程和线程](文档/5-阻塞进程和线程) -> * [6 内核模块的锁](文档/6-内核模块的锁) -> * [7 驱动与用户交互](文档/7-驱动与用户交互) -> * [8 调度与中断](文档/8-调度与中断) -> * [](文档/) - -## 版权说明 - -本项目翻译的原项目是基于GPL-2协议开源, 为了遵守协议约束, 本项目有且只有继续使用GPL-2协议开源 +> * [0 准备工作](文档/00-准备工作.md) +> * [1 编写驱动](文档/01-编写驱动.md) +> * [2 驱动相关文件](文档/02-驱动相关文件.md) +> * [3 ioctl](文档/03-ioctl.md) +> * [4 系统调用](文档/04-系统调用.md) +> * [5 阻塞进程和线程](文档/05-阻塞进程和线程.md) +> * [6 内核模块的锁](文档/06-内核模块的锁.md) +> * [7 驱动与用户交互](文档/07-驱动与用户交互.md) +> * [8 调度与中断](文档/08-调度与中断.md) +> * [9 设备驱动](文档/09-设备驱动.md) +> * [10 优化与常见问题](文档/10-优化与常见问题.md) diff --git a/引言.md b/引言.md new file mode 100644 index 0000000..aa48b6c --- /dev/null +++ b/引言.md @@ -0,0 +1,29 @@ +# 引言 + +《Linux内核模块编程指南》是一本免费的书;你可以根据 [开放软件许可证(Open Software License)](https://opensource.org/licenses/OSL-3.0) 3.0版的条款复制和/或修改它。 + +本书的发行是希望它会有用, 但没有任何担保, 甚至没有对适销性或特定用途的适用性的暗示担保。 + +作者鼓励广泛分发本书供个人或商业使用, 前提是上述版权声明保持不变, 并且该方法符合[开放软件许可证(Open Software License)](https://opensource.org/licenses/OSL-3.0) +的规定。总之, 你可以免费复制和分发本书, 也可以赢利。以任何媒介、物理或电子形式复制本书, 无需获得作者的明确许可。 + +本文档的衍生作品和翻译必须置于开放软件许可证下, 并且原始版权声明必须保持不变。如果你为本书贡献了新材料, 则必须为你的修订提供材料和源代码。请直接向文档维护者Jim Huang提供修订和更新。 +这将允许合并更新, 并为Linux社区提供一致的修订。 + +如果你以商业方式出版或分发本书, 作者和[Linux Documentation Project](https://tldp.org/) (LDP)将非常感谢你的捐赠、版税和/或印刷本。以这种方式贡献表明你支持自由软件和LDP。如果你有任何问题或意见, 请联系上述地址。 + +# 原作者介绍 + +《Linux内核模块编程指南》最初由Ori Pomerantz for Linux v2.2编写。随着Linux内核的发展, Ori维护文档的可用性降低了。因此, Peter Jay Salzman承担了维护者的角色, 并更新了Linuxv2.4指南。在跟踪Linux v2.6中的开发时, Peter也受到了类似的限制, 导致Michael Burian加入为共同维护者, 以使该指南与Linux v2.6同步。Bob Mottram通过更新Linux v3.8和更高版本的示例对该指南做出了贡献。Jim Huang随后承担了更新最新Linux版本(v5.0及更高版本)指南的任务, 并修订LaTeX文档。 + +# 致谢 + +以下人员为本书提供了一些更正与良好的建议: + +Amit Dhingra, Andy Shevchenko, Arush Sharma, Benno Bielmeier, Bob Lee, Brad Baker, Che-Chia Chang, Cheng-Shian Yeh, Chih-En Lin, Chih-Hsuan +Yang, Chih-Yu Chen, Ching-Hua (Vivian) Lin, Chin Yik Ming, cvvletter, Cyril Brulebois, Daniele Paolo Scarpazza, David Porter, demonsome, Dimo +Velev, Ekang Monyet, Ethan Chan, Francois Audeon, Gilad Reti, heartofrain, Horst Schirmeier, Hsin-Hsiang Peng, Ignacio Martin, I-Hsin +Cheng, Iûnn Kiàn-îng, Jian-Xing Wu, Johan Calle, keytouch, Kohei Otsuka, Kuan-Wei Chiu, manbing, Marconi Jiang, mengxinayan, Meng-Zong Tsai, +Peter Lin, Roman Lakeev, Sam Erickson, Shao-Tse Hung, Shih-Sheng Yang, Stacy Prowell, Steven Lung, Tristan Lelong, Tse-Wei Lin, Tucker Polomik, +Tyler Fanelli, VxTeemo, Wei-Hsin Yeh, Wei-Lun Tsai, Xatierlike Lee, Yen-Yu Chen, Yin-Chiuan Chen, Yi-Wei Lin, Yo-Jung Lin, Yu-Hsiang Tseng, YYGO. + diff --git a/示例/8-调度与中断/Makefile b/示例/8-调度与中断/Makefile index c6fd30d..f7d4909 100644 --- a/示例/8-调度与中断/Makefile +++ b/示例/8-调度与中断/Makefile @@ -1,7 +1,8 @@ obj-m += example_tasklet.o obj-m += sched.o obj-m += intrpt.o -obj-m += intrpt_old.o +obj-m += bottomhalf.o +obj-m += bh_thread.c PWD := $(CURDIR) diff --git a/示例/8-调度与中断/bh_thread.c b/示例/8-调度与中断/bh_thread.c new file mode 100644 index 0000000..61d15ce --- /dev/null +++ b/示例/8-调度与中断/bh_thread.c @@ -0,0 +1,151 @@ +/* + * bh_thread.c - 起始部分和后续部分中断处理 + * + * 基于 Stefan Wendler(devnull@kaltpost.de)的 RPi 示例 + * 来源于: + * https://github.com/wendlers/rpi-kmod-samples + * + * 按下一个按钮打开 LED,另一个按钮关闭 LED + */ + +#include +#include +#include +#include +#include + +static int button_irqs[] = { -1, -1 }; + +/* 定义 LED 的 GPIO。 + * FIXME: 根据你的板子的 GPIO 号码修改这些数字。 + */ +static struct gpio leds[] = { { 1, GPIOF_OUT_INIT_LOW, "LED 1" } }; + +/* 定义按钮的 GPIO + * FIXME: 根据你的板子的 GPIO 号码修改这些数字。 + */ +static struct gpio buttons[] = { + { 2, GPIOF_IN, "LED 1 开按钮" }, + { 3, GPIOF_IN, "LED 1 关按钮" }, +}; + +/* 当 IRQ 触发时立即发生 */ +static irqreturn_t button_top_half(int irq, void *ident) +{ + return IRQ_WAKE_THREAD; +} + +/* 可以在空闲时处理,释放 IRQ 给其他高优先级任务 */ +static irqreturn_t button_bottom_half(int irq, void *ident) +{ + pr_info("后续部分任务开始\n"); + mdelay(500); /* 执行一些耗时的操作 */ + pr_info("后续部分任务结束\n"); + return IRQ_HANDLED; +} + +static int __init bottomhalf_init(void) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + /* 注册 LED GPIO */ + ret = gpio_request_array(leds, ARRAY_SIZE(leds)); + + if (ret) { + pr_err("无法请求 LED 的 GPIOs: %d\n", ret); + return ret; + } + + /* 注册按钮 GPIO */ + ret = gpio_request_array(buttons, ARRAY_SIZE(buttons)); + + if (ret) { + pr_err("无法请求 BUTTON 的 GPIOs: %d\n", ret); + goto fail1; + } + + pr_info("当前 button1 的值: %d\n", gpio_get_value(buttons[0].gpio)); + + ret = gpio_to_irq(buttons[0].gpio); + + if (ret < 0) { + pr_err("无法请求 IRQ: %d\n", ret); + goto fail2; + } + + button_irqs[0] = ret; + + pr_info("成功请求 BUTTON1 IRQ # %d\n", button_irqs[0]); + + ret = request_threaded_irq(button_irqs[0], button_top_half, + button_bottom_half, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "gpiomod#button1", &buttons[0]); + + if (ret) { + pr_err("无法请求 IRQ: %d\n", ret); + goto fail2; + } + + ret = gpio_to_irq(buttons[1].gpio); + + if (ret < 0) { + pr_err("无法请求 IRQ: %d\n", ret); + goto fail2; + } + + button_irqs[1] = ret; + + pr_info("成功请求 BUTTON2 IRQ # %d\n", button_irqs[1]); + + ret = request_threaded_irq(button_irqs[1], button_top_half, + button_bottom_half, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "gpiomod#button2", &buttons[1]); + + if (ret) { + pr_err("无法请求 IRQ: %d\n", ret); + goto fail3; + } + + return 0; + +/* 清理已设置的部分 */ +fail3: + free_irq(button_irqs[0], NULL); + +fail2: + gpio_free_array(buttons, ARRAY_SIZE(leds)); + +fail1: + gpio_free_array(leds, ARRAY_SIZE(leds)); + + return ret; +} + +static void __exit bottomhalf_exit(void) +{ + int i; + + pr_info("%s\n", __func__); + + /* 释放 IRQ */ + free_irq(button_irqs[0], NULL); + free_irq(button_irqs[1], NULL); + + /* 关闭所有 LED */ + for (i = 0; i < ARRAY_SIZE(leds); i++) + gpio_set_value(leds[i].gpio, 0); + + /* 注销 */ + gpio_free_array(leds, ARRAY_SIZE(leds)); + gpio_free_array(buttons, ARRAY_SIZE(buttons)); +} + +module_init(bottomhalf_init); +module_exit(bottomhalf_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("起始部分和后续部分中断处理"); diff --git a/示例/8-调度与中断/bottomhalf.c b/示例/8-调度与中断/bottomhalf.c new file mode 100644 index 0000000..50a9870 --- /dev/null +++ b/示例/8-调度与中断/bottomhalf.c @@ -0,0 +1,171 @@ +/* + * bottomhalf.c - 起始与后续部分中断处理 + * + * 基于 Stefan Wendler (devnull@kaltpost.de) 提供的 RPi 示例 + * 来自: + * https://github.com/wendlers/rpi-kmod-samples + * + * 按下一个按钮以点亮 LED,按下另一个按钮以熄灭它 + */ + +#include +#include +#include +#include +#include +#include + +/* 宏 DECLARE_TASKLET_OLD 的存在是为了确保兼容 + * 详细信息: https://lwn.net/Articles/830964/ + */ +#ifndef DECLARE_TASKLET_OLD +#define DECLARE_TASKLET_OLD(arg1, arg2) DECLARE_TASKLET(arg1, arg2, 0L) +#endif + +static int button_irqs[] = { -1, -1 }; + +/* 定义 LED 的 GPIO。 + * TODO: 根据你的板子更改 GPIO 编号。 + */ +static struct gpio leds[] = { { 1, GPIOF_OUT_INIT_LOW, "LED 1" } }; + +/* 定义按钮的 GPIO。 + * TODO: 根据你的板子更改 GPIO 编号。 + */ +static struct gpio buttons[] = { { 2, GPIOF_IN, "LED 1 打开按钮" }, + { 3, GPIOF_IN, "LED 1 关闭按钮" } }; + +/* 包含一些复杂处理的任务队列函数 */ +static void bottomhalf_tasklet_fn(unsigned long data) +{ + pr_info("底半部任务队列开始\n"); + /* 执行一些耗时的操作 */ + mdelay(500); + pr_info("底半部任务队列结束\n"); +} + +static DECLARE_TASKLET_OLD(buttontask, bottomhalf_tasklet_fn); + +/* 按钮按下时触发的中断函数 */ +static irqreturn_t button_isr(int irq, void *data) +{ + /* 立即执行一些操作 */ + if (irq == button_irqs[0] && !gpio_get_value(leds[0].gpio)) + gpio_set_value(leds[0].gpio, 1); // 点亮 LED + else if (irq == button_irqs[1] && gpio_get_value(leds[0].gpio)) + gpio_set_value(leds[0].gpio, 0); // 熄灭 LED + + /* 将剩余工作交给调度程序处理 */ + tasklet_schedule(&buttontask); + + return IRQ_HANDLED; // 表示中断已经处理完毕 +} + +static int __init bottomhalf_init(void) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + /* 请求 LED 的 GPIO */ + ret = gpio_request_array(leds, ARRAY_SIZE(leds)); + + if (ret) { + pr_err("无法请求 LED 的 GPIOs: %d\n", ret); + return ret; + } + + /* 请求按钮的 GPIO */ + ret = gpio_request_array(buttons, ARRAY_SIZE(buttons)); + + if (ret) { + pr_err("无法请求按钮的 GPIOs: %d\n", ret); + goto fail1; + } + + pr_info("当前按钮1的值: %d\n", gpio_get_value(buttons[0].gpio)); + + /* 获取按钮 1 的中断号 */ + ret = gpio_to_irq(buttons[0].gpio); + + if (ret < 0) { + pr_err("无法请求中断: %d\n", ret); + goto fail2; + } + + button_irqs[0] = ret; + + pr_info("成功请求了按钮1的中断号 # %d\n", button_irqs[0]); + + /* 注册按钮 1 的中断 */ + ret = request_irq(button_irqs[0], button_isr, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "gpio模块#按钮1", NULL); + + if (ret) { + pr_err("无法请求中断: %d\n", ret); + goto fail2; + } + + /* 获取按钮 2 的中断号 */ + ret = gpio_to_irq(buttons[1].gpio); + + if (ret < 0) { + pr_err("无法请求中断: %d\n", ret); + goto fail2; + } + + button_irqs[1] = ret; + + pr_info("成功请求了按钮2的中断号 # %d\n", button_irqs[1]); + + /* 注册按钮 2 的中断 */ + ret = request_irq(button_irqs[1], button_isr, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "gpio模块#按钮2", NULL); + + if (ret) { + pr_err("无法请求中断: %d\n", ret); + goto fail3; + } + + return 0; + +/* 清理已经设置的资源 */ +fail3: + free_irq(button_irqs[0], NULL); + +fail2: + gpio_free_array(buttons, ARRAY_SIZE(buttons)); + +fail1: + gpio_free_array(leds, ARRAY_SIZE(leds)); + + return ret; +} + +static void __exit bottomhalf_exit(void) +{ + int i; + + pr_info("%s\n", __func__); + + /* 释放中断 */ + free_irq(button_irqs[0], NULL); + free_irq(button_irqs[1], NULL); + + /* 关闭所有 LED */ + for (i = 0; i < ARRAY_SIZE(leds); i++) + gpio_set_value(leds[i].gpio, 0); + + /* 注销 GPIO */ + gpio_free_array(leds, ARRAY_SIZE(leds)); + gpio_free_array(buttons, ARRAY_SIZE(buttons)); +} + +module_init(bottomhalf_init); +module_exit(bottomhalf_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("起始与后续部分中断处理"); + diff --git a/示例/8-调度与中断/intrpt.c b/示例/8-调度与中断/intrpt.c index 7602c86..aaaa3d8 100644 --- a/示例/8-调度与中断/intrpt.c +++ b/示例/8-调度与中断/intrpt.c @@ -9,7 +9,7 @@ #include #include -#include /* 用于 ARRAY_SIZE() */ +#include /* 引入 ARRAY_SIZE() */ #include #include