Chinaunix首页 | 论坛 | 博客
  • 博客访问: 379295
  • 博文数量: 57
  • 博客积分: 2299
  • 博客等级: 大尉
  • 技术积分: 1109
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-27 23:12
文章分类
文章存档

2011年(4)

2010年(53)

分类: 嵌入式

2010-02-28 00:24:17

第一个例程:hello.c

/*
*Name :hello.c
*Function :test modules
*Author :gufeiyang
*From :<>
*Time :2010-2-1 home yunnan
*/

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE(“Dual BSD/GPL”);
static int hello_init(void)
{
        printk(KERN_ALERT “Hello, world,I am gufeiyang!\n”);
        return 0;
}
static void hello_exit(void)
{
        printk(KERN_ALERT “Goodbye, cruel world,YY will be back!\n”);
}
module_init(hello_init);
module_exit(hello_exit);


参照《linux设备驱动程序》编写Makefile
# If KERNELRELEASE is defined, we’ve been invoked from the
# kernel build system and can use its language.
FILENAME = hello.o
ifneq ($(KERNELRELEASE),)
 obj-m := $(FILENAME)
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
Else
 KERNELDIR ?= /root/kernel/linux-2.6.25.8/
 PWD := $(shell pwd)
default:
       $(MAKE) –C $(KERNELDIR) M=$(PWD) modules
       @rm –vf *.o *.order *.symvers *.mod.* *~
       @cp –vf *.ko /nfs
Endif
通过NFS在板子上即可看到
[root@gfy-S3C2440 /tmp]# insmod hello.ko
Hello, world,I am gufeiyang!
[root@gfy-S3C2440 /tmp]# rmmod hello.ko
Goodbye, cruel world,YY will be back!
 [root@gfy-S3C2440 /tmp]# lsmod
Not tainted
hello 1504 0 – Live 0xbf000000
[root@gfy-S3C2440 /tmp]#
心得:
1、  模块只可以调用内核导出的那些函数,不能调用libc之类的函数库;
2、  模块运行在内核空间,应用程序应用在用户空间;
3、  缺乏对浮点数的支持;
4、  驱动模块和应用程序的一个重要不同是:应用程序退出时可不管资源释放或者其他的清除工作,但模块的退出函数必须仔细撤销初始化函数所作的一切,否则,在系统重新

引导之前某些东西就会残留在系统中;
5、  处理器的多种工作模式(级别)其实就是为了操作系统的用户空间和内核空间设计的。在Unix类的操作系统中只用到了两个级别:最高和最低级别;
6、  要注意驱动程序的并发处理;
7、  内核API中具有双下划线(_ _)的函数,通常是接口的底层组件,应慎用;
8、  Makefile文件分析
ifneq ($(KERNELRELEASE),)判断是否在内核根目录下,也就是判断该Makefile是否在内核目录树中,从内核顶层的Makefile可以看到KERNELRELEASE的定义为
# Read KERNELRELEASE from include/config/kernel.release (if it exists)
KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null 在看看include/config/kernel.release里面是什么东西
[root@localhost linux-2.6.25.8]# cat include/config/kernel.release 2.6.25.8
include/config/kernel.release里面放的是该内核的版本号,也就是说如果存在include/config/kernel.release这个文件,就把内核版本号给KERNELRELEASE。如果版本号就从

内核目录树编译。Obj-m := hello.o  代表了我们要构造的模块名为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) 指定了模块源代码的位置
    modules目标指向obj-m变量中设定的模块。
说明:在一个典型的构造过程中,Makefile将被调用2次,第一次是构造内核目录树,
# If KERNELRELEASE is defined, we’ve been invoked from the
# kernel build system and can use its language.
FILENAME = hello
ifneq ($(KERNELRELEASE),)
 obj-m := $(FILENAME).o
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
Else
 KERNELDIR ?= /root/kernel/linux-2.6.25.8/
