Linux内核的编译及加载 - STEMHA's Blog

Linux内核的编译及加载

编译内核需要做什么?

  1. 查找并且下载一份内核源代码
  2. 配置内核。
  3. 编译内核和模块。
  4. 配置启动文件。

Linux内核代码获取与解压

建议直接去官方网站( www.kernel.org )下载Linux内核代码。
编译内核时,一般将源码解压到/usr/src目录下,解压完成后会在该目录下生成一个与源码包版本号一致的目录。

1
2
3
4
5
mv linux-3.12.6.tar.xz /usr/src 	//将下载的内核移动到/usr/src/目录下

// 切换到/usr/src/ 下载的压缩包是.tar.xz后缀,解压分为两步
xz -d linux-3.12.6.tar.xz
tar -xvf linux-3.12.6.tar

linux DOC 编译生成

linux源根目录/Documentation/00-INDEX:目录索引
linux源根目录/Documentation/HOWTO:指南
生成linux内核帮助文档:在linux源根目录(Documentation) 执行下面的命令

1
2
make htmldocs
//ubuntu16下需要执行sudo apt-get install xmlto安装插件才可生成doc文档

https://www.kernel.org/doc/html/latest/index.html 也能查看到官方生成的文档

内核编译的准备工作

说明一下:本文章是根据Ubuntu系统的命令格式来写的
更换内核之前,先更新一下源:

1
sudo apt-get update

然后安装一些必要的编译内核的工具

1
apt-get install libncurses5-dev build-essential kernel-package
  • libncurses5-dev是为了运行make menuconfig
  • Build-essential是编译工具
  • kernel-package是编译内核工具

进入下载的内核主目录:

1
cd linux-3.12.6

清理所有产生的文件与config配置文件(第一次编译内核时可省略)

1
sudo make mrproper

配置内核

在编译内核时,需要配置内核,可以使用下面命令中的一个:

1
2
3
4
5
6
make config     #(基于文本的最为传统的配置界面,不推荐使用)
make menuconfig #(基于文本菜单的配置界面)开启文本菜单选项,对窗口有限制,尽量调大窗口,否则会出错
#使用此命令需安装gcc和ncurses-devel

make xconfig #(要求QT被安装)
make gconfig #(要求GTK+被安装)

make menuconfig最值得推荐,它不依赖于QT或GTK+,且非常直观,对/home/baohua/develop/linux中的Linux 4.0-rc1内核运行 make ARCH=arm menuconfig后的界面如下图所示。

图1,Linux内核编译配置

对每一个配置选项,用户有三种选含义如下:

  • <*>或[*]——编译进内核本体
  • [M]——编译成内核模块
  • [ ]——不将该功能编译进内核

Linux内核的配置系统由以下3个部分组成。

  • Makefile:分布在Linux内核源代码中,定义Linux内核的编译规则。
  • Kconfig(配置文件):给用户提供配置选择的功能。
  • 配置工具:包括配置命令解释器(对配置脚本中使用的配置命令进行解释)和配置用户界面(提供字符界面和图形界面)。这些配置工具使用的都是脚本语言,如用Tcl/TK、Perl等。

.config配置文件(隐藏文件):

  • 使用make config、make menuconfig等命令后,会生成一个.config配置文件存储配置信息,记录哪些部分被编译入内核、哪些部分被编译为内核模块。
  • 如果想方便配置,可复制/boot/config文件覆盖.config文件,直接修改即可。

运行make menuconfig等时,配置工具首先分析与体系结构对应的/arch/xxx/Kconfig文件(xxx即为传入的ARCH参数)

  1. 通过source引入多层次的Kconfig文件:/arch/xxx/Kconfig文件中除本身包含一些与体系结构相关的配置项和配置菜单以外,还通过source语句引入了一系列Kconfig文件,而这些Kconfig又可能再次通过source引入下一层的Kconfig。
  2. 配置工具依据Kconfig包含的菜单和条目即可描绘出一个如图1所示的分层结构。

编译内核和模块

编译

1
2
sudo make -j4 # 以4个内核多线程方式编译内核
sudo make modules -j4

安装

1
2
sudo make modules_install
sudo make install

此时,内核编译完成。安装完成后会在/boot目录下生成几个新内核的文件。
剩下是修改启动程序配置,然后运行新的内核程序。

配置启动文件

进入引导程序(boot loader)的配置文件看看,对于不同的引导程序(LILO和GRUB),其配置文件所在路径不同。

  • 对于LILO,路径为:/etc/lilo.conf
  • 对于GRUB,路径为:/boot/grub/grub.cfg

本系统为GRUB引导,因此进入/boot/grub/grub.cfg
查看grub.conf配置文件,会发现新内核的信息已经写入了

