Skip to content

Latest commit

 

History

History
150 lines (92 loc) · 15.2 KB

File metadata and controls

150 lines (92 loc) · 15.2 KB

零、前言

本书旨在帮助您以实用、动手的方式学习 Linux 字符设备驱动程序开发的基础知识,以及必要的理论背景,让您全面了解这个广阔而有趣的主题领域。公平地说,这本书的范围被刻意限制在(大部分)学习如何在 Linux 操作系统上编写misc类字符设备驱动程序。通过这种方式,您将能够深入吸收基本和必要的驱动程序作者技能,然后能够相对轻松地处理不同类型的 Linux 驱动程序项目。

重点是通过强大的可加载内核模块 ( LKM )框架进行实际的驱动程序开发;大多数内核驱动程序开发都是以这种方式完成的。重点放在实际操作驱动程序代码上,在需要的地方充分深入地理解内部,并牢记安全性。

一个我们再怎么强烈也提不出来的建议:要真正学好、理解好细节,真的最好你先看懂这本书的姊妹篇, *Linux 内核编程。*它涵盖了各种关键领域——从源代码构建内核、通过 LKM 框架编写内核模块、内核内部,包括内核架构、内存系统、内存分配/分配应用编程接口、中央处理器调度等等。这两本书的结合会给你一个确定而深刻的优势。

这本书没有浪费时间——第一章让你学习了 Linux 驱动框架的细节,以及如何编写一个简单而完整的杂项类字符设备驱动程序。接下来,您将学习如何做一些非常必要的事情:使用各种技术高效地将您的驱动程序与用户空间进程接口(其中一些技术也有助于调试/诊断!).然后介绍对硬件(外围芯片)输入/输出存储器的理解和使用。处理硬件中断的详细内容如下。这包括学习和使用几种现代驱动技术——使用线程化的 IRQs、利用资源管理的驱动程序接口、I/O 资源分配等等。它涵盖了什么是上/下半部分,使用小任务和软 IRQ,以及测量中断延迟。接下来将介绍您通常使用的内核机制——使用内核定时器、设置延迟、创建和管理内核线程和工作队列。

本书剩下的两章深入探讨了一个相对复杂但对现代专业级驱动程序或内核开发人员来说很难理解的主题:理解和使用内核同步。

本书使用的是最新的,在编写的时候,5.4 长期支持 ( LTS ) Linux 内核。这是一个将从 2019 年 11 月一直维护到 2025 年 12 月的内核(包括 bug 和安全修复)!这是一个关键点,确保这本书的内容在未来几年保持最新和有效!

我们非常相信实践经验方法:这本书的 GitHub 存储库中有超过 20 个内核模块(除了一些用户应用和 shell 脚本)让学习变得生动起来,使它变得有趣、有趣和有用。

我们真的希望你能从这本书中学到东西并喜欢它。快乐阅读!

这本书是给谁的

这本书主要是为开始寻找设备驱动程序开发方法的 Linux 程序员准备的。寻求克服频繁和常见的内核/驱动程序开发问题的 Linux 设备驱动程序开发人员,以及理解和学习执行常见的驱动程序任务——现代 Linux 设备模型 ( LDM )框架、用户内核接口、执行外围 I/O、处理硬件中断、处理并发性等等——将从本书中受益。需要对 Linux 内核内部(和通用 API)、内核模块开发和 C 编程有基本的了解。

这本书涵盖了什么

第 1 章编写一个简单的杂项字符设备驱动程序,首先介绍最基本的东西——驱动程序应该做什么,设备命名空间,sysfs,以及 LDM 的基本原则。然后我们深入研究编写简单字符设备驱动程序的细节;在此过程中,您将了解框架——实际上,是“如果不是流程,就是文件”理念/架构的内部实现!您将学习如何用各种方法实现杂项类字符设备驱动程序;几个代码示例有助于强化概念。涵盖了用户内核空间和用户内核空间之间的基本数据复制。还涵盖了关键的安全问题以及如何解决这些问题(在这种情况下);一个引起特权升级问题的“坏”司机实际上被证明了!

第二章用户-内核通信路径,讲述了作为内核模块/驱动作者,如何在内核和用户空间之间进行通信,这对你来说至关重要。在这里,您将了解各种通信接口或路径。这是编写内核/驱动程序代码的一个重要方面。采用了几种技术:通过传统 procfs 的通信,驱动程序通过 sysfs 的更好的方式,以及其他几种技术,通过 debugfs、netlink sockets 和 ioctl(2)系统调用。

