Chinaunix首页 | 论坛 | 博客
  • 博客访问: 180625
  • 博文数量: 80
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 835
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-29 10:30
文章分类
文章存档

2009年(12)

2008年(60)

2007年(8)

我的朋友

分类: LINUX

2009-04-15 15:26:40

(一)linux内核驱动模块的形式

/*hello.c*/

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void)
{
        printk(KERN_ALERT "Hello, leo\n");
        return 0;
}
static void hello_exit(void)
{

        printk(KERN_ALERT "Goodbye, leo\n");
}

module_init(hello_init);
module_exit(hello_exit);


    这个模块定义了两个函数, 一个在模块加载到内核时被调用( hello_init )以及一个在模块被去除时被调用( hello_exit ). moudle_init 和 module_exit 这几行使用了特别的内核宏来指出这两个函数的角色. 另一个特别的宏 (MODULE_LICENSE) 是用来告知内核, 该模块带有一个自由的许可证; 没有这样的说明, 在模块加载时内核会抱怨。

 

(二)linux内核模块的编译与加载

    1.编译

建立如下makefile文件后直接make就可完成编译

KERNELDIR = /home/sys2410/linux-2.6.15
    # The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
INSTALLDIR = /home/modules

CROSS_COMPILE =/usr/local/arm/3.4.1/bin/arm-linux-

CC = $(CROSS_COMPILE)gcc

obj-m := hello.o

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

modules_install:
    cp hello.ko $(INSTALLDIR)

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

.PHONY: modules modules_install clean

这个makefile用了gnu make的扩展语法

    obj-m := hello.

代表了我们要构造的模块名为hell.ko,make 会在该目录下自动找到hell.c文件进行编译。如果 hello.o是由其他名称的源文件生成(比如file1.c和file2.c)的,则要加上这句
hello-objs := file1.o file2.o ......

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

其中 -C $(KERNELDIR) 指定了内核源代码的位置,其中保存有内核的顶层makefile文件。
M=$(PWD这个 M= 选项使 makefile 在试图建立模块目标前, 回到你的模块源码目录。
modules目标指向obj-m变量中设定的模块。
  

   2.加载

insmod命令用于模块的加载 它依赖一个在 kernel/module.c 中定义的系统调用sys_init_module 该函数用vmalloc分配内核空间接着拷贝模块的代码段到这块内存区,然后执行一个类似 ld 的函数, 它连接模块中任何未解决的符号连接到内核的符号表上借助内核符号表解决模块中的内核引用. 但是不象连接器, 内核不修改模块的磁盘文件, 而是内存内的拷贝,最后调用模块的初始化函数来启动所有东西。

linux用”可移出“符号的形式提供了供模块使用的变量及函数的名称 可移出符号的定义

#define EXPORT_SYMBOL(abc)

有了这些可移出符号,在实际的模块设计中,当需要内核的某个变量或函数时,在声明了这是引用的一个外部符号后,就可以直接在该变量或函数的位置引用。编译器在对模块进行编译时就会在这个位置留下一个空位,以便在模块的加载时再根据提供的可移出符号表来具体确定这些变量或函数,更确切的说,是确定这些变量或函数的地址。

 

modprobe 工具值得快速提及一下. modprobe, 如同 insmod, 加载一个模块到内核. 它的不同在于它会查看要加载的模块, 看是否它引用了当前内核没有定义的符号. 如果发现有, modprobe 在定义相关符号的当前模块搜索路径中寻找其他模块. 当 modprobe 找到这些模块( 要加载模块需要的 ), 它也把它们加载到内核. 如果你在这种情况下代替以使用 insmod , 命令会失败, 在系统日志文件中留下一条 " unresolved symbols "消息。

 rmmod 工具用于将模块从内核去除. 注意, 如果内核认为模块还在用( 就是说, 一个程序仍然有一个打开文件对应模块输出的设备 ), 或者内核被配置成不允许模块去除, 模块去除会失败. 可以配置内核允许"强行"去除模块, 甚至在它们看来是忙的. 如果你到了需要这选项的地步, 但是, 事情可能已经错的太严重以至于最好的动作就是重启了.

lsmod 程序生成一个内核中当前加载的模块的列表. 一些其他信息, 例如使用了一个特定模块的其他模块, 也提供了. lsmod 通过读取 /proc/modules 虚拟文件工作. 当前加载的模块的信息也可在位于 /sys/module 的 sysfs 虚拟文件系统找到。

    

(三)linux内核驱动模块及应用程序的比较

    1.驱动模块运行在内核空间,运行时不能依赖于任何函数库和模块连接,所以在写驱动时所调用的函数只能是作为内核一部分的函数。


    2
.硬件CPU设计有不同的工作模式(级别) 内核在最高级运行( 也称之为超级模式 ), 这里任何事情都允许, 而应用程序在最低级运行(所谓的用户模式)。每种模式有自己的内存空间于是有了内核空间和用户空间。

    3.通过系统调用或者硬件中断可由用户空间进入到内核空间,但是注意 执行系统调用的内核代码在进程的上下文中工作 它代表调用进程并且可以存取该进程的地址空间. 而处理中断的代码在中断上下文工作 它对进程来说是异步的, 不和任何特别的进程有关。

    4.大部分应用程序, 多线程的应用程序是一个明显的例外, 典型地是顺序运行的,而内核驱动模块应注意并发问题。并发的来源有 中断(异步运行)几个软件抽象(内核定时器)多处理器系统( SMP ) 内核抢占。

    5. 当前进程的概念  尽管内核模块不象应用程序一样顺序执行, 内核做的大部分动作是代表一个特定进程的. 内核代码可以引用当前进程, 通过存取全局项 current, 它在 中定义, 它产生一个指针指向结构 task_struct, 在 定义. current 指针指向当前在运行的进程. 在一个系统调用执行期间, 例如 open 或者 read, 当前进程是发出调用的进程. 内核代码可以通过使用 current 来使用进程特定的信息。

(四)模块参数

驱动需要知道的几个参数因不同的系统而不同. 从使用的设备号( 如我们在下一章见到的 )到驱动应当任何操作的几个方面. 例如, SCSI 适配器的驱动常常有选项控制标记命令队列的使用, IDE 驱动允许用户控制 DMA 操作. 如果你的驱动控制老的硬件, 还需要被明确告知哪里去找硬件的 I/O 端口或者 I/O 内存地址. 内核通过在加载驱动的模块时指定可变参数的值, 支持这些要求。

这些参数的值可由 insmod 或者 modprobe 在加载时指定; 后者也可以从它的配置文件(/etc/modprobe.conf)读取参数的值。

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