zoukankan      html  css  js  c++  java
  • Linux 2.6内核的编译步骤及模块的动态加载

    本文是基于2.6的内核,也建议各位可以先看一下《Linux内核设计与实现(第二版)》作为一个基础知识的铺垫。当然,从实践角度来看,只要按着以下的步骤去做也应该可以实现成功编译内核及加载模块。

    个人用的Linux版本为:Debian GNU/Linux,内核版本为:2.6.20-1-686.

    第一步,下载Linux内核的源代码,即构建LDD3(Linux Device Drivers 3rd)上面所说的内核树。
    如过安装的Linux系统中已经自带了源代码的话,应该在/usr/src目录下。如果该目录为空的话,则需要自己手动下载源代码。下载代码的方法和链接很多,也可以在CU上通过http://download.chinaunix.net/search/?key=&;q=kernel&frmid=53去下载。不过,下载的内核版本最好和所运行的Linux系统的内核版本一致。当然,也可以比Linux系统内核的版本低,但高的话应该不行(个人尚未实践)。
    Debian下可以很方便的通过Debian源下载:
    首先查找一下可下载的内核源代码:
    # apt-cache search linux-source
    其中显示的有:linux-source-2.6.20,没有和我的内核版本完全匹配,不过也没关系,直接下载就可以了:
    # apt-get install linux-source-2.6.20
    下载完成后,安装在/usr/src下,文件名为:linux-source-2.6.20.tar.bz2,是一个压缩包,解压缩既可以得到整个内核的源代码:
    # tar jxvf linux-source-2.6.20.tar.bz2
    解压后生成一个新的目录/usr/src/linux--source-2.6.20,所有的源代码都在该目录下。
    注:该目录会因内核版本的不同而不同,各位动手实践的朋友只需知道自己的源代码所在的具体位置即可。

    第二步:配置及编译内核。
    进入/usr/src/linux--source-2.6.20目录下,可以看到Makefile文件,它包含了整个内核树编译信息。该文件最上面四行是关于内核版本的信息。对于整个Makefile可以不用做修改,采用默认的就可以了。
    一般情况下,需要先用命令诸如"make menuconfig", "make xconfig"或者"make oldcofig"对内核进行配置,这几个都是对内核进行配置的命令,只是它们运行的环境不一样,执行一下这几个命令中的任何一个即可对内核进行配置:
    make menuconfig是基于界面的内核配置方法,make xconfig应该是基于QT库的,还有make gcofig也是基于图形的配置方法,应该是需要GTK的环境,make oldcofig就是对内核树原有的.config文件进行配置一下即可。
    其实内核的配置部分,主要是保证内核启动模块可动态加载的配置,默认配置里面应该已经包含了这样的内容,因此,我用的是make oldconfig.

    内核的详细配置请见另外一位网友的帖子,这里给出链接:
    http://linux.chinaunix.net/bbs/viewthread.php?tid=885597&extra=page%3D1%26amp%3Bfilter%3Ddigest

    在内核源码的目录下执行:
    # make
    # make bzImage
    其中,第一个make也可以不执行,直接make bzImage。这个过程可能要持续一个小时左右,因此是对整个内核重新编译了。执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux, 其属性为-rwxr-xr-x。
    然后执行:
    # make modules
    # make modules_install
    对内核的所有模块进行编译和安装。
    执行结束之后,会在/lib/modules下生成新的目录/lib/modules/2.6.20/。 在随后的编译模块文件时,要用到这个路径下的build目录。至此,内核编译完成。可以重启一下系统。

    第三步:编写模块文件及Makefile
    以LDD3上的hello.c为例:
    //hello.c
    #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文件的内容为:

    obj-m := hello.o
    KERNELDIR := /lib/modules/2.6.20/build
    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

    其中,hello.c和Makefile文件应该位于同一个目录下,可以放在/home下,我的两个文件都位于/home/david/.

    第四步:编译和装载模块

    在文件所处的目录下,执行:
    debian:/home/david # make

    然后查看该目录下有哪些文件生成:
    debian:/home/david # ls -l
    总计 28
    drwxr-xr-x 2 david david 4096 2007-02-07 17:49 Desktop
    -rw-r--r-- 1 david david  462 2007-07-20 13:42 hello.c
    -rw-r--r-- 1 root  root  2432 2007-07-20 13:55 hello.ko
    -rw-r--r-- 1 root  root   607 2007-07-20 13:55 hello.mod.c
    -rw-r--r-- 1 root  root  1968 2007-07-20 13:55 hello.mod.o
    -rw-r--r-- 1 root  root  1140 2007-07-20 13:55 hello.o
    -rw-r--r-- 1 david david  267 2007-07-20 13:48 Makefile
    -rw-r--r-- 1 root  root     0 2007-07-05 14:11 Module.symvers

    可见,已经生成模块文件hello.ko.
    然后,就可以加载该模块:
    debian:/home/david # insmod hello.ko

    查看模块是否加载进内核:
    debian:/home/david # lsmod
    Module                  Size  Used by
    hello                   1344  0
    nfs                   219468  0
    nfsd                  202224  17
    ...                 ...

    其中Module名为hello的即为我们所加载的模块.

    卸载模块:

    debian:/home/david # rmmod hello

    同样可以通过lsmod来查看该模块是否被卸载.

    这里有两个问题,其一就是printk()输出的问题.LDD3上也说,在加载和卸载模块的时候都会有信息输出在屏幕上,如果通过终端仿真器(,则在屏幕上看不到任何输出.我同时在虚拟机和和物理机都运行了该模块,均未看到有"Hello, world"(加载模块时printk的输出)或"Goodby, cruel world"(卸载模块时printk的输出). 这个不知道是我操作系统发行版的原因还是系统配置的问题,请了解这个问题的朋友指点一下.


    其二,书上讲到如果屏幕上看不到信息,可能输出在某个日志文件里面了,并说可能在/var/log/messages文件中.并且看到网上很多网友也说是输出到这个文件里面.我不知道有没有发现输出在其他日志文件里的,不过我的这个信息输出在/var/log/syslog里面.在加载和卸载完该模块后,执行命令:
    debian:/home/david # cat /var/log/syslog | grep world
    可以看到有两行内容.当然,也可以不用grep world, 应该会出现在最后两行.

    Jul 20 14:15:29 localhost kernel: Hello, world
    Jul 20 14:15:34 localhost kernel: Goodbye, cruel world

    这就是printk应该输出的信息.

    这里有另外一个方法,可以实现printk的信息输出在屏幕上,即更改printk输出的优先级.例子中的优先级为:KERN_ALERT,优先级为<1>,如果将优先级改为KERN_EMERG即<0>,则可以看到屏幕的输出信息.
    修改的方法只是修改一下hello.c中两句printk()的内容,修改后的hello.c如下:

    #include <linux/init.h>
    #include <linux/module.h>
    MODULE_LICENSE("Dual BSD/GPL");

    static int hello_init(void)
    {
        printk(KERN_EMERG "Hello, world\n");  /*改动部分*/
        return 0;
    }

    static void hello_exit(void)
    {
        printk(KERN_EMERG"Goodbye, cruel world\n"); /*改动部分*/
    }

    module_init(hello_init);
    module_exit(hello_exit);

    同样的方法编译生成模块,再次用insmod和rmmod,则在屏幕上看到的输出信息为:

    debian:/home/david# insmod hello.ko
    debian:/home/david#
    Message from syslogd@localhost at Fri Jul 20 14:27:32 2007 ...
    localhost kernel: Hello, world

    debian:/home/david# rmmod hello
    debian:/home/david#
    Message from syslogd@localhost at Fri Jul 20 14:27:42 2007 ...
    localhost kernel: Goodbye, cruel world

    debian:/home/david
    但是,是否能够将printk()的优先级改为KERN_EMERG值得商榷.因为在Linux Kernel Development中,对该优先级的描述为: An emergency condition; the system is probably dead.

    以上就是整个2.6内核编译步骤以及模块动态加载的方法.理解和解释有误的地方,也请各位浏览本文的朋友指点,也希望能和对内核和驱动感兴趣的朋友交流.
     

    在Linux 2.6内核下编译可以加载的内核模块

    By:
     吴垠
     
    Date:
     2007-05-18
     
    Email:
     lazy_fox#msn.com
     
    Homepage:
     http://blog.csdn.net/wooin
     
    Link:
     http://blog.csdn.net/wooin/archive/2007/05/21/1619141.aspx
     
    版权信息:
     该文章版权由Wu Yin所有。可在非商业目的下任意传播和复制。
    对于商业目的下对本文的任何行为需经作者同意。
    联系方式:lazy_fox#msn.com
     


    --------------------------------------------------------------------------------

    1.
     在旧的版本下(如linux 2.4)linux内核模块的编译只需要有内核的头文件就行了,就可以通过和编译其他程序一样的方法编译成filename.o文件,这个.o文件是直接 可以加载道内核中的,加载之后就可以用了。然而在2.6下就截然不同了,在linux 2.6下内核的编译要有系统内核树的支持,下面介绍一下这个“内核树”是如何建立的。
     
     
     
     
    2.
     本文的工作环境是Fedora Core 5,用“uname -r”查看内核版本是:2.6.15-1.2054_FC5
    Fedora Core 5 与旧版本不同,不包含 kernel-source 软件包,我是网上下载的rpm包,地址是:

    下面的工作都是用root用户执行的。
     
     
     
     
    3.
     安装内核源码包:

    # rpm –Uvh kernel-2.6.15-1.2054_FC5.src.rpm
     

    这个命令将 RPM 内容写到路径
    /usr/src/redhat/SOURSE

    /usr/src/redhat/SPECS
     
    4.
     build源码包:

    # cd /usr/src/redhat/SPECS
    # rpmbuild -bp --target i686 kernel-2.6.spec
     

    这个命令将会把内核源码树放到 目录
    /usr/src/redhat/BUILD/kernel-2.6.15/kernel-2.6.15.686
     
    5.
     配置内核:
    Fedora Core 附带的内核配置文件在 configs/ 目录。
    例如,i686 SMP 配置文件被命名为
    configs/kernel-version-i686-smp.config。
    使用下列命令来将需要的配置文件复制到合适的位置,用来编译:

    # cd /usr/src/redhat/BUILD/kernel-2.6.15/linux-2.6.15.i686
    # cp configs/kernel-version-i686-smp.config .config
     

    您也可以在 /lib/modules/version/build/.config 这个位置找到与您当前的内核匹配的 .config 文件。

    注意:
    您的内核必须已经启用这些选项进行了编译(用make menuconfig调出内核配置菜单):
    Loadable module support --->
    [*] Enable loadable module support
    [*] Module unloading
    [ ] Module versioning support (EXPERIMENTAL)
    [*] Automatic kernel module loading
     
    6.
     稍微更改一下Makefile:
    每个内核的名字都包含了它的版本号,这也是 uname -r 命令显示的值。内核Makefile 的前四行定义了内核的名字。为了保护官方的内核不被破坏,Makefile
    经过了修改,以生成一个与运行中的内核不同的名字。在一个模块插入运行中的内核前,这个模块必须针对运行中的内核进行编译。为此,您必须编辑内核的
    Makefile。
    例如,如果 uname -r 返回字符串 2.6.15-1.2054_FC5,就将 EXTRAVERSION 定义从:
    EXTRAVERSION = -prep
    修改为:
    EXTRAVERSION = -1.2054_FC5
    也就是最后一个连字符后面的所有内容。
     
    7.
     编译内核:
    跟普遍的编译方法一样了:

    # make bzImage        编译内核
    # make modules        编译模块
    # make modules_install  安装编译
     

     
    8.
     完成“内核树”的安装:
    目录“/usr/src/redhat/BUILD/kernel-2.6.15/kernel-2.6.15.686/”中就是所谓的“内核代码树”
    但是“/lib/modules/2.6.15-1.2054_FC5/build”是个符号链接,也指向这个目录,所以这里也可以叫做“内核代码树”
     
    9.
     编写内核模块源文件:

    // hello.c

    #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:

    # Makefile

    obj-m:=hello.o
    KDIR:=/lib/modules/2.6.15-1.2054_FC5/build
    PWD:=$(shell pwd)

    default:
        $(MAKE) -C $(KDIR) M=$(PWD) modules
     

     
    10.
     执行make命令进行编译就行了, 执行完毕后,会生成几个文件:
    hello.ko
    hello.mod.c
    hello.mod.o
    hello.o
    运行命令:

    # insmod hello.ko
     

    应该可以看到返回的信息:Hello, world
    然后再运行命令:

    # rmmod hello
     

    应该可以看到返回的信息:Goodbye, cruel world

    如果没看到,就是输出到系统的日志文件中去了,可以查看文件:
    /var/log/messages
    应该有信息的输出。

  • 相关阅读:
    爱牛网站
    阿里云文档
    Linux下iptables屏蔽IP和端口号
    oracle 启动监听报错TNS-12547: TNS:lost contact
    http://www.opensymphony.com/
    jmx
    联想笔记本装win7
    包解析
    js为字符串编码
    JAVA版本号微信公众账号开源项目版本号公布-jeewx1.0(捷微)
  • 原文地址:https://www.cnblogs.com/avril/p/1692050.html
Copyright © 2011-2022 走看看