精华内容
下载资源
问答
  • 《嵌入式系统 Linux 内核开发实战指南(ARM 平台) 》凝聚了作者 12 年的工作经验和学习心得与体会,内容覆盖了嵌入式系统 Linux 内核开发的各个方面。 作者根据自己 11 年的一线工作经验,介绍了嵌入式系统的概念、...
  • 嵌入式系统Linux内核开发实战指南(ARM平台).pdf和配合的(嵌入式系统Linux内核开发实战指南.iso)光盘镜像.受上传所限,分3卷.这是第一卷
  • Linux 内核开发

    千次阅读 2018-11-08 23:27:35
    Linux 内核开发 简介 如你所知,我从去年开始写了一系列关于 x86_64 架构汇编语言程序设计的博文。除了大学期间写过一些 Hello World 这样无实用价值的程序之外,我从来没写过哪怕一行的底层代码。那些程序也...

    Linux 内核开发

    简介

    如你所知,我从去年开始写了一系列关于 x86_64 架构汇编语言程序设计的博文。除了大学期间写过一些 Hello World 这样无实用价值的程序之外,我从来没写过哪怕一行的底层代码。那些程序也是很久以前的事情了,就像我刚才说的,我几乎完全没有写过底层代码。直到不久前,我才开始对这些事情感兴趣,因为我意识到我虽然可以写出程序,但是我却不知道我的程序是怎样被组织运行的。

    在写了一些汇编代码之后,我开始大致了解了程序在编译之后会变成什么样子。尽管如此,还是有很多其他的东西我不能够理解。例如:当 syscall 指令在我的汇编程序内执行时究竟发生了什么,当 printf 函数开始工作时又发生了什么,还有,我的程序是如何通过网络与其他计算机进行通信的。汇编语言并没有为这些问题带来答案,于是我决定做一番深入研究。我开始学习 Linux 内核的源代码,并且尝试着理解那些让我感兴趣的东西。然而 Linux 内核源代码也没有解答我所有的问题,不过我自身关于 Linux 内核及其外围流程的知识确实掌握的更好了。

    在我开始学习 Linux 内核的九个半月之后,我写了这部分内容,并且发布了本书的第一部分。到现在为止,本书共包括了四个部分,而这并不是终点。我之所以写这一系列关于 Linux 内核的文章其实更多的是为了我自己。你也知道,Linux 内核的代码量极其巨大,另外还非常容易忘记这一块或那一块内核代码做了什么,或者忘记某些东西是怎么实现的。出乎意料的是 linux-insides 很快就火了,并且在九个月后积攒了 9096 个星星:

    github

    看起来人们对 Linux 内核的内在机制非常的感兴趣。除此之外,在我写 linux-insides 的这段时间里,我收到了很多人发来的问题,这些问题大都是关于如何开始向 Linux 内核贡献代码。通常来说,人们是很有兴趣为开源项目做贡献的,Linux 内核也不例外:

    google-linux

    这么看起来大家对 Linux 内核的开发流程非常感兴趣。我认为如果这么一本关于 Linux 内核的书却不包括一部分来讲讲如何参与 Linux 内核开发的话,那就非常奇怪了。这就是我决定写这篇文章的原因。在本文中,你不会看到为什么你应该对贡献 Linux 内核感兴趣,但是如果你想参与 Linux 内核开发的话,那这部分就是为你而作。

    让我们开始吧。

    如何入门 Linux 内核

    首先,让我们看看如何获取、构建并运行 Linux 内核。你可以通过两种方式来运行你自己定制的内核:

    • 在虚拟机里运行 Linux 内核;
    • 在真实的硬件上运行 Linux 内核。

    我会对这两种方式都展开描述。在我们开始对 Linux 内核做些什么之前,我们首先需要先获取它。根据你目的的不同,有两种方式可以做到这一点。如果你只是想更新一下你电脑上的 Linux 内核版本,那么你可以使用特定于你 Linux 发行版的命令。

    在这种情况下,你只需要使用软件包管理器下载新版本的 Linux 内核。例如,为了将 Ubuntu (Vivid Vervet) 系统的 Linux 内核更新至 4.1版本,你只需要执行以下命令:

     
    1. $ sudo add-apt-repository ppa:kernel-ppa/ppa

    2. $ sudo apt-get update

    在这之后,再执行下面的命令:

    $ apt-cache showpkg linux-headers
    

    然后选择你感兴趣的 Linux 内核的版本。最后,执行下面的命令并且将 ${version} 替换为你从上一条命令的输出中选择的版本号。

    $ sudo apt-get install linux-headers-${version} linux-headers-${version}-generic linux-image-${version}-generic --fix-missing
    

    最后重启你的系统。重启完成后,你将在 grub 菜单中看到新的内核。

    另一方面,如果你对 Linux 内核开发感兴趣,那么你就需要获得 Linux 内核的源代码。你可以在 kernel.org 网站上找到它并且下载一个包含了 Linux 内核源代码的归档文件。实际上,Linux 内核的开发流程完全建立在 git 版本控制系统之上,所以你需要通过 git 来从 kernel.org 上获取内核源代码:

    $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    

    我不知道你怎么看,但是我本身是非常喜欢 github 的。它上面有一个 Linux 内核主线仓库的镜像,你可以通过以下命令克隆它:

    $ git clone git@github.com:torvalds/linux.git
    

    我是用我自己 fork 的仓库来进行开发的,等到我想从主线仓库拉取更新的时候,我只需要执行下方的命令即可:

     
    1. $ git checkout master

    2. $ git pull upstream master

    注意这个主线仓库的远程主机名叫做 upstream。为了将主线 Linux 仓库添加为一个新的远程主机,你可以执行:

    git remote add upstream git@github.com:torvalds/linux.git
    

    在此之后,你将有两个远程主机:

     
    1. ~/dev/linux (master) $ git remote -v

    2. origin git@github.com:0xAX/linux.git (fetch)

    3. origin git@github.com:0xAX/linux.git (push)

    4. upstream https://github.com/torvalds/linux.git (fetch)

    5. upstream https://github.com/torvalds/linux.git (push)

    其中一个远程主机是你的 fork 仓库 (origin),另一个是主线仓库 (upstream)。

    现在,我们已经有了一份 Linux 内核源代码的本地副本,我们需要配置并编译内核。Linux 内核的配置有很多不同的方式,最简单的方式就是直接拷贝 /boot 目录下已安装内核的配置文件:

    $ sudo cp /boot/config-$(uname -r) ~/dev/linux/.config
    

    如果你当前的内核被编译为支持访问 /proc/config.gz 文件,你也可以使用以下命令复制当前内核的配置文件:

    $ cat /proc/config.gz | gunzip > ~/dev/linux/.config
    

    如果你对发行版维护者提供的标准内核配置文件并不满意,你也可以手动配置 Linux 内核,有两种方式可以做到这一点。Linux 内核的根 Makefile 文件提供了一系列可配置的目标选项。例如 menuconfig 为内核配置提供了一个菜单界面:

    menuconfig

    defconfig 参数会为当前的架构生成默认的内核配置文件,例如 x86_64 defconfig。你可以将 ARCH 命令行参数传递给 make,以此来为给定架构创建 defconfig 配置文件:

    $ make ARCH=arm64 defconfig
    

    allnoconfig、 allyesconfig 以及 allmodconfig 参数也允许你生成新的配置文件,其效果分别为尽可能多的选项都关闭、尽可能多的选项都启用或尽可能多的选项都作为模块启用。nconfig 命令行参数提供了基于 ncurses 的菜单程序来配置 Linux 内核:

    nconfig

    randconfig 参数甚至可以随机地生成 Linux 内核配置文件。我不会讨论如何去配置 Linux 内核或启用哪个选项,因为没有必要这么做:首先,我不知道你的硬件配置;其次,如果我知道了你的硬件配置,那么剩下的问题就是搞清楚如何使用程序生成内核配置,而这些程序的使用都是非常容易的。

    好了,我们现在有了 Linux 内核的源代码并且完成了配置。下一步就是编译 Linux 内核了。最简单的编译 Linux 内核的方式就是执行以下命令:

     
    1. $ make

    2. scripts/kconfig/conf --silentoldconfig Kconfig

    3. #

    4. # configuration written to .config

    5. #

    6. CHK include/config/kernel.release

    7. UPD include/config/kernel.release

    8. CHK include/generated/uapi/linux/version.h

    9. CHK include/generated/utsrelease.h

    10. ...

    11. ...

    12. ...

    13. OBJCOPY arch/x86/boot/vmlinux.bin

    14. AS arch/x86/boot/header.o

    15. LD arch/x86/boot/setup.elf

    16. OBJCOPY arch/x86/boot/setup.bin

    17. BUILD arch/x86/boot/bzImage

    18. Setup is 15740 bytes (padded to 15872 bytes).

    19. System is 4342 kB

    20. CRC 82703414

    21. Kernel: arch/x86/boot/bzImage is ready (#73)

    为了增加内核的编译速度,你可以给 make 传递命令行参数 -jN,这里的 N 指定了并发执行的命令数目:

    $ make -j8
    

    如果你想为一个架构构建一个与当前内核不同的内核,那么最简单的方式就是传递下面两个参数:

    • ARCH 命令行参数是目标架构名;
    • CROSS_COMPILER 命令行参数是交叉编译工具的前缀;

    例如,如果我们想使用默认内核配置文件为 arm64 架构编译 Linux 内核,我们需要执行以下命令:

     
    1. $ make -j4 ARCH=arm64 CROSS_COMPILER=aarch64-linux-gnu- defconfig

    2. $ make -j4 ARCH=arm64 CROSS_COMPILER=aarch64-linux-gnu-

    编译的结果就是你会看到压缩后的内核文件 - arch/x86/boot/bzImage。既然我们已经编译好了内核,那么就可以把它安装到我们的电脑上或者只是将它运行在模拟器里。

    安装 Linux 内核

    就像我之前写的,我们将考察两种运行新内核的方法:第一种情况,我们可以在真实的硬件上安装并运行新版本的 Linux 内核,第二种情况就是在虚拟机上运行 Linux 内核。在前面的段落中我们看到了如何从源代码来构建 Linux 内核,并且我们现在已经得到了内核的压缩镜像:

     
    1. ...

    2. ...

    3. ...

    4. Kernel: arch/x86/boot/bzImage is ready (#73)

    在我们获得了 bzImage 之后,我们需要使用以下命令来为新的 Linux 内核安装 headers 和 modules

     
    1. $ sudo make headers_install

    2. $ sudo make modules_install

    以及内核自身:

    $ sudo make install
    

    从这时起,我们已经安装好了新版本的 Linux 内核,现在我们需要通知 bootloader 新内核已经安装完成。我们当然可以手动编辑/boot/grub2/grub.cfg 配置文件并将新内核添加进去,但是我更推荐使用脚本来完成这件事。我现在在使用两种不同的 Linux 发行版:Fedora 和 Ubuntu,有两种方式可以用来更新 grub 配置文件,我目前正在使用下面的脚本来达到这一目的:

    #!/bin/bash
    
    source "term-colors"
    
    DISTRIBUTIVE=$(cat /etc/*-release | grep NAME | head -1 | sed -n -e 's/NAME\=//p')
    echo -e "Distributive: ${Green}${DISTRIBUTIVE}${Color_Off}"
    
    if [[ "$DISTRIBUTIVE" == "Fedora" ]] ;
    then
        su -c 'grub2-mkconfig -o /boot/grub2/grub.cfg'
    else
        sudo update-grub
    fi
    
    echo "${Green}Done.${Color_Off}"

    这是新 Linux 内核安装过程中的最后一步,在这之后你可以重启你的电脑,然后在启动过程中选择新版本的内核。

    第二种情况就是在虚拟机内运行新的 Linux 内核,我更倾向于使用 qemu。首先我们需要为此构建初始的虚拟内存盘 - initrdinitrd 是一个临时的根文件系统,它在初始化期间被 Linux 内核使用,而那时其他的文件系统尚未被挂载。我们可以使用以下命令构建 initrd

    首先我们需要下载 busybox,然后运行 menuconfig 命令配置它:

    $ mkdir initrd
    $ cd initrd
    $ curl http://busybox.net/downloads/busybox-1.23.2.tar.bz2 | tar xjf -
    $ cd busybox-1.23.2/
    $ make menuconfig
    $ make -j4

    busybox 是一个可执行文件 - /bin/busybox,它包括了一系列类似于 coreutils 的标准工具。在 busysbox 菜单界面上我们需要启用 Build BusyBox as a static binary (no shared libs) 选项:

    busysbox menu

    我们可以按照下方的路径找到这个菜单项:

     
    1. Busybox Settings

    2. --> Build Options

    之后,我们从 busysbox 的配置菜单退出去,然后执行下面的命令来构建并安装它:

     
    1. $ make -j4

    2. $ sudo make install

    既然 busybox 已经安装完了,那么我们就可以开始构建 initrd 了。为了完成构建过程,我们需要返回到之前的 initrd 目录并且运行命令:

     
    1. $ cd ..

    2. $ mkdir -p initramfs

    3. $ cd initramfs

    4. $ mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}

    5. $ cp -av ../busybox-1.23.2/_install/* .

    这会把 busybox 复制到 bin 目录、sbin 目录以及其他相关目录内。现在我们需要创建可执行的 init 文件,该文件将会在系统内作为第一个进程执行。我的 init 文件仅仅挂载了 procfs 和 sysfs 文件系统并且执行了 shell 程序:

    #!/bin/sh
    
    mount -t proc none /proc
    mount -t sysfs none /sys
    
    exec /bin/sh

    最后,我们创建一个归档文件,这就是我们的 initrd 了:

    $ find . -print0 | cpio --null -ov --format=newc | gzip -9 > ~/dev/initrd_x86_64.gz
    

    我们现在可以在虚拟机里运行内核了。就像我之前写过的,我偏向于使用 qemu 来完成这些工作,下面的命令可以用来运行我们的 Linux 内核:

    $ qemu-system-x86_64 -snapshot -m 8GB -serial stdio -kernel ~/dev/linux/arch/x86_64/boot/bzImage -initrd ~/dev/initrd_x86_64.gz -append "root=/dev/sda1 ignore_loglevel"
    

    qemu

    从现在起,我们就可以在虚拟机内运行 Linux 内核了,这意味着我们可以开始对内核进行修改和测试了。

    除了上面的手动过程之外,还可以考虑使用 ivandaviov/minimal 来自动生成 initrd

    Linux 内核开发入门

    这部分的核心内容主要回答了两个问题:在你发送第一个 Linux 内核补丁之前你应该做什么 (to do) 和不能做什么 (not to do)。请千万不要把应该做的事 (to do) 和待办事项 (todo) 搞混了。我无法回答你能为 Linux 内核修复什么问题,我只是想告诉你我拿 Linux 内核源代码做实验的过程。

    首先,我需要使用以下命令从 Linus 的仓库中拉取最新的更新:

     
    1. $ git checkout master

    2. $ git pull upstream master

    在这之后,我的本地 Linux 内核源代码仓库已经和主线仓库同步了。现在我们可以在源代码上做些修改了。就像我之前写的,关于从哪开始修改或者可以做些什么,我并不能给你太多建议。不过,对于新手来说最好的地方就是 staging 源码树,也就是 drivers/staging 上的驱动集合。staging 源码树的主要维护者是 Greg Kroah-Hartman,该源码树正是你的琐碎补丁可以被接受的地方。让我们看一个简单的例子,该例子描述了如何生成补丁、检查补丁以及如何将补丁发送到 Linux 内核邮件列表

    如果我们查看一下为 Digi International EPCA PCI 基础设备所写的驱动程序,在 295 行我们将会看到 dgap_sindex 函数:

    static char *dgap_sindex(char *string, char *group)
    {
        char *ptr;
    
        if (!string || !group)
            return NULL;
    
        for (; *string; string++) {
            for (ptr = group; *ptr; ptr++) {
                if (*ptr == *string)
                    return string;
            }
        }
    
        return NULL;
    }

    这个函数查找 group 和 string 共有的字符并返回其位置。在研究 Linux 内核源代码期间,我注意到 lib/string.c 文件里实现了一个 strpbrk 函数,该函数和 dgap_sinidex 函数做了同样的事。使用现存函数的另一种自定义实现并不是一个好主意,所以我们可以从drivers/staging/dgap/dgap.c 源码文件中移除 dgap_sindex 函数并使用 strpbrk 替换它。

    首先,让我们基于当前主分支创建一个新的 git 分支,该分支与 Linux 内核主仓库同步:

    $ git checkout -b "dgap-remove-dgap_sindex"
    

    然后,我们可以将 dgap_sindex 函数替换为 strpbrk。做完这些修改之后,我们需要重新编译 Linux 内核或者只重编译 dgap 目录。不要忘了在内核配置文件中启用这个驱动,你可以在如下位置找到该驱动:

     
    1. Device Drivers

    2. --> Staging drivers

    3. ----> Digi EPCA PCI products

    dgap menu

    现在是时候提交修改了,我使用下面的命令组合来完成这件事:

     
    1. $ git add .

    2. $ git commit -s -v

    最后一条命令运行后将会打开一个编辑器,该编辑器会从 $GIT_EDITOR 或 $EDITOR 环境变量中进行选择。 -s 命令行参数会在提交信息的末尾按照提交者名字加上一行 Signed-off-by。你在每一条提交信息的最后都能看到这一行,例如 - 00cc1633。这一行的主要目的是追踪谁做的修改。-v 选项按照合并格式显示 HEAD 提交和即将进行的最新提交之间的差异。这样做不是并必须的,但有时候却很有用。再来说下提交信息,实际上,一条提交信息由两部分组成:

    第一部分放在第一行,它包括了一句对所做修改的简短描述。这一行以 [PATCH] 做前缀,后面跟上子系统、驱动或架构的名字,以及在 :之后的简述信息。在我们这个例子中,这一行信息如下所示:

    [PATCH] staging/dgap: Use strpbrk() instead of dgap_sindex()
    

    在简述信息之后,我们通常空一行再加上对本次提交的详尽描述。在我们的这个例子中,这些信息如下所示:

     
    1. The <linux/string.h> provides strpbrk() function that does the same that the

    2. dgap_sindex(). Let's use already defined function instead of writing custom.

    在提交信息的最后是 Sign-off-by 这一行。注意,提交信息的每一行不能超过 80 个字符并且提交信息必须详细地描述你所做的修改。千万不要只写一条类似于 Custom function removed 这样的信息,你需要描述你做了什么以及为什么这样做。补丁的审核者必须据此知道他们正在审核什么内容,除此之外,这里的提交信息本身也非常有用。每当你不能理解一些东西的时候,我们都可以使用 git blame 命令来阅读关于修改的描述。

    提交修改之后,是时候生成补丁文件了。我们可以使用 format-patch 命令来完成:

     
    1. $ git format-patch master

    2. 0001-staging-dgap-Use-strpbrk-instead-of-dgap_sindex.patch

    我们把分支名字 (这里是master) 传递给 format-patch 命令,该命令会根据那些包括在 dgap-remove-dgap_sindex 分支但不在 master 分支的最新改动来生成补丁。你会发现, format-patch 命令生成的文件包含了最新所做的修改,该文件的名字是基于提交信息的简述来生成的。如果你想按照自定义的文件名来生成补丁,你可以使用 --stdout 选项:

    $ git format-patch master --stdout > dgap-patch-1.patch
    

    最后一步就是在我们生成补丁之后将之发送到 Linux 内核邮件列表。当然,你可以使用任意的邮件客户端,不过 git 为此提供了一个专门的命令:git send-email。在发送补丁之前,你需要知道发到哪里。虽然你可以直接把它发送到 linux-kernel@vger.kernel.org 这个邮件列表,但这很可能让你的补丁因为巨大的消息流而被忽略掉。最好的选择是将补丁发送到你的修改所属子系统的维护者那里。你可以使用 get_maintainer.pl 这个脚本来找到这些维护者的名字。你所需要做的就是将你代码所在的文件或目录作为参数传递给脚本。

     
    1. $ ./scripts/get_maintainer.pl -f drivers/staging/dgap/dgap.c

    2. Lidza Louina <lidza.louina@gmail.com> (maintainer:DIGI EPCA PCI PRODUCTS)

    3. Mark Hounschell <markh@compro.net> (maintainer:DIGI EPCA PCI PRODUCTS)

    4. Daeseok Youn <daeseok.youn@gmail.com> (maintainer:DIGI EPCA PCI PRODUCTS)

    5. Greg Kroah-Hartman <gregkh@linuxfoundation.org> (supporter:STAGING SUBSYSTEM)

    6. driverdev-devel@linuxdriverproject.org (open list:DIGI EPCA PCI PRODUCTS)

    7. devel@driverdev.osuosl.org (open list:STAGING SUBSYSTEM)

    8. linux-kernel@vger.kernel.org (open list)

    你将会看到一组姓名和与之相关的邮件地址。现在你可以通过下面的命令发送补丁了:

     
    1. $ git send-email --to "Lidza Louina <lidza.louina@gmail.com>" \

    2. --cc "Mark Hounschell <markh@compro.net>" \

    3. --cc "Daeseok Youn <daeseok.youn@gmail.com>" \

    4. --cc "Greg Kroah-Hartman <gregkh@linuxfoundation.org>" \

    5. --cc "driverdev-devel@linuxdriverproject.org" \

    6. --cc "devel@driverdev.osuosl.org" \

    7. --cc "linux-kernel@vger.kernel.org"

    这就是全部的过程。补丁被发出去了,现在你所需要做的就是等待 Linux 内核开发者的反馈。在你发送完补丁并且维护者接受它之后,你将在维护者的仓库中看到它 (例如前文你看到的补丁)。一段时间后,维护者将会向 Linus 发送一个拉取请求,之后你就会在主线仓库里看到你的补丁了。

    这就是全部内容。

    一些建议

    在该部分的最后,我想给你一些建议,这些建议大都是关于在 Linux 内核的开发过程中需要做什么以及不能做什么的:

    • 考虑,考虑,再考虑。在你决定发送补丁之前再三考虑。

    • 在你每次改完 Linux 内核源代码之后 - 试着编译它。我指的是任何修改之后,都要不断的编译。没有人喜欢那些连编译都不通过修改。

    • Linux 内核有一套代码规范指南,你需要遵守它。有一个很棒的脚本可以帮你检查所做的修改。这个脚本就是 -scripts/checkpatch.pl。只需要将被改动的源码文件传递给它即可,然后你就会看到如下输出:

     
    1. $ ./scripts/checkpatch.pl -f drivers/staging/dgap/dgap.c

    2. WARNING: Block comments use * on subsequent lines

    3. #94: FILE: drivers/staging/dgap/dgap.c:94:

    4. +/*

    5. + SUPPORTED PRODUCTS

    6.  
    7. CHECK: spaces preferred around that '|' (ctx:VxV)

    8. #143: FILE: drivers/staging/dgap/dgap.c:143:

    9. + { PPCM, PCI_DEV_XEM_NAME, 64, (T_PCXM|T_PCLITE|T_PCIBUS) },

    10.  
    11.  

    在 git diff 命令的帮助下,你也会看到一些有问题的地方:

    git diff

    • Linus 不接受 github pull requests

    • 如果你的修改是由一些不同的且不相关的改动所组成的,你需要通过分离提交来切分修改。git format-patch 命令将会为每个提交生成一个补丁,每个补丁的标题会包含一个 vN 前缀,其中 N 是补丁的编号。如果你打算发送一系列补丁,也许给 git format-patch命令传递 --cover-letter 选项会对此很有帮助。这会生成一个附加文件,该文件包括的附函可以用来描述你的补丁集所做的改动。在 git send-email 命令中使用 --in-reply-to 选项也是一个好主意,该选项允许你将补丁集作为对附函的回复发送出去。对于维护者来说,你补丁集的结构看起来就像下面这样:

     
    1. |--> cover letter

    2. |----> patch_1

    3. |----> patch_2

    你可以将 message-id 参数传递给 --in-reply-to 选项,该选项可以在 git send-email 命令的输出中找到。

    有一件非常重要的事,那就是你的邮件必须是纯文本格式。通常来说,send-email 和 format-patch 这两个命令在内核开发中都是非常有用的,所以请查阅这些命令的的相关文档,你会发现很多有用的选项,例如:git send-email 和 git format-patch

    • 如果你发完补丁之后没有得到立即答复,请不要惊讶,因为维护者们都是很忙的。

    • scripts 目录包含了很多对 Linux 内核开发有用的脚本。我们已经看过此目录中的两个脚本了:checkpatch.pl 和get_maintainer.pl。除此之外,你还可以找到 stackusage 脚本,它可以打印栈的使用情况,extract-vmlinux 脚本可以提取出未经压缩的内镜镜像,还有很多其他的脚本。在 scripts 目录之外,你也会发现很多有用的脚本,这些脚本是 Lorenzo Stoakes 为内核开发而编写的。

    • 订阅 Linux 内核邮件列表。lkml 列表中每天都会有大量的信件,但是阅读它们并了解一些类似于 Linux 内核目前开发状态的内容是很有帮助的。除了 lkml 之外,还有一些其他的邮件列表,它们分别对应于不同的 Linux 内核子系统。

    • 如果你发的补丁第一次没有被接受,你就会收到 Linux 内核开发者的反馈。请做一些修改然后以 [PATCH vN](N 是补丁版本号) 为前缀重新发送补丁,例如:

    [PATCH v2] staging/dgap: Use strpbrk() instead of dgap_sindex()
    

    同样的,这次的补丁也必须包括更新日志以便描述自上一次的补丁以来所做的修改。当然,本文并不是对 Linux 内核开发详尽无遗的指导清单,但是一些最重要的事项已经都被阐明了。

    Happy Hacking!

    总结

    我希望这篇文章能够帮助其他人加入 Linux 内核社区!如果你有其他问题或建议,可以给我写邮件或者在 Twitter 上联系

    请注意,英语并不是我的母语,对此带来的不便我感到很抱歉。如果你发现了错误,请通过邮件或发 PR 来通知我。

     

    原文:https://github.com/MintCN/linux-insides-zh/blob/master/Misc/contribute.md

    展开全文
  • 《嵌入式系统Linux开发实战指南(ARM平台)》凝聚了作者12年的工作经验和学习心得与体会,内容覆盖了嵌入式系统Linux内核开发的各个方面。作者根据自己11年的一线工作经验,介绍了嵌入式系统的概念、组织架构、工作...
  • 嵌入式系统Linux内核开发实战指南(ARM平台).pdf和配合的(嵌入式系统Linux内核开发实战指南.iso)光盘镜像.受上传所限,分3卷.这是第三卷
  • linux内核开发

    2016-02-02 11:54:09
    linux内核开发
  • 经典嵌入式黒皮巨著《嵌入式系统Linux内核开发实战指南(ARM平台)王洪辉著.pdf》。值得一读!
  • 嵌入式系统Linux内核开发实战指南(ARM平台).pdf和配合的(嵌入式系统Linux内核开发实战指南.iso)光盘镜像.受上传所限,分3卷.这是第二卷
  • Linux内核开发_1_编译LInux内核

    千次阅读 2020-05-13 01:23:32
    Linux内核版本: cat /proc/version Linux version 4.15.0-20-generic (buildd@lgw01-amd64-039)\ (gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)) #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 ...

    目录

    1. 准备工作

    1.1 学习环境

    1.2 下载Linux内核源码

    1.3 解压Linux内核

    1.4 目录结构介绍

    2. Linux内核配置

    2.1 配置选项

    1. make config

    2. make menuconfig

    3. make gconfig

    3 开始配置

    1. 配置解释

    General setup 通用选项

    Enable loadable module support 可加载模块

    Enable the block layer 块设备层

    System Type arm 占用配置,一般是厂家提供,与第7项代替了原有的Processor type and features

    [ ]FIQ Mode Serial Debugger,一般不选。

    6Bus support 总线支持

    x86_64_defconfig

    我的配置

    4. 编译Linux源码

    4.1 Linux编译生成文件解释

    5 运行Linux内核

    5.1 qemu

    6. 简单的文件系统和init

    建议

    编译问题收录及解决方案



    1. 准备工作

    1.1 学习环境

    本系列教程使用的环境如下:
    操作系统版本:

    Linux ubuntu 18.04
    

    Linux内核版本:

    cat /proc/version
    Linux version 4.15.0-20-generic (buildd@lgw01-amd64-039)\
     (gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)) #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018
    

    学习的Linux内核版本:

    linux-4.10.15
    

    1.2 下载Linux内核源码

    首先我们需要下载Linux-4.10.15内核,我们可以直接使用wget下载:

    wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.10.15.tar.xz
    

    下载完成之后就会看到一个名为 “linux-4.10.15.tar.xz”的文件,可以看到后缀格式是.tar.xz,双重压缩格式

    1.3 解压Linux内核

    双重压缩格式,我们依次解压先用“xz”命令解压:

    xz -d linux-4.10.15.tar.xz
    

    -d是代表解压的意思
    解压完成后就会在当前目录看到一个名为“linux-4.10.15.tar”,解压完xz后还有一重tar,在使用tar命令解压一次就可以得到原内核文件,这里建议解压到/usr/src目录下,这里没有别的意思,是Linux内核开发者们给我的建议,这个在行业里是一个开发标准,一般Linux源代码都是放在这个目录下,你可以在任何发行版里的这个目录下看到他们所使用的Linux内核源码

    sudo tar -xf linux-4.10.15.tar -C /usr/src/.
    

    之后我们就可以在/usr/src目录下看到我们的linux源码了,同时还有发行版的
    在这里插入图片描述
    随后我们进入到这个目录下,查看一下这个目录的文件体系

    cd linux-4.10.15
    

    1.4 目录结构介绍

    /arch

    不同CPU架构下的核心代码。其中的每一个子目录都代表Linux支持的CPU架构

    /block

    块设备通用函数

    /crypto

    常见的加密算法的C语言实现代码,譬如crc32、md5、sha1等

    /Documentation

    说明文档,对每个目录的具体作用进行说明

    /drivers

    内核中所有设备的驱动程序,其中的每一个子目录对应一种设备驱动

    /firmware

    固件代码

    /fs

    Linux支持的文件系统代码,及各种类型的文件的操作代码。每个子目录都代表Linux支持的一种文件系统类型

    /include

    内核编译通用的头文件

    /init

    内核初始化的核心代码

    /ipc

    内核中进程间的通信代码

    /kernel

    内核的核心代码,此目录下实现了大多数Linux系统的内核函数。与处理器架构相关的内核代码在/kernel/$ARCH/kernel

    /lib

    内核共用的函数库,与处理器架构相关的库在/kernel/$ARCH/lib

    /mm

    内存管理代码,譬如页式存储管理内存的分配和释放等。与具体处理器架构相关的内存管理代码位于/arch/$ARCH/mm目录下

    /net

    网络通信相关代码

    /samples

    示例代码

    /scripts

    用于内核配置的脚本文件,用于实现内核配置的图形界面

    /security

    安全性相关的代码

    /tools

    Linux中的常用工具

    /usr

    内核启动相关的代码

    /virt

    内核虚拟机相关的代码

    2. Linux内核配置

    2.1 配置选项

    Linux内核提供了三种配置的模式

    1. make config

    此模式非常不建议使用,除非你的时间非常多,因为这个方式是通过终端输出的方式挨个,也就是逐个问你设置选项,非常的耗时,而且不方便

    我们测试一下,在终端窗口输入:

    make config

    呀哈,出了一个错:

    “Command 'make' not found, but can be installed with:”

    提示我们系统上没有make这个命令,这是因为我在学习时使用的是新系统,下载的是简洁版的,里面只带了基本的工具链,这样的第三方命令没有包含其中,所以需要我们自己下载,这里下载一下就好啦:

    sudo apt install make

    安装完成之后输入上面说的命令:

    又出错了,这次是告诉我们没有GCC:“/bin/sh: 1: gcc: not found”

    我们在安装一下,这里不需要指定gcc版本,直接输入gcc,软件仓库会给我们根据当前系统内核选择一个最优的gcc版本下来,提示y/n,选择y即可:

    sudo apt install gcc
    

    安装完成之后在接着输入“make config”:

    make config

    这次出现了一个报错:

    “scripts/basic/fixdep.c:449:1: fatal error: opening dependency file scripts/basic/.fixdep.d: Permission denied”

    不要急,一开始没有看到后面的“Permission denied”时,我以为是文件代码出错了,后面仔细一看,是告诉我们没有权限,因为是在用户目录下,所以需要加上“sudo”来获取权限

    sudo make config

    加上sudo后正确运行了:

    他会逐步询问所有的配置信息,要求你输入y/n,这样是非常耗时的,而且很多选项初学者可能根本不太了解,所以非常不建议使用这个选项,我们按下ctrl+c退出这个配置工具。

    2. make menuconfig

    这个配置工具是基于menu文字图形库编写的,非常推荐这个选项,界面友好,其次是第一次使用这个选项会提供一些默认参数,无论是对初学者还是经验丰富的开发者们来说,都是一个非常好的选择

    sudo make menuconfig

    报了一个错误信息:

    “<command-line>:0:12: fatal error: curses.h: No such file or directory”

    提示找不到curses.h,这个是menu里的库文件,这很明显告诉我们缺少menu的库

    所以这里我们安装一下:

    sudo apt install libncurses5-dev

    在此运行后输出:

    “Your display is too small to run Menuconfig!
    It must be at least 19 lines by 80 columns.
    scripts/kconfig/Makefile:28: recipe for target 'menuconfig' failed”

    意思是,这个配置工具在编写时对我们的终端窗口大小进行了限制,长和宽必须满足19*80

    这里我们把终端窗口扩大一点,或者直接f11进入全屏模式都可以(全屏模式下按f11会退出全屏模式),然后在运行:

    成功进入到配置界面

    注:linux内核中一个功能模块有三种编译方法:一种是编入、一种去去除、一种是模块化。所谓编入就是将这个模块的代码直接编译连接到zImage中去,去除就是将这个模块不编译链接到zImage中,模块化是将这个模块仍然编译,但是不会将其链接到zImage中,会将这个模块单独链接成一个内核模块.ko文件,将来linux系统内核启动起来后可以动态的加载或卸载这个模块。
    在menuconfig中选项前面的括号里,*表示编入,空白表示去除,M表示模块化。

    3. make gconfig

    sudo make gconfig

    报错:

    /bin/sh: 1: pkg-config: not found
    *
    * Unable to find the GTK+ installation. Please make sure that
    * the GTK+ 2.0 development package is correctly installed...
    * You need gtk+-2.0, glib-2.0 and libglade-2.0.
    *
    原因也很明确的告诉我们需要安装gtk+2.0的库,这里我们安装一下:

    sudo apt-get install libgtk2.0-dev libglib2.0-dev libglade2-dev

    安装完成之后运行一次看一下效果:

    sudo make gconfig

    3 开始配置

    1. 配置解释

    General setup 通用选项

    选项

    作用

    [*]Prompt for development and/or incomplete code/drivers

    设置界面中显示还在开发或者还没有完成的代码与驱动,最好选上,许多设备都需要它才能配置。

    [ ]Cross-compiler tool prefix

    交叉编译工具前缀,如果你要使用交叉编译工具的话输入相关前缀。默认不使用。嵌入式linux更不需要。

    [ ]Local version - append to kernel release

    自定义版本,也就是uname -r可以看到的版本,可以自行修改,没多大意义。

    [ ]Automatically append version information to the version string

    自动生成版本信息。这个选项会自动探测你的内核并且生成相应的版本,使之不会和原先的重复。这需要Perl的支持。由于在编译的命令make-kpkg 中我们会加入- – append-to-version 选项来生成自定义版本,所以这里选N。

    Kernel compression mode (LZMA)

    选择压缩方式。

    [ ]Support for paging of anonymous memory (swap)

    交换分区支持,也就是虚拟内存支持,嵌入式不需要。

    [*]System V IPC

    为进程提供通信机制,这将使系统中各进程间有交换信息与保持同步的能力。有些程序只有在选Y的情况下才能运行,所以不用考虑,这里一定要选。

    [*]POSIX Message Queues

    这是POSIX的消息队列,它同样是一种IPC(进程间通讯)。建议你最好将它选上。

    [*]BSD Process Accounting

    允许进程访问内核,将账户信息写入文件中,主要包括进程的创建时间/创建者/内存占用等信息。可以选上,无所谓。

    [*]BSD Process Accounting version 3 file format

    选用的话统计信息将会以新的格式(V3)写入,注意这个格式和以前的 v0/v1/v2 格式不兼容,选不选无所谓。

    [ ]Export task/process statistics through netlink (EXPERIMENTAL)

    通过通用的网络输出工作/进程的相应数据,和BSD不同的是,这些数据在进程运行的时候就可以通过相关命令访问。和BSD类似,数据将在进程结束时送入用户空间。如果不清楚,选N(实验阶段功能,下同)。

    [ ]Auditing support

    审计功能,某些内核模块需要它(SELINUX),如果不知道,不用选。

    [ ]RCU Subsystem

    一个高性能的锁机制RCU 子系统,不懂不了解,按默认就行

    [ ]Kernel .config support

    将.config配置信息保存在内核中,选上它及它的子项使得其它用户能从/proc/ config.gz中得到内核的配置,选上,重新配置内核时可以利用已有配置

    [ ]Enable access to .config through /proc/config.gz

    上一项的子项,可以通过/proc/ config.gz访问.config配置,上一个选的话,建议选上。

    16)Kernel log buffer size (16 => 64KB, 17 => 128KB)

    内核日志缓存的大小,使用默认值即可。12 => 4 KB,13 => 8 KB,14 => 16 KB单处理器,15 => 32 KB多处理器,16 => 64 KB,17 => 128 KB。

    [ ]Control Group support(有子项)

    控制组支持,使用默认即可

    Example debug cgroup subsystem

    cgroup子系统调试例子

    Namespace cgroup subsystem

    cgroup子系统命名空间

    Device controller for cgroups

    cgroups设备控制器

    Cpuset support

    只有含有大量CPU(大于16个)的SMP系统或NUMA(非一致内存访问)系统才需要它。

    [ ]enable deprecated sysfs features to support old userspace tools

    在某些文件系统上(比如debugfs)提供从内核空间向用户空间传递大量数据的接口,一般不选

    [ ]Kernel->user space relay support (formerly relayfs)

    内核系统区和用户区进行传递通讯的支持,这个选项在特定文件系统(relayfs)中提供数据接口支持,它可以支持从内核空间到用户空间的大批量数据传递的支持。不清楚可以不选。

    [ ]Namespaces support,(有子项)

    命名空间支持,允许服务器为不同的用户信息提供不同的用户名空间服务。

    []Initial RAM filesystem and RAM disk (initramfs/initrd) support

    初始RAM的文件和RAM磁盘( initramfs /initrd)支持(如果要采用initrd启动则要选择,否则可以不选),不需要,不用选。嵌入式linux一般不选。

    [ ]Optimize for size

    -Os代替-O2参数,可能会有二进制错误问题,一般不选。

    (0)Default panic timeout

    添0即可。

    [*]Configure standard kernel features (for small systems)

    特殊内核用到,可以不选,嵌入式linux则必选。

    [ ]Enable the Anonymous Shared Memory Subsystem

    启用匿名共享内存子系统,不清楚可以不选。

    [ ]Enable AIO support

    支持AIO(Asynchronous I/O 异步事件非阻塞I/O),(包含aio.h, aio_read,向内核发出读命令,aio_write向内核写命令,详细见‘AIO介绍‘文档),AIO机制为服务器端高并发应用程序提供了一种性能优化的手段。加大了系统吞吐量,所以一般用于大型服务器,一般不用选。

    [ ]Kernel Performance Events And Counters(有子项)

    性能相关的事件和计数器支持(既有硬件的支持也有软件的支持).大多数现代CPU都会通过性能计数寄存器对特定类型的硬件事件(指令执行,缓存未命中,分支预测失败)进行计数,同时又丝毫不会减慢内核和应用程序的运行速度.这些寄存器还会在某些事件计数到达特定的阈值时触发中断,从而可以对代码进行性能分析. Linux Performance Event 子系统对上述特性进行了抽象,提供了针对每个进程和每个CPU的计数器,并可以被 tools/perf/ 目录中的"perf"工具使用.

    [*]Enable VM event counters for /proc/vmstat

    允许在/proc/vmstat中包含虚拟内存事件记数器。

    [*]Enable SLUB debugging support

    支持SLUB内存分配管理器调试,

    [ ]Disable heap randomization

    禁用随即head,选不选均可。

    Choose SLAB allocator (SLUB (Unqueued Allocator)) --->

    选择内存分配管理器,强烈推荐使用SLUB。

    [ ]Profiling support

    剖面支持,用一个工具来扫描和计算计算机的剖面图,支持系统测评,一般开发人员使用,不选。

    [ ]Kprobes

    Kprobes 提供了一个强行进入任何内核例程并从中断处理器无干扰地收集信息的接口。使用 Kprobes 可以 轻松地收集处理器寄存器和全局数据结构等调试信息。开发者甚至可以使用 Kprobes 来修改 寄存器值和全局数据结构的值。

    选中后linux内核 将附带此工具

    GCOV-based kernel profiling --->

    基于GCOV的代码覆盖率,可以来审评代码

    Enable loadable module support 可加载模块

    选项

    作用

    [ ]Forced module loading

    强行加载模块,不建议选。

    [*]Module unloading

    支持模块卸载,必须选上。

    [ ]Forced module unloading

    强行卸载模块,即使内核认为这样并不安全,也就是说你可以把正在使用中的模快卸载掉。如果你不是内核开发人员或者骨灰级的玩家,不建议选。

    [ ]Module versioning suppor

    这个功能可以让你使用其它版本的内核模块,除非特殊需要,一般不选。

    [ ]Source checksum for all modules

    这个功能是为了防止更改了内核模块的代码但忘记更改版本号而造成版本冲突,现在很少使用,不选。

    Enable the block layer 块设备层

    选项

    作用

    [*]Support for large (2TB+) block devices and files

    仅在使用大于2TB的块设备时需要

    [*]Block layer SG support v4

    通用SCSI设备第四版支持。

    [*]Block layer data integrity support

    块设备数据完整性支持。

    [*]IO Schedulers --->(有子项)

    IO调度器

    [ ]Anticipatory I/O scheduler

    抢先式I/O调度器,大多数块设备只有一个物理查找磁头(例如一个单独的SATA硬盘),将多个随机的小写入流合并成一个大写入流,用写入延时换取最大的写入吞吐量.适用于大多数环境,特别是写入较多的环境(比如文件服务器)

    [ ]Deadline I/O scheduler

    期限式I/O调度器,轮询的调度器,简洁小巧,提供了最小的读取延迟和尚佳的吞吐量,特别适合于读取较多的环境(比如数据库)。

    ]CFQ I/O scheduler

    使用QoS策略为所有任务分配等量的带宽,避免进程被饿死并实现了较低的延迟,可以认为是上述两种调度器的折中.适用于有大量进程的多用户系统CFQ调度器尝试为所有进程提供相同的带宽。它将提供平等的工作环境,对于桌面系统很合适。

    Default I/O scheduler (CFQ) --->

    默认IO调度器有上面三个IO调度器:抢先式是传统的,它的原理是一有响应,就优先考虑调度。如果你的硬盘此时在运行一项工作,它也会暂停下来先响应用户。期限式则是:所有的工作都有最终期限,在这之前必须完成。当用户有响应时,它会根据自己的工作能否完成,来决定是否响应用户。CFQ则是平均分配资源,不管你的响应多急,也不管它的工作量是多少,它都是平均分配,一视同仁的。

    System Type arm 占用配置,一般是厂家提供,与第7项代替了原有的Processor type and features

    <无子选项>

    [ ]FIQ Mode Serial Debugger,一般不选。

    <无子选项>

    6Bus support 总线支持

    选项

    作用

    PCI support

    PCI总线支持,主板上最长用的插槽,最好选上,arm linux可以不选,arm一般没有PCI总线。

    PCCard (PCMCIA/CardBus) support

    微通道总线,一般老式笔记本有这种插槽,笔记本选上,arm linux 不选。

    Kernel Features 内核特征

    选项

    作用

    [ ] Tickless System (Dynamic Ticks)

    非固定平率系统,能让内核运行的更有效率,并且省电,pc下可选,特别是笔记本,arm linux一般不用选。

    [ ] High Resolution Timer Support

    支持高频率时间发生器,需要硬件兼容,但大多数PC和ARM都不支持,不选

    Memory split (2G/2G user/kernel split) --->

    内核与用户空间各占2G,内核空间0-0x7FFFFFFF,用户空间80000000-FFFFFFFF

    Preemption Model (No Forced Preemption (Server)) --->

    内核抢占模式。普通PC用户一般选2,arm linux 选1就可以。

    No Forced Preemption (Server)

    禁止内核抢占,适合服务器环境。针对于高吞吐量的设计,但有可能延时较长,适用于服务器或科学运算,或向要最大的运算能力,而不理会调度上的延时。

    Voluntary Kernel Preemption (Desktop)

    自愿内核抢占,适合普通的桌面环境。已降低吞吐量为代价,降低内核调度的最大延时,提供更快的应用程序响应,即使系统已经高负荷运转,应用程序仍然能运行的很“流畅”,适合用户桌面环境

    Preemptible Kernel (Low-Latency Desktop)

    主动内核抢占,适合运行实时程序的环境。更低的吞吐量,进一步降低内核的调度延时,使应用程序更加流畅。

    [ ]Compile the kernel in Thumb-2 mode

    编译Thumb-2 mode内核,一般不选

    *] Use the ARM EABI to compile the kernel

    与下面绑定配置。

    [*] Allow old ABI binaries to run with this kernel (EXPERIMENTAL)

    对于嵌入式系统(jffs2 yaffs2),这两个要选上,否则很可能启动的时候报错(kernel panic- not syncing: Attempted to kill init!)

    [ ] High Memory Support (EXPERIMENTAL)(有子项)

    1G物理内存以下不选,超过1G才选。(配置略有变化,以前的选项是OFF(<1G),4G(>1G && <4G),64G(>4G))。1

    Allocate 2nd-level pagetables from highmem

    1G物理内存以下不选,超过1G(小于4G)才选

    Allocate 3nd-level pagetables from highmem

    大于4G,选择此项目。

    Memory model (Flat Memory) --->

    一般选"Flat Memory",其他选项涉及内存热插拔。

    [ ] Enable KSM for page merging

    允许linux内核识别出包含相同内容的内存页,然后合并这些内存页,将数据整合在一个位置可以多次引用,特殊功能,不用选。

    (4096) Low address space to protect from user allocation

    设置低端内存大小,默认4096即可

    [ ] Use kernel mem{cpy,set}() for {copy_to,clear}_user() (EXPERIMENTAL)

    这个资料目前暂时没找到合理的解释,可以先忽略不选

    x86_64_defconfig

    这里我们可以使用Linux下一些自带的配置文件,可以使用“make x86_64_defconfig” 这样就生成了一个x86_64的amd架构的linux内核(64位),如果要生成arm平台的架构的话需要修改配置文件,这里我目前还没有打算学习arm架构的配置工作,所以先选中amd的,如果要生成i386的可以使用"make i386_defconfig"

    你可以在arch/架构名/configs目录下找到对应的配置文件,也可以直接copy到你的根目录名字改为.config就可以了,这些是Linux内核自带的一些基础配置

    我的配置

    下面这个是我的配置,因为在Linux下配置不当,虽然编译可以过但是运行会出现问题,如内核恐慌,或者VFS加载失败等,这里是我在之前实验中编译成功且运行没有问题的一次配置,如果你编译时遇到了问题,可以参考下面的配置:

    链接: https://pan.baidu.com/s/1rdre2xcv0-GZWeRw-xsyyw

    提取码: 6zfg

    下载后用unzip命令解压:

    unzip config.zip

    然后目录下会出现一个config的文件,在copy到我们的linux内核根目录下,copy的新名字要在前面加个“.”,这样Linux内核才能识别:

    cp config 你的linux内核目录源码/.config

    4. 编译Linux源码

    编译方法:

    sudo make bzImage -j4

    这里解释一下,如果直接sudo make是无法生成bzImage的,在之前的版本里可以,但是在4.4版本上无法这样,bzImage是x86_64架构的压缩镜像文件

    选项里“j”,代表多线程编译,n代表线程数

    如:“make -j32”

    拆分32个线程来编译这个项目,线程数量请根据自己机器的配置,如果配置不好开这么多线程,调度起来很慢,也容易卡住。

    一般情况下,建议你的处理器如果是4核,那么建议每个核分出2个线程也就是8线程

    “make j8”

    这样让你的CPU压力不至于那么大。

    如果make的时候看到输出信息,你觉得很乱不舒服,可以使用重定向“>”的方法来屏蔽这些输出信息

    make > test.txt

    当出错时make会停下来,就可以到这个文件里去查看输出信息了

    如果不想生成输出文件又想屏蔽输出信息,Linux开发者没有为我们提供这个选项,但是我们可以利用Linux系统下的dev/null 黑洞文件,把输出信息重定向进去

    make > /dev/null

    黑洞文件相当于windows回收站,但是这个文件不会保存数据,凡是进去的任何数据,就会被自动删除,找不回的.

    输入命令后,Make就会开始自动化编译

    这个期间,可以去喝杯咖啡,因为编译非常耗时

    一步到位,没有出现任何编译出错的问题,这就是选择相仿内核版本的好处

    4.1 Linux编译生成文件解释

    arch里有不同架构的文件夹,如arm,x86,x86_64,当编译完成之后这些文件的boot目录里会生成这些压缩文件,根目录下会生成vmlinux文件,这个文件是未压缩的目标文件

    下面是这些文件的作用:

    vmlinux

    编译出来的最原始的内核文件,未压缩。

    zImage

    是vmlinux经过gzip压缩后的文件。

    bzImage

    bz表示“big zImage”,不是用bzip2压缩的。两者的不同之处在于,zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么采用zImage或bzImage都行,如果比较大应该用bzImage。

    uImage

    U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的tag。

    vmlinuz

    是bzImage/zImage文件的拷贝或指向bzImage/zImage的链接。

    initrd

    是“initial ramdisk”的简写。一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态。

    5 运行Linux内核

    5.1 qemu

    我们精简版的操作系统是不带这些第三方工具的,所以我们先安装一下:

    sudo apt install qemu

    安装完成之后,arch下x86是指32位的Linux内核,而x86_64是指64位的内核,64位是可以运行32位程序的,未来32位架构将逐渐被淘汰

    这里通过给qemu的-kernel指定内核参数,上面我们说过编译产生的文件是压缩文件,qemu可以正确运行吗?

    答:可以,qemu会自动帮我们解析,我们只需要使用-kernel指定就好了

    -kernel是指定内核文件的意思

    qemu-system-x86_64 -kernel arch/x86/boot/bzImage
    

    运行结果:

    可以看到内核成功跑起来了,但是报了一个错误

    end Kernel painc - not syncing: VFS:Unable to mount root fso on unknown-block(0,0)

    Linux内核在运行时需要文件根系统的支持,但是这里我们并没有生成文件系统,所以Linux会报这个错误

    除了文件系统以外Linux还会在初始化完成之后并且成功加载文件系统之后会去fork一个进程,名为init,这里先不做详细讨论,后续的学习文章里对Linux内核这块做一个详细的解析

    这个init就是守护进程,所有用户空间下的进程都由它来主动创建,就类似我们刚刚打开终端产生的shell一样

    6. 简单的文件系统和init

    这里先教大家制作一个简单的文件系统和init

    Linux内核对文件系统有一定的格式要求,如NFS和SVR格式的文件系统,这是基于UNIX演化来的,所以我们需要把文件系统制作成NFS/SVR/EXT等文件系统格式,这里推荐一个命令:“CPIO”,这个命令可以帮助我们生成SVR格式的文件系统,我们的配置选项里默认使用SVR的文件系统格式。

    在制作根文件系统之前,我们需要一个init,先用c语言制作一个init:

    vim init.c

    代码:

    #include <stdio.h>
    
    int main(){
        printf("\nhello Linux Kernel!\n");
        while(1);
    }

    注意这里末尾一定要加while(1),否则无法正确执行init和输出,经过分析inux内核的0号进程(init)运行时会初始化相关工作,然后在去fork一个子进程并把cpu控制权交给子进程。

    同时,init作为父进程不能被结束,因为一旦死掉,用户态空间下就没一个进程,而这个init就是我们Linux上被称为守护进程的东西,一旦死掉,整个用户态下所有的进程都会被一并杀死。

    这样的话用户态就相当于没了,那么内核就会产生异常了,会报内核恐慌,attempted kill init这样的问题,来告诉我们init有问题

    这是我根据资料查到的Linux内核第一次调度INIt的一个过程。

    这里我们只是简单的写一个init程序,后面我们使用buysbox来完成相关初始化,目前正在研究buysbox是如何去完成这些初始化的,等研究完成,会写一篇文章来告诉大家。

    这里我们使用静态编译,因为我们等下要使用的文件系统是临时制作的,里面除了包含init以外不会有任何库,所以不可以动态加载,必须使用静态:

    gcc -static -O0 init.c -o init

    这里使用“-O0".不要让编译器给我们的init进行优化,防止编译器偷懒优化掉某些指令,但是这段代码比较少,也没啥可以优化的,也可以不加。

    这里我们制作一个临时的根目录文件:

    echo init | cpio -o --format=newc > rootfs

    注意CPIO的格式,CPIO选项 -o 是从输出流里读取数据,而echo init是把init文件输出到输出流里,然后CPIO从输出流将文件读取到rootfs里,这里 --format=newc 是指使用SVR4的格式,而>是流重定向。

    注意这里在使用这个命令前不要创建目录,不然会出错,cpio会自动帮我们生成对应格式的文件

    输出:

    1651 blocks

    如果生成成功,会告诉我们输出的文件大小

    这里给上它权限,保证qemu在运行时加载到rootfs时有足够的权限

    sudo chmod 777 rootfs

    这里我们需要使用的运行命令是:

    qemu-system-x86_64   \
         -kernel ./bzImage 内核文件
         -initrd ./rootfs  临时根文件系统
         -append "root=根文件系统 rdinit=第一个init程序"

    这里给大家解释一下这些选项的意思,-kernel上面说过了,-initrd的意思是临时的根文件系统,Linux内核在加载根文件系统之前,VFS会去使用临时的文件系统做相关的初始化工作,当一切就绪后才会去调用实际的根文件系统。

    这里我们将它指向我们的临时文件系统,我们这个简单的文件系统可以先给Linux内核使用,实际的根文件系统是通过-append选项指定的,这个选项可以给内核运行参数,其中root就是指向了根文件系统,这里我们也可以给它指定我们用于临时的文件系统,但是根文件系统不是这么简单的,所以我们上面的简单文件系统只有一个init,真正的根文件系统还需要一些其它的设备文件,这里我们先不做多讨论,后面文件系统这块我们在深入探讨。

    这里rdinit的意思是告诉内核启动后从根文件系统里寻找一个可执行程序的文件名

    我们输入命令:

    qemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd ./rootfs1 -append "rdinit=/init"
    

    因为我电脑上没有根文件系统,所以root就没传参进去。

    运行结果:

    可以看到我们的“hello linux kernel!”打印出来了!

    这是一件非常值得高兴的事,因为我编译了许多天,我在一边编译一遍学习它的内核源码,虽然进度很缓慢,但是我觉得这是一件能让人成长的事情,非常值得高兴,我踩了很多坑,所以这里非常建议大家在编译时一定要选择与发行版内核相仿的Linux内核版本编译。

    因为内核向前兼容,如果你书上用的老版本代码,那么在新版本一样可以用,甚至新版本上的代码会比老版本的代码更好,更健壮,因为Linux主版本会收录许多次版本上的优点,同时也会修复许多bug,Linux在不断的完善。

    建议


    这一段话是我在经历许多天的编译Linux内核过程中习得的总结,可以跳过。
       起初我学习Linux内核的时候,是参考“Linux内核设计与实现”这本书来学的,书上使用的Linux内核版本是:“2.34.6”,这个版本已经很老了,最初我使用的是ubnutu20来编译它的,虽然编译过程中遇到了很多问题,但是都一 一解决了,最后运行时会出现许许多多的问题,如:VFS无法加载根文件系统而引起的文件恐慌,还有kill init这样的问题,最初我以为是配置的问题,在我根据查找到资料,和仔细学习了一边如何配置Linux内核后,我发现其实这些基本上用默认的就可以了。
          大多数的除了特定需求一般无需裁剪,尤其是刚入门的学生,最好是使用默认配置,后来我觉得可能是最新版的ubtunu使用的软件仓库里的lib版本太高了,虽然可以编译过,但是有一小部分的lib库可能对旧函数不支持或者说已经废弃了,这是我目前认为的原因,我只是在ubtunu上安装了旧版本的gcc,但是并没有选择降级glib等库,这可能是原因之一,但是如果我使用了旧版本的glib库还有openssl等,那么一些ubtunu上较新的软件可能使用了新版本glib库里的一些新增的特性,导致这些使用动态加载的软件们无法正常运行。
          所以最后我选择使用老版本的centOS,可是无论是centOS或者是ubtunu对这些老版本的操作系统所使用的仓库代码已经废弃了,所以在这个上面是无法下载任何lib的,我必须修改源,使用国内或者国外带有老版本仓库的源才可以,即便使用了这些,ubtunu或者centOS这些老版本的操作系统在编译过程中也会出现一些零零散散的问题,由于版本太老使用起来非常不顺手,最后我向一些Linux内核的开发者们寻求意见,他们给出的一件事建议去编译3.0或者4.0以上的Linux版本内核,因为太老的Linux内核在目前较新的操作系统平台上已经很难在编译出来了,原因是因为Linux依赖gnu的软件体系,而gnu的软件体系在不断的升级进化,每次的升级,都会被用在正在开发中最新的Linux内核,而除了原始版的Linux内核不是在Linux上编译出来的以外,其余的Linux内核版本都是在Linux内核上开发而来的,而使用的开发工具(gcc)就是那个时代最新的版本。
          其次是不同的发行版它自带的一些依赖LIB都是比较新的,因为它也要为自己的Linux内核提供一个运行环境,而问题的关键是,当我们编译Linux内核时,不确定编译器会不会把当前操作系统上的一些依赖LIB编译到Linux内核里去。
          如正在编译Linux2.6,他使用的glib是3,那么我们当前的发行版使用的是6
          一旦编译起来,gcc会从系统环境里把6的一些依赖lib链接到2.6里去了,从而导致某些函数可能在未来的运行结果或者参数要求发生了一点变化,因为有向前兼容的方法,但是这个函数或者编译器选项很可能会被废弃掉。
          
       不确定的因素很多,所以这里我给大家的建议是,如果你想编译Linux内核,最好选择一个与它使用的Linux内核版本相仿的Linux发行版来编译它
          如我选择学习Linux内核,并且选择的Linux内核版本是4.10.15,那么我需要一个与它使用的内核版本相仿的操作系统,所以这里我选择ubtunu18.04,它使用的内核版本是4.15.0。

    编译问题收录及解决方案:

    1.fatal error: openssl opensslv.h: No

    sudo apt install libssl-dev

    2. Unsupported relocation type: R_X86_64_PLT32

    原因是因为linux要进行重定位地址,就是逻辑地址空间变换成内存中的实际物理地址空间,但是如果生成pic代码,也就是动态链接的格式代码,则会生成与位置无关的偏移代码,这个代码依赖程序加载到内存的hand,同时编译器不允许进行重定位。

    所以我们要修改不让它生成pic就可以了。

    1.在根目录的Makefile文件里找到“cc”,大概在349行

    2.在后面加上“ -fno-pic”保存退出

    展开全文
  • vscode搭建linux内核开发环境

    千次阅读 2021-02-20 18:13:43
    而且没有原生linux版本,要是想在纯linux环境下进行linux驱动开发,就只能wine+Source insight,而wine版的不好用容易卡死而且cpu占用还高,我就想到用开源跨平台的vscode进行linux内核开发,体验能否和source ...

    vscode在linux下搭建内核驱动开发环境

    一、前言

    Souce insight是一个阅读、开发linux内核驱动模块的好工具,但是Source insight是收费的软件,而且没有原生linux版本,要是想在纯linux环境下进行linux驱动开发,就只能wine+Source insight,而wine版的不好用容易卡死而且cpu占用还高,我就想到用开源跨平台的vscode进行linux内核开发,体验能否和source insight一样好呢?

    二、实践

    经过搜索和自己亲身实践,最后发现vscode+global插件,可以实现和source insight相媲美的体验

    三、准备工作

    • linux内核源代码文件夹
    • linux下安装好vscode

    四、安装global软件包和vscode global插件

    我用的是deepin-linux操作系统,在终端输入apt命令安装global

    sudo apt install global

    Shell

    Copy

    在vscode的插件商店里搜索安装global插件

     

    五、打开linux源码文件夹

    利用vscode的打开文件夹的功能,打开linux内核源码的文件夹

     

    提示无法在这个工作区中监视文件更改,按照网页的提示,修改最大文件监控数就可以了,不改对开发也没什么影响

     

    六、安装c/c++扩展

    随便打开一个c文件,右下角会提示你安装c/c++扩展,点安装就可以自动安装了

     

    安装完毕,又会提示你配置 IntelliSense 设置以帮助查找缺少的标头,点配置即可,这里我配置了一个交叉编译器的地址,这个其实也没什么用,不配置也可以

     

    关闭配置页面,这个页面不配置也不影响,这个时候下面会有问题输出,不用管

     

    七、生成global数据库

    在vscode下Ctrl+Shift+P进入命令行模式,输入gl,选择Global: Rebuild Gtags Database执行

     

    过几分钟生成完毕,就会在linux源码目录下生成GTAGS、GRTAGS、GPATH三个tag数据库文件

     

     

    接着,就可以用F12愉快的跳转定义啦,输入代码的时候也会有给力的代码提示

     

    备注:global的版本要注意

    又备注:目前在使用过程中,有自定义的结构体成员比如u32这种类型的无法识别无法提示的问题,不知大家有无方法解决,可以在下方评论我

    展开全文
  • 嵌入式系统Linux内核开发实战指南(ARM平台)-完整版 作者:王洪辉 编著
  • Linux内核开发-入门篇

    千次阅读 2019-01-09 19:17:18
    如何入门 Linux 内核 首先,让我们看看如何获取、构建并运行 Linux 内核。你可以通过两种方式来运行你自己定制的内核: 在虚拟机里运行 Linux 内核; 在真实的硬件上运行 Linux 内核。 我会对这两种...

    如何入门 Linux 内核

    首先,让我们看看如何获取、构建并运行 Linux 内核。你可以通过两种方式来运行你自己定制的内核:

    在虚拟机里运行 Linux 内核;
    在真实的硬件上运行 Linux 内核。
    

    我会对这两种方式都展开描述。在我们开始对 Linux 内核做些什么之前,我们首先需要先获取它。根据你目的的不同,有两种方式可以做到这一点。如果你只是想更新一下你电脑上的 Linux 内核版本,那么你可以使用特定于你 Linux 发行版的命令。

    快捷键

    • 加粗 Ctrl + B
    • 斜体 Ctrl + I
    • 引用 Ctrl + Q
    • 插入链接 Ctrl + L
    • 插入代码 Ctrl + K
    • 插入图片 Ctrl + G
    • 提升标题 Ctrl + H
    • 有序列表 Ctrl + O
    • 无序列表 Ctrl + U
    • 横线 Ctrl + R
    • 撤销 Ctrl + Z
    • 重做 Ctrl + Y

    Markdown及扩展

    Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的HTML页面。 —— [ 维基百科 ]

    使用简单的符号标识不同的标题,将某些文字标记为粗体或者斜体,创建一个链接等,详细语法参考帮助?。

    本编辑器支持 Markdown Extra ,  扩展了很多好用的功能。具体请参考Github.

    表格

    Markdown Extra 表格语法:

    项目价格
    Computer$1600
    Phone$12
    Pipe$1

    可以使用冒号来定义对齐方式:

    项目价格数量
    Computer1600 元5
    Phone12 元12
    Pipe1 元234

    ###定义列表

    Markdown Extra 定义列表语法:
    项目1
    项目2
    : 定义 A
    : 定义 B

    项目3

    定义 C

    定义 D

    定义D内容

    代码块

    代码块语法遵循标准markdown代码,例如:

    @requires_authorization
    def somefunc(param1='', param2=0):
        '''A docstring'''
        if param1 > param2: # interesting
            print 'Greater'
        return (param2 - param1 + 1) or None
    class SomeClass:
        pass
    >>> message = '''interpreter
    ... prompt'''
    

    ###脚注
    生成一个脚注1.

    目录

    [TOC]来生成目录:

    数学公式

    使用MathJax渲染LaTex 数学公式,详见math.stackexchange.com.

    • 行内公式,数学公式为: Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN
    • 块级公式:

    x = − b ± b 2 − 4 a c 2 a x = \dfrac{-b \pm \sqrt{b^2 - 4ac}}{2a} x=2ab±b24ac

    更多LaTex语法请参考 这儿.

    UML 图:

    可以渲染序列图:

    张三 李四 嘿,小四儿, 写博客了没? 李四愣了一下,说 忙得吐血,哪有时间写。 张三 李四

    或者流程图:

    Created with Raphaël 2.2.0 开始 我的操作 确认? 结束 yes no
    • 关于 序列图 语法,参考 这儿,
    • 关于 流程图 语法,参考 这儿.

    离线写博客

    即使用户在没有网络的情况下,也可以通过本编辑器离线写博客(直接在曾经使用过的浏览器中输入write.blog.csdn.net/mdeditor即可。Markdown编辑器使用浏览器离线存储将内容保存在本地。

    用户写博客的过程中,内容实时保存在浏览器缓存中,在用户关闭浏览器或者其它异常情况下,内容不会丢失。用户再次打开浏览器时,会显示上次用户正在编辑的没有发表的内容。

    博客发表后,本地缓存将被删除。

    用户可以选择 把正在写的博客保存到服务器草稿箱,即使换浏览器或者清除缓存,内容也不会丢失。

    **注意:**虽然浏览器存储大部分时候都比较可靠,但为了您的数据安全,在联网后,请务必及时发表或者保存到服务器草稿箱

    ##浏览器兼容

    1. 目前,本编辑器对Chrome浏览器支持最为完整。建议大家使用较新版本的Chrome。
    2. IE9以下不支持
    3. IE9,10,11存在以下问题
      1. 不支持离线功能
      2. IE9不支持文件导入导出
      3. IE10不支持拖拽文件导入


    1. 这里是 脚注内容. ↩︎

    展开全文
  • 学完C语言之后,最终是要应用到某个领域的,比如后端开发,应用开发或者Linux内核开发等。本文将介绍一下Linux内核相关的内容,Linux内核开发是相对比较难的领域,主要是门槛相对较高。虽然门槛较高,但有它的好处,...
  • linux内核开发教程

    2012-01-11 18:12:28
    非常好的资源,对于学习Linux内核开发技术很有帮助。另外,对于那些想要从事Android驱动开发的人也是一个必学的资源。
  • 如何参与linux内核开发

    千次阅读 2016-05-02 22:43:05
    这是一篇将如何参与Linux内核开发的相关问题一网打尽的终极秘笈。它将指导你 成为一名Linux内核开发者,并且学会如何同Linux内核开发社区合作。它尽可能不 包括任何关于内核编程的技术细节,但会给你指引一条...
  • Linux内核开发:使用Git管理源代码Git 是 Linux Torvalds 为了帮助管理 Linux:registered: 内核开发而开发的一个开放源码的版本控制软件。我们可以自己下载这个软件用于对内核的 hack 分析,或者用来管理自己的软件...
  • 使用 Eclipse 搭建 Linux 内核开发环境

    千次阅读 2017-08-02 17:06:54
      时光流逝,记得十年前还是个游手好闲的少年,无聊时觉得应该分析一下Linux内核源码,没有别的动机,只觉得这样很酷。   没有任何项目经验的话,突然眼前出现一个浩大的工程,除了惊叹之余,再无其他可言。...
  • 嵌入式系统Linux内核开发实战指南(ARM平台),想看就看吧
  • 非常详细的linux内核视频教程,比较健全,适合入门到精通。
  • 嵌入式系统Linux内核开发实战指南(ARM平台)附书光盘part1
  • 学习linux内核编译必读书籍,Micheal Beck著,张瑜译,扫描清晰。
  • 嵌入式系统Linux内核开发实战指南(ARM平台)附书光盘part7
  • 浅谈 Linux 内核开发之 PCI 设备驱动
  • 使用qemu搭建linux内核开发环境详细教程

    万次阅读 多人点赞 2019-03-11 19:29:52
    目录 一.安装交叉编译链(根据自己的需求去安装编译链,我这里安装的是32位) step1:下载编译链,执行命令: step2:解压源码 step3:添加环境变量,使你的编译链全局可用 ...下载内核 step1:下载内...
  • 王红辉 写的 完整版 书籍嵌入式系统Linux内核开发实战指南(ARM平台)
  • 嵌入式系统Linux内核开发实战指南__ARM平台 王红辉 原书
  • 嵌入式系统Linux内核开发实战指南 ARM平台 王洪辉 2009_12176663.part2.rar 完
  • Linux基金会分析了13500多名开发人员10多年的工作成果,为Linux内核的发展趋势与方法提供洞见,为成千上万形形色色的不同的个体所参考,他们也许能创造出这个星球上最重要的软件代码。 随着Linux内核发布...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 260,771
精华内容 104,308
关键字:

linux内核开发

linux 订阅