linux内核驱动入门程序

自:http://blog.sina.com.cn/s/blog_5ae509900100bfft.html

装载驱动:

#include <linux/init.h>
    定义的宏
    __init __initdata        //模块始初化的函数或数据标记,标记为初始化的项目会在初始化结束后丢弃;
    __exit __exitdata        //模块清除阶段的函数或数据标记,标记为清除化项目会在内核示被配置为可卸载模块的情况下被丢弃。
                    //内核将以上相应的目标对象放置在可执行文件的特殊ELF段中而让这些标记起做
    内核源码中的宏定义:
    #define __init        __section(.init.text) __cold       
    #define __initdata    __section(.init.data)
    #define __exit          __section(.exit.text) __exitused __cold
    #define __exitdata    __section(.exit.data)
                    //用来指定函数或数据所属区段,内核模块装载时从指定区段读取。如没有在指区段没有找到相应入口点,则装载失败。
    定义的宏
    module_init ( __init func );    //模块装载入口

    module_exit ( __exit func );    //模块卸载出口    如果未定义,则表明该驱动无法卸载

内核源码中的宏定义:
   
    #define module_init(initfn)                    \
    static inline initcall_t __inittest(void)        \
    { return initfn; }                    \
    int init_module(void) __attribute__((alias(#initfn)));

   
    #define module_exit(exitfn)                    \
    static inline exitcall_t __exittest(void)        \
    { return exitfn; }                    \
    void cleanup_module(void) __attribute__((alias(#exitfn)));

说明:__attribute__是gcc专有的,用来说明函数的属性
weak 和 alias 分别是两个属性。weak 使得 main 这个符号在目标文件中作为 weak symbol 而不是 global symbol。用 nm 命令查看编译 dummy.c 生成的目标文件可用看到 main 是一个 weaksymbol,它前面的标记是 W。
而 alias 则使cleanup_module是#exitfn 的一个别名,#exitfncleanup_module必须在同一个编译单元中定义,否则会编译出错。


#include <linux/module.h>        //设备驱动必须包含的头文件,包含可装载模块需要的大量符号和函数的定义
    常用宏:
    MODULE_LICENSE ( "Daul BSD/GPL" );//为模块指定代码所使用的许可证,有许多许可证类型。这里就不列举了。
                    //如果模块没有显式地标记内核可识别的许可证,则会被假定为专有的。内核加载这种模块就会抱怨内核被“污染”。
    MODULE_AUTHOR ( AUTOHR );    //模述模块作者
    MODULE_DESCRIPTION ( description );//对模块的描述信息
    MODULE_VERSION ( version );    //指定模块版本
    MODULE_DEVICE_TABLE ( table_information );//模块设备表信息
    MODULE_ALIAS ( alternate_information );    //指模块的相关文档信息

    EXPORT_SYMBOL ( symbol_name );
    EXPORT_SYMBOL_GPL ( symbol_name );
                    //当需要模块层叠技术时(如:msdos文件系统依赖于由fat模块导出的符号),应该了解导出内核符号的用法。
                    //EXPORT_SYMBOL_GPL使得要导出的模块只能被GPL许可证下的模块使用。符号必须在模块文件的全局部分导出,不能在函数中导出。
                    //因为上面这两上宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。该变量将在模块可执行文件的特殊部分(ELF段)保存。
                    //装载时,内核通过这个段来寻找模块导出的变量。
                    //更多宏定义请查看该头文件注释   
#include <linux/moduleparam.h>
    常用宏:
    module_param ( variable, type, perm );
                    //用来创建模块参数的宏,用户可在装载模块时调整这些参数的值。
                    //type类型:
                    //    bool charp(字符指针) int invbool(反转逻辑) long short ushort uint ulong.
    module_param_array ( name, type, num, perm );
                    //以上perm的值在 <linux/stat.h>中定义
                    //如果perm被设置为0,就不会有对应的sysfs入口点,否则模块参数会在/sys/module中出现。
                    //S_IRUGO,任一用户均可以读取:记忆方法:S=status(状态)IR=ReadOnly(只读) UGO=user,group,other(用户,用户组,其它)
                    //S_IWUSE,只有root可以修改该参数
   
#include <linux/kernel.h>
    常用函数:
    static inline int __cold printk(const char *s, ...);//printk函数原型定义,用法跟标准C库printf类似。
                    //定义此函数的原因是:内核模块装载后在内核空间执行,不能依赖C库函数。printk不到持浮点数(float类型)。

#include <linux/errno.h>
    //在linux内核中,错误编译是定义在该头文件中的负整数。如下是部分定义:
   #define ERESTARTSYS    512
    #define ERESTARTNOINTR    513
    #define ERESTARTNOHAND    514   
    #define ENOIOCTLCMD    515   
    #define ERESTART_RESTARTBLOCK 516

   
    #define EBADHANDLE    521   
    #define ENOTSYNC    522   
    #define EBADCOOKIE    523   
    #define ENOTSUPP    524   
    #define ETOOSMALL    525   
    #define ESERVERFAULT    526   
    #define EBADTYPE    527   
    #define EJUKEBOX    528   
    #define EIOCBQUEUED    529   
    #define EIOCBRETRY    530   

内核装裁与卸载演示代码,尽可能将上面的演示代码使用起来:
    //在测试代码之前得重新编译内核,构建自己的代码树:

<section_one.c>
    #include <linux/init.h>     
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/moduleparam.h>
    #include <linux/stat.h>       
    MODULE_LICENSE ( "Daul BSD/GPL" );

    static char* p_param = "hello word !";
    static int number = 1;

    static int section_begin ( void )
    {
        printk ( KERN_INFO "section_one begin,param:%s,number:%d", p_param, number );
        return 0;
    }

    static void section_end ( void )
    {
        printk ( KERN_ALERT "section_one end !" );
    }

    module_init ( section_begin );
    module_exit ( section_end );

    module_param ( p_param, charp, S_IRUGO );
    module_param ( number, int, S_IRUGO );

    EXPORT_SYMBOL_GPL ( section_begin );
    EXPORT_SYMBOL_GPL ( section_end );

    MODULE_AUTHOR ( "Leehom" );
    MODULE_DESCRIPTION ( "A simple demo !" );
    MODULE_VERSION ( "Version 1.0" );

<Makefile>
    ifneq ($(KERNELRELEASE),)
        obj-m:=section_one.o
    else
        KERN_DIR:=/usr/src/linux
        CURRENT_DIR:=$(shell pwd)
    default:
        $(MAKE) -C $(KERN_DIR) M=$(CURRENT_DIR)
    endif
   
    编译代码:
    [leehom@leehom section_one]$ make
    make -C /usr/src/linux M=/home/leehom/Public/section_one
    make[1]: Entering directory `/usr/src/linux-2.6.26'
      CC [M]  /home/leehom/Public/section_one/section_one.o
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /home/leehom/Public/section_one/section_one.mod.o
      LD [M]  /home/leehom/Public/section_one/section_one.ko
    make[1]: Leaving directory `/usr/src/linux-2.6.26'
    查看编译结果,内核模块文件section_one.ko已经构建好了。
    [leehom@leehom section_one]$ ls
    built-in.o  Module.markers  section_one.c   section_one.mod.c
    Makefile    modules.order   section_one.c~  section_one.mod.o
    Makefile~   Module.symvers  section_one.ko  section_one.o
    加载驱动模块:
    [leehom@leehom section_one]$ sudo insmod section_one.ko
    查看加载结果:
    [leehom@leehom section_one]$ dmesg | tail -n 1
    section_one begin,param:hello word !,number:1
    查看模块信息:
    [leehom@leehom section_one]$ modinfo section_one.ko
    filename:       section_one.ko
    version:        Version 1.0
    description:    A simple demo !
    author:         Leehom
    license:        Daul BSD/GPL
    srcversion:     1E48889243E5036D4DD16AA
    depends:       
    vermagic:       2.6.26 SMP mod_unload 686 4KSTACKS
    parm:           p_param:charp
    parm:           number:int
    卸载模块驱动并查看结果:
    [leehom@leehom section_one]$ sudo rmmod section_one.ko
    [leehom@leehom section_one]$ dmesg | tail -n 1
    section_one begin,param:hello word !,number:1<1>section_one end !
    加载模块驱动并传递参数:
    [leehom@leehom section_one]$ sudo insmod section_one.ko p_param='test' number=2
    [leehom@leehom section_one]$ dmesg | tail -n 1
    section_one begin,param:hello word !,number:1<1>section_one end !<6>section_one begin,param:test,number:2
    查看导出的符号:
    [leehom@leehom /]$ sudo cat -n /proc/kallsyms | grep section_one
    [sudo] password for leehom:
     32525    00000000 a section_one.c    [section_one]
     32526    d097d000 t section_end    [section_one]
     32527    d097d010 t section_begin    [section_one]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值