Linux设备驱动程序的学习 (一)

 

不管我们学习什么编程语言,和我们见面的第一个程序就是“hello world!” 相信各位道上的朋友都遇到过这种个程序!!

学习驱动程序也不例外,我学的第一个驱动程序就是“hello world!!”

具体的程序代码如下:

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
    printk(KERN_ALERT"Hello, world!\n");
    return 0;
}
static void hello_exit(void)
{
    printk(KERN_ALERT"byby FriendyARM mini2440!\n");
}
module_init(hello_init);
module_exit(hello_exit);

 

将其复制到工作目录下,并编写一个简单的Makefile文件:

 

由于每个人使用的Linux系统不一样且每个人内核源代码所存放的位置也不是一样的。所以编写Makefile文件的时候,参考别人的进行修改是一个很不错的

的学习Makefile文件的方法。当然你能把Linux内核的Makefile文件了解一下,对你了解Linux内核有很大的帮助的。

学习心得:

1、驱动模块运行在内核空间,运行是不能依赖任何函数库和模块连接,所以在写驱动程序的时候

所调用的函数只能是作为内核一部分的函数。

2、驱动模块和应用程序的一个重要不同是:应用程序退出时可不管资源释放或者其他的清除

工作,但模块的退出啊哈念书必须仔细撤销初始化函数所做的一切,否则,在系统想重新引导之前某些

东西就会残留在系统中。

3、处理器的多种工作模式其实就是为了操作系统的用户空间和内核空间设计的,在Unix类的操作系统

中只是用到了两个级别:最高级别和最低级别。

4、要十分注意驱动程序的并发处理。在Linux驱动程序中必须解决的一个问题就是多个进程对共享资源的并发访问.Linux对解决并发访问可能导致的竟态问题提供了几种机制:中断屏蔽、原子操作、自旋锁、信号量等机制。

5、内核API中具有下划线(__)的函数,通常是接口的底层组件,应该慎用。

6、内核代码不能实现浮点运算。内核中没有提供一套进行浮点运算的完整的环境。

7、Makefile文件的分析:

obj-m := hello.o 代表了我们要构建的模块名为hello.ko,make会子啊该目录下自动找到hello.c文件进行编译。如果hello.o文件是有其他的源文件生成(比如file.1和file1.c)的,则在下面加上:

hello-objs := file.o file1.o ......(其中用红色标志的是对应关系)

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

其中-C $(KERNELDIR)指定了内核源代码的位置,其中保存有内核的顶层makefile文件。

M=$(PWD) 指定了模块源代码的位置

modules 目标指向obj-m变量中设定的模块

8、insmod使用公共内核符号表来解析模块中未定义的符号,公共内核符号表中包含了的、所有的全局内核项(即函数和变量的地址),这是实现模块化驱动程序所必须的。

9、Linux使用模块层叠技术,我们可以将模块划分为多个层次,通过简化每个层可以缩短开发周期。如果一个模块需要向其他模块导出符号,则使用下面宏:

EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);

符号必须子啊模块文件的全局变量部分导出,因为这两个宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。

10、所有的模块代码都必须包含下面两个头文件:

#include <linux/init.h>

#include <linux/module.h>

11、所有模块代码都应指定所使用的许可证:

MODULE_LICENSE("Dual BSD/GPL");

12、初始化和关闭

初始化的实际定义通常是:

staticint _ _init initialization_function(void)
{
/*初始化代码*/
}
module_init(initialization_function)

清除函数的实际定义是:

static int _ _exit cleanup_function(void)
{
/*清除代码*/
}
module_exit(cleanup_function)

13、还有一些是可选的其他的描述型的定义:

MODULE_AUTHOR("");
MODULE_DESCRIPTION("");
MODULE_VERSION("");
MODULE_ALIAS("");
MODULE_DEVICE_TABLE("");

这些模块的声明习惯性的放在模块程序的最后面。

14、Linux内核模块的初始化出错处理一般使用“goto”语句,通常情况下很少使用“goto”,但是出错处理是(可能是唯一的情况),它却非常的有用。

在大一学习C语言的时候,老师就建议不要使用“goto”语句,并说很少会用到,在这里遇到第一个建议使用“goto”语句的。在追求效率的代码中使用goto语句一直是最好的错误恢复机制。下面是我截下来的一段关于使用goto语句实现错误处理的程序:

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;
}

 

 

 

 

 

 

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