pc wap 装修公司网站源码,那家网站建设公司好,广州市公共资源交易中心,莱芜搜狗推广推荐宏内核和微内核继续前面第一章的知识#xff0c;虽然有点啰嗦#xff0c;既然啰嗦了就继续啰嗦下去吧#xff0c;也是给第一章的内容增加解释。我们知道内核如果按种类来划分的话#xff0c;可以分为宏内核和微内核#xff0c;微内核是一个比较先进的内核#xff0c;我不… 宏内核和微内核继续前面第一章的知识虽然有点啰嗦既然啰嗦了就继续啰嗦下去吧也是给第一章的内容增加解释。我们知道内核如果按种类来划分的话可以分为宏内核和微内核微内核是一个比较先进的内核我不知道是不是真的先进但是 BSD 系统的设计就是想用微内核的思想来完成的结果导致系统稳定性很差不能达到正常使用的程度。什么是微内核呢 如果把CPU当成是皇帝的话皇帝身边的大臣们就可以划分为各种进程和线程了比如打印机是一个进程微内核的思想就是其他进程之间不能直接通信他们只能跟CPU中央处理器进程通信然后中央进程再做调度处理这样看起来非常完美也避免了内核很多高耦合的代码。什么是宏内核呢宏内核的设计就跟微内核有点相反宏内核的子进程之间是可以互相通信的各个模块之间是可以进行函数调用的你可以观察下 Linux 内核模块导出内核符号表后可以在其他模块调用。这样设计从理论角度来看代码耦合度比较高出问题的概率会增大然后实力打脸的是Linux 非常成功耦合度高有弊端也有优点其中优点得到了充分的诠释再加上内核模块可以动态的加载和卸载这简直不要太完美哦。可以点击放大这个图如果你这样看可能看不是很清楚因为图片比较大你需要点击放大了来看就可以看到里面的端倪和差别了。Monolithic kernel 是我们所说的宏内核了里面的设备文件驱动进程间通信进程间通信也可以叫做IPC这些都是柔和在一个一个模式里面这个模式叫做内核态宏内核可以动态加载内核模块当然了也可以动态卸载内核模块你看上面图片那个深红色的地方就是内核态。Micro kernel 是我们说的微内核了微内核就是中央集权统治的了既然是中央集权统治就应该精简毕竟贵族的话都想自己身份特殊点所以里面就是黄色的往下了很多。Hybrid kernel是混合式内核又是微内核又是宏内核windows的设计者就是这么厉害你们俩都说自己最厉害又都不退一步可能你们没有发现可能我才是最厉害的所以有人说Windows 是宏内核嗯这样说没什么问题看看目录[c:\windows\system32\drivers]下面有很多驱动相关的然后又有人跑出来说Windows是微内核嗯这样说也没有什么不对。Linux 环境安装先说下我在第一章节说的会在这里给大家一个资源链接我其中包括ubuntu的下载链接ubuntu如果运行不了的话需要进入bios打开虚拟机选项这个是比较干净的版本里面的vim ,git什么的都还没有安装大家打开后需要自己安装我觉得如果是初学者的话这样也是要经历的了解系统的一个过程。https://github.com/weiqifa0/linuxBook作为一个Linux的狂热分子我设置的Linux账号密码就是[ Linux ]大家可要记住啊。Linux 内核可以运行在 x86 体系结构上也可以运行在 ARM 体系结构上。我刚开始学习 Linux 的时候非常迫切的想要在 ARM 上运行代码经过这么多年的摸爬滚打我终于发现那是一个非常玩笑的想法Linux 内核博大精深你学的是内核是思想我们后面会从是嵌入式Linux开发安卓开发openwrt开发MIFI开发等等用到的都是Linux内核会有差别但是原理性的东西不会差别太大。所以我想自己给大家写书的时候还是用 ubuntu 来介绍 Linux 内核。至于在在ARM开发板上运行给大家说一个东西叫做交叉编译链什么是交叉编译链本来windows是X86体系结构的正常编译出来的文件应该只能在X86体系结构上执行但是有了交叉编译器之后呢我可以在windows上编译ARM体系结构的固件。反过来说你学好了Linux内核理清了其中的脉络结构不管你是做安卓开发还是嵌入式开发以及其他衍生的Linux应用或者驱动开发都会得心应手。我安装好的 ubuntu 开发环境有需要的可以下载下载链接查看前面部分提到的 github 链接本书的所有例程都上传到 github 上做备份到前面链接上下载 ubuntu 虚拟机压缩包下载后用 VMware 打开可能会打开会有提示出错重启电脑进入 Bios 把虚拟机功能选项设置为 Enable再重新进入系统就可以看到一个可以让你学习的 ubuntu 系统了。Linux 内核模块可以做什么Linux 内核下面的代码几乎都是用内核模块完成开发的我们换一个角度来看内核模块看起来就像是一个应用程序的一段代码这个点我觉得应该很容易理解但是它有跟应用程序又不一样我们知道内核空间和用户空间地址段是不一样的内核模块编写需要非常谨慎因为稍微不注意就会可能引起系统的崩溃我这样说大家应该能理解吧应用空间运行的应用程序有问题崩溃了没关系那只是它自己挂掉了但是内核模块不一样如果内核模块有调用它的地方比如音频的 ALSA 驱动如果崩溃了那么整个系统看语音部分就不能工作了。设备驱动使用内核模块编程就比如一个触摸屏驱动在设备模型章节我们好好聊下这个事情但是我们这里就提一下一个设备需要依赖一个驱动没有驱动设备是不可能正常工作了所以内核模块在系统下的驱动都是用内核模块编写的Linux 内核下面有非常多的设备当然就需要非常多的驱动。文件系统驱动说到设备文件不得不提一句Linux 下的所有都可以看做是文件不同的文件系统还有有一个统一的虚拟文件系统来管理这些都是需要内核模块来完成的。系统调用用户空间的程序需要调用内核服务就必须要使用系统调用正常的系统调用比如关机读写文件如果你需要新建一个系统调用也是可以的那就需要你这个大神自己写个内核模块。我大概就知道这么多可能还有些没写出来的也是内核模块编程范围的比如网络编程等。Hello World 内核模块学习 C 语言的时候我们会写一个 Hello World 来证明一下自己然后我们学习 C 的时候我们也会写一个 Hello World 来证明一下自己再然后我们会学习 java、python、还有其他很多很多的编程语言但是最开始你总是想写一个 Hello World 来证明一下自己。我也很想知道为什么我们跟 Hello World 那么过不去来个比喻这就像我们习惯了喝热水一样如果仔细去追究好像喝热水并不能如何如何为什么我们习惯喝热水很大的原因是因为我们的妈妈想让我们喝热水因为我们的妈妈觉得喝热水是一个好习惯喝热水是可以强身健体的喝热水包治百病。再反回来看我们为什么会在刚开始学习一个东西的时候喜欢去写 Hello World 因为你接触的很多人告诉你这样做如果可以你明明是不需要些 Hello World 的而是写 Hello You 或者 Hello 等其他什么东西的。说到这里我还是要写一个 Hello World 来让自己入个门进入那个深不可测的世界聆听 Linux 教诲、体会Linux带给我们的快感。Hello World 内核模块代码#include linux/init.h#include linux/module.hMODULE_LICENSE(Dual BSD/GPL);static int hello_init(void){ printk(KERN_ALERT Hello, world\n); return 0;}static void hello_exit(void){ printk(KERN_ALERT Goodbye, cruel world\n);}module_init(hello_init);module_exit(hello_exit);就是这简单的几行代码我们来简单的分析一下前面两行是包含头文件这个跟我们学习 C 语言没有什么区别有了头文件才能调用函数就是这么简单。第三行MODULE_LICENSE 这个是声明模块的许可证说明的比喻一下我们开车就必须有一个驾驶证如果我们没有驾驶证就会被交警叔叔给抓住模块代码也一样如果没有许可证声明就会被内核给抓住模块的许可证声明 从 2.4.10 内核版本开始模块必须通过 MODULE_LICENSE 宏声明此模块的许可证否则在加载此模块时会收到内核被污染 “kernel tainted” 的警告。从 linux/module.h 文件中可以看到被内核接受的有意义的许可证有 GPLGPL v2GPL and additional rightsDual BSD/GPLDual MPL/GPLProprietary在同时支持2.4与2.6内核的设备驱动中模块可按如下方式声明自己的许可证。MODULE_LICENSE(“GPL”);然后下面就是声明的两个函数函数里面调用了 printk 来打印内容后面再有一个 module_init 和 module_exit 望文生义我们可以知道这两行代码是用来说明模块初始化和模块退出时调用的函数。printk 内核打印和调试工具内核里面是没有 C 库(C库是我们写C语言的那个库函数)的大家刚开始学习 C 语言的时候知道一个函数叫做 printf 这个也是一个打印函数但是这个打印函数需要 C 库的帮忙内核里面需要没有C库但是需要自己的打印函数这个函数就叫做 printk。不知道大家想过没有为什么内核模块里面可以直接调用printk函数内核模块是属于内核的printk也是属于内核的如果内核函数在内核导出了内核符号表那么这个函数可以被内核里面的其他地方使用我又要惊叹一下这样的设计了。KERN_ALERT 这个是一个宏定义用来控制内核打印的等级等级很重要通过控制等级可以动态的控制日志的输出Linux 经久不衰的原因也是因为有这么多优良的基因而其中的这个基因肯定是包括 printk关于内核的打印部分我觉得后面可以起一个章节单独说明调试打印还是非常有必要的。编写Makefile编译HelloWorld模块有源码还是不够的就好像我们做饭准备了很多食材我们还需要一个锅来料理这些食材源代码也是一样它们也是码农的食材但是也是需要料理的所以就出现了很多烹饪手法我们码农也接触到了很多编译器而在 Linux 下最常用的 就是 GCC 编译器使用GCC 就离不开 makefile。我们给上面的 Hello World 模块编写一个 makefile 吧ifneq ($(KERNELRELEASE),)obj-m : hello.oelsePWD : $(shell pwd)KVER : $(shell uname -r)KDIR : /lib/modules/$(KVER)/buildall: $(MAKE) -C $(KDIR) M$(PWD) modulesclean: rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versionsendif很多人写好了Makefile 但是编译却不成功因为里面还有一些需要注意的地方特别是初学者可能要被这几行代码调戏很长时间其中需要注意的是要用4空格的Tab键要不然很大概率会出现编译出错。我们编译内核需要编译工具Makefile还需要一个很重要的东西就是内核库文件我现在是自己安装了一个ubuntu系统这个系统在安装的时候已经有了一个内核编译好的库文件位置就是 KDIR 指定的位置里面的东西大概如下。HelloWorld 模块编译的时候需要从这个库文件里面去找到它需要依赖的函数头文件等等。linuxubuntu:/lib/modules/3.13.0-32-generic/build$ lsarch Documentation fs ipc kernel mm samples sound usrblock drivers include Kbuild lib Module.symvers scripts tools virtcrypto firmware init Kconfig Makefile net security ubuntulinuxubuntu:/lib/modules/3.13.0-32-generic/build$假设像这样的错误linuxubuntu:~/linux$ makeMakefile:11: *** missing separator. Stop.我们编译出来的内核模块是包含一定的信息的我们可以使用命令来查看这些信息当然了你也可以用这个方法去查看别人编写的内核模块的信息很多初学者写代码都是哐哐哐编译通过开心一下运行成功了开心一下完全不去考虑其中的细节查看内核模块信息就是其中的一个细节使用命令和运行结果如下。linuxubuntu:~/linuxBook/HelloWorld$ modinfo hello.kofilename: hello.kolicense: Dual BSD/GPLsrcversion: 31FE72DA6A560C890FF9B3Fdepends: vermagic: 3.13.0-117-generic SMP mod_unload modversionslinuxubuntu:~/linuxBook/HelloWorld$内核模块的加载和卸载内核模块编译好了之后在当前的路径下会生成一些产物如下图linuxubuntu:~/linux$ lshello.c hello.ko hello.mod.c hello.mod.o hello.o Makefile modules.order Module.symverslinuxubuntu:~/linux$这时候我们就需要装载这个内核模块到内核里Linux 内核里面有一个链表来维护在运行中的内核模块我们装载内核模块就是往这个链表插入我们的内核模块像下面的图片一样如果你想得极端一点就有点像插队。装载 HelloWorld 内核模块linuxubuntu:~/linux$ sudo insmod hello.ko[sudo] password for linux:linuxubuntu:~/linux$输入 sudo insmod hello.ko 后需要输入 su 超级用户的密码这个时候我们理论上就会执行module_init 会有一个打印出来我们去哪里看这个打印了这样输入命令 dmesg |tail -5 查看后面 5 行日志。linuxubuntu:~/linux$ dmesg |tail -5[ 1154.971812] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready[41991.859527] e1000: eth0 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: None[41991.869932] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready[41991.870367] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready[42035.102864] Hello, worldlinuxubuntu:~/linux$同样我们去卸载内核模块的时候执行指令sudo rmmod hello.kolinuxubuntu:~/linux$ dmesg |tail -5[41991.859527] e1000: eth0 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: None[41991.869932] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready[41991.870367] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready[42035.102864] Hello, world[42284.945557] Goodbye, cruel worldlinuxubuntu:~/linux$insmod 和rmmod 的流程图如下面的图片我这个图片引用的是其他书籍里面的有可能跟最新的内核有出入但是大致不会相差太多可以作为参考。内核模块加载的流程图Module_initLinux 内核里面非常多的代码都是通过模块的方式加载的但是每个模块的加载顺序都是不一样的比如设备驱动因为设备驱动是在系统启动完成后再进行加载的属于偏后面一些但是肯定有一些核心组件需要提前加载的比如时钟I2C core等因为需要不同的加载顺序就有了不同级别的xx_init级别。在\include\linux\init.h里面定义了不同的级别函数数字越低代表的级别越高越早加载。/* * A pure initcall has no dependencies on anything else, and purely * initializes variables that couldnt be statically initialized. * * This only exists for built-in code, not for modules. * Keep main.c:initcall_level_names[] in sync. */#define pure_initcall(fn) __define_initcall(fn, 0)#define core_initcall(fn) __define_initcall(fn, 1)#define core_initcall_sync(fn) __define_initcall(fn, 1s)#define postcore_initcall(fn) __define_initcall(fn, 2)#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)#define arch_initcall(fn) __define_initcall(fn, 3)#define arch_initcall_sync(fn) __define_initcall(fn, 3s)#define subsys_initcall(fn) __define_initcall(fn, 4)#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)#define fs_initcall(fn) __define_initcall(fn, 5)#define fs_initcall_sync(fn) __define_initcall(fn, 5s)#define rootfs_initcall(fn) __define_initcall(fn, rootfs)#define device_initcall(fn) __define_initcall(fn, 6)#define device_initcall_sync(fn) __define_initcall(fn, 6s)#define late_initcall(fn) __define_initcall(fn, 7)#define late_initcall_sync(fn) __define_initcall(fn, 7s)#define __initcall(fn) device_initcall(fn)#define __exitcall(fn) \ static exitcall_t __exitcall_##fn __exit_call fn#define console_initcall(fn) \ static initcall_t __initcall_##fn \ __used __section(.con_initcall.init) fn....../** * module_init() - driver initialization entry point * x: function to be run at kernel boot time or module insertion * * module_init() will either be called during do_initcalls() (if * builtin) or at module insertion time (if a module). There can only * be one per module. */#define module_init(x) __initcall(x);/** * module_exit() - driver exit entry point * x: function to be run when driver is removed * * module_exit() will wrap the driver clean-up code * with cleanup_module() when used with rmmod when * the driver is a module. If the driver is statically * compiled into the kernel, module_exit() has no effect. * There can only be one per module. */#define module_exit(x) __exitcall(x);可以看到 module_init 实际上是__define_initcall(fn, 6) 属于设备级别的初始化我们之前一直在说Linux 内核支持动态模块加载Linux内核的很多子系统都是通过模块化来设计的既然有动态加载那就必须有静态加载如果需要内核静态加载的话就必须把这些内核模块编译进内核里面我们学习内核编译的时候肯定还记得一个 [*]和[M]只要简单的配置一下就决定了你这个模块是作为一个单独的模块还是作为内核的一个部分。这个加载顺序我认为是非常有用的我之前做项目的时候我们同事有个需求需要A驱动必须要在B驱动之前加载而且尽可能早的加载这时候就用到了不同的优先级别的xx_init了。Linux 内核模块的依赖安装动态加载模块我好像在之前说过Linux 内核是宏内核内核模块之间是可以互相进行函数调用的这样的耦合性是比较高的所以呢Linux 内核肯定是存在依赖关系的就好像我像喝水然后我首先要找到一个杯子然后还要找到饮水机等等等等。按照上面的命令我们安装一个内核模块使用 insmod 命令如果这个模块依赖另一个模块呢那就再需要用 insmod 安装另一个模块这样看起来比较麻烦所以就有了这个命令 modprobemodprobe可以动态的安装内核模块如果发现有依赖关系就把需要依赖的内核模块也安装了。提醒一下初学者做这个的时候肯定会遇到一些意想不到的问题所以我也针对这些问题做了些总结希望看完文章的同学不会踩坑。Makefile 文件$(info $(PWD))$(info $(VER))ifneq ($(KERNELRELEASE),)obj-m : hello.oelsePWD : $(shell pwd)KVER : $(shell uname -r)KDIR : /lib/modules/$(KVER)/buildall: make -C /lib/modules/$(shell uname -r)/build M$(PWD) modulesinstall: make -C /lib/modules/$(shell uname -r)/build M$(PWD) modules_installclean: make -C /lib/modules/$(shell uname -r)/build M$(PWD) cleanendif源文件跟之前的HelloWorld一样就不贴出来了这里主要是多了一个 make install 编译用来安装内核模块。运行modprobe 之前要先运行depmod -a来更新依赖文件linuxubuntu:/lib/modules$ uname -r3.13.0-117-genericlinuxubuntu:/lib/modules$ cd /lib/modules/3.13.0-117-generic/linuxubuntu:/lib/modules/3.13.0-117-generic$ sudo depmod -alinuxubuntu:/lib/modules/3.13.0-117-generic$ cat modules.dep |tail -2kernel/lib/percpu_test.ko:extra/hello.ko:linuxubuntu:/lib/modules/3.13.0-117-generic$然后再执行 modprobe -v hello 后面跟的是 hello 而不是 hello.ko 如果你输入 hello.ko 的话会一直提示找不到该模块。linuxubuntu:/lib/modules/3.13.0-117-generic$ modprobe -v hellolinuxubuntu:/lib/modules/3.13.0-117-generic$ dmesg|tail -2[244664.594921] Goodbye, cruel world[245400.931435] Hello, worldlinuxubuntu:/lib/modules/3.13.0-117-generic$ sudo modprobe -r hellolinuxubuntu:/lib/modules/3.13.0-117-generic$ dmesg|tail -2[245400.931435] Hello, world[246484.772567] Goodbye, cruel worldlinuxubuntu:/lib/modules/3.13.0-117-generic$内核空间和用户空间上面这两个名字有点拗口也许可以这样说内核地址和用户地址内核态和用户态实际上就是通过地址来区分的。我觉得作为初学者把这个图片看明白然后再把旁边的英文自己看明白然后把他们吃透多看几次多理解几次多问几次这样之后再去看其他的内容。如果把Linux 看作是一个一个家庭一个家庭首先就需要有一个房子这就是硬件设备然后呢父母就是这个家庭的内核孩子们就是这个家庭用户空间进程父母需要为这个家庭支付房租水电买吃的喝的还有给孩子们煮饭洗衣服等等。那孩子们做什么事情呢孩子们就负责上学负责玩他们要吃了就找父母去拿想睡觉了就去睡觉总之他们不是这个家庭的基础如果内核空间没有了这个家庭肯定就瓦解了但是用户没有了内核还是会存在这就是我们所说的丁克家庭。我举个例子家庭里的小孩有一天想吃饭好了他把碗拿到桌子边上敲着碗说爸爸妈妈爸爸妈妈快给我吃饭然后呢CPU就需要跳转到内核态让父母去给小孩准备食物准备好了小孩就可以吃饭了这时候CPU又回到了用户态执行。有一天我很开心的在上网然后又一张图片印入了我的眼帘你应该猜到了就是下面这张图片这张图片非常完美的解释了用户空间和内核空间作者给我们举得例子就是系统调用系统调用了write的函数方法在调用的过程中CPU跳转到内核空间执行函数执行结束后又会跳转到用户空间执行。构建自己的 Linux 内核我们想学习 Linux 内核一个最重要的条件是会构建自己的 Linux 内核要不然就是只是一个空谈家我的书籍也是希望给大家一个这样动手的条件理论加实践相结合来渗透进入 Linux 内核我在前面已经给大家提供了一个安装好的 ubuntu 系统我们就用这个 ubuntu 开刀扒出我们自己的 Linux 内核。获取内核源码sudo apt-cache search linux-sourcesudo apt-get install linux-source-3.2.0第一句是查询 Linux 内核源码就是说我们现在运行的这个 ubuntu的系统内核源码是哪个版本的我们如果要开发自己的内核必须要有完整的内核源码我说的 ubuntu 指的是我上面给出链接的那个。第二句是安装 Linux 内核源码安装完后在 /usr/src 下面有有源码压缩包。ubuntu 下的 Linux 内核源码位置/usr/src我们安装了 ubuntu 下面就有了 linux 内核源码但是这个源码需要我们自己解压出来对应的应该是linuxubuntu:/usr/src$ lslinux-headers-3.13.0-117 linux-headers-3.13.0-32-genericlinux-headers-3.13.0-117-generic linux-source-3.2.0linux-headers-3.13.0-32 linux-source-3.2.0.tar.bz2linuxubuntu:/usr/src$所以就是这个压缩包了 linux-source-3.2.0.tar.bz2使用 make menuconfig 配置自己的内核我们有了自己的源码还需要一个 .config 来配置内核世界上的硬件设备千千万万我们就只有一个内核源码他们最大的区别就是配置文件了所以我们就需要一个配置文件这一步也很简单。使用 uname -a 查看内核版本号rootubuntu:/usr/src# uname -aLinux ubuntu 3.13.0-117-generic #164~precise1-Ubuntu SMP Mon Apr 10 16:16:25 UTC 2017 x86_64 x86_64 x86_64 GNU/Linuxrootubuntu:/usr/src#把内核版本号下面的 .config 复制到内核源码下面rootubuntu:/usr/src# cp linux-headers-3.13.0-117-generic/.config linux-source-3.2.0/linux-source-3.2.0/使用 make menuconfig 配置内核这一步不要做任何修改直接 exit 就可以了可以执行看看配置的界面爽一把是可以的。编译内核执行 make bzImage -jN 编译内核我截取了编译过程中的一些输出日志这里的 N 可以指定并行编译的线程但不限定是core的个数。linuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$sudo make bzImage -j6[sudo] password for linux: CHK include/linux/version.hmake[1]: Nothing to be done for relocs. CHK include/generated/utsrelease.h CALL scripts/checksyscalls.sh CHK include/generated/compile.h VDSOSYM arch/x86/vdso/vdso-syms.lds VDSOSYM arch/x86/vdso/vdso32-int80-syms.lds VDSOSYM arch/x86/vdso/vdso32-syscall-syms.lds VDSOSYM arch/x86/vdso/vdso32-sysenter-syms.lds VDSOSYM arch/x86/vdso/vdso32-syms.lds LD arch/x86/vdso/built-in.o LD arch/x86/built-in.o LD vmlinux.o MODPOST vmlinux.o GEN .version CHK include/generated/compile.h UPD include/generated/compile.h CC init/version.o LD init/built-in.o LD .tmp_vmlinux1 KSYM .tmp_kallsyms1.S AS .tmp_kallsyms1.o LD .tmp_vmlinux2 KSYM .tmp_kallsyms2.S AS .tmp_kallsyms2.o LD vmlinux SYSMAP System.map SYSMAP .tmp_System.map VOFFSET arch/x86/boot/voffset.h CC arch/x86/boot/version.o OBJCOPY arch/x86/boot/compressed/vmlinux.bin GZIP arch/x86/boot/compressed/vmlinux.bin.gz MKPIGGY arch/x86/boot/compressed/piggy.S AS arch/x86/boot/compressed/piggy.o LD arch/x86/boot/compressed/vmlinux ZOFFSET arch/x86/boot/zoffset.h OBJCOPY arch/x86/boot/vmlinux.bin AS arch/x86/boot/header.o LD arch/x86/boot/setup.elf OBJCOPY arch/x86/boot/setup.bin BUILD arch/x86/boot/bzImageSetup is 17052 bytes (padded to 17408 bytes).System is 4422 kBCRC e36fccb0Kernel: arch/x86/boot/bzImage is ready (#2)linuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$编译 Linux 内核模块我们执行 make modules - j4 编译内核模块我们前面说了Linux 内核下面都是按照模块化来编程的我们需要把这些模块编译出来然后再安装到现在运行的 ubuntu 系统上就完成了Linux 内核的安装。执行的时候有可能出现一些什么问题找到 error 相关的 Log 百度找一下答案就可以了如下是我执行的结果linuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$ sudo make modules -j6[sudo] password for linux: CHK include/linux/version.hmake[1]: Nothing to be done for relocs. CHK include/generated/utsrelease.h CALL scripts/checksyscalls.sh Building modules, stage 2. MODPOST 2801 modulesWARNING: modpost: Found 4 section mismatch(es).To see full details build your kernel with:make CONFIG_DEBUG_SECTION_MISMATCHylinuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$安装内核先安装内核模块使用这个骚命令make modules_install$sudo make modules_install INSTALL /lib/firmware/keyspan_pda/keyspan_pda.fw INSTALL /lib/firmware/keyspan_pda/xircom_pgs.fw INSTALL /lib/firmware/cpia2/stv0672_vp4.bin INSTALL /lib/firmware/yam/1200.bin INSTALL /lib/firmware/yam/9600.bin DEPMOD 3.2.79linuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$然后安装内核使用这个命令make installlinuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$ sudo make installsh /usr/src/linux-source-3.2.0/linux-source-3.2.0/arch/x86/boot/install.sh 3.2.79 arch/x86/boot/bzImage \ System.map /bootrun-parts: executing /etc/kernel/postinst.d/apt-auto-removal 3.2.79 /boot/vmlinuz-3.2.79run-parts: executing /etc/kernel/postinst.d/initramfs-tools 3.2.79 /boot/vmlinuz-3.2.79update-initramfs: Generating /boot/initrd.img-3.2.79run-parts: executing /etc/kernel/postinst.d/pm-utils 3.2.79 /boot/vmlinuz-3.2.79run-parts: executing /etc/kernel/postinst.d/update-notifier 3.2.79 /boot/vmlinuz-3.2.79run-parts: executing /etc/kernel/postinst.d/zz-update-grub 3.2.79 /boot/vmlinuz-3.2.79Generating grub.cfg ...Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.Found linux image: /boot/vmlinuz-3.13.0-117-genericFound initrd image: /boot/initrd.img-3.13.0-117-genericFound linux image: /boot/vmlinuz-3.13.0-32-genericFound initrd image: /boot/initrd.img-3.13.0-32-genericFound linux image: /boot/vmlinuz-3.2.79Found initrd image: /boot/initrd.img-3.2.79Found memtest86 image: /boot/memtest86.bindonelinuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$查看新安装的内核内核安装完成之后我们怎么判断新安装的内核是自己编译出来的呢这个操作非常关键很多码农写了代码烧录后发现执行的情况不如自己的预期也没有去查看有没有烧录成功就不断的修改代码排查这个是非常低级的错误。生成initrd.img文件使用命令update-initramfs。#cd /lib/modules/3.2.79rootubuntu:/lib/modules# update-initramfs -c -k 3.2.79update-initramfs: Generating /boot/initrd.img-3.2.79rootubuntu:/lib/modules#使用 update-grub2 更新新的内核这个命令会去查找当前存在的新内核然后用新的内核去安装引导。linuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$ sudo update-grub2Generating grub.cfg ...Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.Found linux image: /boot/vmlinuz-3.13.0-117-genericFound initrd image: /boot/initrd.img-3.13.0-117-genericFound linux image: /boot/vmlinuz-3.13.0-32-genericFound initrd image: /boot/initrd.img-3.13.0-32-genericFound linux image: /boot/vmlinuz-3.2.79Found initrd image: /boot/initrd.img-3.2.79Found memtest86 image: /boot/memtest86.bindonelinuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$查看重启前运行的内核版本linuxubuntu:/usr/src/linux-source-3.2.0/linux-source-3.2.0$ uname -aLinux ubuntu 3.13.0-117-generic #164~precise1-Ubuntu SMP Mon Apr 10 16:16:25 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux重启系统 shutdown -r now这时候重启你应该是看不到我们的内核版本的因为我们用的是 ubuntu 还有一些诡异的地方需要设置其中就需要设置的是 /etc/grub.d/00_headervim /etc/grub.d/00_header 修改stylecountdown为stylemenu,这样我们在开机的时候就可以选择我们系统里面的内核了要不然就一直是一个默认的内核。通过上面的骚操作之后我们再重启重启的过程中会提示我们选择内核版本我们记得别选原来的内核版本了选一个我们原来不认识的内核版本然后自信的按下回车键等待一下。这时候我们再来查看一下内核版本rootubuntu:~# uname -aLinux ubuntu 3.2.79 #4 SMP Wed Jun 12 19:33:11 PDT 2019 x86_64 x86_64 x86_64 GNU/Linuxrootubuntu:~#总结内核模块是基础是我们开发内核的基础但是又因为基础的东西难度不是非常大所以写的也比较轻松不过学习的时候不应该怠慢该敲代码的时候就敲代码该看书的时候就看看书有理解不了的就提下问题。扎实的基础就应该在学习的时候边动手边思考加深自己的印象。原创文章写起来非常不容易辛苦是在所难免的因为从后面开始没一个章节的篇幅就增加很多所以我会拆开来写比如一个章节我发几次。我的文章后面默认打开赞赏原因是我的原创名字和赞赏账号一样也是因为懒的原因这样我只需要点击一下就可以填好原创的作者打开赞赏不是想大家给我多少钱也是想得到读者们的肯定如果是学生还不如留着钱去追下妹子大学美好时光来之不易该浪荡的时候就不要浪费了但是觉得不错有帮助的可以给个转发支持一下感激不尽。下一章节会讲内核调试的一些方法包括GDB/proc文件系统调试还有加dump_stack等手段来调试等等推荐阅读第1章 Linux内核概述推荐一下我自己的知识星球1、首先非常感谢大家付费进入我的知识星球这是对我的肯定我也将竭尽全力帮助大家一起成长如果有任何问题请点击[提问]咨询我。如果有问题我解答不了的请放心我也会竭尽所能帮助你周立功是我的微信好友我也有认识一些天使投资人创业者在各大企业从事工作的很多专业性的朋友。 2、其次我想说人在成长过程中肯定是需要一个指引者的需要一个让你相信他的人很多时候我们也不知道前方对错我们很想得到一个背后的力量我觉得这个非常重要互相鼓励共同成长。 3、再次我觉得为了鼓励大家交流如果主动回答问题被采纳的话我会给予 5 元赞赏奖励赞赏的钱会到达你的知识星球钱包里面。邀请朋友进来的我会给予 20 元奖励鼓励形成一个互助的社群。