你的位置:首页 > 操作系统

[操作系统]ldd3


编程环境搭建:

因为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 作者实现了机会所有最新版本的代码迁移,所以估计如果对内核驱动框架比较熟悉,那么不同内核版本差异带来的驱动差异并不是很大,所以学习驱动不要纠结内核版本