简单的虚拟字符设备驱动的实现
Linux業已成为嵌入式系统的主流,而各种Linux驱动程序的类型中,字符设备无疑是应用最广泛的。本文实现了一个简单的虚拟字符设备的驱动程序,用以演示Linux字符设备驱动的基本原理。在嵌入式Linux的教学中具有重要的作用。
标签:Linux 驱动程序 字符设备 虚拟 嵌入式
Linux作为一种开放源代码的操作系统,在嵌入式系统领域业已成为主流,而为嵌入式Linux系统开发设备驱动程序,也成为一项重要的工作。Linux系统中的驱动程序主要分为三种:字符设备驱动程序、块设备驱动程序和网络驱动程序。其中字符设备是一类只能顺序读写,没有缓存的驱动程序,其实现方法相对简单,而应用则最为广泛。在嵌入式Linux的教学中,字符设备驱动程序也是一项重要内容。为了让学生能够理解字符设备驱动程序的原理,需要一个简单的字符设备驱动的例子,用以进行演示。
一、基本原理
把设备当作文件处理,是Linux系统的重要思想,即“一切皆文件”。在用户空间中,应用程序对
字符设备的操作跟读写普通文件没有什么区别,也是通过open()、close()、read()、write()等函数实现的。操作系统将这些用户空间中的函数分别映射到内核空间中由驱动程序提供的对应接口。因此,内核空间中的驱动程序就需要通过对对应接口函数的实现来实现对用户空间中应用程序的支持。
file_opreations是字符设备驱动中最重要的结构,它包含了字符设备各种可能的接口函数。通常在嵌入式编程中,我们不需要全部实现,只需要实现我们实际用到的接口就可以了,这样可以有效降低程序的大小。该结构被定义在头文件“linux/fs.h”中,使用时只需声明该结构的一个变量并进行填充即可。
二、环境准备
为了进行嵌入式Linux的开发,必须首先安装Linux系统。这里采用最常用的Windows系统+VMWare虚拟机的形式,系统版本为RedHat Enterprise Linux 6.4,其自带的内核版本为2.6.32-358.el6.i686。该版本比之前沿用的RedHat9更新,同时也是一个被验证过的非常稳定的系统。
交叉编译器采用网上下载的Arm-Linux-gcc 4.5.1版本,同样兼顾到版本更新和稳定性之间的平衡关系。
各软件的安装过程本文不再赘述。
三、Hello world模块
在Linux系统中,驱动程序是一个系统模块,可以被加载如内核,也可以在有需要时随时加载。因此,编写驱动程序必须以内核模块为基础。一个Helloworld模块的功能为载入时在系统日志中写入“Hello world!”,并在卸载时写入“Goodbye world!”。部分源代码如下:
int __init hello_init(void){
printk(KERN_ALERT”Hello world!”);
return 0;
}
void __exit hello_exit(void){
printk(KERN_ALERT”Goodbye world!\n”);
}
四、字符设备驱动的框架设计
字符设备驱动的主要作用,是实现file_operations结构中包含的各种接口函数。为了简单起见,本文只实现了读取和写入所需的两个函数。设备的读写分别对应ssize_t (*read) (struct file *, char __user *, size_t, loff_t *)和ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *)两个函数,其中char __user *表示用户空间中读写内容的指针,size_t为要读写的字节数,返回值为实际读写的字节数。
接口函数实现以后,需要声明一个file_operations结构的变量并将其成员赋值为刚实现的接口函数,最后在模块初始化函数中进行注册。
相关源代码如下:
ssize_t read(struct file* fp, char __user *buff, size_t size, loff_t* loff){
int i;
for(i=0;i<size;i++){
if(Char_read(mycd, &buff[i]) == -1)
return i;
}
return i;
}
ssize_t write(struct file* fp, const char __user *buff, size_t size, loff_t* loff){
int i;
for(i=0;i<size;i++){
if(Char_write(mycd, buff[i]) == -1)
return i;
}
return i;
}
struct file_operations fop={
.read = read, .write = write,
.open = open, .release = release,
.owner = THIS_MODULE字符串长度头文件
};其中Char_read()和Char_write()用于操作硬件读写单个字符,将在下一步实现。结构体file_operations的变量fop采用了指定初始化的方式,这种写法具有更大的灵活性,可以不必按照结构体定义的顺序对成员进行初始化,属于C99的新增规则。
五、具体功能的实现
为了增强程序的可读性,也为了扩展方便,本例子将程序的框架和具体功能分开编写,具体的功能实现包含在单独的头文件中。
首先是一个自定义的设备结构。这里采用虚拟设备,该设备用于保存并读写一定长度的字符串。结构定义为:
struct MyCharDev{
char reg1; size_t count;
char* buff; size_t size;
};
分别用两个函数Char_init()和Char_uninit()对虚拟设备进行初始化和清理工作,分别在模块初始化和模块卸载函数中调用这两个函数。此外,还定义了读取和写入两个函数。
六、程序的测试
编译源代码。创建文件节点,加载设备,并编写一个简单的应用程序进行测试。
测试程序部分源代码:
int main(){
int fd = 0;
char str1[] = “Hello world!”;
char str2[20] = ““;
fd = open(”./MyChrDev”, O_RDWR);
puts(str1);
write(fd, str1, 12);
read(fd, str2, 12);
puts(str2);
return 0;
}
测试结果:
[root@XiaocaihuRHEL MyCharDev]# ./test
Hello world!
!dlrow olleH
总结
本文实现了一个用于教学目的的简单的虚拟字符设备驱动程序,用简单的代码演示了字符设备驱动的原理,并进行了测试。但为了简单起见,只实现了最基本的功能,并且简化了错误处理的代码。这些都是后续可以进一步探讨的内容。
参考文献
[1]曹颖鹏.基于嵌入式Linux驱動程序的研究与设计[D].西安电子科技大学.2010
[2]聂和平.基于ARM9的嵌入式Linux系统移植与驱动开发[D].南京邮电大学.2013
[3]付阳.基于ARM9的嵌入式Linux移植和驱动程序设计[D].华中科技大学.2012
[4]李桦,高飞,孙磊.嵌入式Linux设备驱动程序研究[J].微计算机信息,2010,14:68-70.
[5]张吉红.嵌入式Linux设备驱动并发控制的研究[D].长安大学.2013.
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论