第二章 构造和运行模块

来源:互联网 发布:华安股票交易软件 编辑:程序博客网 时间:2024/05/01 08:08

  《1》 module_init ( xxx )  模块在装载到内核时调用,module_exit( xxx ) 模块在被移除时调用。

  《2》 内核API中看到具有两个下划线的前缀(__)的函数名称,具有这种名称的函数通常是接口的底层组件,应谨慎使用。

  《3》 在构造内核模块之前,应确保具备了正确版本的编译器,模块工具和其他必要的工具。内核文档目录中的Documentation/Changes文件列出了需要的工具版本。

  《4》 modprobe也用来将模块装入到内核中。它和insmod的区别在于,它会考虑要装载是否引用了一些当前内核不存在的符号。如果有这类引用,modprobe会在当前模块搜索路径中查找定义了这些符号的其他模块。如果modprobe找到了这些模块(即要装载的模块所依赖的模块),它会同时将这些模块装载到内核。在加载模块失败的时候,可以通过查看/var/log/messages或者系统配置使用的文件。

  《5》 针对内核版本的查看,在linux/version.h中相关的定义,这个头文件自动包含于linux/module.h,并定义了下面的宏:


 UTS_RELEASE
           宏UTS_RELEASE扩展至为一个描述内核版本的字符串,例如 “2.6.10”。

 LINUX_VERSION_CODE

           宏LINUX_VERSION_CODE扩展为内核版本的二进制表示,版本发行号中的每一部分对应一个字节。例如,2.6.10对应的LINUX_VERSION_CODE是132618(即0x02060a)。使用这个宏,我们很容易确定正在使用的内核版本.

KERNEL_VERSION(major , minor , release)

           宏KERNEL_VERSION以组成版本号的三部分(三个整数)为参数,创建整数的版本号。例如, KERNEL_VERSION(2 ,6 ,10)扩展为132618.这个宏在我们需要将当前版本和一个已知的检查点比较时非常有用。

   《6》 如果一个每模块需要向其他模块导出符号,则应该使用下面的宏。EXPORT_SYMBOL(name) ; EXPORT_SYMBOL_GPL(name) ; _GPL版本使得要导出的模块只能被GPL许可证下的模块使用。

   《7》 所有模块代码都需要包含这两行代码:

           #include <linux/module.h>  包含有可装载模块需要的大量符号和函数定义。

           #include <linux/init.h>          包含init.h的目的是指定初始化和清除函数。

           大部分模块还包括moduleparam.h头文件,这样可以在装载模块时向模块传递参数。

   《8》  虽然不是严格要求模块代码使用许可证,但还是需要考虑使用的,例如:MODULE_LECENSE(" GPL") ; 内核可以识别的许可证有“GPL”(任一版本的GNU通用公关许可证),“GPL v2”(GPL版本2), “GPL and additional rights ”(GPL及附加权利) “,” Dual BSD/GPL (GPL双重许可证)“,”Dual MPL/GPL(MPL/GPL双重许可证)“ 以及 ”Proprietary(专有)的“,没有显示标记为上述可识别许可证,则会被假定是专有的,而内核转载这种内核就会被"污染"。

   《9》 模块中可以包含的其他描述性定义包括MODULE_AUTHOR(描述模块作者),MODULE_DESCRIPTION(用来说明模块用途的简短描述),MODULE_VERSION(代码修订号;有关版本字符串的创建惯例,参考<linux/module.h>中的注释),MODULE_ALIAS(模块的别名)以及MODULE_DEVICE_TABLE(告诉用户空间模块所支持的设备)。

   《10》模块初始化函数通常如下:

              /*__=2个_*/

               static int  __init initialization_function(void)   _ _ init 给内核暗示,在模块被装载之后,模块装载器就会将初始化函数扔掉,释放内存。 

               {

                     /* 初始化代码*/

               }

              module_init(initialization_function) ;

              static void  __exit   cleanup_function(void)

              {

                    /* 这里是清除代码*/

             }

             module_exit(cleanup_function);                          //帮助内核找到模块的清除函数是必须的

            清除函数没有返回值, _ _exit 修饰词标记该代码仅用于模块卸载, 这样的模块只能在卸载或者关闭时被调用,其他的任何用法都是错误的。

           《11》当我们在内核中注册设施时,要时刻铭记注册可能会失败,当注册失败的时候,我们要撤销之前的任何注册工作。

                       示例:

                    int  __init  my_init_function (void)

                     {

                          int err;

                           err = register_this(ptr1 , "skull");

                           if(err)   goto  fail_this;

                            err = register_that(ptr2 , "skull");

                           if(err)  goto fail_that;

                           err = register_those(ptr3 , "skull");

                          if(err)  goto fail_those;


                         return 0 ; /*success*/


                         fail_those: unregister_that(ptr2 , "skull");

                         fail_that :  unregister_this(ptr1 , "skull");

                         fail_this :  return err;

                    }

                    不支持goto的使用的人,就是记录任何成功注册的措施,然后再出错的时候调用模块清除函数。

                    显然,清除函数需要撤销初始化函数所注册的所有设施,并且在习惯上(但不是必须的),以相反于注册的顺序撤销设施:

                   void   __exit   my_cleanup_function(void)

                  {

                          unregister_those(ptr3 , “skull”);

                          unregister_that(ptr2 , “skull”);

                          unregister_this(ptr1, skull);

                          return ;

                 }

           《12》可以再insmod 或 modprobe 命令加载模块的时候传人参数,参数必须使用module_param宏来声明,这个宏在moduleparam.h中定义。这个宏必须是放在任何函数之外, 通常是源文件的头部。

                 主体的部分就是:  static char  *whom = “world” ;

                                                    static int  howmany = 1 ;

                                                    module_param (howmany , int , S_IRUGO) ;

                                                    module_param (whom , charp , S_IRUGO) ;

                   使用的命令比如: insmod  hellop  howmany=10  whom=“Mom” 

                    内核所能支持的模块参数类型如下: 

                     bool 

                     invbool   /* 布尔值(取true 或 false ), 关联的变量应该是 int 型。 invbool 类型反转其值, 也就是说, true 值变成 false ,而false 变成 true 。*/

                     charp     /*字符指针值。内核会为用户提供的字符串分配内存, 并相应设置指针。*/

                     int   long   short   uint   ulong    ushort   具有不同长度的基本整数值。以u开头的类型用于无符号值。

                     模块装载器也支持数组参数, 要声明数组参数需要使用下面的宏:

                     module_param_array(name , type , num , perm);   /*name  数组名称  type 数组类型  num 为用户提供的值得个数  perm 常见的访问许可值。具体的细节请参阅moduleparam.h 文件*/  module_param 中最后一个参数是访问许可值, 我们应使用<linux / stat . h> 中存在的定义。这个值用来控制谁能够访问sysfs中对模块参数的表述。如果perm被设置成0 ,就不会有对应的sysfs入口项 ; 否则参数会在 /sys/module 中出现,并设置为给定的访问许可。如果对参数使用S_IRUGO , 则任何人均可读取该参数, 但不能修改; S_IRUGO | S_IWUSR  允许root用户修改该参数。 



原创粉丝点击