【转】关于Linux内核模块的编写

来源:互联网 发布:2016域名投资趋势 编辑:程序博客网 时间:2024/06/09 12:47

Hello,Module 最简单的内核模块

第一个最简单的内核模块什么事都不做,仅在内核加载时打印 “ hello,kernel world ” ,在模块卸载时打印 “ goodbye,kernel world” ,虽然什么事情也做不了,但这已经是一个可以加载如内核的简单模块了。

在新建hello文件夹下建立两个文件:

hello.c  Makefile

hello.c 内容如下:

[cpp] view plaincopy
  1. #include <linux/init.h>  
  2. #include <linux/module.h>  
  3.   
  4. MODULE_LICENSE("Dual BSD/GPL");  
  5. static int __init hello_init(void//模块的入口,当此模块被加载的时候调用  
  6. {  
  7.   printk(KERN_INFO "hello , kernel world\n");  
  8.   return 0;  
  9. }  
  10.   
  11. static void __exit hello_exit(void//模块的出口,当模块被卸载的时候调用  
  12. {  
  13.   printk(KERN_INFO " goodbye,kernel world\n ");  
  14. }  
  15.   
  16. module_init(hello_init);//指定模块入口函数  
  17. module_exit(hello_exit);//指定模块出口函数  
  18.   
  19. //一些基本的模块信息,可以通过 "modinfo 模块名" 查看  
  20. MODULE_AUTHOR("xxxx");  
  21. MODULE_DESCRIPTION("A simple Hello World Module");  
  22. MODULE_ALIAS("a simplest module");    

备注:

1. 引入一些思考,__init __exit 的作用是什么?   以下是完整的宏定义:

#define __init    __attribute__((__section__(".init.text") ) )

#define __exit   __attribute__((__section__(".exit.text")) )

另外,数据部分同样包含以下两个宏。

#define __initdata    __attribute__((__section__(".init.data")))

#define __exitdata   __attribute__((__section__(".ext.data")))

以上四个宏将指定了函数或者数据需要被编译器指定的section位置(代码段,数据段,==)。 

init.text和.init.data这两个section段将会在模块被加载后释放掉。

.exit.text.和.exit.data这两个section.段将会在模块被卸载后释放掉。  

所以程序编写过程中可以把某些合适的数据和函数指定到这四个seciton,以达到节约内存的目的。

如果有另一个问题,什么是section? ,这将涉及到一些ELF文件格式和编译方面的知识,读者可以google之。


2.printk函数

内核空间不能使用标准函数库,为了获得内核的打印输出,我们可以使用printk函数,该函数由以下几个级别定义:

[cpp] view plaincopy
  1. #define KERN_EMERG    "<0>"    /* system is unusable */  
  2. #define KERN_ALERT    "<1>"    /* action must be taken immediately */  
  3. #define KERN_CRIT     "<2>"    /* critical conditions */  
  4. #define KERN_ERR      "<3>"    /* error conditions */  
  5. #define KERN_WARNING  "<4>"    /* warning conditions */  
  6. #define KERN_NOTICE   "<5>"    /* normal but significant */  
  7. #define KERN_INFO     "<6>"    /* informational */  
  8. #define KERN_DEBUG    "<7>"    /* debug-level messages */  

使用方法为: printk(级别 "Hello, world!/n"); 

未指定日志级别的 printk() 采用的默认级别是 DEFAULT_MESSAGE_LOGLEVEL,这个宏在 kernel/printk.c 中被定义为整数 4,即对应KERN_WARNING。

在 /proc/sys/kernel/printk 会显示4个数值(可由 echo 修改),分别表示当前控制台日志级别、未明确指定日志级别的默认消息日志级别、最小(最高)允许设置的控制台日志级别、引导时默认的日志级别。当 printk() 中的消息日志级别小于当前控制台日志级别时,printk 的信息(要有/n符)就会在控制台上显示。但无论当前控制台日志级别是何值,通过 /proc/kmsg (或使用dmesg)总能查看。另外如果配置好并运行了 syslogd 或 klogd,没有在控制台上显示的 printk 的信息也会追加到 /var/log/messages.log 中。



Makefile 内容如下:

[plain] view plaincopy
  1. obj-m:=hello.o  


备注:

如果内核模块由多个文件组成那应该怎么写这个Makefile呢?  答案是 : obj-m:=hello.o good.o  xxx.o ...


在命令行执行
$ make -C /usr/src/linux M=`pwd` modules
无错误的话将会生成hello.ko 模块,运行insmod工具把模块插入内核:
# insmod hello.ko 
运行 rmmod工具将模块从内核卸载:
# rmmod hello
输入以下命令在终端中查看内核输出
# dmesg | tail -10 
如何可以看到以下输出,那恭喜你成功了!!
[ 2840.707373] hello,kernel world
[ 3054.477649] goodbye,kernel world
以上内容估计学习过内核开发的都很熟悉,介绍内核开发的书有ldd3 《Linux 设备驱动开发》第三版,此书已被翻译成中文,可在网上搜索下载。本文这里给出此书的下载链接:

http://ishare.iask.sina.com.cn/f/8189282.html

0 0
原创粉丝点击