内核编程入门:简析 linux modules

内核编程入门:简析 linux modules

 

 

 

 

 

       这是篇Linux编程入门级的文章。

 

1 什么是module

              什么是module?打一个简单的比方,要是把Linux内核比成一个书架,书架上所有的书的内容就是就是内核所提供的功能,那我们每把一本书放进书架就相当于像内核插入一个module。要是插入一本有内容独立的书,那么内核就相当于插入一个功能独立的module。整个书架的书描述的内容增加了,整个内核支持的东西也增加了。拿走一本书就相当于卸载一个module,书架里的所有的书总体描述的内容减少了,内核支持的东西也减少了。当然书里面可以没有任何内容,module也是如此,可以不提供任何功能,甚至module可以轻易的屏蔽其他内核里面的功能,做rootkit时常用。

              这样,我们总结出,可以动态的(不用重新编译内核的情况下)修改内核的功能的程序,称为模块(module)

2 模块的写法

              2.4的内核模块的编写和2.6有点点区别,这里顺便也说明一下。

a  内核模块的写法

                     内核module与一般的C应用程序从代码比较明显的区别就是入口点函数不一样(对于内核模块运行与内核空间,应用程序运行于用户空间我们这里这篇文章先不进行讨论)。下面我先比较一下一个普通的C程序中打印helloworl 2.6版本的内核中打印helloworld.o有什么不同。

                     打印Helloworld普通C应用程序源码如下:

 

#include <stdio.h>

/*

+—————————————————————————————+

|    红毛羽的博客: http:/blog.csdn.net/hongmy525                               |

|      Power by hongmy525          E-Mail:hongmy525@163.com              |

+—————————————————————————————+

*/

int main(int argc, char * argv[] )

{

       ptintf("    Hello,World!/n");

ptintf("    MyBlog   http:/blog.csdn.net/hongmy525/n");

       return 0;

}

 

 

内核的版本为2.6的情景下打印helloworld的模块的源码如下:


  
  
   
    
  
  

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

/*

+—————————————————————————————+

|    红毛羽的博客: http:/blog.csdn.net/hongmy525                               |

|      Power by hongmy525          E-Mail:hongmy525@163.com              |

+—————————————————————————————+

*/

                    

static int My_init(void)

{

        printk(KERN_ALERT "Hello,World!/n");

        return 0;

}

 

 

static void My_exit(void)

{

        printk(KERN_ALERT "This is my first module!/n GoodBye Now!/n");

ptintk(KERN_ALERT "MyBlog   http:/blog.csdn.net/hongmy525/n");

 

}

module_init(My_init);

module_exit(My_exit);

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Hongmy525");

 

这是内核版本为2.6的内核中内核模块的写法,当你把它编译好装载的时候由宏module_init调用函数My_init, My_init 这个函数是你的模块的入口,在这里My_init的功能是打印“Hello,World!”!当你卸载模块的时候由module_exit宏调用My_exit函数,这是函数的出口。退出时打印"This is my first module!/n GoodBye Now!"。这里使用的是printk而不是printf,因为在内核中不能直接这样调用C库的函数。头文件module.hkernel.h是内核模块必须的投文件,MODULE_LICENSE("GPL")表示说这个模块遵循GPL规则,MODULE_AUTHOR是指模块的作者。

以前2.4的内核模块中,

 

#include <linux/module.h>

/*

+—————————————————————————————+

|    红毛羽的博客: http:/blog.csdn.net/hongmy525                               |

|      Power by hongmy525          E-Mail:hongmy525@163.com              |

+—————————————————————————————+

*/

                    

static int init_module(void)

{

        printk( "Hello,World!/n");

        return 0;

}

 

 

static int cleanup_module(void)

{

        printk("This is my first module!/n GoodBye Now!/n");

ptintk("MyBlog   http:/blog.csdn.net/hongmy525/n");

              return 0;

}

 

很显然,主要的入口函数不同,这里2.4是用init_module作伪入口函数,cleanup_module作伪出口函数,这两个函数是2.4内核的模块中必须的。打印的信息在/var/log/message中,可以用命令”dmesg”来查看模块打印的信息。

                    

 

b  编译

                     编译前的准备:在写好了模块以后,想要编译它,首先得安装内核源码,简单的办法下载源码到/usr/src/目录中,解压,进入那个目录编译一遍内核就好,编译内核的文章在网上也很多,可以找找,不过最好找版本基本对应的,下载源码编译的时候使用内核默认的.config文件相对就会安全一些了。不然编译的时候没有办法找到所需的源代码,自然就编译不了。若你在当前内核下编译的module,拷贝到其他内核中运行,也有可能运行不了。

                     在编译应用程序时我们可以用命令(在linux环境下):gcc –o helloworld helloworld.c就能把源码用gcc编译成二进制文件,但是2.6版本的内核编译内核模块得使用的是makefile,关于makefile这里也先不详细的介绍,以后可能以后会介绍一下。这里给出一个编译上述2.6版本内核内核模块的makefile

 

