Linux Device Driver 3rd 上

第一章 设备驱动程序的简介

处于上层应用与底层硬件设备的软件层

区分机制和策略是Linux最好的思想之一,机制指的是需要提供什么功能,策略指的是如何使用这个功能!

通常不同的环境需要不同的方式来使用硬件,则驱动应当尽可能地不实现策略.

驱动程序设计需要考虑一下几个方面的因素:

  • 提供给用户尽量多的选项

  • 编写驱动程序所占用的时间,驱动程序的操作耗时需要尽量缩减.

  • 尽量保持程序简单

内核概览:

  • 进程管理:

    负责创建和销毁进程,并处理它们和外部世界之间的连接(输入输出),调度器也属于进程管理中的

  • 内存管理:

    内核在有限的可用资源上为每一个进程都创建了一个虚拟地址空间

  • 设备管理:

    几乎所有的系统操作都会映射到具体的硬件设备上

  • 文件系统:

    一切皆文件,一个kernel可能包含多个文件系统,比如 存储硬盘被抽象成ext3文件系统

  • 网络:

    网络功能必须用操作系统来管理,因为大部分的网络操作和具体的进程无关:数据包的传入是异步事件

设备以及模块的分类
模块

内核模块可以随时载入到系统中,即便系统已经处于运行状态,通常使用的方法是insmod加载,rmmod卸载
字符模块,块模块,网络模块,这三个模块并不是必须单独存在的,在设计模块的时候,常常需要对于同一
个硬件设备或者功能定义成不同的模块,这样可以增强后期的扩展性。

设备
  • 字符设备

那些可以以字节流的方式进行访问的设备, 字符设备与常规的文件的区别在于字符设备只是数据管道,另外,字符设备也可以被看成数据区

  • 块设备

块设备也是通过文件节点进行操作的,比如 disk硬件就是属于块设备并且挂载了一个文件系统.和字符设备类似,块设备也是通过/dev 目录下的文件系统节点来访问。另外,Linux可以让应用程序向字符设备一样地读写块设备,允许一次传递任意多字节的数据,那两者的区别是什么呢?两者的区别仅仅在于内核内部管理数据的方式,也就是内核以驱动程序间的软件接口。

  • 网络接口:

网络接口由内核中的网络子系统驱动,负责发送和接收数据包。由于不是面向流的设备,因此无法将网络设备映射到/dev 下系统节点,所以内核和网络设备驱动程序间的通讯完全不同于内核和字符以及块驱动程序之间的通讯,内核拥有一套和数据包传输相关的函数。

文件系统

文件系统也是内核模块化的表现,它决定了如何在块设备上组织数据,以表示目录和文件形成的树,但是文件系统因为没有实际物理设备所以并不是设备驱动程序,相反,文件系统是一个软件驱动程序,它将低层数据结构映射到高层数据结构,决定文件名以及在目录项中存储文件的哪些信息等等。

文件系统模块必须实现访问目录和文件的最底层系统调用,方法是将文件名和路径(以及其他一些信息,比如访问模式等)映射到数据块中的数据结构中,这种接口完全独立于在磁盘上传输的实际数据,而数据的传输由块设备驱动程序负责完成。

驱动程序编写者应当尽量避免在代码中实现安全策略,安全策略问题最好在系统管理员的控制之下,由内核的高层来实现。

任何从内核中得到的内存,都必须在提供给用户进程或者设备之前清零或者以其他方式初始化,否则就可能发生信息泄露(如数据和密码的泄露等)。

Linux内核可以编译为不支持模块方式,从而可以关闭任何模块相关的安全漏洞。

第二章 构造和运行模块

内核模块与应用程序的对比

大多数应用程序是从头到尾执行单个任务,而内核模块却只是预先注册自己以便服务需要的某个请求,比如初始化函数的任务就是为了以后调用模块函数预先作准备。

应用程序在退出时,可以不管资源的释放或者其他的清除工作,但内核模块的退出函数却必须仔细撤销初始化函数所做的一切,否则,在系统重新引导之前某些东西就会残留在系统中。

应用程序可以调用它并未定义的函数,主要原因是链接过程能够解析外部引用从而使用适当的函数库。然后,内核模块仅仅被链接到内核,所以它仅仅能够调用由内核导出的那些函数,而不存在任何可链接的函数库。

在各环境下的处理错误的方式两者也是不同的,应用程序开发过程中的段错误是无害的,并且总是可以使用调试跟踪到源代码中的问题所在,而一个内核错误即使不影响整个系统,也至少会杀死当前进程。

用户空间和内核空间

模块运行在内核空间,应用程序运行在用户空间,Unix使用两种操作模式或级别来实现针对CPU的不同操作,内核运行在最高级别(也称为超级用户态),在这个级别中可以进行所有的操作,而应用程序运行在最低级别(即所谓的用户态),在这个级别中,处理器控制着对硬件的直接访问以及堆内存的非授权访问。两种的不同的级别是针对CPU而言的,所以当应用程序调用系统调用或者被硬件中断挂起的时候,CPU的保护级别就从最低级别硬件上切换到最高级别了。另外,一旦从用户空间切换到内核空间,执行系统调用的内核代码运行在进程上下文中,它代表调用进程执行操作,因此能够访问进程地址空间的所有数据,而处理硬件中断的内核代码和进程是异步的,与任何一个特定进程无关。

内核中的并发

为什么内核编程需要考虑并发问题?

  • Linux系统中通常正在运行多个并发进程,并且可能有多个进程同时使用我们的驱动程序。

  • 大多数设备能够中断处理器,而中断处理程序异步运行,而且可能在驱动程序正试图处理其他任务时被调用

  • 部分软件抽象(内核定时器)也在异步运行着

  • 多处理器系统,可能存在多个CPU运行驱动程序

  • 内核已经实现可抢占

当前进程
struct task_struct *current;

内核代码可以通过访问全局项current来获得当前进程,而2.6后,current不再是一个全局变量(为什么呢? 因为需要支持多处理器系统),而是将指向task_struct结构的指针隐藏在内核栈中。

其他

应用程序在虚拟内存中布局,并具有一块很大的栈空间(栈是用来保存函数调用历史以及当前活动函数中的自动变量的),相反内核具有非常小的栈(可能是4096字节大小),我们自己的函数必须和整个内核空间调用链一同共享这个栈,则尽量避免声明大的自动变量。

内核代码不能实现浮点数运算,为什么?

因为如果打开了浮点支持,在某些架构上需要在进入和退出内核空间时保存和恢复浮点处理器的状态

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值