第5章 中断和设备驱动

什么是驱动?

驱动是操作系统中用来管理一个特定设备的代码:

  • 配置硬件设备;
  • 告诉设备执行操作;
  • 处理设备中断;
  • 跟可能在等待来自设备的I/O的进程进行交互;

驱动代码需要一定的技巧,因为驱动是跟其管理的设备并发执行的。

驱动必须理解设备的硬件接口。

需要操作系统关注的设备通常被配置能生成中断。

当一个设备触发一个中断时,内核陷阱处理代码会识别出来,并调用设备的中断处理程序,比如在xv6中,这种分发机制发生在devintr中。

许多驱动是在两种上下文中执行代码的:

  • 上半部分在进程的内核线程里运行;
  • 下半部分在中断时执行;

上半部分是通过诸如read和write等需要设备执行I/O的系统调用被调用的。这段代码可能要求硬件开始一个操作(比如请求磁盘读取块等),然后等着操作完成。最终,设备完成了操作,触发一个中断。

下半部分是设备中断处理程序

  • 指出什么操作已完成;
  • 如果合适的话,唤醒一个等待的进程;
  • 告诉硬件开始处理任何下一个等待的操作;

5.1 代码:控制台输入

控制台驱动是对驱动结构的简单示例。

控制台驱动接收由人通过附加到RISC-V上的UART串行接口键入的字符。

控制台驱动一次累计一行输入,可处理特殊输入字符,比如backspace、control-u等。

诸如shell等用户进程,使用read系统调用来获取来自控制台的输入行。

当你键入输入给在QEMU中的xv6时,你的击键是通过QEMU模拟的UART硬件传递给xv6的。

驱动与之交互的UART硬件是一个由QEMU模拟的16550芯片。在实际的计算机上,16650芯片管理的是一个可连接到终端或者其他计算机的RS232串行链接。当运行QEMU时,16650芯片连接的是键盘和显示器。

UART硬件似乎是一组由内存映射控制寄存器组成的软件。即,RISC-V硬件将一些物理地址连接到UART硬件,使得加载和存储等指令直接跟UART设备交互,而不是RAM。

UART的内存映射地址UART0开始。有一撮UART控制寄存器,每个寄存器的大小是1字节。
从UART0开始的偏移量定义在uart.c中。

比如LSR寄存器包含的位用来标记是否有输入字符正等待着被软件读取。

RAH寄存器:存储可用的输入字符

THR寄存器

每当读取一个字符,UART硬件就会将该字符从等待字符的内部FIFO删除;当FIFO变空时,UART就会清除LSR中的ready标记位。

UART的发送硬件跟接收硬件是独立的。

如果软件向THR寄存器中写入一个字节,UART就发送这个字节。

xv6的main函数调用consoleinit来初始化UART硬件。这段代码配置UART,使得:当UART每接收一个字节的输入,就生成一个接收中断;每当UART每发送完一个字节的输出,就生成一个发送完成中断

xv6的shell程序通过init.c打开的文件描述符来从终端读取数据。
对read系统调用的调用通过内核进入到consoleread。
consoleread等待着输入到达并缓冲在cons.buf中,拷贝输入到用户空间,返回给用户进程。
如果用户还没有键入一个完整的行,则所有的读取进程都将以sleep调用的形式等待。

当用户键入一个字符时,发生了哪些事?

  • 当用户键入一个字符时,UART硬件请求RISC-V触发一个中断,中断会激活xv6的陷阱处理程序。
  • 陷阱处理程序调用devintr
  • devintr查看RISC-V的scause寄存器,发现中断来自一个外部设备,然后请求PLIC硬件单元来告诉它是哪个设备发生了中断。如果是UART,则devintr会调用uartintr
  • uartintr从UART硬件中读取任何等待的输入字符,将这些字符交给consoleintr
    uartintr不会等待字符,因为新的输入会触发新的中断。
  • consoleintrcons.buf中累计输入字符直到一整行输入到达;consoleintr会特殊处理backspace字符和其他一些字符;当有新行到达时,consoleintr会唤醒一个等待的consoleread
  • 一旦被唤醒,consoleread将观察到在cons.buf中有一个完成的输入行,把数据从cons.buf拷贝到用户空间,并通过系统调用机制返回到用户空间。

5.2 代码:控制台输出

对连接到控制台的文件描述符的write系统调用最终会到达uartputc。

设备驱动维护了一个输出缓冲区,使得写进程不必邓艾UART发成发送。

uartputc将每个字符添加到缓冲区,调用uartstart来开始设备发送,并返回。

uartputc等待的唯一情形就是缓冲区已经满了。

每当UART完成发送一个字节,则UART就生成一个中断。

uartintr调用uartstart:检查设备是否已完成发送,交给设备下一个缓冲的输出字符。因此,如果一个进程向控制台写入了多个字节,通常第一个字节是由uartputc调用uartstart发送的,剩余的字节是由uartintr调用uartstart作为发送完成中断达到来发送。

什么是I/O并发?

值得注意的模式:通过缓冲和中断将设备活动与进程活动解耦。即使没有进程在等待读,控制台驱动也能处理输入;下一次读将会看到输入。类似地,进程发送输出也不用等待设备。这种解耦能提升性能,通过允许进程跟设备I/O并发;当设备很慢或者需要立即关注时,就显得特备重要。这种思维就是I/O并发。

5.3 在驱动里的并发

注意到在consoleread和consoleintr中,都有调用acquire。

调用acquire会获得一把锁,用来保护控制台驱动的数据结构不受并发访问的影响。

