注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

亲民博客

记录生活中的点点滴滴

 
 
 

日志

 
 

Linux设备驱动程序简介[整理]  

2015-01-05 21:55:00|  分类: 安卓开发 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
系统调用是操作系统内核与应用程序之间的接口,设备驱动程序则是操作系统内核与机器硬件的接口。几乎所有的系统操作最终映射到物理设备,除了CPU、内存和少数其它设备,所有的设备控制操作都由该设备特殊的可执行代码实现,此代码就是设备驱动程序。操作系统内核需要访问两类主要设备:字符设备和块设备。与此相关主要有两类设备驱动程序,字符设备驱动程序和块设备驱动程序。Linux(也是所有UNIX)的基本原理之一是:系统试图使它对所有各类设备的输入、输出看起来就好象对普通文件的输入、输出一样。设备驱动程序本身具有文件的外部特征,它们都能使用象 open(),close(),read(),write()等系统调用。为使设备的存取能象文件一样处理,所有设备在目录中应有对应的文件名称,才可使用有关系统调用。 
驱动程序是内核与硬件的接口,他把系统调用映射到具体设备对于实际硬件的特定操作上,关系如下图所示:
Linux设备驱动程序简介[整理] - maxma - 铁马冰河
通过这种方法,应用程序就可以像操作普通文件一样操作硬件设备,用户程序只需要关心这个抽象出来的文件,而一切同硬件打交道的工作都交给了驱动程序。比如我们要向屏幕输出一串字符串,我们只需要把字符串写到显卡所抽象出来的文件里,而真正把字符串传到显卡里的工作就是由驱动程序来完成的。
.
在Linux下,驱动程序是内核的一部分,运行在内核态下,你可以将驱动静态的和内核编译在一起,这样的缺点是内核会比较大,而且如果驱动出错,会导致整个系统崩溃;也可以以module的方式编译,在需要的时候动态的载入。如果你编译过内核,应该记得在make menuconfig中,选项前面是可以选择和的,就分别表示"编译到内核中"和"编译成模块"。

下面介绍下模块,一个简单的“helloworld module”如下所示:

#include      
#include      
 int init_module(void)
{
   printk("Hello world 1.\n"); 
   // A non 0 return means init_module failed; module can't be loaded.
   return 0;
}
 
void cleanup_module(void)
{
  printk(KERN_ALERT "Goodbye world 1.\n");
}
其中,init_module函数是加载模块时会被调用的,一般作一些初始化的工作;cleanup_module函数是卸载模块时会被调用的,做一些清理的工作。因为模块是运行在内核态的,你自然不能使用库函数,因此要打印信息,需要使用printk函数而不是printf函数。另外,可以使用任意函数名(只要同内核函数名不冲突)来替换init_module和cleanup_module这两个函数名,但必须使用module_init(初始化函数名),module_exit(卸载时函数名)这两个宏来声明一下,也就是说,下面这个模块和上面的模块是等价的:

#include      
#include      
 int helloworld(void)
{
   printk("<1>Hello world 1.\n");
    // A non 0 return means init_module failed; module can't be loaded.
   return 0;
}
 
void goodbyeworld(void)
{
  printk(KERN_ALERT "Goodbye world 1.\n");
}
module_init(helloworld);
module_exit(goodbyeworld);

我们通过make编译生成一个模块文件".ko"之后,使用"insmod"命令来加载模块,那么“insmod”具体做了什么呢?
下面来介绍下insmod这个工具:
insmod是linux下加载模块的工具,路径一般是/sbin/insmod,当调用这个工具后,它的工作基本如下:

在用户空间打开待安装的module
调用query_module()系统调用询问无法落实的符号在内核或其他模块中的地址
链接操作,落实模块中的符号引用
调用create_module()系统调用在内核中创建module数据结构,并申请所需的内核空间
调用init_module()系统调用将链接好的module映像装入内核空间,然后调用模块中的init_module()函数(注意:这里面的两个init_module函数不一样,一个是系统调用,一个是你写在模块里面的函数

从已加载模块中卸载模块使用的是“rmmod”,"rmmod"所做工作如下:

调用delete_module()系统调用释放模块的module结构,同时释放模块所占的内核空间
调用模块中的cleanup_module()的函数
.

那么,一般情况下,驱动程序会在init_module()和cleanu_module()函数中做些什么呢?
init_module(): 向内核登记本模块中一些包含着函数指针的数据结构(如:file_operations)
cleanup_module(): 向内核撤销曾经的数据结构的登记,使内核在模块卸载后不至于再企图访问这些数据结构

Linux设备驱动程序简介[整理] - maxma - 铁马冰河


内核通过 printk() 输出的信息具有日志级别,日志级别是通过在 printk() 输出的字符串前加一个带尖括号的整数来控制的,如 printk("<6>Hello, world!/n");。内核中共提供了八种不同的日志级别,在 linux/kernel.h 中有相应的宏对应,以Ubuntu10.04为例具体位置在usr/src/linux-headers-2.6.32-21/include/linux/kernel.h中:
#define KERN_EMERG "<0>"
#define KERN_ALERT "<1>"
#define KERN_CRIT "<2>"
#define KERN_ERR "<3>"
#define KERN_WARNING "<4>"
#define KERN_NOTICE "<5>"
#define KERN_INFO "<6>"
#define KERN_DEBUG "<7>"

参考如下网址:
http://www.spongeliu.com/110.html
http://blog.chinaunix.net/uid-23860671-id-150493.html
http://blog.csdn.net/yeqishi/article/details/5800198

  评论这张
 
阅读(2)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018