到此即可找到内核目录树,此时KERNELRELEASE已被定义
第二次调用时发现KERNELRELEASE已被定义,那么才定义obj-m := $(FILENAME).o这个模块,然后才执行$(MAKE) –C $(KERNELDIR) M=$(PWD) modules
第一次看的时候发现个问题,如果Makefile只调用一次,那么未定义KERNELRELEASE的时候obj-m := $(FILENAME).o这个模块就不能增加,后来仔细阅读了LDD3才发现一个典型的

构造Makefile需要被调用2次。这才解决了疑问。
9、  insmod使用公共内核符号表来解析模块中未定义的符号。公共内核符号表中包含了所有的全局内核项(即函数和变量的地址),这是实现模块化驱动程序所必须的;
10、    Linux使用模块层叠技术,我们可以将模块划分为多个层,通过简化每个层可缩短开发周期。如果一个模块需要向其他模块导出符号,则使用下面的宏:
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);
符号必须在模块文件的全局变量部分导出,因为这两个宏将被扩展为一个特殊变量的声明,而该变量必须是全局的;
11、    所有模块代码中都包含一下两个头文件:
#include
#include
12、    所有模块代码都应该指定所使用的许可证:
MODULE_LICENSE(“Dual BSD/GPL”);
此外还有可选的其他描述性定义:
MODULE_AUTHOR(“”);                    声明谁编写了模块
MODULE_DESCRIPTION(“”);           一个人可读的关于模块做什么的声明
MODULE_VERSION(“”);                   一个代码修订版本号
MODULE_ALIAS(“”);                        模块为人所知的另一个名子
MODULE_DEVICE_TABLE(“”);         来告知用户空间, 模块支持那些设备
上述MODULE_声明习惯上放在文件最后
13、    初始化和关闭
初始化
static int __init initialization_function(void)
{
 /* Initialization code here */
}
module_init(initialization_function);
清除函数
static void __exit cleanup_function(void)
{
 /* Cleanup code here */
}
module_exit(cleanup_function);
这里首次接触在模块内使用static void __exit cleanup_function(void),说明static的一种用法:1)在模块内,一个被声明为静态的函数只可被这一模块内的其他函数调用。

那就是,这个函数被限制在声明他的模块的本地范围内使用;
另外两种用法;
2)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
3) 在模块内(但在函数体外),一个被声明为静态的变量能够被模块内所用函数访问,但不能被模块外其他函数访问。他是个本地的全局变量。
14、    错误恢复有时用 goto 语句处理是最好的. 我们通常不愿使用 goto, 但是在我们的观念里, 这是一个它有用的地方. 在错误情形下小心使用 goto 可以去掉大量的复杂

, 过度对齐的, “结构形” 的逻辑. 因此, 在内核里, goto 是处理错误经常用到;以下是《linux设备驱动程序3》给出的关于错误处理的框架;
struct something *item1;
struct somethingelse *item2;
int stuff_ok;
void my_cleanup(void)
{
        if (item1)
                release_thing(item1);
        if (item2)
                release_thing2(item2);
        if (stuff_ok)
                unregister_stuff();
        return;
}
int __init my_init(void)
{
        int err = -ENOMEM;
        item1 = allocate_thing(arguments);
        item2 = allocate_thing2(arguments2);
        if (!item2 || !item2)
                goto fail;
        err = register_stuff(item1, item2);
        if (!err)
                stuff_ok = 1;
        else
                goto fail;
        return 0; /* success */
fail:
        my_cleanup();
        return err;
}
15、    模块参数: 内核允许对驱动程序指定参数,而这些参数可在装载驱动程序模块时改变。以下是我的实验程序:

/*
*Name :hello.c
*Function :test modules
*Author :gufeiyang
*From :<>
*Time :2010-2-1 home yunnan
*/

