Linux字符设备驱动框架详解


所谓驱动程序,本质上讲是硬件接口,因为操作系统不可能实现每种硬件的接口,所以只对厂商提供接口,只要厂商实现这些接口,就可被操作系统调用,Linux系统驱动程序分为字符设备驱动和块设备驱动,所谓字符设备驱动就是例如键盘驱动,只能顺次读取数据,块设备驱动入硬盘等,可以随机分块读取。而有些程序虽然符合驱动程序规范,但却不真正驱动硬件,而是对操作系统功能的扩充,也称作内核模块。所以驱动程序和内核模块本质上讲属于同一种类别。

操作系统对字符设备驱动提供 file_operations 结构,该结构成员大部分都是回调函数(以下代码摘自Linux 2.6.34内核源码):

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, struct dentry *, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
};

作为驱动程序,只需要实现部分回调函数,注册该结构体,用户态进程便可通过系统调用open,read等调用相应回调函数指针。

首先,系统提供若干宏声明驱动程序的属性,如,入口,作者,描述信息等等。

初始化程序原型为 static int __init initialization(void);若初始化成功则返回0,否则返回错误码。
清理函数原型为static void __exit cleanup(void);其中__init __exit是指定代码段属性的宏,当然也可不指定此属性。

另外宏MODULE_AUTHOR指明作者等等

实现该函数后便可通过宏指明入口点:

module_init(initialization);
module_exit(cleanup);

最简单的驱动程序就是仅仅实现这两个函数,文件simple.c如下:

#include<linux/init.h>
#include<linux/module.h>

static int __init initialization(void)
{
	printk(KERN_INFO " init simple\n");
	return 0;
}

static void __exit cleanup(void)
{
	printk(KERN_INFO " cleanup simple\n");
}

module_init(initialization);
module_exit(cleanup);

MODULE_AUTHOR("alloc cppbreak@gmail.com");
MODULE_DESCRIPTION("A simple linux kernel module");
MODULE_VERSION("V0.1");
MODULE_LICENSE("Dual BSD/GPL");

驱动的编译需要写Makefile文件,内容如下
obj-m := simple.o

编译时需指定Linux内核源码所处位置:
make -C /usr/src/linux M=$PWD modules
其中/usr/src/linux为当前内核源码目录,$PWD为驱动程序所处目录,PWD为当前目录。

执行成功后,会生成simple.ko文件,此文件即为驱动程序。

加载驱动程序可执行 insmod simple.ko 卸载驱动执行 rmmod simple
命令 lsmod 可以查看目前系统加载的驱动程序,modinfo simple.ko 查看程序的基本信息,输出即为程序声明信息:

filename:       simple.ko
license:        Dual BSD/GPL
version:        V0.1
description:    A simple linux kernel module
author:         alloc cppbreak@gmail.com
srcversion:     95E3CE3AB899900656E9CAD
depends:        
vermagic:       2.6.33.3-85.fc13.x86_64 SMP mod_unload

在程序中调用了两次printk,为内核输出函数,这里的输出不会显示到控制台,只会输出到内核,可以通过读取/proc/kmsg文件查看信息,或者调用dmesg命令查看,此为内核跟踪错误的重要手段。KERN_INFO宏只是一个数字字符串,含义为日志级别,可以通过echo num > /proc/sys/kernel/printk 来控制输出信息的级别。

当然,只有此两个函数只能正常加载卸载驱动程序,并没有任何意义,下面通过注册回调函数来实现字符设备的功能,只举一个简单的例子,实现open,read,close函数。

根据file_operations结构成员的原型,这里我们需要实现如下回调:

int (*open) (struct inode *, struct file *);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
int (*release) (struct inode *, struct file *);

open和close函数为了简单起见不做任何处理,只是简单的输出kernel信息:

int simple_open(struct inode * pnode, struct file * pfile)
{
	printk(KERN_INFO "open simple\n");
	return 0;
}

int simple_release(struct inode * pnode, struct file * pfile)
{
	printk(KERN_INFO "close simple\n");
	return 0;
}
  • 1
  • 2
  • 下一页

相关内容