了解并学习Linux设备驱动的基础知识

了解并学习Linux设备驱动的基础知识,第1张

设备驱动充当了硬件和应用软件之间的纽带,它使得应用软件只需要调用系统软件的应用编程接口(API)就可让硬件去完成要求的工作。本文主要讲解了Linux设备驱动与硬件的关系,Linux设备驱动的开发模式以及内核中相关的重要基础数据结构。

设备驱动与硬件的关系

对设备驱动最通俗的解释就是“驱使硬件设备行动”。驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮询、中断处理、 DMA 通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据。

设备分类

Linux对将外设分为3类:

字符设备

字符设备指那些必须以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等。

块设备

块设备可以用任意顺序进行访问,以块为单位进行 *** 作,如硬盘、软驱等。

网络设备

网络设备面向数据包的接收和发送而设计,它并不对应于文件系统的节点。

总体框图

了解并学习Linux设备驱动的基础知识,第2张

机制与策略

机制强调“提供什么能力”,而策略旨在“如何使用这些能力”。因此驱动开发需要遵守的是驱动程序的角色是提供机制, 而不是策略。
编写内核代码来存取硬件, 但是不能强加特别的策略给用户, 因为不同的用户有不同的需求。驱动应当做到使硬件可用, 将所有关于如何使用硬件的事情留给应用程序。

内核模块

Linux 提供了一种代码动态地加载到内核中机制,这种机制被称为模块(Module)。具有如下特点:

模块本身不被编译入内核映像, 从而控制了内核的大小。

模块一旦被加载,它就和内核中的其他部分完全一样。

可动态加载与移除,不需重启系统,节约开发时间。

模块放在用户空间,这部分代码可以不开源。

因此驱动多数情况以内核模块的形式加载到内核。

组成

一个 Linux 内核模块主要由如下几个部分组成:

模块加载函数(一般需要)

当通过 insmod 或 modprobe 命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。staTIc int __init iniTIalizaTIon_funcTIon(void){ /* 初始化代码 */}module_init(initialization_function);

模块卸载函数(一般需要)

当通过 rmmod 命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块卸载函数相反的功能。static void __exit cleanup_function(void){ /* 释放代码 */}module_exit(cleanup_function);

模块许可证声明(必须)

许可证( LICENSE)声明描述内核模块的许可权限,如果不声明 LICENSE,模块被加载时,将收到内核被污染 ( kernel tainted)的警告。在 Linux 2.6 内核中,可接受的 LICENSE 包括“ GPL”、“ GPL v2”、“ GPL and additional rights”、“ Dual BSD/GPL”、“ Dual MPL/GPL” 和“ Proprietary”。

模块参数(可选)

模块参数是模块被加载的时候可以被传递给它的值,它本身对应模块内部的全局变量。module_param(参数名,参数类型,参数读/写权限);module_param_array(数组名,数组类型,数组长,参数读/写权限);

模块导出符号(可选)

内核模块可以导出符号( symbol,对应于函数或变量),这样其他模块可以使用本模块中的变量或函数。EXPORT_SYMBOL(符号名);EXPORT_SYMBOL_GPL(符号名);

模块作者等信息声明(可选)

MODULE_AUTHOR(author);MODULE_DESCRIPTION(description);MODULE_VERSION(version_string);MODULE_DEVICE_TABLE(table_info);MODULE_ALIAS(alternate_name);

编译

# Makefile2.6TARGET = demo_moduleifneq ($(KERNELRELEASE),)#kbuild syntax. dependency relationshsip of files and target modules are listed here.obj-m := $(TARGET).o else# build from shell directly not in kernel rootCURDIR = $(shell pwd)KVER := $(shell uname -r)KDIR := /lib/modules/$(KVER)/buildall: $(MAKE) -C $(KDIR) M=$(CURDIR) modulesclean: $(MAKE) -C $(KDIR) M=$(CURDIR) cleaninsert: sudo insmod $(TARGET).koremove: sudo rmmod $(TARGET)endif

重要的数据结构

大部分的基础性的驱动 *** 作包括3个重要的内核数据结构, 称为 file_operations, file, 和 inode。

file_operations

file_operations 结构体中的成员函数是设备驱动程序设计的主体内容,这些函数实际会在应用程序进行 Linux 的 open()、 write()、 read()、 close()等系统调用时最终被调用。

file

file 结构体代表一个打开的文件(设备对应于设备文件),系统中每个打开的文件在内核空间都有一个关联的 struct file。它由内核在打开文件时创建,并传递给在文件上进行 *** 作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。

struct file { const struct file_operations *f_op; /* 和文件关联的 *** 作*/ unsigned int f_flags; /*文件标志,如 O_RDONLY、 O_NONBLOCK、 O_SYNC*/ fmode_t f_mode; /*文件读/写模式, FMODE_READ 和 FMODE_WRITE*/ loff_t f_pos; /* 当前读写位置*/ void *private_data; /*文件私有数据,可存储自定义数据的指针*/ ...};

inode

VFS inode 包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。它是 Linux 管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。

struct inode { umode_t i_mode; /* inode 的权限 */ uid_t i_uid; /* inode 拥有者的 id */ gid_t i_gid; /* inode 所属的群组 id */ dev_t i_rdev; /* 若是设备文件,此字段将记录设备的设备号 */ loff_t i_size; /* inode 所代表的文件大小 */ struct timespec i_atime; /* inode 最近一次的存取时间 */ struct timespec i_mtime; /* inode 最近一次的修改时间 */ struct timespec i_ctime; /* inode 的产生时间 */ unsigned long i_blksize; /* inode 在做 I/O 时的区块大小 */ unsigned long i_blocks; /* inode 所使用的 block 数,一个 block 为 512 byte*/ struct block_device *i_bdev; /*若是块设备,为其对应的 block_device 结构体指针*/ struct cdev *i_cdev; /*若是字符设备,为其对应的 cdev 结构体指针*/ ...};
 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/dianzi/2612923.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-08-10
下一篇 2022-08-10

发表评论

登录后才能评论

评论列表(0条)

保存