歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux字符設備驅動框架詳解

Linux字符設備驅動框架詳解

日期:2017/3/1 10:15:13   编辑: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 [email protected]");
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 [email protected]
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;
}
Copyright © Linux教程網 All Rights Reserved