在这里有4个并发危险:

  • 在不同CPU上的两个进程同时调用consoleread;
  • 当CPU已经在consoleread内部执行的同时,硬件可能请求该CPU发出一个控制台中断;
  • 在consoleread执行的同时,硬件可能会在另一个不同的CPU上发出一个控制台中断;
  • 有一个进程可能正在等待来自某个设备的输入,但是输入中断信号到达的时候可能是另一个不同的进程在运行;

不允许中断处理程序来考虑被中断的进程或者代码。比如即使有了当前进程的页表,中断处理程序也没法安全地调用copyout。通常,中断处理程序只做相对较少的工作(比如,仅拷贝输入数据到缓冲区等),然后唤醒上半部分代码来做剩余的事情。

5.4 计时器中断

xv6使用计时器中断来维护它的时钟,来实现在计算密集型的进程间切换的功能。

在usertrap和kerneltrap中调用yield会导致这类切换。计时器中断来自添加到每个RISC-V CPU上的时钟硬件。xv6对这个时钟硬件进行编程来周期性地中断每个CPU。

RISC-V要求计时器中断必须在机器模式下处理,而不是在内核模式下。因为RISC-V机器模式执行时不需要分页,有单独的控制寄存器集,所以在机器模式下运行普通代码是不切实际的。因此,xv6处理计时器中断是方式是完全不同于陷阱机制的。

如何设置计时器中断?

start.c里以机器模式运行的代码在main之前,用来设置接收计时器中断。一部分任务是对CLINT硬件编程来在指定延迟后生成一个中断。另一部分的任务是建立scratch区,来帮助计时器中断处理程序保存寄存器和CLINT寄存的地址。最后,start设置mevec为timervec,使得计时器中断生效。

如何处理计时器中断?

因为计时器中断可以在执行用户代码或者内核代码的任何一个点处发生,且于内核没有办法使计时器中断在关键操作期间失效,所以计时器中断处理程序必须以不干扰被中断内核代码的方式来处理计时器中断。
基本的策略是中断处理程序请求RISC-V触发一个软件中断,并立即返回。RISC-V将软件中断传递给使用普通陷阱机制的内核,并允许内核来使其失效。处理由计时器中断产生的软件中断的代码可参考devintr。

机器模式的计时器中断向量是timervec。它在有start准备好的scratch区里保存一些寄存器,告诉CLINT何时生成下一个中断,请求RISC-V生成一个软件中断,检索寄存器,并返回。注意:在计时器中断处理程序中没有C代码的。

5.5 真实世界

xv6允许:在内核中执行或者执行用户程序时,发生设备中断和计时器中断。

注意:即使是在内核中执行,计时器中断也会从计时器中断处理程序中强制进行线程切换(比如调用yield)。

如果内核线程有时花费许多时间在计算而没有返回用户空间,则在内核线程之间对CPU进行时间分片就变得很重要了。

由于计时器中断,内核代码可能会被挂起,之后在不同的CPU上恢复执行,这是xv6中复杂性的一个来源。
如果仅在执行用户代码时才会发生设备中断和计时器重案,则内核可以更简单点。

在一台典型的计算机上支持所有的设备是一项艰巨的任务,因为存在许多设备,每个设备都有许多特征,在设备和驱动之间的协议可能会很复杂且文档记录很差。在许多操作系统上,驱动占据的代码要大于核心内核的代码。

可编程I/O VS DMA

UART驱动通过读取UART控制寄存器来每次读取一个字节的数据。这种模式是可编程I/O,因为软件驱动着数据移动。可编程I/O虽然简单,但是太慢了以至于不能用于高数据传输速率。

需要高速移动数据的设备通常都使用DMA。DMA设备硬件直接将输入数据写入到RAM,直接从RAM读取输出数据。现代的磁盘和网络设备都使用DMA。
DMA驱动会在RAM中准备好数据,然后向控制寄存器中单独写入来告诉设备来处理准备好的数据。

中断 VS 轮训

当设备需要关注的时不可预测的,和次数不频繁时,中断就很有意义。但是,中断有很高的CPU开销。因此,诸如网络和磁盘控制器等高速设备会使用一些技巧来减少对中断的需要。一个技巧是对批量输入请求或者输出请求触发一次中断。
另一个技巧是完全使中断失效,周期性地检查设备来看是否需要关注。这种技术称作轮询。如果设备执行操作非常快,则轮询就有意义。但是如果设备大部分时间处于空间,则轮询会浪费CPU时间。有些驱动会根据当前设备的负载在中断和轮询之间动态切换。

UART驱动首先将输入数据拷贝到内核中的缓冲区,然后再从缓冲区拷贝到用户空间。如果数速率很低,则这样是有意义的。但是对那些消费或者产生数据非常快的设备来说,这样的两次拷贝会严重降低性能。一些操作系统能使用DMA在用户空间的缓冲区和设备硬件之间直接移动数据。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • 有3类事件可导致CPU把普通的指令执行搁置在一边,强制把控制权转移到能处理事件的特定代码处。 系统调用用户程序执行...
    橡树人阅读 3,690评论 0 3
  • 参考《RISC-V架构与嵌入式开发快速入门》 1 进入异常 进入异常时,RISC-V架构规定的硬件行为可以简述如下...
    Swinner阅读 11,126评论 0 1
  • 一、前言 笔者一直对中断只有一个简单粗浅的认识,仅仅停留在处理外部事件或者异步事件上。所以对于中断的认识不够清晰。...
    wipping的技术小栈阅读 2,688评论 0 4
  • 嵌入式系统驱动程序的开发有别于WIndows或Linux。后者除了必须了解新设备的硬件特性,把控制硬件的程序尽快完...
    Leon_Geo阅读 1,100评论 0 2
  • QNX相关历史文章:QNX简介QNX Neutrino微内核QNX IPC机制QNX进程管理器QNX资源管理器QN...
    Loyen阅读 7,070评论 0 5