diff --git a/content/blog/2020/syscall_szp1/img/1.png b/content/blog/2020/syscall_szp1/img/1.png new file mode 100644 index 0000000000000000000000000000000000000000..1329ffa34a70381e7fbd37ef80275f9b9e45210d Binary files /dev/null and b/content/blog/2020/syscall_szp1/img/1.png differ diff --git a/content/blog/2020/syscall_szp1/index.md b/content/blog/2020/syscall_szp1/index.md new file mode 100644 index 0000000000000000000000000000000000000000..372397cddee25af90093519e6a52c5d233c3563c --- /dev/null +++ b/content/blog/2020/syscall_szp1/index.md @@ -0,0 +1,299 @@ +--- +title: "Linux内核开发之将驱动程序添加到内核" +date: 2020-08-08T20:27:28+08:00 +author: "你的名字" +keywords: ["你的关键词"] +categories : ["你的分类"] +banner : "img/blogimg/ljrimg5.jpg" +summary : "例如我们把驱动程序 usbtmc 存放在 drivers/char/ 目录下,那么你要注意,在该目录下同时会存在大量的 C 源代码文件和许多其他目录。所有对于仅仅只有一两个源文件的设备驱动程序" +--- + + + +一 . 概述: + +  在 linux 内核中增加程序需要完成以下三项工作: + +   1. 将编写的源代码复制到 Linux 内核源代码的相应目录 +   2. 在目录的 Kconfig 文件中增加新源代码对应项目的编译配置选项 +  3. 在目录的 Makefile 文件中增加对新源代码的编译条目 + + + +![](img/1.png) + + + + + + +  二 . 实例 + +  1. 先把驱动代码 usbtmc( 文件夹 ) 赋值到 /usr/src/linux-headers-2.6.32-31-generic/drivers/char 下 + + +首先你要清楚你的模块应在内核源代码树中处于何处。 + +  1> 设备驱动程序存放在内核源码树根目录 drivers/ 的子目录下,在其内部,设备驱动文件进一步按照类别,类型等有序地组织起来。 + +  a. 字符设备存在于 drivers/char/ 目录下 + +  b. 块设备存放在 drivers/block/ 目录下 + +  c.USB 设备则存放在 drivers/usb/ 目录下。 + +  注意: + +  (1) 此处的文件组织规则并非绝对不变,例如: USB 设备也属于字符设备,也可以存放在 drivers/usb/ 目录下。 + +  (2) 例如我们把驱动程序 usbtmc 存放在 drivers/char/ 目录下,那么你要注意,在该目录下同时会存在大量的 C 源代码文件和许多其他目录。所有对于仅仅只有一两个源文件的设备驱动程序,可以直接存放在该目录下,但如果驱动程序包含许多源文件和其他辅助文件,那么可以创建一个新子目录。 + +  此处,我们是把 usbtmc 目录放在了 drivers/char 目录下面 + +  2. 修改 char 目录下的 Kconfig 和 Makefile + +  (1) 修改 Kconfig + +  sudo gedit Kconfig + +  添加下面一句后 + +  source "drivers/char/usbtmc/Kconfig" + +  它表示将 usbtmc 目录下的 Kconfig 挂载到 char 目录下的 Kconfig 里面 ( 为了使本层的 Kconfig 文件能起作用,我们需要修改父目录的 Kconfig 文件,加入 source 语句 ) + +  1> 对驱动程序而言, Kconfig 通常和源代码处于同一目录。 + +  2> 如果你建立了一个新字目录,而且也希望 Kconfig 文件存在于该目录中的话,那么就必须在一个已存在的 Kconfig 文件中将它引入,需要用上面的 + +  语句将其挂接在 drivers/char 目录中的 Kconfig 中。 + +  (2) 修改 Makefile + +  添加一句话: + +  obj-$(CONFIG_USBTMC) +=usbtmc/ + +  这行编译指令告诉模块构建系统在编译模块时需要进入 usbtmc/ 子目录中。此时的驱动程序的编译取决于一个特殊配置 CONFIG_USBTMC 配置选项。 + +  3. 现在在我们自己些驱动程序文件夹中添加 Kconfig 和 Makefile + +  (1) 修改 Kconfig + +  新建一个 Kconfig 添加下面的话 + +  menu "USBTMC" + +  comment "USBTMC Driver" + +  config USBTMC + +  tristate "USBTMC" + +  default n + +  help + +  If you say Y here,support for the usbtmc with computer interface will be compiled into he kernel and accessible via device node. You can also say M here and the driver will be built as a module named usbtmc.ko. + +  If unsure,say N. + +  endmenu + +  endmenu + +  正确配置好后,我们在源码下执行 sudo make menuconfig 后,在出现的 Linux Kernel Configuration 图形界面中选择 Device Drivers 下的 character devcie 中 ,将会看到新加的 USBTCM 菜单, + +  (2) 修改 Makefile + +  新建一个 Makefile ,添加下面的话 + +  obj-$(CONFIG_USBTMC) +=usbtmc.o + +  此时,构建系统运行就将会进入 usbtmc/ 目录下,并且将 usbtmc.c 编译为 usbtmc.ko 模块 + +  注: + +  如果驱动程序源文件可能不只有一个,可以把 Makefile 做如下修改: + +  obj-$(CONFIG_USBTMC) +=usbtmc.o + +  usbtmc-objs :=usbtmc-main.o usbtmc-usb1.o + +  此时, usbtmc-main.c 和 usbtmc-usb1.c 就一起被编译和连接到了 usbtmc.ko 某块中。 + +  4. 现在已经 Ok 了,现在我们可以进入 linux 内核目录下通过 menuconfig 可以找到我们的 USBTMC 选项(在 Device_Drivers 下的 character devices 里可以找到 USBTMC )对其进行选定。然后退出,编译内核,就搞定了。 + +  5. 删除: + +  删除也很简单,首先在 drivers/char 目录下删掉自己的驱动文件夹。其次再删除 Makefile 和 Kconfig 之前添加的东西,就搞定了 + +  三 . 详解: + +  Makefile , Kconfig 和配置工具组成了 Linux 2.6 内核的配置系统。 +其中 Makefile 定义了 Linux 内核的编译规则,它是大型项目开发的产物。 Linux 环境下的大型项目开发中,系统被分为很多模块,而这些模块一般会经历几次修改,而在修改后的编译过程中,由于某些文件中存在依赖关系,人工编译效率低 ( 有些文件不需要重新编译 ) 且易出错, Makefile 文件便应运而生。 Makefile 文件定义了模块间的依赖关系,指定文件的编译顺序,以及编译所使用的命令。它和 make 命令使得项目的源程序文件可以自动编译,提高了软件开发效率。到此,再谈一下 make ,它是用来维护程序模块关系和生成可执行程序的工具,它可以根据程序模块的修改情况重新编译链接生成中间代码或最终的可执行程序,省去那些重复的不必要的编译工作,提高编译效率。 +Kconfig 给用户提供配置选择的功能。通常配置内核会有四种方法, make config (字符界面配置), make menuconfig (菜单界面配置), make xconfig (依赖 QT ), make gconfig (依赖 GTK+ )。 make config 比较适合专业人员,像初学者比较适合 make menuconfig ,让我们重点关注一下它。当我们运行 make menuconfig 时,配置工具会首先分析与体系结构相对应的 /arch/xxx/Kconfig 文件( xxx 为传入的 arch 参数),它里面包含了除一些与体系结构相关的配置项和配置菜单外,还通过 source 语句引入了一系列 Kconfig ,配置工具依据这些 Kconfig 包含的菜单和项目就可以描绘出一个分层结构。 +例如当我们运行 make zImagine 、 make bzImagine 等生成映像的命令时,会先检索顶层的 Makefie (在 arch/xxx/ 目录下的 Makefile 为顶层 Makefile 补充体系结构相关的信息),顶层 Makefile 的两个主要任务是:产生内核映像文件和内核模块。接着顶层 Makefile 会去递归地进入内核的各个子目录,然后分别调用子目录中的 Makefile (这些 Makefile 记录编译目标),而进入哪些子目录取决于内核的配置。 +当使用 make menuconfig , make config 命令时,生成的nfig 会在源码目录下记录哪些部分被编译入内核,哪些部分被编译为内核模块。简而言之,它是保存内核配置结果的文件。当我们装上 Linux 系统时,第一次查看源码下的所有文件,会发现没有nfig 文件,那是因为从来没配置过内核。当你运行 make menuconfig 保存并退出时,再次查看就有这个文件了。 +配置工具,包括配置命令解释器(对配置脚本中使用的命令进行解释)和配置用户界面(提供字符界面和图形界面),配置工具都是用脚本语言编写的。 + +  1. 在进入 menuconfig 配置界面时,会发现每个配置项目为布尔型(要么编译入内核,要么不编译,选项为“ Y” 或“ N” ),菜单上为配置选项的名字例如:“ XXX Driver”,help 后面的内容为帮助信息。 + +  1> 除了布尔型的配置项目外,还存在一种三态型 (tristate) 配置选项,它意味着要么编译入内核,要么编译为内核模块,要么不编译,选项为“ Y” ,“ M” 或“ N” 。 + +  eg: obj-$(CONFIG_USBTMC) +=usbtmc.o + +  上面的脚本含义是:如果 USBTMC 选项被选择为“ Y” 或“ M” ,即 obj-$(CONFIG_USBTMC) 就等同于 obj-y 或 obj-m 时,则编译 usbtmc.c ,选 Y 的情况直接会将生成的目标代码直接连接到内核,为“ M” 的情况则会生成模块 usbtmc.ko ,如果 USBTMC 配置选项被选择为“ N” ,即 obj-$(CONFIG_USBTMC) 等同于 obj-n 时,则不编译 usbtmc.c + +  2.Makefile + +  对内核源代码各级子目录中的 kbuild Makefile 进行介绍, + +  (1) 目标定义 + +  目标定义用来定义哪些内容要作为模块编译,哪些要编译并连接进内核 + +  (a)obj-y:=foo.o + +  表示要由 foo.c 或者 foo.s 文件编译得到 foo.o 并连接进内核,而 obj-m 则表示该文件要作为模块编译。处了 y,m 以外的 obj-x 形式的目标都不会被编译。 + +  ( b) 我们最常用的的做法是根据nfig 文件的 CONFIG_ 变量来决定文件的编译方式: + +  eg: + +  obj-$(CONFIG_ISDN) +=isdn.o + +  (c) 多个文件模块的定义 + +  如果一个模块由多个文件组成,这时候应采用模块名加 -objs 后缀或者 -y 后缀的形式来定义模块的组成文件。 + +  如: + +  obj-$(CONFIG_EXT2_FS) +=ext2.o + +  ext2-y :=balloc.o bitmap.o + +  模块的名字是 ext2, 由 balloc.o 和 bitmap.o 两个文件最终连接生成 ext2.o 直至 ext2.ko 文件。 + +  3.Kconfig + +  内核配置脚本语法: + +  (1) 大多数的内核配置选项都对应一个 Kconfig 中的一个菜单入口。 + +  menu "USBTMC" + +  comment "USBTMC Driver" + +  config USBTMC + +  tristate "USBTMC" + +  default n + +  endmenu + +  (a)“config” 关键字定义新的配置选项,之后的几行定义了该配置选项的属性。配置选项的属性包括类型,数据 , 输入提示,依赖关系(及反向依赖关系),帮助信息和默认值等。 + +  (b) 每个配置选项都必须指定类型,其他类型都基于这两种基本类型。类型定义后可以紧跟输入提示,下面两个脚本是等价的 + +  脚本 1 : + +  bool “Networking support” + +  脚本 2 : + +  bool + +  promt “Networking support” + +  输入提示的一般格式如下提示: + +  prompt [if ] + +  其中可选的 if 用来表示该提示的依赖关系。 + +  默认值的格式如下所示: + +  default [if ] + +  一个配置选项可以存在任意多个默认值,这种情况下,只有第一个被定义的值是可用的。如果用户不设置对应的选项,配置选项的值就是默认值。 + +  (c) 依赖关系的格式如下所示: + +  depends on ( 或者 requires) + +  如果定义了多个依赖关系,它们之间用” &&” 间隔。依赖关系也可以应用到该菜单中所有的其他选项中。 + +  (4) 反向依赖关系的格式如下所示: + +  select [if ] + +  A.depends 能限定一个 symbol 的上限,即如果 A 依赖于 B ,则在 B 被配置为“ Y” 的情况下, A 可以为“ Y” ,“ M” 和” N”; 在 B 被配置为“ M” 的情况下, A 可以被配置为“ M” 或“ N” ; B 在被配置为“ N” 的情况下, A 只能为” N” 。 + +  B.select 能限定一个 symbol 的下限,若 A 反向依赖于 B ,则 A 的配置值会高于或等于 B (正好与 depends 相反)。如果 symbol 反向依赖于多个对象,则它的下限是这些对象的对大值。 + +  (5) 帮助信息的格式如下: + +  help( 或 ---help---) + +  开始 + +  。。。 + +  结束 + +  帮组信息完全靠文本缩进识别结束。“ ---help---” 和” help” 的初衷在于将文件中的配置逻辑与给开发人员的提示分开。 + +  3. 菜单结构 + +  菜单入口在菜单数结构中的位置可由两种方法决定。 + +  (1) 第一中方式如下所下: + +  menu “Network device support” + +  depends on NET + +  config NETDEVICES + + +endmenu + +  所有处于” menu” 和” endmenu” 之间的菜单入口都会成为“ Network device support” 的子菜单。而且,所有子菜单选项都会继承父菜单的依赖关系,比如:“ Network device support” 对“ NET” 的依赖被加到了配置选项 NETDEVICES 的依赖列表中。 + +  (2) 另一种方式是通过分析依赖关系生成菜单结构。如果菜单选项在一定程度上依赖于前面的选项,它就能成为该选项的子菜单。如果父选项为“ N” ,则子选项不可见;如果父选项为“ Y” 或“ M” ,则子选项可见。 + +  Eg: + +  config MODULES + +  bool “Enable loadable module support” + +  config MODVERSIONS + +  bool “Set version information on all module symbole” + +  depends on MODULES + +  comment “module support disabled” + +  depends on !MODULES + +  MODVERSIONS 直接依赖 MODULES ,如果 MODULES 不为“ N” ,该选项才可见。 + +  (3) 除此之外, Kconfig 中还可能使用“ choices …...endchoice”,”comment”,”if....endif” 这样的语法结构。 + +  其中 + +  choice + +   + +   + +  enchoice + +  它定义一个选择群,其接受的选项( choice options )可以是前面描述的任何属性。在一个硬件有多个驱动的情况下使用,使用选择可以实现最终只有一个驱动被编译进内核或模块。选择群还可以接受的另一个选项是“ optional”, + +  这样菜单入口就被设置为“ N” ,没有被选中。 \ No newline at end of file