Chinaunix首页 | 论坛 | 博客
  • 博客访问: 497359
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 508
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-10 13:18
个人简介

Keep looking Donot settle

文章分类

全部博文(144)

文章存档

2019年(1)

2016年(31)

2015年(51)

2014年(61)

分类: 嵌入式

2016-10-30 13:35:03

内核模块是内核向外部提供的一个插口,其全称为动态可加载内核模块(Loadable Kernel ModuleLKM),我们简称为模块Linux内核之所以提供模块机制,是因为它本身是一个单内核(monolithic kernel)。单内核的最大优点是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。

一、 什么是模块

模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功能。


应用程序与内核模块的比较
为了加深对内核模块的了解,表一给出应用程序与内核模块程序的比较。
表一 应用程序与内核模块程序的比较

  C语言应用程序 内核模块程序
使用函数 Libc 内核函数
运行空间 用户空间 内核空间
运行权限 普通用户 超级用户
入口函数 main() module_init()
出口函数 exit() module_exit()
编译 Gcc –c Makefile
连接 Gcc insmod
运行 直接运行 insmod
调试 Gdb kdbug, kdb,kgdb

从表一我们可以看出,内核模块程序不能调用libc库中的函数,它运行在内核空间,且只有超级用户可以对其运行。另外,模块程序必须通过module_init()module-exit()函数来告诉内核“我来了”和“我走了”。

二、 编写一个简单的模块

模块和内核都在内核空间运行,模块编程在一定意义上说就是内核编程。因为内核版本的每次变化,其中的某些函数名也会相应地发生变化,因此模块编程与内核版本密切相关。以下例子针对Ubuntu 9.10  内核2.6.31-14-generic


root@robert-Compaq-Presario-CQ40-Notebook-PC:~/lkm#
ls
hello.c  Makefile
root@robert-Compaq-Presario-CQ40-Notebook-PC:~/lkm# pwd
/root/lkm
root@robert-Compaq-Presario-CQ40-Notebook-PC:~/lkm# cat hello.c
#include    
#include    
#include    
MODULE_LICENSE("GPL");    
static int __init lkp_init(void)    
{    
    printk(KERN_ALERT "Hello World!/n");    
    return 0;    
}    
static void __exit lkp_cleanup(void)    
{    
    printk(KERN_ALERT "Bye World!/n");    
}    
module_init(lkp_init);    
module_exit(lkp_cleanup);    
MODULE_AUTHOR("heyutao");    
MODULE_DESCRIPTION("hello");

root@robert-Compaq-Presario-CQ40-Notebook-PC:~/lkm# cat Makefile
obj-m := hello.o  
KERNELBUILD :=/lib/modules/$(shell uname -r)/build  
default:  
    make -C $(KERNELBUILD) M=$(shell pwd) modules  
clean:  
    rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers .tmp_versions
root@robert-Compaq-Presario-CQ40-Notebook-PC:~/lkm# make
make -C /lib/modules/3.19.0-30-generic/build   M=/root/lkm modules  
make[1]: Entering directory `/usr/src/linux-headers-3.19.0-30-generic'
  CC [M]  /root/lkm/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /root/lkm/hello.mod.o
  LD [M]  /root/lkm/hello.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.19.0-30-generic'
root@robert-Compaq-Presario-CQ40-Notebook-PC:~/lkm# ls
hello.c  hello.ko  hello.mod.c  hello.mod.o  hello.o  Makefile  modules.order  Module.symvers
root@robert-Compaq-Presario-CQ40-Notebook-PC:~/lkm#

说明:
1. hello.c

所有模块都要使用头文件module.h,此文件必须包含进来。 头文件kernel.h包含了常用的内核函数。 头文件init.h包含了宏_init和_exit,它们允许释放内核占用的内存。 lkp_init是模块的初始化函数,它必需包含诸如要编译的代码、初始化数据结构等内容。

使用了printk()函数,该函数是由内核定义的,功能与C库中的printf()类似,它把要打印的信息输出到终端或系统日志。
lkp_cleanup是模块的退出和清理函数。此处可以做所有终止该驱动程序时相关的清理工作。 module_init()和cleanup_exit()是模块编程中最基本也是必须的两个函数。
module_init()是驱动程序初始化的入口点。而cleanup_exit()注销由模块提供的所有功能。  

2.Makefile

注意makefile里面要求的tab
KERNELBUILD :=/lib/modules/$(shell uname -r)/build是编译内核模块需要的Makefile的路径,Ubuntu下是
/lib/modules/2.6.31-14-generic/build
make -C $(KERNELBUILD) M=$(shell pwd) modules 编译内核模块。-C
将工作目录转到KERNELBUILD,调用该目录下的Makefile,并向这个Makefile传递参数M的值是$(shell pwd) modules。

3. 编译模块
#sudo make    (调用第一个命令default)
这时,在hello.c 所在文件夹就会有 hello.ko ,这个就是我们需要的内核模块啦

#sudo make clean
清理编译垃圾,hello.ko 也会清理掉。

4. 插入模块,让其工作。注意必须是root权限
#sudo insmod ./hello.ko 我们用dmesg 就可以看到 产生的内核信息啦,Hello world!
如果没有输出"hello from hello world",因为如果你在字符终端而不是终端模拟器下运行的话,就会输出,因为在终端模拟器下时会把内核消息输出到日志文件/var/log/kern.log中。

#sudo rmmod ./hello 再用dmesg 可以看到 Bye world!
modutils是管理内核模块的一个软件包。可以在任何获得内核源代码的地方获取Modutils(modutils-x.y.z.tar.gz)源代码,
然后选择最高级别的patch.x.y.z等于或小于当前的内核版本,安装后在/sbin目录下就会有insomod、rmmod、ksyms、lsmod、modprobe等实用程序。
当然,通常我们在加载Linux内核时,modutils已经被载入。
1.Insmod命令 调用insmod程序把需要插入的模块以目标代码的形式插入到内核中。在插入的时候,insmod自动调用init_module()函数运行。
注意,只有超级用户才能使用这个命令,其命令格式为: # insmod  [path] modulename.ko
2. rmmod命令 调用rmmod程序将已经插入内核的模块从内核中移出,rmmod会自动运行cleanup_module()函数,其命令格式为: #rmmod  [path] modulename.ko
3.lsmod命令 调用lsmod程序将显示当前系统中正在使用的模块信息。实际上这个程序的功能就是读取/proc文件系统中的文件/proc/modules中的信息,其命令格式为: #lsmod
4.ksyms命令 ksyms这个程序用来显示内核符号和模块符号表的信息。与lsmod相似,它的功能是读取/proc文件系统中的另一个文件/proc/kallsyms。

这就是在2.6.xx下一个最简单的模块编写过程。

--------------------  static int _init embed_hello_init (void) 1)embed_hello_init 不是结构体名,是函数名
2)int _init部分,int 表示函数的返回值类型,是整型
扣除_init去看,static int embed_hello_init (void),就是定义一个静态的无入参函数,返回值是整型。这些概念跟嵌入式,linux,驱动都没有任何关系,是C语法的概念。

回到_init,这是linux 内核编程的一个特殊宏,,展开是一个gcc的扩展属性语法,限制了函数链接时放elf文件的那个section。
定义大概如下(不同可能有差异):
#define  __attribute__((__section__(".init")))

通过把init函数限制在一个固定的section,一个作用是在启动时简单遍历section调用初始化函数即可,
另外一个作用是在初始化完成后,可以马上释放该section所占空间给系统用(因为初始化函数通常只在系统启动后执行一次)。


from: http://blog.csdn.net/blank_bird/article/details/6538910

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