Linux 内核树编译

前言

其实我现在还不懂内核树是个什么东西?

为什么写驱动要编译内核树,不是只用头文件就好了吗?

反正昨天是搭建好了驱动开发环境(仅仅是安装了对应的操作系统和一些工具),现在记录一下编译内核的过程。


编译好的内核

先来看下编译好之后的内核是个什么样子的。

这个图是刚启动的时候,如果啥也不干直接进入到 2.6.25-14 版本的内核中:

图 1 系统启动界面

然后上面有个提示:Press any key to enter the menu,我们随便安一个键:

图 2 内核选择界面

可以看到出现了两个版本:

  • Fedora (2.6.29.4)
  • Fedora (2.6.25-14.fc9.i686)

版本高的那个是我从书上照葫芦画瓢编译出来的,倒是没有出现啥问题,很顺利。

但是其实原理我是不知道的,比如:

  • 虚拟机有两个内核共存的原理是什么?
  • 以前安装的软件也是它们共用吗?不会有版本冲突么?
  • make menuconfig 怎么用?
  • 为什么要升级版本?

希望写完这篇,我自己能搞懂上面的问题,然后对于其他版本的内核树也可以随心所欲的编译和使用。


理论学习

内核

内核是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。

根据内核是否被修改过,可以将内核分为标准内核厂商内核两类。

图 3 内核与模块版本之间的关系

标准内核源码和标准内核

标准内核源码是指从 kernel.org 官方网站下载的标准代码。

它是 Linux 内核开发者经过严格测试所构建的内核代码。

标准内核是将标准内核源码编译后得到的二进制映像文件,如图 3 左半部所示。

厂商内核源码和厂商内核

在某些情况下,发型版厂商会对标准内核源码进行适当的修改,以优化内核性能。

这种经过修改后的标准内核源码,就是厂商修改过的内核源码。

将厂商修改过的内核源码编译后,会形成厂商发型版内核。

所以,厂商发行版内核是对标准内核的修改和优化。

这里,需要注意的是,厂商发型版本内核和标准内核对于驱动程序是不兼容的,根据不同内核源码编译出来的驱动程序是不能互用的。

两者兼容性问题

构建驱动程序模块时,必须考虑驱动程序与内核的兼容性。

使用标准内核源码构建的内核模块,就是标准内核模块,其不能在厂商内核中使用。

使用厂商修改过的内核源码构建的内核模块,就是特定厂商的内核模块,其不能在标准内核中使用。

这里,需要注意的是,即使模块代码相同,标准内核模块和特定厂商的内核模块,其模块格式也是不同的。

标准内核模块可以加载进标准内核中,却不能加载进厂商发型内核中。

同理,特定厂商的内核模块可以加载进厂商发行版内核中,却不能加载进标准内核中。

Fedora Core 9 的内核版本是 2.6.25-14.fc9.i686,可以通过 uname -r 来查看。

如果要编写特定厂商的内核模块,那么就要找到 Fedora Core 9 的内核源码。

Fedora Core 9 并没有默认安装内核源码,并且程序员也很难找到该源码。

所以,这里将采用目前较新的标准内核源码来构建内核模块。

这就是将要升级内核的原因。

多个内核版本共存原理

找出系统已经安装的内核版本,删除Linux多余的内核

查询当前系统版本:cat /proc/version

找出系统已经安装的内核版本

ubuntu命令:$ dpkg --get-selections | grep linux-image

fedora命令:$ su -c 'rpm -qa kernel'

卸载旧的内核版本

ubuntu 命令:$ sudo apt-get remove linux-image-2.6.35-22-generic   //删除旧的内核

fedora 命令:$ su -c 'yum remove kernel-3.10.0-693.el7.x86_64'     //注意替换成上面列出的内核项目

重新启动:reboot

一个linux系统里面能有多个kernel吗?如果可以应该怎么添加呢?

能有多个 kernel 但这是启动系统的东西,只能用一个。

内核在 /boot 里面,vmlinuz 那些个就是,其他的文件都是辅助用的。其中还有用的是 initrd ,这东西是随内核一起被引导器——现在基本就是 GRUB ——一起读取到内存中,内核启动后会读取这里的文件,并且把它作为临时的根文件系统,之后再过渡启动到硬盘。不过 initrd 不是必须存在的,他因为在启动内核时一同读取到内存,所以他的硬件限制很少,可以作为提供驱动的数据文件,也可以实现一些挂载你的硬盘跟分区前的检测工作。

