热门搜索: 新浪 网易爸爸 阿里妈妈 百度爷爷 新浪 网易爸爸 阿里妈妈 百度爷爷 新浪

当前位置:首页 > 数码配件 > 正文

Linux设备驱动Hello World程序介绍

作者:  数码之家 | 发布时间:  2009年6月17日 | 栏目:  (数码配件)

自古以来,学习一门新编程语言的第一步就是写一个打印“hello world”的程序在本文中,我们将用同样的方式学习如何编写一个简单的linux内核模块和设备驱动程序。我将学习到如何在内核模式下以三种不同的方式来打印hello world,这三种方式分别是: printk(),/proc文件,/dev下的设备文件。

准备:安装内核模块的编译环境

一个内核模块kernel module是一段能被内核动态加载和卸载的内核代码,因为内核模块程序是内核的一个部分,并且和内核紧密的交互,所以内核模块不可能脱离内核编译环境,至少,它需要内核的头文件和用于加载的配置信息。编译内核模块同样需要相关的开发工具,比如说编译器。为了简化,本文只简要讨论如何在Debian、Fedora和其他以.tar.gz形式提供的原版linux内核下进行核模块的编译。在这种情况下,你必须根据你正在运行内核相对应的内核源代码来编译你的内核模块kernel module(当你的内核模块一旦被装载到你内核中时,内核就将执行该模块的代码)
 
必须要注意内核源代码的位置,权限:内核程序通常在/usr/src/linux目录下,并且属主是root。如今,推荐的方式是将内核程序放在一个非root用户的home目录下。本文中所有命令都运行在非root的用户下,只有在必要的时候,才使用sudo来获得临时的root权限。配置和使用sudo可以man sudo(8) visudo(8) 和sudoers(5)。或者切换到root用户下执行相关的命令。不管什么方式,你都需要root权限才能执行本文中的一些命令。
在Debian下编译内核模块的准备
使用如下的命令安装和配置用于在Debian编译内核模块的module-assitant包
 $ sudo apt-get install module-assistant
以此你就可以开始编译内核模块,你可以在《Debian Linux Kernel Handbook》这本书中找到对Debian内核相关任务的更深度的讨论。
Fedora的kernel-devel包包含了你编译Fedora内核模块的所有必要内核头文件和工具。你可以通过如下命令得到这个包。
 $ sudo yum install kernel-devel
有了这个包,你就可以编译你的内核模块kernel modules。关于Fedora编译内核模块的相关文档你可以从Fedora release notes中找到。
一般Linux 内核源代码和配置
(译者注,下面的编译很复杂,如果你的Linux不是上面的系统,你可以使用REHL AS4系统,这个系统的内核就是2.6的内核,并且可以通过安装直接安装内核编译支持环境,从而就省下了如下的步骤。而且下面的步骤比较复杂,建议在虚拟机安装Linux进行实验。)
如果你选择使用一般的Linux内核源代吗,你必须,配置,编译,安装和重启的你编译内核。这个过程非常复杂,并且本文只会讨论使用一般内核源代码的基本概念。
linux的著名的内核源代码在http://kernel.org上都可以找到。最近新发布的稳定版本的代码在首页上。下载全版本的源代码,不要下载补丁代码。例如,当前发布稳定版本在url: http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.21.5.tar.bz2上。如果需要更快速的下载,从htpp://kernel.org/mirrors上找到最近的镜像进行下载。最简单获得源代码的方式是以断点续传的方式使用wget。如今的http很少发生中断,但是如果你在下载过程中发生了中断,这个命令将帮助你继续下载剩下的部分。
解包内核源代码
$ tar xjvf linux-<version>.tar.bz2
现在你的内核源代码位于linux-/目录下。转到这个目录下,并配置它:
$ cd linux-<version>
$ make menuconfig
一些非常易用的编译目标make targets提供了多种编译安装内核的形式:Debian 包,RPM包,gzip后的tar文件 等等,使用如下命令查看所有可以编译的目标形式
 $ make help
一个可以工作在任何linux的目标是:(译者注:REHL AS4上没有tar-pkg这个目标,你可以任选一个rpm编译,编译完后再上层目录可以看到有一个linux-.tar.gz可以使用)
 $ make tar-pkg
当编译完成后,可以调用如下命令安装你的内核
 $ sudo tar -C / -xvf linux-<version>.tar
在标准位置建立的到内核源代码的链接
 $ sudo ln -s <location of top-level source directory> /lib/modules/'uname -r'/build
