首页 > 其他分享 >xv6 device driver

xv6 device driver

时间:2023-12-01 21:33:21浏览次数:33  
标签:驱动程序 UART driver xv6 硬件 descriptor tail device 设备

Interrupts and device drivers

驱动程序是操作系统中管理特定设备的代码:它配置设备硬件,告诉设备执行操作,处理由此产生的中断,并与可能等待设备I/O的进程进行交互。驱动程序需要与它所管理的设备并发执行并且必须理解设备的硬件接口,编写代码可能很棘手。

设备通常可以产生中断,内核陷阱处理代码识别设备何时引发中断并调用驱动程序的中断处理程序;在xv6中,这个分派发生在devintr中。

许多设备驱动程序在两种上下文中执行代码:上半部分在进程的内核线程中运行,下半部分在中断时执行。上半部分是通过系统调用来调用的,比如读和写,它们需要设备执行I/O。这段代码可以要求硬件启动一个操作(例如,要求磁盘读取一个块);然后,代码等待操作完成。最终,设备完成操作并引发中断。驱动程序的中断处理程序,作为底层,计算出哪个操作已经完成,如果合适的话,唤醒一个等待的进程,并告诉硬件开始任何等待的下一个操作。

console 输入

以console为例,展示了驱动程序如何工作。
console驱动程序是一种简单典型的驱动程序。控制台驱动程序通过连接到RISC-V的UART串行端口硬件接受键入的字符,一次累积一行输入,处理特殊的输入字符,如退格和。用户进程(如shell)使用read系统调用从控制台获取输入行。在QEMU中向xv6输入时,将通过QEMU的模拟UART硬件传递给xv6

UART硬件对软件来说是一组内存映射的控制寄存器,RISC-V硬件连接到UART设备的一些物理地址,以便加载和存储与设备硬件而不是RAM交互。
UART的内存映射地址从0x10000000(UART0)开始。包含了UART控制寄存器,每个寄存器的宽度为一个字节。例如,LSR寄存器包含指示输入字符是否等待软件读取的位。而实际字符可以从RHR寄存器中读取。每次读取一个字符时,UART硬件将其从等待字符的内部队列中删除,并在队列为空时清除LSR中的就绪位。UART的发送硬件在很大程度上独立于接收硬件;如果软件向THR寄存器写入一个字节,则UART传输该字节。
image

xv6的主程序调用consoleinit来初始化UART硬件,并将CONSOLE设备的read和write设置为consoleread和consolewrite。

当用户输入一个字符时,UART硬件要求RISC-V引发一个中断,从而激活xv6的trap陷阱处理程序。trap处理程序调用devintr,查看RISC-V cause寄存器以发现中断来自外部设备。如果是UART, devintr会调用uartintr,读取任何等待的输入字符,并将它们交给consoleintr。consoleintr的工作是处理特殊字符,回显,然后将输入字符写入buffer中。换行符到达时,consoleinter唤醒一个等待的consoleread,将字符复制到用户空间,然后退出返回到用户空间。

console 输出

console文件描述符的write调用最终到达uartputc。设备驱动程序维护一个输出缓冲区(uart_tx_buf,输入缓冲区在console),这样写进程不必等待UART完成发送,uartputc将每个字符追加到缓冲区,调用uartstart启动设备传输,然后返回。唯一需要等待的情况是缓冲区已经满了。

每次UART发送完一个字节,它就产生一个中断。uartintr调用uartstart,检查设备是否真的完成了发送,并将下一个缓冲的输出字符交给设备。因此,如果一个进程向console输出多个字节,通常第一个字节将由uartputc调用uartstart发送,而剩余的缓冲字节将在传输完成中断到达时从uartintr中调用uartstart发送。

通过缓冲和中断将设备活动与进程活动解耦。控制台驱动程序可以处理输入,即使没有进程等待读取它,随后进程可以读取输入。类似地,进程可以发送输出而不必等待设备。这种解耦可以通过允许进程与设备I/O并发执行来提高性能,并且在设备速度较慢(如UART)或需要立即注意(如回显输入字符)时尤为重要。

并发保护

consoleread和consoleinter中调用了acquire,获得一个锁,保护控制台驱动程序的数据结构不受并发访问。
这里存在三个并发性危险:

  • 不同cpu上的两个进程可能同时调用consoleread;
  • 当CPU已经在consoleread内部执行时,硬件可能会要求CPU发送console(实际上是UART)中断;
  • 当consoleread执行时,硬件可能会在不同的CPU上发送console中断。

锁的保护可以让避免数据竞争,但xv6不实现进程的完整输入(或许可以说一次输入在结果上表现的是原子的)。从结果上来看,如果同时有两个进程读取console,那么两个进程都会读取到输入的字符,而不是一个进程完整读取所需字节数或者读取一行使读取操作完成。

对于以下代码

int main()
{
    int pid = fork();
    char buf[100];
    if(pid == 0)
    {
        read(0, buf, 3);
        buf[3] = '\0';
        printf("child: %s\n", buf);
    }
    else
    {
        read(0, buf, 3);
        buf[3] = '\0';
        printf("parent: %s\n", buf);
    }
    return 0;
}

和输入helo
xv6下输出
image

ubuntu 20.04/22.04下输出
image

解释
image

lab

rx_ring

image

Software adds receive descriptors by writing the tail pointer with the index of the entry beyond the
last valid descriptor. As packets arrive, they are stored in memory and the head pointer is
incremented by hardware. When the head pointer is equal to the tail pointer, the ring is empty.
Hardware stops storing packets in system memory until software advances the tail pointer, making
more receive buffers available.