obj-m   := helloworld.o

 

KERNELDIR := /lib/modules/$(shell uname -r)/build

PWD       := $(shell pwd)

 

all:

       $(MAKE) -C $(KERNELDIR) M=$(PWD)

clean:

       rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

 

Makefilehelloworld.c放在同一个目录下,在shell下执行”make”命令就能把helloworld.c编译成helloworld.ko了。如果你的源码文件是“XXX.c,你要把XXX.c编译成XXX.ko,那么把makefile里面的这句话“obj-m   := helloworld.o”里面的helloworld.o改成XXX.o就可以了。还有一点需要说明一下,在2.4的内核中所有的模块的后缀都是.o,2.6的内核中内核模块的后缀是.ko,普通模块的后缀是.o

(注意makefile里面源码的空白处是不能用空格健上网空格来填补的必须用”Tab”键来填补空格)2.6中的内核模块只能用makefile来编译(这里有点模糊,可能还有其他办法,没查:),而编译模块在2.4版本的内核中可以用命令来编译,也可以用makefile来实现。

 

给出一个2.4版本的内核Makefile:

 

CC=gcc

CFLAGS = -O2 -DMODULE -D__KERNEL__ -Wall /

         -I/usr/src/linux-2.4/include

 

helloworld.o: helloworld.c

       $(CC) $(CFLAGS) -c helloworld.c

install:

       /sbin/insmod helloworld.o

remove:

       /sbin/rmmod helloworld

clean:

       rm -f helloworld.o

      

这个Makefile其实很简单,和2.6大同小异,我就不详细的解释了。

安装模块: make install

卸载模块: make remove

也可以用make clean来删除模块。

 

2.4内核编译模块的命令是:

gcc –o -O2 -DMODULE -D__KERNEL__ -Wall -I/usr/src/linux-2.4/include -c helloworld.c

 

其实就是makefile给解出来了。

3 安装和卸载module

              假设我们的模块名字是XXX.ko或者XXX.o

             

a、用命令装载和卸载模块

             装载模块:

insmod XXX.ko (2.4版本中为XXX.o)

卸载模块:

rmmod XXX

你也可以查看已经安装的模块,用:

lsmod

 

b modproper工具

              modproper这个工具通常是用来管理内核目录中的模块,包括安装和卸载还有查看模块的依赖性等。

              安装模块:

              modproper XXX.ko [ module parameters]

              (module parameters是模块的参数,更详细的信息可以查询《Linux Kernel Developmentby Robert Love)

              卸载模块:

              modproper –r XXX

              modproper还能查看内核模块的依赖关系,用命令:

              modproper –A XXX

        

4 其他模块的属性

a 模块参数

              Linux提供了一个简单的框架,它驱动程序声明参数,用户可以在系统启动或者模块装载指定参数的值,这些参数对于驱动程序属于全局变量。模块参数同时也出现在sysfs文件系统中。

b导出符号表

       当模块被装载时,它被动态链接到内核.像用户空间的动态库一样,只有把函数导出才能 被调用. 在内核中, 导出内核函数使用这两个声明: called EXPORT_ SYMBOL() EXPORT_SYMBOL_GPL().

       导出内核的符号表后被看作还是导出的内核的接口,有时,也称为内核的API.(详细请看《Linux kernel DevelopmentSecond Edition16章第7小节)

 

c 添加内核选项

              "kbuild"系统是2.6内核的新特性,这个特性提供了把这个驱动模块添加到内核编译选项里的功能,下面我们看看如何利用这个特性。如果你的的模块在/drivers//char下,你会找到一个kconfig的文件,向kconfig文件中加入一条命令,source "drivers/char/XXXX/Kconfig",如果你是在/drivers//char下新建的一个文件夹XXXX,那你需要在drivers/char/Kconfig中添加

 

source "drivers/char/XXXX/Kconfig"

 

这样一句话,然后在Kconfig中加入:

 

config HelloWorld
        tristate " HelloWorld support"
        default n
        help
          If you say Y here, support for the XXXX with computer interface will be compiled into the kernel and accessible via device node. You can also say M here and the driver will be built as a module named HelloWorld.ko.

  
  
   
    
  
  
          If unsure, say N.

  
  
   
    
  
  

  
  
   
    
  
  

config HelloWorld定义了配置目标,tristate说是选择“Y”把它编进内核,选择“M”是把它编成模块,选择“N”是不编译进内核。

参考文献:

Linux kernel DevelopmentRebert Love Second Edition

Linux Dvice Drivers

Linux 模块编程完全指南》

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值