【驱动】linux设备驱动入门

linux设备驱动

驱动程序英文全称Device Driver,也称作设备驱动程序。驱动程序是用于计算机和外部设备通信的特殊程序,相当于软件和硬件的接口,通常只有操作系统能使用驱动程序。

在现代计算机体系结构中,操作系统并不直接于硬件打交道,而是通过驱动程序于硬件通信。

设备驱动介绍

驱动程序是附加到操作系统的一段程序,通常用于硬件通信。

每种硬件都有自己的驱动程序,其中包含了硬件设备的信息。操作系统通过驱动程序提供的硬件信息与硬件设备通信。由于驱动设备的重要性,在安装操作系统后需要安装驱动程序,外部设备才能正常工作。

Linux内核自带了相当多的设备驱动程序,几乎可以驱动目前主流的各种硬件设备。

在同一台计算机上,尽管设备是相同的,但是由于操作系统不同,驱动程序是有很大差别的。但是,无论什么系统驱动程序的功能都是相似的,可以归纳为下面三点:

初始化硬件设备。

这是驱动程序最基本的功能,初始化通过总线识别设备,访问设备寄存器,按照需求配置设备地端口,设置中断等。

向操作系统提供统一的软件接口。

设备驱动程序向操作系统提供了一类设备通用的软件接口,如硬盘设备向操作系统提供了读写磁盘块、寻址等接口,无论是哪种品牌的硬盘驱动向操作系统提供的接口都是一致的。

提供辅助功能。

现代计算机的处理能力越来越强,操作系统有一类虚拟设备驱动,可以模拟真实设备的操作,如虚拟打印机驱动向操作系统提供了打印机的接口,在系统没有打印机制情况下仍然可以执行打印操作。

Linux内核模块

Linux内核模块是一种可以被内核动态加载和卸载的可执行程序。

通过内核模块可以扩展内核的功能,通常内核模块被用于设备驱动、文件系统等。如果没有内核模块,需要向内核添加功能就需要修改代码、重新编译内核、安装新内核等步骤,不仅繁琐而且容易保出错,不易于调试。

内核模块简介

Linux内核是一个整体结构,可以把内核想象成一个巨大的程序,各种功能结合在一起。当修改和添加新功能的时候,需要重新生成内核,效率较低。

为了弥补整体式内核的缺点,Linux内核的开发者设计了内核模块机制。

从代码的角度看,内核模块是一组可以完成某种功能的函数集合。

从执行的角度看,内核模块可以看做是一个已经编译但是没有连接的程序。

内核模块是一个应用程序,但是与普通应用程序有所不同,区别在于:

运行环境不同。

内核模块运行在内核空间,可以访问系统的几乎所有的软硬件资源;普通应用程序运行在用户空间,可以访问的资源受到限制。这也是内核模块与普通应用程序最主要的区别。由于内核模块可以获得与操作系统内核相同的权限,因此在编程的时候应该格外注意,可能在用户空间看到的一点小错误在内核空间就会导致系统崩溃。

功能定位不同。

普通应用程序为了完成某个特定的目标,功能定位明确;内核模块是为其他的内核模块以及应用程序服务的,通常提供的是通用的功能。

函数调用方式不同。

内核模块只能调用内核提供的函数,访问其他的函数会导致运行异常;普通应用程序可能调用自身以外的函数,只要能正确连接就有运行。

内核模块的结构

内核编程与用户空间编程最大的区别就是程序的并发性。

在用户空间,除多线程应用程序外,大部分应用程序的运行是顺序执行的,在程序执行过程中不必担心被其他程序改变执行的环境。而内核的程序执行环境要复杂的多,即时最简单的内核模块也要考虑到并发执行的问题。

设计内核模块的数据结构要十分小心。由于代码的可重入特性,必须考虑到数据结构在多线程环境下不被其他线程破坏,对于共享数据更是应该采用加锁的方法保护。驱动程序员的通常错误是假定某段代码不会出现并发,导致数据被破坏而很难于调试。

linux内核模块使用物理内存,这点与应用程序不同。应用程序使用虚拟内存,有一个巨大的地址空间,在应用程序中可以分配大块的内存。内核模块可以供使用的内存非常小,最小可能小到一个内存页面(4096字节)。在编写内核模块代码的时候要注意内存的分配和使用。

内核模块至少支持加载和卸载两种操作。因此,一个内核模块至少包括加载和卸载两个函数。在linux 2.6系列内核中,通过module_init()宏可以在加载内核模块的时候调用内核模块的初始化函数,module_exit()宏可以在卸载内核模块的时候调用内核模块的卸载函数。

内核模块的初始化和卸载函数是有固定格式的。

static int __init init_func(void); //初始化函数static void __exit exit_func(void); //清除函数

这两个函数的名称可以由用户自己定义,但是必须使用规定的返回值和参数格式。

static修饰符的作用是函数仅在当前文件有效,外部不可见;

__init关键字告诉编译器,该函数代码在初始化完毕后被忽略;

__exit关键字告诉编译器,该代码仅在卸载模块的时候被调用;

内核模块的加载

linux内核提供了一个kmod的模块用来管理内核模块。

kmod模块与用户态的kmodule模块通信,获取内核模块的信息。

通过insmod命令和modprobe命令都可以加载一个内核模块。

insmod命令加载内核模块的时候不检查内核模块的符号是否已经在内核中定义。

modprobe不仅检查内核模块符号表,而且还会检查模块的依赖关系。

另外,linux内核可以在需要加载某个模块的时候,通过kmod机制通知用户态的modprobe加载模块。

然后拍一些美得想哭的照片,留给老年的自己。

【驱动】linux设备驱动入门

相关文章:

你感兴趣的文章:

标签云: