Chinaunix首页 | 论坛 | 博客
  • 博客访问: 61395
  • 博文数量: 23
  • 博客积分: 270
  • 博客等级: 二等列兵
  • 技术积分: 215
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-31 11:01
文章分类

全部博文(23)

文章存档

2013年(21)

2012年(2)

我的朋友

分类: LINUX

2013-01-01 20:23:38

     对于我这样子的新手,想要搞清楚kernel module的实现机制着实不容易啊
     先说一下kernel module大概是怎么一回事。说到 linux kernel大家都感觉非常牛逼的样子,可是我们却看不见摸不着,唯一能看见的是目录中有一个二进制文件
vmlinuz,一般是在boot目录下,名字叫vmlinuz。这个就是最小版的linux kernel,非常的小,一是kernel本来就小,二是这还是个压缩文件。就是这个文件会被解压缩然后装载进内存,然后它实现内核的功能。可是我们想要扩展和维护内核时怎么办,作为传统的单内核,那就只能把整个内核源码拿来再加代码,再重新编译出一个vmlinuz出来,替换掉,这非常麻烦。所以linux就从传统的单内核给改进成了kernel module机制。所谓的kernel module机制用白话说就是,除了这
个主内核,我们在硬盘上其他地方又放着其他的文件(
一般是在/lib/modules目录下,
扩展名一般为.o或者.ko),这些文件用某种机制给加载进内核,而且是加载进内核空间,具和主内核一样的权限作为内核的一部分实现其内核功能.用白话文说出来的kernel module
似乎和微内核的实现几乎一样,只是这里有个主内核
vmlinuzl罢了。
    但是再来讲kernel module的具体实现,就知道这并非微内核。是用什么手段把module
加载进内核空间作为内核的一部分的呢?内核又是怎么样来维护各个模块让其成为一
整体工作的呢?
    内核提供了几个系统调用,linux都会有个指令insmod来实现装载模块,insmod便是
集成了几个系统调用。insmod大致用
sys_create_module()完成此功能

asmlinkage unsigned long

sys_create_module(const char *name_user, size_t size)

{

         char *name;

         long namelen, error;

         struct module *mod;

         unsigned long flags;

 

         if (!capable(CAP_SYS_MODULE))

                 return -EPERM;

         lock_kernel();

         if ((namelen = get_mod_name(name_user, &name)) < 0) {

                error = namelen;

                 goto err0;

         }

        if (size < sizeof(struct module)+namelen) {

                 error = -EINVAL;

                goto err1;

         }

        if (find_module(name) != NULL) {

                error = -EEXIST;

                goto err1;

         }

        if ((mod = (struct module *)module_map(size)) == NULL) {

                error = -ENOMEM;
                 
goto err1;

         }

 

         memset(mod, 0, sizeof(*mod));

         mod->size_of_struct = sizeof(*mod);

         mod->name = (char *)(mod + 1);

         mod->size = size;

        memcpy((char*)(mod+1), name, namelen+1);

 

         put_mod_name(name);

 

         spin_lock_irqsave(&modlist_lock, flags);

         mod->next = module_list;

         module_list = mod;      /* link it in */

        spin_unlock_irqrestore(&modlist_lock, flags);

 

         error = (long) mod;

         goto err0;

err1:

         put_mod_name(name);

err0:

         unlock_kernel();

         return error;

}


下面对该函数中的主要语句给予解释。

·      capable(CAP_SYS_MODULE)检查当前进程是否有创建模块的特权。

·      参数size表示模块的大小,它等于module结构的大小加上模块名的大小,再加上

      模块映像的大小,显然,size不能小于后两项之

·      get_mod_name()函数获得模块名的长度。

·      find_module()函数检查是否存在同名的模块,因为模块名是模块的唯一标识。

·      调用module_map()分配空间,对i386来说,就是调用vmalloc()函数从内核空

      间的非连续区分配空间。

·      memset()将分配给module结构的空间全部填充为0,也就是说,把通过module_map()所分配空间的开头部分给了module结构;然后(module+1

     表示从mod所指的地址加上一个module结构的大小,在此处放上模块的名字;最

      后,剩余的空间给模块映像。

·      新建module结构只填充了三个值,其余值有待于从用户空间传递过来。

·      put_mod_name()释放局部变量name所占的空间。

·      将新创建的模块结构链入module_list链表的首部。
struct module数据结构原型如下(包含有大量信息,所以用红色):

struct module

 {

        unsigned long size_of_struct;   /* 模块结构的大小,即sizeof(module) */

        struct module *next;  * 指向下一个模块 */

        const char *name;   *模块名,最长为64个字符*/

        unsigned long size;  *以页为单位的模块大小*/

 

        union

           {

                 atomic_t usecount; *使用计数,对其增减是原子操作*/

                 long pad;

         } uc;                      /* Needs to keep its size - so says rth */

 

        unsigned long flags;            /* 模块的标志 */

 

        unsigned nsyms;        /* 模块中符号的个数 */

        unsigned ndeps;       /* 模块依赖的个数 */

        struct module_symbol *syms; /* 指向模块的符号表,表的大小为nsyms */

 

            struct module_ref deps; *指向模块引用的数组,大小为ndeps */

        struct module_ref *refs;

        int (*init)(void);      /* 指向模块的init_module()函数 */

        void (*cleanup)(void);  /* 指向模块的cleanup_module()函数 */

        const struct exception_table_entry *ex_table_start;

        const struct exception_table_entry *ex_table_end;

 

 

/* 以下域是在以上基本域的基础上的一种扩展,因此是可选的。可以调用

mod_member_present()函数来检查以下域的存在与否。  */

        const struct module_persist *persist_start; *尚未定义*/

        const struct module_persist *persist_end;

        int (*can_unload)(void);

        int runsize                      *尚未使用*/

        const char *kallsyms_start;     /*用于内核调试的所有符号 */

        const char *kallsyms_end;

                const char *archdata_start;     /* 与体系结构相关的特定数据*/
               
const char *archdata_end;

        const char *kernel_data;        /*保留 */

};

上面给出了struct module原型,也写出来sys_create_module()函数,但

sys_create_module函数仅仅在内核为模块开辟了一块空间,但是模块的

代码根本没有拷贝过来。实际上,模块的真正安装工作及其他的一些初始化工作由

 sys_init_module()函数完成,

该函数的原型为:

        asmlinkage long sys_init_module(const char *name_user, struct
        module *mod_user)

       此函数有点长,但大概用途就是步步为营曲折的把内核中开辟出来的空间用正确
        数据填满。以上模块安装已经不多结束了,但
insmod命令还集成了调用
      init_module的功能,其实就是模块初始化函数。所以应该是初始化后才能算是安装
完成。


    以上讲了kernel module是如何实现的,但本文最后还要提一下,模块是如何被用
的,其实module 的使用和kernel的使用没有区别。
说白了,就是你在模块里写了一些函数,然后把这些函数注册给Linux内核,怎么使用,就看具体应用程序了。至于如何在内核
注册的,大概是通过内核符号表。这个内核符号应该是内核中一些函数名,变量变的符号和地址的映射表。通过这种地址的映射,让各个模块和主内核的函数及变量能被其他的模块甚至是应用程序(做成系统调用)找到,从而调用。

    具体的内核各模块及主内核的各种调用关系有多复杂大家都懂的,所以具体细节需要以后再深入学习再写。
  

阅读(1095) | 评论(2) | 转发(1) |
给主人留下些什么吧!~~

lmnos2013-01-03 19:42:08

望博主 继续更新