/lib/modules/ 里面都是按内核版本号分别保存的其他内核需要和提供的文件,主要是内核模块。以及针对这个内核的开发需要的相应文件(不光是头文件,虽然开发主要是需要 C Header)。

模块目录具体结构请看一些专业的介绍资料吧。

题外:/usr/src 里面一般存放内核的源代码,如果是自己编译的内核,或者某些特殊情况的内核。这里也会放一些东西。/lib/modules 里面的内核其他数据目录里面会有一些内容连接到这里的。这个规划具体看发行版的设计。不过一般大家的习惯是这里必然有内核源代码,所以很多驱动程序和内核有关的一些应用程序,都会直接来这里找开发数据。所以现在大部分系统偏向于这里保存一些内容。

加载内核镜像

内核映像并不是一个可执行的内核,而是一个压缩过的内核映像。通常它是一个 zImage(压缩映像,小于 512KB)或一个 bzImage(较大的压缩映像,大于 512KB),它是提前使用 zlib 进行压缩过的。在这个内核映像前面是一个例程,它实现少量硬件设置,并对内核映像中包含的内核进行解压,然后将其放入高端内存中,如果有初始 RAM 磁盘映像,就会将它移动到内存中,并标明以后使用。然后该例程会调用内核,并开始启动内核引导的过程。

当 bzImage(用于 i386 映像)被调用时,我们从 ./arch/i386/boot/head.S 的 start 汇编例程开始执行。

这个例程会执行一些基本的硬件设置,并调用 ./arch/i386/boot/compressed/head.S 中的 startup_32,设置一个基本的环境(堆栈等),并清除 Block Started by Symbol(BSS)。然后调用一个叫做 decompress_kernel 的 C 函数(在 ./arch/i386/boot/compressed/misc.c 中)来解压内核。当内核被解压到内存中之后,就可以调用它了。这是另外一个 startup_32 函数,但是这个函数在 ./arch/i386/kernel/head.S 中。

内核和根文件系统

待完成。

多个内核版本可以共用程序的原因

这其实是内核系统与应用程序的关系。

在Linux系统中,内核为用户程序提供了两方面的支持。其一是系统调用接口,另一方面是通过开发环境库函数或内核库函数与内核进行信息交流。不过内核库函数仅供内核创建的任务0和任务1使用,它们最终还是去调用系统调用。

系统调用主要提供给系统软件编程或者用于库函数的实现。而一般用户开发的程序则是通过调用像libc等库函数来访问内核资源。这些库中的函数或资源通常被称为应用程序编程接口(API),其中定义了应用程序使用的一组标准编程接口。通过调用这些库中的程序,应用程序代码能够完成各种常用工作,例如,打开和关闭对文件或设备的访问、进行科学计算、出错处理以及访问组和用户标识号ID等系统信息。

API与系统调用的区别在于:为了实现某一应用程序接口标准,例如POSIX,其中的API可以与一个系统调用对应,也可能由几个系统调用的功能共同实现。当然某些API函数可能根本就不需要使用系统调用,即不使用内核功能。因此函数库可以看做实现像POSIX标准的主体界面,应用程序不用管它与系统调用之间到底存在什么关系。无论一个操作系统提供的系统调用有多么大的区别,但只要它遵循同一个API标准,那么应用程序就可以在这些操作系统之间具有可移植性。

系统调用是内核与外界接口的最高层。在内核中,每个系统调用都有一个序列号(在include/unistd.h头文件中定义),并且常以宏的形式实现。应用程序不应该直接使用系统调用,因为这样的话,程序的移植性就不好了。因此目前Linux标准库(Linux Standard Base,LSB)和许多其他标准都不允许应用程序直接访问系统调用宏。

库函数一般包括C语言没有提供的执行高级功能的用户级函数,如输入/输出和字符串处理函数。某些库函数只是系统调用的增强功能版。例如,标准I/O库函数fopen和fclose提供了与系统调用open和close类似的功能,不过是在更高的层次上。在这种情况下,系统调用通常能提供比库函数略微好一些的性能,但是库函数却能提供更多的功能,而且更具检错能力。

结论:只要应用程序编程接口是相同的,那么不同的内核版本可以使用相同的应用程序,所以不同的内核版本在这个前提下是可以共用应用软件的。比如,有些程序可以在 Win 7 和 Win 10 下同时使用,就是因为不同的内核版本提供了相同的 API。

不知道这个理解对不对。

学了半天理论,下面开始正式进入内核树的编译了。


 

内核树的编译

 

 

 

 

猜你喜欢

转载自www.cnblogs.com/tuhooo/p/11280280.html