如果想开机默认显示grub菜单栏:编辑grub.conf,然后注释掉”GRUB_HIDDEN_TIMEOUT”和”GRUB_HIDDEN_TIMEOUT_QUIET”

1
sudo vi /etc/default/grub

编辑完毕之后更新一下:

1
sudo update-grub

重启,然后选择更改后的内核

1
sudo reboot

Kconfig和Makefile

在Linux内核中增加程序需要完成以下3项工作:

  1. 将编写的源代码复制到Linux内核源代码的相应目录中。
  2. 修改Kconfig配置:在目录的Kconfig文件中增加关于新源代码对应项目的编译配置选项
  3. 修改Makefile编译代码:在目录的Makefile文件中增加对新源代码的编译条目

内核编译中的Makefile

这里主要对内核源代码各级子目录中的kbuild(内核的编译系统)Makefile进行简单介绍,这部分是内核模块或设备驱动开发者最常接触到的。

Makefile的语法包括如下几个方面。

  1. 目标定义:用来定义哪些内容要作为模块编译,哪些要编译并链接进内核。(编译成模块or编译进内核)
    • 代码示例:
      1
      2
      3
      4
      5
      obj -y += foo.o
      ------------------------------------------------------------------------------------
      obj -y 表示要由foo.c或者foo.s文件编译得到foo.o并链接进内核(无条件编译,所以不需要Kconfig配置选项)
      obj -m 则表示该文件要作为模块编译。
      obj -n 形式的目标不会被编译。
    • 更常见的做法是根据make menuconfig后生成的config文件CONFIG_变量来决定文件的编译方式,如:
      1
      2
      obj -$(CONfiG_ISDN) += isdn.o
      obj -$(CONfiG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
    • 除了具有上述obj -形式的目标以外,还有lib -y library库hostprogs -y主机程序等目标,但是这两类基本都应用在特定的目录和场合下。
  2. 多文件模块的定义:处理一个模块由多个文件组成的情况。
  • 最简单的Makefile仅需一行代码就够了。如果一个模块由多个文件组成,会稍微复杂一些,这时候应采用模块名加-y或-$后缀的形式来定义模块的组成文件,如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #
    #Makefile for the linux ext2-filesystem routines.
    #
    obj -$(CONfiG_EXT2_FS) += ext2.o
    ext2 -y := balloc.o dir.o file.o fsync.o ialloc.o inode.o ioctl.o namei.o super.o symlink.o
    ext2 -$(CONfiG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
    ext2 -$(CONfiG_EXT2_FS_POSIX_ACL) += acl.o
    ext2 -$(CONfiG_EXT2_FS_SECURITY) += xattr_security.o
    ext2 -$(CONfiG_EXT2_FS_XIP) += xip.o
  • 模块的名字为ext2
    • 由balloc.o、dir.o、file.o等多个目标文件最终链接生成ext2.o直至ext2.ko文件,
    • 并且是否包括xattr.o、acl.o等则取决于内核配置文件的配置情况,例如,如果CONFIG_EXT2_FS_POSIX_ACL被选择,则编译acl.c得到acl.o并最终链接进ext2。
  1. 目录层次的迭代:处理是否编译一个目录中文件进内核的情况。
    如下例:
    1
    obj -$(CONfiG_EXT2_FS) += ext2/
    当CONFIG_EXT2_FS的值为y或m时,kbuild将会把ext2目录列入向下迭代的目标中。

Kconfig

Kconfig(配置文件):在内核编译时给用户提供配置选择的功能。
主要包括如下几个方面。

  1. 配置选项
  • 大多数内核配置选项都对应Kconfig中的一个配置选项(config):

    1
    2
    3
    4
    5
    config MODVERSIONS       # 配置选项
    bool "Module versioning support" #配置选项的类型
    help # 帮助信息
    Usually, you have to use modules compiled with your kernel.
    Saying Y here makes it ...

    “config”关键字定义新的配置选项,之后的几行代码定义了该配置选项的属性。

  • 配置选项的属性包括:

    • 类型数据范围输入提示
    • 依赖关系选择关系帮助信息默认值等。
  • 每个配置选项都必须指定类型配置选项类型包括booltristatestringhexint

    • 其中tristatestring是两种基本类型,其他类型都基于这两种基本类型。
  • 类型定义后可以紧跟输入提示

    • 下面两段脚本是等价的:
      1
      bool "Networking support"
      1
      2
      bool
      prompt "Networking support"
    • 输入提示的一般格式为:
      1
      prompt <prompt> [if <expr>]	#可选的if用来表示该提示的依赖关系。
    • 配置使用默认值并使用if的格式为:
      1
      default <expr> [if <expr>]
      如果用户不设置对应的选项,配置选项的值就是默认值。//为了便于理解,可以看下面的例子;
  • 依赖关系的格式为:

    1
    depends on(或者requires) <expr>
    • 如果定义了多重依赖关系,它们之间用“&&”间隔。
    • 依赖关系也可以应用到该菜单中所有的其他选项(同样接受if表达式)内,下面两段脚本是等价的:
      1
      2
      bool "foo" if BAR
      default y if BAR
      1
      2
      3
      depends on BAR
      bool "foo"
      default y
  • 选择关系(也称为反向依赖关系):A如果选择了B,则在A被选中的情况下,B自动被选中。(我个人理解为一种包含关系)格式为:

    1
    select <symbol> [if <expr>]
  • 数据范围的格式为:

    1
    range <symbol> <symbol> [if <expr>]

    int和hex类型的选项设置可以接受的输入值范围,用户只能输入大于等于第一个symbol,且小于等于第二个symbol的值。

  • 帮助信息的格式为:

    1
    2
    3
    4
    help(或---help---)
    开始

    结束
    • 帮助信息完全靠文本缩进识别结束。
    • “—help—”和“help”在作用上没有区别,设计“—help—”的初衷在于将文件中的配置逻辑与给开发人员的提示分开。
  1. 菜单结构
    配置选项在菜单树结构中的位置可由两种方法决定。
  • 第一种方式:
    1
    2
    3
    4
    5
    menu "Network device support"
    depends on NET
    config NETDEVICES

    endmenu
    • 所有处于“menu”和“endmenu”之间的配置选项都会成为“Network device support”的子菜单,而且,所有子菜单(config)选项都会继承父菜单(menu)的依赖关系,比如,“Network device support”对“NET”的依赖会被加到配置选项NETDEVICES的依赖列表中。
    • 注意:menu后面跟的“Network device support”项仅仅是1个菜单,没有对应真实的配置选项,也不具备3种不同的状态。这是它和config的区别。
  • 第二种方式:通过分析依赖关系生成菜单结构:如果菜单项在一定程度上依赖于前面的选项,它就能成为该选项的子菜单。如果父选项为“n”,子选项不可见;如果父选项可见,子选项才可见。示例如下:
    1
    2
    3
    4
    5
    6
    7
    config MODULES
    bool "Enable loadable module support"
    config MODVERSIONS
    bool "Set version information on all module symbols"
    depends on MODULES
    comment "module support disabled"
    depends on !MODULES
    • MODVERSIONS直接依赖MODULES,只有MODULES不为“n”时,该选项才可见。
  • 除此之外,Kconfig中还可能使用“choices…endchoice”、“comment”、“if…endif”这样的语法结构。其中“choices…endchoice”的结构为:
    1
    2
    3
    4
    choice
    <choice options>
    <choice block>
    endchoice
    它定义一个选择群,其接受的选项(choice options)可以是前面描述的任何属性,例如,LDD6410的VGA输出分辨率可以是1024×768或者800×600,在drivers/video/samsung/Kconfig中就定义了如下choice:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    choice
    depends on FB_S3C_VGA
    prompt "Select VGA Resolution for S3C Framebuffer"
    default FB_S3C_VGA_1024_768
    config FB_S3C_VGA_1024_768
    bool "1024*768@60Hz"
    ---help---
    TBA
    config FB_S3C_VGA_640_480
    bool "640*480@60Hz"
    ---help---
    TBA
    endchoice
    相关资料:

    用Kconfig配置脚本和Makefile脚本编写的更详细信息,可以分别参见内核文档Documentation目录内的kbuild子目录下的Kconfig-language.txt和Makefiles.txt文件。

注解

内核多次编译过,在编译开始之前可进行清理

1
2
3
 make clean       #清理编译的文件,但保留配置文件
 make mrproper    #移除所有编译生成的文件、配置文件和备份文件
 make distclean   #清理所有产生的文件与config配置文件与编辑过的补丁文件,也就是完全清理

如果想快速编译,可进行如下操作

1
make -j *         #*为cup核心数

如何只编译内核的部分代码

只编译某子目录中的相关代码:

1
2
cd /usr/src/linux
make  path/to/dir/

只编译部分模块:

1
make M=path/to/dir

只编译一个模块:

1
2
3
make path/to/dir/MOD_NAME.ko
```
将编译生成的文件保存至别处:

make O=/path/to/somewhere

```

参考资料

《Linux设备驱动开发详解:基于最新的Linux4.0内核》
linux内核编译详解
Linux 内核编译
Linux源码下生成并安装内核文档
Linux(ubuntu)更换内核方法
linux内核编程入门 hello world
Makefile教程(绝对经典,所有问题看这一篇足够了)
如何系统地学习 Makefile 相关的知识(读/写)?
Makefile-百度百科

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×