编程环境搭建:
因为ubuntu 12.04的内核版本已经是3.x,而目前一些讲解内核驱动的书都是2.6.x。
嵌入式开发的版本一般都是基于3.14移植的,因为嵌入式是跑在开发板上的,所以开发驱动没有问题。但是教材的例子一般都是基于PC机的2.6.x版本,虽然内核内部接口相对稳定,但是我也不太清楚。至于低版本的内核驱动是否直接运行在高版本的Ubuntu上,我也不是太了解这里面的对应关系。为了排除无关的干扰,决定虚拟机安装个Ubuntu 10.04 32位,它对应的内核版本是2.6.32-21,这样一来,编写的驱动应该就可以直接在PC上快速测试了。
由于10.04已经停止更新,为了下载软件包,所以要做些修改,具体看这篇文章Ubuntu 无法找到更新源的问题。
- 更新源
sudo sed -i -e ‘s/archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g‘ /etc/apt/sources.list
sudo apt-get update
sudo apt-get upgrade 为了避免出错,就不更新系统软件了,也不知道是不是这样,不既然没出问题,那么对系统就能不碰就不碰吧
- 把常用软件vim/git/ctags/cscope/装一下
- 安装编译工具
在构造和编译内核模块之前,应该具备了正确版本的编译器、模块工具和其它必要的工具,内核文档目录中的Documentation/Changes文件列出了需要的工具版本;在开始构造模块之前,应该需要查看该文件并确保已安装了正确的工具,比如:
sudo apt-get install build-essential kernel-package libncurses5-dev
说明:kernel-package是Debian提供的一个编译Linux内核的一个工具集,安装kernel-package 会同时安装上build-essential、libncurses-dev、linux-source等一系列工具。通过apt-cache depends kernel-package可以查看该软件包的依赖关系:
libncurses5这个软件包在使用menuconfig配置内核的时候会用到。但缺省情况下,apt-get并不安装推荐和建议的软件包。如果你没有修改缺省配置,则需要额外的安装libncurses-dev等工具的操作。
- 终端字体设置一下,解除ScreenSaver
- mkdir kernel code
- 拷贝内核压缩包到~/kernel
- 乌班图对应内核版本的介绍:
5.04 2.6.10
6.06 LTS 2.6.15
8.04 LTS 2.6.24
10.04 LTS 2.6.32
12.04 LTS 3.2
14.04 LTS 3.13
?
?
处理内核源码:
- uname -r 查看内核版本,显示2.6.32
- 使用apt-cache search linux-source命令可以查看可用的源码包
- 因为ldd3用的是linux-2.6.10,所以先下载个这个版本先用着吧,顺便把2.6.32页下载下来,kernel下载链接,不知道2.6.10驱动程序跑在2.6.32版本上会不会出问题,不过接口和数据结构的改动应该不大,先用用看吧
- 或者直接下载 sudo apt-get install linux-source-2.6.32 但是我不知道这个下载下来的是不是linux原版还发型版,因为书上建议使用原版内核而非经过修改的发行版内核
- sudo cp linux-2.6.10.tar.xz /usr/src
cd /usr/src/
sudo tar xvf linux-2.6.10.tar.xz 超级用户才能完全解压
因为版本问题,不知道有没有用,顺便把linux-2.6.32.tar.xz也解压了吧。
- 上面的几个步骤是实验性的,所以中途最好多拍几个快照,防止虚拟机崩溃。
?
?
?
?
?
?
正确配置和构造内核树:
ldd3上提到了构造内核树,不过我已经糊涂了,不知道我下载的版本、开发的驱动和Ubuntu内核版本这三者之间到底是个什么关系。
Linux驱动学习笔记1:创建Linux内核树 这篇博客对这个讲的比较清楚,具体看连接。
另外还参考的连接先列举如下
- LDD3构造内核树(on ubuntu) 这篇操作步骤不错,没有太多说明
- 够建内核树 这篇是debian的,不过作者好像也没说明,不过幸好debian这方面和Ubuntu差不多,只是软件包有差异
- Ubuntu内核源码树的构建与安装 这篇对编译讲的很详细
?
?
操作步骤上面链接文章都说的很清楚了,待会列下,这里主要理清一下几个概念:
- 下载的版本:linux版本号主要分为4部分,比如2.6.10-5,短线前面的是主要的版本号,比如Ubuntu10.04采用的2.6.32内核,Ubuntu5.04采用的2.6.10内核,也就是不同版本的Ubuntu会采用不同版本的内核。
- Ubuntu其实可以看做类似Android的操作系统,是以linux内核为基础的文件系统,所以重新替换内核版本,也就是保持版本号前三部分相同,第4部分可以不同,不太清楚如果前三部分的版本号不同可不可以。系统启动后,Ubuntu是通过内核挂载上去的。
- 原版内核和发行版内核:书上讲发行版内核的API可能经过修改,所以最好还是使用原版的内核,既然Ubuntu的内核可以替换,那么我们就可以替换成Linux官网上下载的原版内核,前提就是创建内核树。
- 内核树:我的理解就是内核的源码经过编译的状态就是内核树。内核安装主要有两部分,一个是非模块部分,一个是模块部分,这个模块应该是通用的可以直接安装到Ubuntu上,非模块部分就是一个镜像用于启动。
- 下面的步骤主要就是替换Ubuntu的内核为对应版本的原版内核,目前看来应该不能替换成2.6.10了,不过没试过,不折腾了。
?
?
下面是根据上面几篇文章整理的编译步骤和错误的编译步骤(针对x86架构开发,ARM架构这里不说)
- 导入正确的内核配置文件
先说标准流程,然后再说用的简便流程:
标准步骤:
- sudo cp /usr/src/linux-headers-2.6.32-38-generic-pae/.config .config 导入系统自带源码的配置文件来,使用cp命令拷贝到当前源码树的跟目录下
- make menuconfig 使用make menuconfig来对内核配置文件进行配置
选择load an Alternate kernel configuration选项加载本地的.config配置文件,然后再选择save an Alternate kernel configuration再保存退出,并退出配置环境。这样我们的内核配置就按照原系统的配置选项配置完成。
简便步骤:
- sudo make oldconfig 配置内核编译上下文
- 内核编译
- sudo make -j4 执行完成后会在当前目录下面生成一个文件vmlinux.o,如果对内核编译命令不熟悉,可以在内核源码树跟目录下执行make help查看内核提供的编译选项
- 内核核心编译
- make clean 先清除暂存档,这里不做
- make vmlinux 未经压缩的核心,这里不做
- make modules 仅核心模块,这里不做
- make bzImage 经压缩过的核心,这里只做这个
- make all 执行上述三个操作,这里不做
- 说明:常见的在 /boot/ 底下的内核文件,都是经过压缩过的,因此,上述操作中中比较常用的是 modules 和 bzImage 这两个。bzImage 可以制作出压缩过后的内核
- 内核模块编译和安装,编译完 bzImage后在编译modules
- make modules 仅仅编译核心模块
- make modules_install 模块安装到系统,新内核的模块可以在/lib/modules目录下
- 安装完modules再安装压缩过的内核核心,内核文件安装在/boot目录下
sudo make install
------------------------------------------------------------------------------------------
以下是几个看了博文之后的错误步骤可以忽略,接着是步骤 6.
错误步骤:sudo mkinitramfs -o /boot/initrd.img.2.6.32-27 /lib/modules/2.6.32.27
?
?
错误步骤:sudo gedit /boot/grub/grub.cfg
替换32-21-generic为32.27
?
?
错误步骤:
?
?
?
?
------------------------------------------------------------------------------------------
- 生成内核对应的RAMDISK文件,这个估计是用于启动的
sudo update-initramfs -c -k 2.6.32.27
- 更新grub配置文件
sudo update-grub
说明:参考/boot/grub/grub.cfg文件,可以看到新内核的配置已经加到配置文件中。为避免可能出现的无法开机的情况,Grub.cfg的配置缺省值不要设置为新的内核。
- 测试新安装的内核版本
重启后,开机也没有出现选项
- 重新安装vmware tools因为内核改变了(vim插件也重新安装下),因为之前安装过一次,重复安装可能会耗时较长些。重启后,保存快照。
- 测试安装驱动模块,以下例子拷贝自文章:LDD3构造内核树(on ubuntu)
-----------------------------------------------------------
在某一目录下创建2个文件:hello.c和Makefile
hello.c内容如下:
/*
* $Id: hello.c,v 1.5 2011/06/21 03:32:21 eric $
*/
#include <linux/init.h>
#include <linux/module.h>
MODULE_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);
Makefile内容如下:
# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.
ifeq ($(KERNELRELEASE),)
?
?# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it‘s elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build # 内核树build目录的位置
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
?
?modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
?
?modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
?
?clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
# .PHONY修饰的目标就是只有规则没有依赖
.PHONY: modules modules_install clean
?
?else
# called from kernel build system: just declare what our modules are
obj-m := hello.o # 模块目标文件
endif
需要注意的是Makefile的格式要正确。
?
?开始编译模块:
make
不出现错误的话,用ls -al查看该目录,会产生如下文件:
hello.c hello.mod.c hello.o modules.order
hello.ko hello.mod.o Makefile Module.symvers
?
?现在我们就可以将编译好的模块hello加载到内核中去了
sudo insmod ./hello.ko //这个命令把hello.ko加载到内核,模块装载触发hello.c的init()方法
sudo lsmod|grep hello //lsmod 这个命令可以查看当前所有的驱动模块,结果应该显示hello 680 0
sudo rmmod hello //这个命令是把hello这个模块移除掉
?
?程序的输出结果可以在
/var/log/syslog文件中查看
Hello,World
Goodbye,cruel world
这是程序的输出。
-----------------------------------------------------------
?
?
有用的连接:
- Linux kernel device driver programming [closed] 关于ldd3的一些说明
- API changes in the 2.6 kernel series 关于ldd3内核版本往后的API改动
- 上条连接的最后提到了 ldd3-examples-3.x 作者实现了机会所有最新版本的代码迁移,所以估计如果对内核驱动框架比较熟悉,那么不同内核版本差异带来的驱动差异并不是很大,所以学习驱动不要纠结内核版本
?
?