• 第 1 章 Linux驱动开发概述


    设备驱动程序是计算机硬件与应用程序的接口,是软件系统与硬件系统沟通的桥梁。如果没有设备驱动程序,那么硬件设备就只是一堆废铁,没有什么功能。本章将对Linux驱动开发进行简要的概述,使读者理解一些常见的概念。

    1.1 Linux设备驱动的基本概念

    刚刚接触Linux设备驱动的朋友,会对Linux设备驱动中的一些基本概念不太理解。这种不理解,会导致继续学习的困难,所以本节集中讲解一些Linux设备驱动的基本概念,为进一步学习打下良好的基础。

    1.1.1 设备驱动程序概述

    设备驱动程序(Device Driver),简称驱动程序(Driver)。它是一个允许计算机软件(Computer Software)与硬件(Hardware)交互的程序。这种程序建立了一个硬件与硬件,或硬件与软件沟通的界面。CPU经由主板上的总线(Bus)或其他沟通子系统(Subsystem)与硬件形成连接,这样的连接使得硬件设备(Device)之间的数据交换成为可能。

    依据不同的计算机架构与操作系统平台差异,驱动程序可以是8位、16位、32位,64位。不同平台的操作系统需要不同的驱动程序。例如32位的Windows系统需要32位的驱动程序,64位的Windows系统,需要64位的驱动程序;在Windows 3.11的16位操作系统时代,大部分的驱动程序都是16位;到了32位的Windows XP,则大部分是使用32位驱动程序;至于64位的Linux或是Windows 7平台上,就必须使用64位驱动程序。

    1.1.2 设备驱动程序的作用

    设备驱动程序是一种可以使计算机与设备进行通信的特殊程序,可以说相当于硬件的接口。操作系统只有通过这个接口,才能控制硬件设备的工作。假如某设备的驱动程序未能正确安装,便不能正常工作。正因为这个原因,驱动程序在系统中所占的地位十分重要。一般,当操作系统安装完毕后,首要的便是安装硬件设备的驱动程序。并且,当设备驱动程序有更新的时候,新的驱动程序比旧的驱动程序有更好的性能。这是因为新的驱动程序对内存、IO等进行了优化,使硬件能够到达更好的性能。

    但是,大多数情况下,并不需要安装所有硬件设备的驱动程序。例如硬盘、显示器、光驱、键盘和鼠标等就不需要安装驱动程序,而显卡、声卡、扫描仪、摄像头和Modem等就需要安装驱动程序。不需要安装驱动程序并不代表这些硬件不需要驱动程序,而是这些设备所需驱动已经内置在操作系统中。另外,不同版本的操作系统对硬件设备的支持也是不同的,一般情况下,版本越高所支持的硬件设备也越多。

    设备驱动程序用来将硬件本身的功能告诉操作系统(通过提供接口的方式),完成硬件设备电子信号与操作系统及软件的高级编程语言之间的互相翻译。当操作系统需要使用某个硬件时,例如让声卡播放音乐,它会先发送相应指令到声卡的某个I/O端口。声卡驱动程序从该I/O端口接收到数据后,马上将其翻译成声卡才能听懂的电子信号命令,从而让声卡播放音乐。所以简单地说,驱动程序是提供硬件到操作系统的一个接口,并且协调二者之间的关系。而因为驱动程序有如此重要的作用,所以人们都称“驱动程序是硬件的灵魂”、“硬件的主宰”,同时驱动程序也被形象地称为“硬件和系统之间的桥梁”。

    1.1.3 设备驱动的分类

    计算机系统的主要硬件由CPU、存储器和外部设备组成。驱动程序的对象一般是存储器和外部设备。随着芯片制造工艺的提高,为了节约成本,通常将很多原属于外部设备的控制器嵌入到CPU内部。例如Intel的酷睿i5 3450处理器,就内置有GPU单元,配合“需要搭配内建GPU的处理器”的主板,就能够起到显卡的作用。相比独立显卡,性价比上有很大的优势。所以现在的驱动程序应该支持CPU中的嵌入控制器。Linux将这些设备分为3大类,分别是字符设备、块设备和网络设备。

    1. 字符设备

    字符设备是指那些能一个字节一个字节读取数据的设备,如LED灯、键盘和鼠标等。字符设备一般需要在驱动层实现open()、close()、read()、write()、ioctl()等函数。这些函数最终将被文件系统中的相关函数调用。内核为字符设备对应一个文件,如字符设备文件/dev/console。对字符设备的操作可以通过字符设备文件/dev/console来进行。这些字符设备文件与普通文件没有太大的差别,差别之处是字符设备一般不支持寻址,但特殊情况下,有很多字符设备也是支持寻址的。寻址的意思是,对硬件中一块寄存器进行随机地访问。不支持寻址就是只能对硬件中的寄存器进行顺序的读取,读取数据后,由驱动程序自己分析需要哪一部分数据。

    2. 块设备

    块设备与字符设备类似,一般是像磁盘一样的设备。在块设备中还可以容纳文件系统,并存贮大量的信息,如U盘、SD卡。在Linux系统中,进行块设备读写时,每次只能传输一个或者多个块。Linux可以让应用程序像访问字符设备一样访问块设备,一次只读取一个字节。所以块设备从本质上更像一个字符设备的扩展,块设备能完成更多的工作,例如传输一块数据。

    综合来说,块设备比字符设备要求更复杂的数据结构来描述,其内部实现也是不一样的。所以,在Linux内核中,与字符驱动程序相比,块设备驱动程序具有完全不同的API接口。

    3. 网络设备

    计算机连接到互联网上需要一个网络设备,网络设备主要负责主机之间的数据交换。与字符设备和块设备完全不同,网络设备主要是面向数据包的接收和发送而设计的。网络设备在Linux操作系统中是一种非常特殊的设备,其没有实现类似块设备和字符设备的read()、write()和ioctl()等函数。网络设备实现了一种套接字接口,任何网络数据传输都可以通过套接字来完成。

    1.2 Linux操作系统与驱动的关系

    Linux操作系统与设备驱动之间的关系如图1.1所示。用户空间包括应用程序和系统调用两层。应用程序一般依赖于函数库,而函数库是由系统调用来编写的,所以应用程序间接地依赖于系统调用。

    图 1.1 设备驱动程序与操作系统的关系

    系统调用层是内核空间和用户空间的接口层,就是操作系统提供给应用程序最底层的API。通过这个系统调用层,应用程序不需要直接访问内核空间的程序,增加了内核的安全性。同时,应用程序也不能访问硬件设备,只能通过系统调用层来访问硬件设备。如果应用程序需要访问硬件设备,那么应用程序先访问系统调用层,由系统调用层去访问内核层的设备驱动程序。这样的设计,保证了各个模块的功能独立性,也保证了系统的安全。

    系统调用层依赖内核空间的各个模块来实现。在Linux内核中,包含很多实现具体功能的模块。这些模块包括文件系统、网络协议栈、设备驱动、内核调度、内存管理、进程管理等,都属于系统内核空间,也就是这些模块是在内核空间实现的。

    最底层是硬件层,这一层是实际硬件设备的抽象。设备驱动程序的功能就是驱动这一层硬件。设备驱动程序可以工作在有操作系统的情况下,也可以工作在没有操作系统的情况下。如果只需要实现一些简单的控制设备操作,那么可以不使用操作系统。如果嵌入式系统完成的功能比较复杂,则往往需要操作系统来帮忙。例如,单片机程序,就不需要操作系统,因为其功能简单,内存、处理器能力弱,不能也没有必要为其开发操作系统。

    1.3 Linux驱动程序开发

    Linux驱动程序的开发与应用程序的开发有很大的差别。这些差别导致了编写Linux设备驱动程序与编写应用程序有本质的区别,所以对于应用程序的设计技巧很难直接应用在驱动程序的开发上。最经典的例子是应用程序如果错误可以通过try catch等方式,避免程序的崩溃,驱动程序则没有这么好的处理方式。本节将对Linux驱动程序的开发进行简要的讲解。

    1.3.1 用户态和内核态

    Linux操作系统分为用户态和内核态。用户态处理上层的软件工作。内核态用来管理用户态的程序,完成用户态请求的工作。驱动程序与底层的硬件交互,所以工作在内核态。

    简单来说,内核态大部分时间在完成与硬件的交互,比如读取内存,将硬盘上的数据读取到内存中,调度内存中的程序到处理器中运行等。相对于内核态,用户态则自由得多,其实,用户态中的用户可以狭隘的理解为应用程序开发者,他们很少与硬件直接打交道,他们经常的工作是编写Java虚拟机下的应用程序,或者.NET框架下的应用程序。即使他们的编程水平很初级,经常出现程序异常,那么充其量是将Java虚拟机搞崩溃,想把操作系统搞崩溃还是很难的。这一切都归功于内核态对操作系统有很强大的保护能力。

    另一方面,Linux操作系统分为两个状态的原因主要是,为应用程序提供一个统一的计算机硬件抽象。工作在用户态的应用程序完全可以不考虑底层的硬件操作,这些操作由内核态程序来完成。这些内核态程序大部分是设备驱动程序。一个好的操作系统的驱动程序对用户态应用程序应该是透明的,也就是说,应用程序可以在不了解硬件工作原理的情况下,很好地操作硬件设备,同时不会使硬件设备进入非法状态。Linux操作系统很好的做到了这一点。在Linux编程中,程序员经常使用open()方法来读取磁盘中的数据,在调用这个方法的时候,并不需要关心磁盘控制器是怎么读取数据,并将其传到内存中的。这些工作都是驱动程序完成的,这就是驱动程序的透明性。

    一个值得注意的问题是,工作在用户态的应用程序不能因为一些错误而破坏内核态的程序。现代处理器已经充分考虑了这个问题。处理器提供了一些指令,分为特权指令和普通指令。特权指令只有在内核态下才能使用;普通指令既可以在内核态使用,也可以在用户态使用。通过这种限制,用户态程序就不能执行只有在内核态才能执行的程序了,从而起到保护的作用。

    另一个值得注意的问题是,用户态和内核态是可以互相转换的。每当应用程序执行系统调用或者被硬件中断挂起时,Linux操作系统都会从用户态切换到内核态。当系统调用完成或者中断处理完成后,操作系统会从内核态返回用户态,继续执行应用程序。

     

    https://www.ituring.com.cn/book/tupubarticle/2978

  • 相关阅读:
    GitLab使用公钥SSH key登录
    P1305 新二叉树 /// 二叉树的先序遍历
    P1030 求先序排列 /// 二叉树的遍历
    P1020 导弹拦截 /// DP Dilworth定理 LIS、LDS优化
    USACO 2008 November Gold Cheering up the Cows /// MST oj24381
    USACO 2009 Open Grazing2 /// DP+滚动数组oj26223
    Mid-Atlantic 2008 Lawrence of Arabia /// 区间DP oj21080
    炮兵阵地 /// 状压DP oj26314
    Post Office IOI 2000 /// 区间DP oj24077
    Print Article /// 斜率优化DP oj26302
  • 原文地址:https://www.cnblogs.com/feng9exe/p/12356033.html
Copyright © 2020-2023  润新知