现在已经内核源代码已经可以用于编译内核模块了,重启你的机器以使得你根据新内核程序编译的内核可以被装载。

使用printk()函数打印”Hello World”

我们的第一个内核模块,我们将以一个在内核中使用函数printk()打印”Hello world”的内核模块为开始。printk是内核中的printf函数。printk的输出打印在内核的消息缓存kernel message buffer并拷贝到/var/log/messages(关于拷贝的变化依赖于如何配置syslogd)
下载hello_printk 模块的tar包 并解包:
 $ tar xzvf hello_printk.tar.gz
这个包中包含两个文件:Makefile,里面包含如何创建内核模块的指令和一个包含内核模块源代码的hello_printk.c文件。首先,我们将简要的过一下这个Makefile 文件。
 obj-m := hello_printk.o
obj-m指出将要编译成的内核模块列表。.o格式文件会自动地有相应的.c文件生成(不需要显示的罗列所有源代码文件)
 KDIR  := /lib/modules/$(shell uname -r)/build
KDIR表示是内核源代码的位置。在当前标准情况是链接到包含着正在使用内核对应源代码的目录树位置。
 PWD := $(shell pwd)
PWD指示了当前工作目录并且是我们自己内核模块的源代码位置
 default:
     $(MAKE) -C $(KDIR) M=$(PWD) modules
default是默认的编译连接目标;即,make将默认执行本条规则编译目标,除非程序员显示的指明编译其他目标。这里的的编译规则的意思是,在包含内核源代码位置的地方进行make,然后之编译$(PWD)(当前)目录下的modules。这里允许我们使用所有定义在内核源代码树下的所有规则来编译我们的内核模块。
现在我们来看看hello_printk.c这个文件
 1.#include
2.    <linux/init.h>
3.#include
4.    <linux/module.h>
这里包含了内核提供的所有内核模块都需要的头文件。这个文件中包含了类似module_init()宏的定义,这个宏稍后我们将用到
 1.static int __init
2.hello_init(void){
3.    printk("Hello, world!n");
4.    return 0;
5.}
这是内核模块的初始化函数,这个函数在内核模块初始化被装载的时候调用。__init关键字告诉内核这个代码只会被运行一次,而且是在内核装载的时候。printk()函数这一行将打印一个”Hello, world”到内核消息缓存。printk参数的形式在大多数情况和printf(3)一模一样。
 1.module_init(hello_init);
2.module_init()
宏告诉内核当内核模块第一次运行时哪一个函数将被运行。任何在内核模块中其他部分都会受到内核模块初始化函数的影响。
 1.static void __exit
2.hello_exit(void){
3.    printk("Goodbye, world!n");
4.}
5.module_exit(hello_exit);
同样地,退出函数也只在内核模块被卸载的时候会运行一次,module_exit()宏标示了退出函数。__exit关键字告诉内核这段代码只在内核模块被卸载的时候运行一次。
 1.MODULE_LICENSE("GPL");
2.MODULE_AUTHOR("Valerie Henson val@nmt.edu");
3.MODULE_DESCRIPTION("Hello, world!" minimal module");
4.MODULE_VERSION("printk");
5.MODULE_LICENSE()
宏告诉内核,内核模块代码在什么样的license之下,这将影响主那些符号(函数和变量,等等)可以访问主内核。GPLv2 下的模块(如同本例子中)能访问所有的符号。某些内核模块license将会损害内核开源的特性,这些license指示内核将装载一些非公开或不受信的代码。如果内核模块不使用MODULE_LICENSE()宏,就被假定为非GPLv2的,这会损害内核的开源特性,并且大部分Linux内核开发人员都会忽略来自受损内核的bug报告,因为他们无法访问所有的源代码,这使得调试变得更加困难。剩下的MODULE_*()这些宏以标准格式提供有用的标示该内核模块的信息(译者注:这里意思是,你必须使用GPLv2的license,否则你的驱动程序很有可能得不到Linux社区的开发者的支持 :))
现在,开始编译和运行代码。转到相应的目录下,编译内核模块
$ cd hello_printk
$ make
接着,装载内核模块,使用insmod指令,并且通过dmesg来检查打印出的信息,dmesg是打印内核消息缓存的程序。
$ sudo insmod ./hello_printk.ko
$ dmesg | tail
你将从dmesg的屏幕输出中看见”Hello world!”信息。现在卸载使用rmmod卸载内核模块,并检查退出信息。
$ sudo rmmod hello_printk
$ dmesg | tail
到此,你就成功地完成了对内核模块的编译和安装!


标签:数码  科技  网络