The receive descriptor head and tail pointers reference 16-byte blocks of memory. Shaded boxes in
the figure represent descriptors that have stored incoming packets but have not yet been recognized
by software. Software can determine if a receive buffer is valid by reading descriptors in memory
rather than by I/O reads. Any descriptor with a non-zero status byte has been processed by the
hardware, and is ready to be handled by the software.
tail -> header之间的部分存储接受到但还未交由上层软件处理的数据包的descriptor,即tail指向最早的未处理的descriptor,header指向最新的未处理的descriptor的下一个空闲位置。
而header -> tail之间的部分则是剩余的空闲的descriptor。

tx_ring

image

New descriptors are added to the ring by writing descriptors into the
circular buffer memory region and moving the ring’s tail pointer. The tail pointer points one entry
beyond the last hardware owned descriptor (but at a point still within the descriptor ring).

Shaded boxes in Figure 3-4 represent descriptors that have been transmitted but not yet reclaimed
by software. Reclaiming involves freeing up buffers associated with the descriptors.

Transmit Descriptor Tail register (TDT)
This register holds a value which is an offset from the base, and indicates the location beyond
the last descriptor hardware can process. This is the location where software writes the first
new descriptor.

即head -> tail之间的部分是软件写入但还未传输的数据包的descriptor

可以看出,rx_ring和tx_ring的结构是一样的,都是一个循环队列,唯一区别是接收数据放在tail -> header之间,发送数据放在header -> tail之间,
知道这一点区别结合hints还是很好写的。
注意e1000_recv调用net_rx时必须未获得锁,因为存在net_rx -> net_rx_arp -> net_tx_arp -> net_tx_eth -> e1000_transmit, 可能会导致dead lock

标签:驱动程序,UART,driver,xv6,硬件,descriptor,tail,device,设备
From: https://www.cnblogs.com/wangerblog/p/17870903.html

相关文章

  • Device /dev/sdc excluded by a filter ?
    如何解决报错:Device/dev/sdcexcludedbyafilter$lsblk-d-oname,rotaNAMEROTAsda1sdb1sdc1sr01$grep^/sys/block/*/queue/rotational/sys/block/sda/queue/rotational:1/sys/block/sdb/queue/rotational:1/sys/block/sdc/queue/rotation......
  • 文件 inode 与 no space left on device 异常
    转载请注明出处:文件inode在Linux文件系统中,每一个文件或目录都会有一个inode,它是一个数据结构,用于存储文件的元数据,比如文件的权限、所有者、大小、创建和修改的时间等。inode不包含文件的实际内容,只包含文件的元数据。当你在文件系统中创建、修改或者删除文件时,实际上是在修......
  • docker故障:driver failed programming external connectivity on endpoint
    故障现象Errorresponsefromdaemon:driverfailedprogrammingexternalconnectivityonendpointjenkins(ffdc7c9cda72c575d6b045574d1432b256603a3d986a05da319ab7f3af233755):(iptablesfailed:iptables--wait-tnat-ADOCKER-ptcp-d0/0--dport50000-jDN......
  • dvwa解题(1)Ping a device
    选择上来先看sourceLOW<?php//检查是否提交了表单if(isset($_POST['Submit'])){//获取输入的目标IP地址$target=$_REQUEST['ip'];//确定操作系统并执行ping命令if(stristr(php_uname('s'),'WindowsNT')){//如果是W......
  • applicationMaster和driver有啥区别
    在ApacheHadoop和ApacheSpark等分布式计算框架中,有两个重要的概念,即ApplicationMaster(应用程序主管)和Driver(驱动程序)。它们在不同的框架中可能有一些细微的差异,下面是它们的一般定义和区别:ApplicationMaster(应用程序主管):概念:ApplicationMaster是在HadoopYARN和类似框......
  • DEVICENET 从站转 MODBUS-TCP 网关操作案例
    兴达易控DEVICENET从站转MODBUS-TCP网关操作案例兴达易控DEVICENET从站转MODBUS-TCP网关(XD-ETHDE20)是一款DEVICENET从站功能的通讯网关。该产品主要功能是将DEVICENET总线和MODBUS-TCP网络连接起来。兴达易控DEVICENET从站转MODBUS-TCP网关连接到DEVICENET总......
  • xv6 traps
    traps引入三种类型的事件会导致CPU暂时搁置普通指令的执行,并强制将控制转移给处理事件的特殊代码。系统调用。用户程序执行调用指令要求内核为它做一些事情异常。指令(用户或内核)做了一些非法的事情,例如除以零或使用无效的虚拟地址设备中断。当设备发出需要注意的信号时,例......
  • 使用MediaDevices接口实现录屏技术
    摘要:本文将介绍如何使用JavaScript的MediaDevices接口实现录屏功能。我们将通过WebRTC技术捕获用户的屏幕或摄像头画面,并将其编码为MP4视频文件。在线录屏是指在互联网上进行屏幕录制的过程。它允许用户通过网络连接,将自己的屏幕活动记录下来,并可以在需要时进行播放、共享或存档......
  • kubernetes container device interface (CDI)
    CDI是什么?ContainerDeviceInterface(CDI)是一个提议的标准,它定义了如何在容器运行时环境中向容器提供设备。这个提议的目的是使得设备供应商能够更容易地将其设备集成到Kubernetes集群中,而不必修改Kubernetes核心代码。CDI插件通常负责:配置设备以供容器使用(例如,分配......
  • # yyds干货盘点 # chrome老更新的话 driver怎么才能保持更新呢?
    大家好,我是皮皮。一、前言前几天在Python最强王者交流群【鶏啊鶏。】问了一个selenium驱动器的问题,一起来看看吧。问题描述:有没有selenium用的比较多的大佬 想问问一些selenium的定时任务 关于chrome老更新的话 driver怎么才能保持更新呢二、实现过程后来【瑜亮老师】给了一......