嵌入式Linux驱动开发简介

目录

1.  Linux设备驱动

1.1  驱动程序

1.2  驱动设备

1.2.1  字符设备

1.2.2  块设备

1.2.3  网络设备

2.  Linux操作系统与驱动的关系

3.  Lnux驱动程序开发

3.1  用户态和内核态

3.2  模块机制


1.  Linux设备驱动

1.1  驱动程序

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

        依据不同的计算机架构与操作系统差异平台,驱动程序可以是8位(8b)、16位(16b)、 32位(32b),64位(64b),这是为了调和操作系统与驱动程序之间的依存 关系。例如在Windows3.11的16位操作系统时代,大部分的驱动程序都是16位;到了 32位的WindowsXP,则大部分是使用32位驱动程序;至于64位的Linux或是Windows Vista平台上,就必须使用64位驱动程序。

1.2  驱动设备

        计算机系统的主要硬件由CPU、存储器和外部设备组成。驱动程序的对象一般是存储器和外部设备。随着芯片制造工艺的提高,为了节约成本,通常将很多原属于外部设备的控制器嵌入到CPU内部。所以现在的驱动程序应该支持CPU中的嵌入控制器。Linux将这些设备分为3大类,分别是字符设备、块设备、网络设备。

1.2.1  字符设备

        字符设备是指那些能一个字节一个字节读取数据的设备,如LED灯、键盘、鼠标等。字符设备一般需要在驱动层实现open()、 cloge0 、read()、write()、ioctl()等函数。这些函数最终将被文件系统中的相关函数调用。内核为字符设备对应一个文件,如字符设备文件/dev/cosole 。对字符设备的操作可以通过字符设备文件 |dev/cosole 来进行。这些字符设备文件与普通文件没有太大的差别,差别之处是字符设备一般不支持寻址,但特殊情况下,有很多字符设备也是支持寻址的。

1.2.2  块设备

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

1.2.3  网络设备

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

类型特点访问方式典型例子
字符设备以字节流为单位进行顺序访问。通过设备文件(如 /dev/ttyS0),使用 openreadwriteclose 等系统调用。键盘、鼠标、串口、打印机、大部分简单设备。
块设备以数据块为单位进行随机访问。也通过设备文件(如 /dev/sda1),但内核有专门的I/O调度层来优化读写。硬盘、SSD、U盘、SD卡。
网络设备面向数据包。没有设备文件节点,通过套接字接口进行访问。网卡、蓝牙适配器。

2.  Linux操作系统与驱动的关系

        简单来说:内核是管理者,驱动是执行者。

  • Linux内核:像一个政府的核心内阁。它制定法律和标准(接口和规则),负责管理国家核心资源(CPU、内存、进程),并设立各个部门(子系统)来管理不同事务。

  • 设备驱动:像各个政府部门的执行机构(例如,交通部下属的交警支队、铁路局)。它们专门负责与特定的对象(硬件设备)打交道,执行内阁制定的标准,并向内阁汇报。

        用户空间包括应用程序和系统调用两层。应用程序一般依赖于函数库,而函数库是由系统调用来编写的,所以应用程序间接地依赖于系统调用。

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

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

        最底层是硬件层,这一层是实际硬件设备的抽象。设备驱动程序的功能就是驱动这一层硬件。设备驱动程序可以工作在有操作系统的情况下,也可以工作在没有操作系统的情况下。如果只需要实现一些简单的控制设备的操作,那么可以不使用操作系统。如果嵌入式系统完成的功能比较复杂,则往往需要操作系统来帮忙。大多数操作系统都具有多任务的特性,所以对于设备驱动程序来说,应该充分考虑并发、阻塞等问题。

        举一个例子,一个完整的I/O请求(如write)如何穿越应用程序、内核、驱动最终到达硬件:

3.  Lnux驱动程序开发

3.1  用户态和内核态

        Linux操作系统分为用户态和内核态。

  • 用户态:处理上层的软件工作。
  • 内核态:用来管理用户态的程序,完成用户态请求的工作。

        Linux操作系统分为两个状态的原因主要是,为应用程序提供一个统一的计算机硬件抽象。工作在用户态的应用程序完全可以不考虑底层的硬件操作,这些操作由内核态程序来完成。这些内核态程序大部分是设备驱动程序。一个好的操作系统的驱动程序对用户态应用程序应该是透明的,也就是说,应用程序可以在不了解硬件工作原理的情况下,很好地操作硬件设备,同时不会使硬件设备进入非法状态。Linux操作系统很好的做到了这一点。

特性用户态 (User Mode)内核态 (Kernel Mode)
CPU特权级低(如x86的Ring 3)高(如x86的Ring 0)
内存访问只能访问属于自己的虚拟内存空间可以访问整个系统的所有物理内存
指令执行只能执行非特权指令可以执行所有指令,包括特权指令(如操作硬件寄存器)
崩溃影响只导致自身进程崩溃导致整个系统崩溃(内核恐慌)

3.2  模块机制

        模块是可以在运行时加入内核的代码,这是Linux一个很好的特性。这个特性使内核可以很容易地扩大或者缩小,一方面扩大内核可以增加内核的功能,另一方面缩小内核可以减小内核的大小。

        Linux内核支持很多种模块,驱动程序就是其中最重要的一种,甚至文件系统也可以写成一个模块,然后加入内核中。每一个模块由编译好的目标代码组成,可以使用insmod命令将模块加入正在运行的内核,也可以使用rmmod命令将一个未使用的模块从内核中删除。试图删除一个正在使用的模块,将是不允许的。

        模块在内核启动时装载称为静态装载,在内核已经运行时装载称为动态装载。模块可以扩充内核所期望的任何功能,但通常用于实现设备驱动程序。一个模块的最基本框架代码如下:

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

int _init xxx_init(void)
{
    /*模块加载时的初始化工作*/
    return 0;
}

void _exit xxx_exit (void)
{
    /*模块卸载时的销毁工作*/
}

module _init (xxx_init);/*指定模块的初始化函数的宏*/
module _exit (xxx_exit);/*指定模块的卸载函数的宏*/

嵌入式Linux_时光の尘的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时光の尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值