IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]记第一次写Linux Driver

    lincyang发表于 2014-08-24 02:14:36
    love 0
    2.6之后,驱动可以以模块的方式加载到内核中执行。我们在自己的机器上写driver要配合对应的内核树,
    以便和源代码树中的目标文件相连。怎么建立这个内核源码树呢?可以从官网下载相对应的内核源码,编译之。

    首先,看看我的内核版本。

    [linc@localhost ~]$ uname -r
    2.6.35.6-45.fc14.i686

    那么去官网www.kernel.org下载2.6.35.6的源码吧。
    在首页找不到你要的版本不要急,点击FTP的链接,找到你想要的版本吧。
    创建个目录,将源码解压在其中。(tar xfvz yourfile)
    编译的第一项是内核配置,我的系统的Fedora,出现了点问题:
    在源码目录下root执行make menuconfig,
      出现错误:

      *** Unable to find the ncurses libraries or the
      *** required header files.
      *** 'make menuconfig' requires the ncurses libraries.
      ***
      *** Install ncurses (ncurses-devel) and try again.
      ***
      make[1]: *** [scripts/kconfig/dochecklxdialog] 错误 1
      make: *** [menuconfig] 错误 

    解决办法:
    这是由于ncurses图形库导致的,下载它就好了。
      yum install ncurses
      yum install ncurses-devel
    这次继续执行,出现配置界面。按如下来操作:
    Loadable module support ---->
    选择Module versioning support(按Y就ok),这个功能可以让你使用其他版本的内核模块。


    接下来就可以编译了:
    还是此目录下,执行make。很长时间,根据机器配置而异。喝茶去吧。
    接着编译压缩形式的内核,make bzImage

    [root@localhost linux-2.6.35.6]# make bzImage
      CHK     include/linux/version.h
      CHK     include/generated/utsrelease.h
      CALL    scripts/checksyscalls.sh
      CHK     include/generated/compile.h
      CHK     include/linux/version.h
    make[2]: `scripts/unifdef' is up to date.
      TEST    posttest
    Succeed: decoded and checked 1249053 instructions
    Kernel: arch/x86/boot/bzImage is ready  (#2)
    继续编译模块并将它们安装到系统标准位置:
    make modules
    make modules_install
    完成后,/lib/modules/下多了个2.6.35.6目录。


    至此,我们的内核树算是建立完成。
    下面写个hello world来检验一下吧。

    #include <linux/init.h>
    #include <linux/module.h>
    MODULE_LICENSE("Dual BSD/GPL");
    
    static int hello_init(void)
    {
            printk(KERN_ALERT "Hello,world.i am linc.\n");
            return 0;
    }
    
    static void hello_exit(void)
    {
            printk(KERN_ALERT "Goodbye, i am linc.\n");
    
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    写个makefile来编译它:
    #如果已经定义KERNELRELEASE,说明是从内核构造系统调用的,
    #可利用其内建语句
    ifneq ($(KERNELRELEASE),)
    obj-m := test.o
    #否则是从命令行调用的,需要使用内核构造系统
    else
            KDIR := /lib/modules/2.6.35.6/build
    all:
            make -C $(KDIR) M=$(PWD) modules
    clean:
            rm -f *.ko *.o *.mod.o *.mod.c *.symvers
    endif

    编译:
    [root@localhost drivers]# make
    make -C /lib/modules/2.6.35.6/build M=/home/linc/linux/workspace/drivers modules
    make[1]: Entering directory `/usr/src/kernels/linux-2.6.35.6'
      CC [M]  /home/linc/linux/workspace/drivers/test.o
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /home/linc/linux/workspace/drivers/test.mod.o
      LD [M]  /home/linc/linux/workspace/drivers/test.ko
    make[1]: Leaving directory `/usr/src/kernels/linux-2.6.35.6'


    安装和卸载:

    insmod test.ko

    rmmod test.ko

    如果此时仍然遇到问题:

    [root@localhost drivers]# insmod test.ko 
    insmod: error inserting 'test.ko': -1 Invalid module format
    
    我们看一下具体详细信息:

    [root@localhost drivers]# dmesg | tail
    
    [  356.269653] test: version magic '2.6.35.6 SMP mod_unload modversions 686 ' should be '2.6.35.6-45.fc14.i686 SMP mod_unload 686 '
    
    说明ko的kernel版本与当前运行的版本仍然不一致。此时我尝试重新编译内核,结果仍然一样。就参考

    《Linux 2.6.x 内核模块加载错误 “Invalid module format” 解决办法》 将版本强制修改一下,让其保持一致。方法如下:

    找到utsrelease.h文件,在源码树下linux-2.6.35.6/include/generated。将版本号修改。重新编译,发现仍有错误:

    [ 1149.371180] test: version magic '2.6.35.6-45.fc14.i686 SMP mod_unload modversions 686 ' should be '2.6.35.6-45.fc14.i686 SMP mod_unload 686 '
    对比上述信息,发现我的VERMAGIC_STRING多了一个词“modversions”,好吧,为了一直,索性将linux-2.6.35.6/include/linux/vermagic.h文件中对VERMAGIC_STRING做

    下调整。去掉MODULE_VERMAGIC_MODVERSIONS信息。如下:

    #include <generated/utsrelease.h>
    #include <linux/module.h>
    
    /* Simply sanity version stamp for modules. */
    #ifdef CONFIG_SMP
    #define MODULE_VERMAGIC_SMP "SMP "
    #else
    #define MODULE_VERMAGIC_SMP ""
    #endif
    #ifdef CONFIG_PREEMPT
    #define MODULE_VERMAGIC_PREEMPT "preempt "
    #else
    #define MODULE_VERMAGIC_PREEMPT ""
    #endif
    #ifdef CONFIG_MODULE_UNLOAD
    #define MODULE_VERMAGIC_MODULE_UNLOAD "mod_unload "
    #else
    #define MODULE_VERMAGIC_MODULE_UNLOAD ""
    #endif
    #ifdef CONFIG_MODVERSIONS
    #define MODULE_VERMAGIC_MODVERSIONS "modversions "
    #else
    #define MODULE_VERMAGIC_MODVERSIONS ""
    #endif
    #ifndef MODULE_ARCH_VERMAGIC
    #define MODULE_ARCH_VERMAGIC ""
    #endif
    
    #define VERMAGIC_STRING                                                 \
            UTS_RELEASE " "                                                 \
            MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT                     \
            MODULE_VERMAGIC_MODULE_UNLOAD MODULE_ARCH_VERMAGIC
    

    至此,重新编译模块。一切正常。

    [root@localhost drivers]# insmod test.ko
    [root@localhost drivers]# rmmod test.ko
    [root@localhost drivers]# dmesg | tail
    [ 1586.792291] Hello,world.i am linc.
    [ 1696.536632] Goodbye, i am linc.


    参考:
    http://blog.csdn.net/xuxinyl/article/details/6996433
    http://www.cnblogs.com/Jezze/archive/2011/12/23/2299871.html

    《Linux设备驱动程序》第二章





沪ICP备19023445号-2号
友情链接