第 3 章使用硬件输入/输出存储器涵盖了驱动程序编写的一个关键方面——从外围设备或芯片访问硬件存储器(映射存储器输入/输出)的问题(以及解决方案)。我们介绍了使用常见的内存映射 I/O ( MMIO )技术以及(通常在 x86 上)端口 I/O ( PIO )技术进行硬件 I/O 内存访问和操作。还显示了现有内核驱动程序的几个示例。

第 4 章处理硬件中断,详细展示了如何处理和处理硬件中断。我们首先简要介绍内核如何处理硬件中断,然后继续讨论如何“分配”一个 IRQ 行(涵盖现代资源管理的 API),以及如何正确实现中断处理程序例程。然后介绍使用线程处理程序的现代方法(及其原因)、不可屏蔽中断 ( NMI )等等。在代码中使用“上半部分”和“下半部分”中断机制(hardirq、tasklet 和 softirqs)的原因,以及关于硬件中断处理的注意事项和不注意事项的关键信息。用现代[e]BPF 工具集和 Ftrace 测量中断延迟,这一关键章节到此结束。

第 5 章使用内核定时器、线程和工作队列,介绍了如何使用一些有用的(通常被驱动程序使用的)内核机制——延迟、定时器、内核线程和工作队列。它们在许多现实世界中派上了用场。如何执行阻塞和非阻塞延迟(视情况而定),设置和使用内核定时器,创建和使用内核线程,以及理解和使用内核工作队列,这些都在这里讨论。几个示例模块,包括三个版本的简单加密解密 ( sed )示例驱动程序,用于说明在代码中学习的概念。

第 6 章内核同步–第 1 部分,首先涵盖了关于关键部分、原子性、锁在概念上实现了什么的关键概念,以及非常重要的是,所有这些的原因。然后,当在 Linux 内核中工作时,我们将讨论并发问题;这让我们自然而然地转向重要的锁定准则、死锁的含义以及防止死锁的关键方法。然后深入讨论两种最流行的内核锁定技术——互斥锁和自旋锁,以及几个(驱动程序)代码示例。

第 7 章内核同步–第 2 部分,继续内核同步之旅。在这里,您将了解键锁定优化——使用轻量级原子操作符和(较新的)refcount 操作符来安全地操作整数,使用 RMW 位操作符来安全地执行位操作,以及在常规操作符上使用读写自旋锁。还讨论了固有风险,如缓存“错误共享”。然后介绍了无锁编程技术的概述(重点是每 CPU 变量及其用法,以及示例)。接下来将讨论一个关键的主题,锁调试技术,包括内核强大的 lockdep 锁验证器的使用。本章最后简要介绍了内存障碍(以及现有内核网络驱动程序对内存障碍的使用)。

我们再次强调,这本书是为刚开始编写设备驱动程序的内核程序员准备的;几个 Linux 驱动主题超出了本书的范围,不在讨论范围内。这包括其他类型的设备驱动程序(除了字符),使用设备树,等等。Packt 提供了其他有价值的指南,帮助您在这些主题领域获得吸引力。这本书将是一个极好的开端。

充分利用这本书

为了充分利用这本书,我们希望您具备以下方面的知识和经验:

  • 在命令行(Shell)上熟悉 Linux 系统。
  • C 编程语言。
  • 知道如何通过可加载内核模块 ( LKM )框架编写简单的内核模块
  • 理解(至少是基本的)关键的 Linux 内核内部概念:内核架构、内存管理(加上通用的动态内存 alloc/de-alloc API)和 CPU 调度。
  • 这不是强制性的,但是对 Linux 内核编程概念和技术的经验会有很大帮助。

理想情况下,我们强烈建议首先阅读本书的配套书籍 Linux 内核编程

此处显示了本书的硬件和软件要求及其安装的详细信息:

| 章节号 | 所需软件(带版本) | 免费/**专有 | 下载软件链接 | 硬件规格 | 需要操作系统 | | 所有章节 | 最近的 Linux 发行版;我们使用的是 Ubuntu 18.04 LTS(以及 Fedora 31/Ubuntu 20.04 LTS);这些都是合适的。建议您使用 Oracle VirtualBox 6.x(或更高版本)作为虚拟机管理程序,将 Linux 操作系统安装为虚拟机**(虚拟机) | 免费(开源) | Ubuntu(台式机):https://Ubuntu . com/download/desktop甲骨文虚拟盒子:https://www.virtualbox.org/wiki/Downloads | *所需:*配备 4 GB RAM(最低;越多越好),25 GB 的可用磁盘空间,以及良好的互联网连接。*可选:*我们还使用树莓皮 3B+作为试验台。 | 作为独立操作系统的 Linux |

详细的安装步骤(软件方面):

  1. 将 Linux 作为虚拟机安装在 Windows 主机系统上;遵循以下教程之一:
  2. 在 Linux 虚拟机上安装所需的软件包:
    1. 登录到您的 Linux 来宾虚拟机,并首先在终端窗口(在 Shell 上)中运行以下命令:
sudo apt update
sudo apt install gcc make perl
  1. 有用资源:
  2. 详细的说明,以及其他有用的项目,为 ARM 安装跨工具链,等等,在本书配套指南的第 1 章,内核工作区设置中有描述, Linux 内核编程,万凯 N 比利摩里亚,帕克特出版社。

我们已经在这些平台上测试了本书中的所有代码(它也有自己的 GitHub 存储库):

  • x86_64 Ubuntu 18.04 LTS 来宾操作系统(运行在甲骨文虚拟桌面 6.1 上)
  • x86_64 Ubuntu 20.04.1 LTS 来宾操作系统(运行在甲骨文虚拟桌面 6.1 上)
  • x86_64 Ubuntu 20.04.1 LTS 本地操作系统
  • ARM 树莓 Pi 3B+(运行其发行版内核以及我们定制的 5.4 内核);轻度测试。

如果您正在使用本书的数字版本,我们建议您自己键入代码,或者更好的是,通过 GitHub 存储库(下一节中提供的链接)访问代码。这样做将帮助您避免任何与复制和粘贴代码相关的潜在错误。

对于这本书,我们将以名为llkd的用户身份登录。我强烈建议你遵循*的经验方法:不要相信任何人的话,而是自己去尝试和体验。*因此,这本书为你提供了许多你可以而且必须亲自尝试的实践实验和内核驱动代码示例;这将极大地帮助您取得真正的进步,并深入学习和理解 Linux 驱动程序/内核开发的各个方面。

下载示例代码文件

你可以在https://GitHub . com/packt publishing/Linux-Kernel-Programming-Part-2下载本书的示例代码文件。如果代码有更新,它将在现有的 GitHub 存储库中更新。

我们丰富的图书和视频目录中还有其他代码包,可在**【https://github.com/PacktPublishing/】**获得。看看他们!

下载彩色图像

我们还提供了一个 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。可以在这里下载:http://www . packtpub . com/sites/default/files/downloads/9781801079518 _ color images . pdf

使用的约定

本书通篇使用了许多文本约定。

CodeInText:表示文本中的码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟网址、用户输入和推特句柄。这里有一个例子:“应用编程接口返回一个 T2 类型的 KVA(因为它是一个地址位置)。”

代码块设置如下:

static int __init miscdrv_init(void)
{
    int ret;
    struct device *dev;

当我们希望将您的注意力吸引到代码块的特定部分时,相关的行或项目以粗体显示:

#define pr_fmt(fmt) "%s:%s(): " fmt, KBUILD_MODNAME, __func__
[...]
#include <linux/miscdevice.h>
#include <linux/fs.h>             
[...]

任何命令行输入或输出都编写如下:

pi@raspberrypi:~ $ sudo cat /proc/iomem

粗体:表示一个新的术语、一个重要的单词或者你在屏幕上看到的单词。例如,菜单或对话框中的单词像这样出现在文本中。下面是一个示例:“从管理面板中选择系统信息。”

Warnings or important notes appear like this. Tips and tricks appear like this.

取得联系

我们随时欢迎读者的反馈。

一般反馈:如果你对这本书的任何方面有疑问,在你的信息主题中提到书名,发邮件给我们customercare@packtpub.com

勘误表:虽然我们已经尽了最大的努力来保证内容的准确性,但是错误还是会发生。如果你在这本书里发现了一个错误,如果你能向我们报告,我们将不胜感激。请访问www.packtpub.com/support/errata,选择您的图书,点击勘误表提交链接,并输入详细信息。

盗版:如果您在互联网上遇到任何形式的我们作品的非法拷贝,如果您能提供我们的位置地址或网站名称,我们将不胜感激。请通过copyright@packt.com联系我们,并提供材料链接。

如果你有兴趣成为一名作者:如果有一个你有专长的话题,你有兴趣写或者投稿一本书,请访问authors.packtpub.com

复习

请留下评论。一旦你阅读并使用了这本书,为什么不在你购买它的网站上留下评论呢?然后,潜在的读者可以看到并使用您不带偏见的意见来做出购买决定,我们在 Packt 可以了解您对我们产品的看法,我们的作者可以看到您对他们的书的反馈。谢谢大家!

更多关于 Packt 的信息,请访问packt.com