第1个linux驱动___打印"hello world"
为了方便后续的深入,我们在驱动程序中用printk( )函数来打印“hello world”,printk( )是内核中自带的函数,专门用于在打印内核信息。
在安装驱动模块到内核中的时,需要进行驱动模块的初始化,初始化具体做什么我们先不提,我们暂时只用printk( )打印“hello world”:
int first_drv_init(void)
{
printk("hello world!\n");
return 0;
}
· 关于printk的打印级别
在内核中有一个打印级别的概念:
当我们在linux命令行下输入:cat /proc/sys/kernel/printk时,会打印出:4 4 1 7
第一个“4”表示当前系统允许打印级别高于4的字符串显示在命令行中,也就是打印级别为3、2、1、0的都能显示出来,7、6、5、4打印级别的都不能被显示出来,一共有0~7这8个打印级别,宏定义如下所示:
#define KERN_EMERG "<0>" /* 1116.www.qixoo.qixoo.com system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
我们可以在打印函数printk中这样赋予字符串打印级别: printk(KERN_INFO"hello world!\n");
这样"hello world"的打印级别是6,我们可以用vi命令打开/proc/sys/kernel/printk,把第一个4修改成7,这样KERN_INFO所修饰的"hello world"就能够被打印出来。
· static
在int first_drv_init(void)这个函数前,一般习惯加上static关键字进行修饰:static int first_drv_init(void);这样将使得int first_drv_init(void)函数只对能在first_drv.c文件中被使用,即为first_drv.c的私有函数,外部的文件中的代码无法对first_drv.c中的int first_drv_init(void)进行访问,这样做的好处是,可以防止函数重名带来不必要的麻烦,如果函数重名且未加static修饰,编译的时候可能就会出错误或警告,即便程序能运行可能也会出现莫名其妙的问题。
· __init
另外,除了用static关键字来修饰函数外,最好再养成习惯在函数前再加上"__init",这是一个宏定义,在编译器对驱动程序进行编译的时候,就会将__init所修饰的函数放到.init.text段中,不加__init将默认把函数放到.text段中,各种驱动模块凡是由__init修饰的函数将会被统一放到.init.text段,内核启动时会统一加载.init.text段中的这些驱动模块初始化函数,加载完后就会把这个.init.text段释放掉以省出内存。
经过这番分析后,我们第一个驱动模块的初始化函数就变成了:
static int __init first_drv_init(void)
{
printk(KERN_INFO"hello world!\n");
return 0;
}
其实我们经常听说安装驱动,也听说过卸载驱动,那么一个完整的驱动模块也应该是可以被卸载的,我们可以照之前的方法来写一个卸载函数:
static int __exit first_drv_exit(void)
{
printk(KERN_INFO"goodbye world...\n");
return 0;
}
· __exit
__exit宏定义的功能和作用与__init宏定义如出一辙,即将函数放入到.exit.text段,在此不多赘述。
此时我们发现,first_drv.c里面空荡荡的,只有first_drv_init和first_drv_exit两个函数,连个main函数都没有,谁来调用这两个函数呢,总不能让编译器指定先执行first_drv_init,再指定执行first_drv_exit吧,这思路显然是错的,只是我们的一厢情愿。
实际上,在first_drv.c中,我们至始至终都不需要main函数,不论多么复杂、高级的驱动模块都不需要在其"驱动.c"文件中写一个main函数,这是为什么呢?在下一篇博文后,你可能会有更深的体会,现在你只需要明白驱动模块中的函数只是被别的函数或应用程序来进行调用的罢了。
那么被谁调用呢,驱动模块的初始化函数由module_init( )这个宏定义来进行处理,module_init( )定义了一个结构体,当我们写成module_init(first_drv_init)时,那个结构体中就会有一个函数指针指向了first_drv_init函数,当我们在命令行下使用insmod命令来安装first_drv这个驱动模块的时候,内核就会自动去找到那个结构体,通过那个函数指针找到first_drv_init函数进行调用。
对应的,内核也实现了module_exit( )宏定义来让我们实现对first_drv_exit函数的调用,用法为module_exit(first_drv_exit)
由于使用到了module_init( )和module_exit( )这两个宏定义,我们需要添加linux/module.h这个头文件,我们之前也使用了__init和__exit这两个宏定义,同样需要加上头文件linux/init.h
我们的第一个空壳驱动程序就完成了,为什么说是空壳呢,因为这个驱动程序仅仅能够被安装和卸载,不涉及硬件操作,更为重要的是,它也无法被应用程序调用,它还不具备和应用层之间的接口,我们知道你脑中一片乱麻,我将在下一篇博文中讲解整个系统各层次之间的简单关系,到时你的思路就会清晰很多。
下面附上这篇博文中我们写的完整代码:
#include "linux/module.h" //此处没有使用<>而是使用了"",是因为博客编辑的<>之间的内容将不予显示
#include "linux/init.h" //<>还是""的区别不大,不过还是建议大家用<>
static int __init first_drv_init(void)
{
printk(KERN_INFO"hello world!\n");
return 0;
}
static int __exit first_drv_exit(void)
{
printk(KERN_INFO"goodbye world...\n");
return 0;
}
module_init(first_drv_init);
module_exit(first_drv_exit);
它还有一个不足之处,我们将在下下篇博文中进行完善,然后进行测试,大家可以先把它敲到你的first_drv.c中,不要复制,最好是跟着这篇博文的思路从头到尾敲,一步步完善成上面这样。
第1个linux驱动___打印"hello world"的更多相关文章
- Linux驱动学习之常用的模块操作命令
1.常用的模块操作命令 (1)lsmod(list module,将模块列表显示),功能是打印出当前内核中已经安装的模块列表 (2)insmod(install module,安装模块),功能是向当前 ...
- 嵌入式Linux驱动开发日记
嵌入式Linux驱动开发日记 主机硬件环境 开发机:虚拟机Ubuntu12.04 内存: 1G 硬盘:80GB 目标板硬件环境 CPU: SP5V210 (开发板:QT210) SDRAM: 512M ...
- Linux驱动学习步骤(转载)
1. 学会写简单的makefile 2. 编一应用程序,可以用makefile跑起来 3. 学会写驱动的makefile 4. 写一简单char驱动,makefile编译通过,可以insmod, ls ...
- Linux驱动设计—— 驱动调试技术
参考博客与书籍: <Linux设备驱动开发详解> <Linux设备驱动程序> http://blog.chinaunix.net/uid-24219701-id-2884942 ...
- Linux驱动开发之开篇--HelloWorld
Linux驱动的编写,大致分为两个过程,第一个过程为测试阶段,即为某一具体的设备,添加必要的驱动模块,为了节省编译时间,需要将代码单独放在一处,在编译时,只需要要调用内核的头文件即可:第二个过程为布置 ...
- Linux驱动开发 -- 打开dev_dbg()
Linux驱动开发 -- 打开dev_dbg() -- :: 分类: LINUX linux设备驱动调试,我们在内核中看到内核使用dev_dbg来控制输出信息,这个函数的实质是调用printk(KER ...
- Linux驱动开发学习的一些必要步骤
1. 学会写简单的makefile 2. 编一应用程序,可以用makefile跑起来 3. 学会写驱动的makefile 4. 写一简单char驱动,makefile编译通过,可以insmod, ...
- Android系统移植与驱动开发——第六章——使用实例来理解Linux驱动开发及心得
Linux驱动的工作方式就是交互.例如向Linux打印机驱动发送一个打印命令,可以直接使用C语言函数open打开设备文件,在使用C语言函数ioctl向该驱动的设备文件发送打印命令.编写Linux驱动最 ...
- linux驱动调试--段错误之oops信息分析
linux驱动调试--段错误之oops信息分析 http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id= ...
随机推荐
- ASP.NET MVC 多语言实现技巧 最简、最易维护和最快速开发
说说传统做法的缺点 1.做过多语言的都知道这玩意儿太花时间 2.多语言架构一般使用资源文件.XML或者存储数据库来实现.这样就在一定程序上降低了性能 3.页面的可读性变差,需要和资源文件进行来回切换 ...
- 单从Advice(通知)实现AOP
如果你在实际开发中没感觉到OOP的一些缺陷,就不要往下看了! 如果你不了解AOP,或类似AOP的思路,请先去了解一下AOP相关的认识. 如果你是概念党,或是经验党,或是从众党,也请不要看了! 我实现的 ...
- 用户 'IIS APPPOOL\***' 登录失败
用户 'IIS APPPOOL\DefaultAppPool' 登录失败. 我在windows8中安装了iis之后添加了我做的网站打开之后提示用户 'IIS APPPOOL\DefaultAppPoo ...
- PHP后台代码 及 iOS客户端--AF实现上传视频
//视频转换为MP4 //转码操作... _hud.mode = MBProgressHUDModeIndeterminate; _hud.labelText = @"转码中..." ...
- RabbitMQ官方中文入门教程(PHP版) 第三部分:发布/订阅(Publish/Subscribe)
发布/订阅 在上篇教程中,我们搭建了一个工作队列.每个任务之分发给一个工作者(worker).在本篇教程中,我们要做的之前完全不一样——分发一个消息给多个消费者(consumers).这种模式被称为“ ...
- android开发------编写用户界面之线性布局
一个好的应用程序离不开人性化的用户界面.在学习其他东西之前.理应先学习编写程序的布局(外观) 今天,我们就来学习android的UI布局----LinearLayout. LinearLayout,即 ...
- html5 css3中的一些笔记
<!DOCTYPE html> <html> <head> <meta charset="utf-8" > <title> ...
- git 创建别名
git config --global alias.shortname command 例子如下 git config --global alias.psm 'push origin master' ...
- FastDFS搭建及java整合代码【转】
FastDFS软件介绍 1.什么是FastDFS FastDFS是用C语言编写的一款开源的分布式文件系统.FastDFS为互联网量身定制,充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用.高 ...
- 浏览器XMLHttpRequest案例
/* Cross-Browser XMLHttpRequest v1.2 ================================= Emulate Gecko 'XMLHttpRequest ...