[转载至](https://blog.csdn.net/lucien_zhou/article/details/71274447)
64位Ubuntu14.04系统下加载内核模块hello world
- 感谢这篇博客,让折腾了好久的我实现了内核模块加载
- 为什么要构造源码树?
我们做 Linux 开发一般是在PC机上编译好,下到板子上去运行,板子上的 Linux 内核和 PC 机上的 Linux 版本很多时候都是不一样的,比如:pc机上的是 Linux 2.6,板子上的系统是 Linux 3.1,这个时候就要在pc本地编译环境中下载 Linux 3.1 的内核,用它编译出驱动模块,从而在板子上加载,不然会因为内核版本不一致而出错。如果只是在PC上运行,内核模块不是用在板子上去,是不用下载内核源码树和编译内核源码的。
- 驱动程序和用户程序不一样,它是作为一个模块连接到内核模块来运行的,运行在内核空间里面。所以要运行我们自己构造的模块,需要自己的系统已经配置好内核树,然后把目标模块和内核树连接起来运行!
如何查看自己的 ubuntu 系统中是否已经有了内核源码树了?直接去查看 /lib/modules/ 目录下是否存在 build 目录,如果存在,则表示我们自己的系统已经拥有内核源码树了(我的本地编译环境 ubuntu14.04 是拥有内核源码树的),如下所示。
![](https://i-blog.csdnimg.cn/blog_migrate/182f0159fcc48121ecc72b0a3f9d668b.png)
- 但为了熟悉整个的过程,我们也重新下载对应自己的编译环境的“内核源码”并对其进行编译,构造内核源码树。首先要查看自己使用的系统“内核版本 ”:
我用的是 Ubuntu 14.04
![](https://i-blog.csdnimg.cn/blog_migrate/93ef237d77f9dd9cd872c94f62216477.png)
![](https://i-blog.csdnimg.cn/blog_migrate/89c4e3f07c87da6c1c051a4ed24484b9.png)
查看自己 ubuntu 系统的版本:有两种方法,第一种,cat /etc/issue ; 第二种,lsb_release -a 命令
![](https://i-blog.csdnimg.cn/blog_migrate/3d0fe851f870e30fab0f5e24612a1804.png)
- 内核源码树构建步骤:
(1)安装编译内核所需的软件(要用 make menuconfig 命令的话得安装,用 make oldconfig 的话就不用安装,不论采用哪种生成配置文件的方式,都在系统中安装下面的软件)
sudo apt-get install build-essential kernel-package libncurses5-dev fakeroot
(2)下载内核源码,执行上面的命令 sudo apt-cache search Linux-source ,系统会给出适合我们所安装系统的“内核源码版本 ”
![](https://i-blog.csdnimg.cn/blog_migrate/bcccd9f8da07f7b1861c1bef23111a82.png)
(3)使用命令 sudo apt-get install linux-source-3.13.0 下载安装该“内核源码 ”
(4)解压内核源码包:进入 /usr/src/ 目录,能找到 linux-source-3.13.0.tar.bz2 文件,用解压命令 sudo tar xjvf linux-source-3.13.0.tar.bz2 解压。
要注意上面目录下的 linux-source-3.13.0.tar.bz2 文件并不是软件压缩包文件,而是一个软链接,不能直接使用 tar 进行解压。
![](https://i-blog.csdnimg.cn/blog_migrate/bddd7f944bdf4b372199e76c505908c6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f261d6ca397a03a0b6cac5f3f7746464.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d73436b30aad4b2d0b112236833b41be.png)
进入 linux-source-3.13.0 文件夹,发现真正的 tar 源码压缩包,解压。
![](https://i-blog.csdnimg.cn/blog_migrate/cfe8012740a58037136ab3b0dbec239a.png)
(5)对“内核源码”进行编译前的配置从下面截图可以看出,在解压后,该文件夹下并没有 .config 文件。
![](https://i-blog.csdnimg.cn/blog_migrate/d47788145899c7f803c5dcade87655e1.png)
进入源码解压后的文件夹里,为了方便,可以直接在使用 make menuconfig 命令打开可视化配置窗口,然后什么都不要修改的退出。
![](https://i-blog.csdnimg.cn/blog_migrate/c7c17def551e8128fdbd7235c19c8aae.png)
在运行完 sudo make menuconfig 命令后并不会像上面那样执行完毕,生成一个 .config 文件,而是会先打开下面的可视化配置,毕竟我们的命令是 menuconfig(菜单配置)。
![](https://i-blog.csdnimg.cn/blog_migrate/31a8a3f3ec8f2c24399efeead8f70071.png)
什么都不要操作,直接 Esc 退出,选择 Save,从而可以在该文件夹下生成一个 .config 编译配置文件。
![](https://i-blog.csdnimg.cn/blog_migrate/5a4d62accb2802509e69add470af1eeb.png)
.config 配置文件如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/214c39521fc8e79cdd14f82194f7812b.png)
(6)使用 sudo make 命令进行内核编译,当然不只是下面截图那样简单,整个内核编译过程是以小时计算的,编译吧,linux君,我玩会再来看你。
![](https://i-blog.csdnimg.cn/blog_migrate/195d2c5a9587dd22b5935bbeb9f75a1f.png)
编译完,该文件夹下就生成了一个名为 vmlinux 的文件了,但网上的教程大都说要执行完下面的 sudo make bzImage 命令才会生成 vmlinux 文件。
![](https://i-blog.csdnimg.cn/blog_migrate/6403d424c31480b719989bbd8512bb13.png)
![](https://i-blog.csdnimg.cn/blog_migrate/231dbd89e657f35c06ec99a7400b9b3f.png)
(7)sudo make zImage 命令在我的本地编译环境下是不能使用的。
![](https://i-blog.csdnimg.cn/blog_migrate/169fffabbb4a779c18840dcdce60ec2b.png)
(8)使用命令 sudo make bzImage 后,下面两个截图对其过程的中间过程省略。
![](https://i-blog.csdnimg.cn/blog_migrate/f414455ff7072fe8a29f1c78b698c05a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fdfd7a174644706b4ba8d4f6b310da6b.png)
(9)使用 sudo make modules 命令编译模块
(10)使用 sudo make modules_install 命令安装模块,执行完后,会在 /lib/modules 目录下生成一个新的目录 /lib/modules/3.13.0/ 。至此,内核就编译完成了。
![](https://i-blog.csdnimg.cn/blog_migrate/eec3b2b563ed98d86957bf7a6ad76a1e.png)
(11)由于我的本地 ubuntu 编译环境中的内核版本就是为 3.13.0-32-generic,所以 /lib/modules/3.13.0-24-generic 本身就存在,所以其实我们这里的 sudo make modules 和 sudo make modules_install 这两步是不需要执行的。
![](https://i-blog.csdnimg.cn/blog_migrate/d370270655bd3bb827efb4edf6f68679.png)
(12)但我们这里还是执行了 sudo make modules 和 sudo make modules_install 命令,看上面的截图也就能判断出,其实,在我们执行完 sudo make moduels 和 sudo make modules_install 命令后编译生成了目录 /lib/modules/3.13.11-ckt39
- 现在可以编写 hello world 程序并加载该内核模块了
下面是 hello.c 文件:
![](https://i-blog.csdnimg.cn/blog_migrate/dbefd4ddcead78bd2eabd0700993b346.png)
- 下面是是 Makefile (必须为这个文件名,连 makefile 都是不能直接使用 make 命令来进行编译的)文件,解释下该文件,加深理解。
(1)obj-m 的意思是将后面的东东编译为“内核模块”,相对应的还有 obj-y 是编译进内核,obj-n 是不编译。
(2)KERNELDIR 是 Makefile 文件中的一个内容标识,这里标识内核源码目录,目录中包含了内核驱动模块所需要的各种头文件及依赖。
(3)-C 表示让 make 命令进入指定的目录,这里即 KERNELDIR ,是内核源代码目录,调用该目录顶层下的 Makefile,目标为 modules。
(4)M=$(PWD) 选项是让该 Makefile 在构造 modules 目标之前返回到模块源代码目录,并在当前目录下生成 obj-m 指定的 xxx.o 目标模块。
(5)三种赋值等号的区别 ?= ,=,:=
“:=”表示:它的右边如果为变量,那么该变量在这条语句之前就要定义好,而不能在使用这条语句之后定义的变量
“=”表示:当它右边如果变量时,这个变量可以在这条语句之前或者之后使用
“?=”表示:当它左边的变量在这条语句之前没有定义过,则执行本条语句,如果已经定义了,则什么都不做。
所以下面截图中的 Makefile 文件中三种赋值都是可以的。
(6)modules 和 modules_install 是 make 需要执行的命令
(7)整理到这里????请继续先阅读该博客????xxxx
![](https://i-blog.csdnimg.cn/blog_migrate/cf7e0bf7e2c5a99d3efc6399581125fb.png)
(8)make 定义了很多默认变量,像常用的命令或者是命令选项之类的,什么 CC ,什么 CFLAGS,$(MAKE) 就是预设的 make 这个命令的名称(或者是路径)。make -p 可以查看所有预定义的变量的当前值。看下面的截图的最后一行就可以看出, $(MAKE) 变量其实就是 make 命令。
![](https://i-blog.csdnimg.cn/blog_migrate/f8434c211517fd2ad5471fd956357f38.png)
- 对驱动程序进行编译的时候,发现 /lib/modules/ 目录下的两个库文件夹都是能用的(在前面说过,其实安装好的 ubuntu本地编译环境是拥有内核源码树的)。
(1)在写好 Makefile 文件的驱动程序目录下,可以直接使用用户权限执行 make 命令编译。
(2)CC是编译
(3)LD是链接
![](https://i-blog.csdnimg.cn/blog_migrate/fa7cbaeb8b8a3840c54f9f54fec0503c.png)
- 编译后,本来文件夹内只有 hello.c 和 Makefie 文件,当执行完 sudo make 命令后会出现:
![](https://i-blog.csdnimg.cn/blog_migrate/5423cef81587bcc4a9cb1c72ee88a6b0.png)
====================
参考以下博客: