当前位置:Linux教程 - Linux - 在Linux可加载内核模块中探秘(2)

在Linux可加载内核模块中探秘(2)



         第一部分:基础知识
    第二章:LKM的概念及hello,world程序
    作者:CoolBoy
    既然是介绍LKM,那么我们还是先来了解LKM是个什么东东。

      LKM英文是:Loadable Kernel Modules,翻译过来就是“可加载内核模块程序”,这是一种区别于一般应用程序的系统级程序,它主要用于扩展linux的内核功能。那么,为什么我们不在系统核心中直接添加功能,非得使用LKM呢?

      ——因为LKM可以动态地加载到内存中,无须重新编译内核。由于LKM具有这样的特点,所以它经常被用于一些设备的驱动程序,例如声卡,网卡等等。讲到这里,大家是不是回忆起MSDOS中的TSR和Windows中的VxD呢?是的,这三者其实都是差不多的东西,它们都是常驻内存程序,并且可以捕获任何一个系统中断,功能十分强大。

      现在我们就来看一个最简单的LKM是怎样构成的。

      首先,和一般应用程序不同的是,LKM没有main()函数。相反,在LKM中下面两个函数则是必须的:

      int init_module(void) /*这是初始化函数*/
      {
      ...
      }

      void cleanup_module(void) /*这是关闭函数*/
      {
      ...
      }

      那么,最简单的LKM程序就可以直接由这两个函数构成,我们还是照老规矩,打印出一个hello,world出来,如下例:

      #define MODULE
      #include

      int init_module(void)
      {
      printk(\"<1>Hello World\\n\");
      return 0;
      }

      void cleanup_module(void)
      {
      printk(\"<1>Bye, Bye\");
      }

      用字处理程序编辑上段程序,名字就叫hello.c。然后编译,命令为

      # gcc -c -O3 hello.c

      这样我们就得到了一个hello.o的文件,然后我们就可以把这个文件加载到内存中去,使用的命令是insmod(注意,通常只有root才有insmod的权限,如果你用一般用户名登陆系统,是执行不了insmod命令的):

      # insmod hello.o

      怎么样?hello,world显示到屏幕上了吗?若显示出来,就表明hello.o这个lkm已经初始化了,没显示出来就说明你的操作有问题,不关我的事啊。:)

      那么怎么才能确定这个LKM真正加载到内存中去了呢?可以使用lsmod命令查看:

      # lsmod Module Pages Used by hello 1 0 lsmod命令从/proc/modules文件中读取信息,以显示当前哪个程序已经加载到内存中去了。\Pages\显示了内存中的信息状态(表示这个程序占据了多少页),而\Used by\域告诉我们这个程序在系统中被引用的频度(引用计数)。当计数为0的时候,我们唯一能做的事情就是把这个模块卸载掉。

      这个程序的引用计数恰好为0,那么把它卸载了吧。卸载命令是:

      # rmmod hello

      此时屏幕上会显示byebye,表明这个LKM已经成功地卸载了。

      好了,把戏变完了,接下来我们来分析一下这个LKM程序的构成和原理。

      整个程序由两个函数构成,一个是初始化函数init_module,在LKM被初始化的时候会去执行这个函数,整个函数也执行一些初始化工作,比如打印一些信息,初始化一些变量什么的。另一个就是关闭函数cleanup_module,在LKM被卸载的时候,此函数做些收尾的清洁工作。

      那么,命令insmod hello.o会做些什么事情呢?

      首先,它会确定加载的模块是hello.o;然后去呼叫系统调用create_module,为hello.o模块重新分配内存(稍后我们解释什么是系统调用);然后再去调用系统调用get_kernel_syms,通过读取“内核符号表”找到一个没有分配的内核符号,为hello.o分配一个内核符号(内核符号表也在后面要讲到);最后调用init_module初始化LKM。

      LKM初始化完成之后,就要做点什么事情,我们这里只是打印一句话,不过有点特别的是,我们并没有使用printf()而是使用的printk()——很奇怪吧?这是因为我们现在是在系统的内核空间进行编程,在内核空间和用户空间编程是有些不一样的,手头上仅有一些很少量的函数可以使用(以后我们会将这些函数列出来)。这些函数能完成的功能很少,所以稍后我们还将学习怎样利用系统的缺陷在核心空间来调用用户空间的函数。

      OK,这个程序就分析到这里,在lKM的世界里,我们现在前进了一小步,很小很小的一步,今天课程的用意只是让大家了解一个LKM的构成,而有些陌生的概念我们将陆续解释下来,希望大家能继续关注本栏目,下次我们讲“系统调用”。



    发布人:Crystal 来自:Linux专区