#include <linux/init.h>
#include <linux/module.h>
static char *whom = “world”;
static int howmany = 4;
static char array[] = {‘a’,’b’,’c’,’d’};
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);
module_param_array(array, charp, 4, S_IRUGO);
static int hello_init(void)
{
         int i;
         for(i =0; i < howmany; i++){
                 printk(KERN_ALERT “%darray[%d] = %c\n”,i,i,array[i]);
         }
         printk(“\nTest string\nwhom = %s\n”,whom);
         printk(KERN_ALERT “Hello, world,I am gufeiyang!\n”);
         return 0;
}
static void hello_exit(void)
{
         printk(KERN_ALERT “Goodbye, cruel world,YY will be back!\n”);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE(“Dual BSD/GPL”);

136不传递参数执行后:
[root@gfy-S3C2440 /tmp]# insmod hello.ko
0array[0] = a
1array[1] = b
2array[2] = c
3array[3] = d
 
Test string
whom = world
Hello, world,I am gufeiyang!
2)传递参数执行后:
[root@gfy-S3C2440 /tmp]# insmod hello.ko whom=gufeiyang howmany=6
0array[0] = a
1array[1] = b
2array[2] = c
3array[3] = d
4array[4] =
5array[5] =
Test string
whom = gufeiyang
Hello, world,I am gufeiyang!
说明:以上主要测试了module_param_array(name,type,num,perm); num这个参数没有起到限制提供给用户参数的个数,而是array实际长度确定,正如Tekkaman Ninja证实那样


Perm字段是一个权限值,需使用 中定义的值。
快速参考:以下摘自《linux设备驱动程序(第三版)》第一章:快速参考

本节总结了我们在本章接触到的内核函数, 变量, 宏定义, 和 /proc 文件. 它的用意是作为一个参考. 每一项列都在相关头文件的后面, 如果有. 从这里开始, 在几乎每章的结

尾会有类似一节, 总结一章中介绍的新符号. 本节中的项通常以在本章中出现的顺序排列:

insmod
modprobe
rmmod
用户空间工具, 加载模块到运行中的内核以及去除它们.
#include
module_init(init_function);
module_exit(cleanup_function);
指定模块的初始化和清理函数的宏定义.
__init
__initdata
__exit
__exitdata
函数( __init 和 __exit )和数据 (__initdata 和 __exitdata)的标记, 只用在模块初始化或者清理时间. 为初始化所标识的项可能会在初始化完成后丢弃; 退出的项可能被丢

弃如果内核没有配置模块卸载. 这些标记通过使相关的目标在可执行文件的特定的 ELF 节里被替换来工作.
#include
最重要的头文件中的一个. 这个文件包含很多驱动使用的内核 API 的定义, 包括睡眠函数和许多变量声明.
Struct task_struct *current;
当前进程.
Current->pid
current->comm
进程 ID 和 当前进程的命令名.
Obj-m
一个 makefile 符号, 内核建立系统用来决定当前目录下的哪个模块应当被建立.
/sys/module
/proc/modules
/sys/module 是一个 sysfs 目录层次, 包含当前加载模块的信息. /proc/moudles 是旧式的, 那种信息的单个文件版本. 其中的条目包含了模块名, 每个模块占用的内存数量, 以及使用计数. 另外的字串追加到每行的末尾来指定标志, 对这个模块当前是活动的.
Vermagic.o
来自内核源码目录的目标文件, 描述一个模块为之建立的环境.
#include
必需的头文件. 它必须在一个模块源码中包含.
#include
头文件, 包含在建立的内核版本信息.
LINUX_VERSION_CODE
整型宏定义, 对 #ifdef 版本依赖有用.
EXPORT_SYMBOL (symbol);
EXPORT_SYMBOL_GPL (symbol);
宏定义, 用来输出一个符号给内核. 第 2 种形式输出没有版本信息, 第 3 种限制输出给 GPL 许可的模块.
MODULE_AUTHOR(author);
MODULE_DESCRIPTION(description);
MODULE_VERSION(version_string);
MODULE_DEVICE_TABLE(table_info);
MODULE_ALIAS(alternate_name);
放置文档在目标文件的模块中.
Module_init(init_function);
module_exit(exit_function);
宏定义, 声明一个模块的初始化和清理函数.
#include
module_param(variable, type, perm);
宏定义, 创建模块参数, 可以被用户在模块加载时调整( 或者在启动时间, 对于内嵌代码). 类型可以是 bool, charp, int, invbool, short, ushort, uint, ulong, 或者 intarray.
#include
int printk(const char * fmt, …);
内核代码的 printf 类似物。

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