内核驱动

2024-11-01

内核驱动(精选5篇)

内核驱动 篇1

0 引言

设备驱动是操作系统不可缺少的一部分, 它负责与外部设备的交互, 管理着外部设备。操作系统本身并不负责具体设备管理, 只提供一系列的接口, 只要驱动程序实现这些接口, 用户就能够使用这些设备。随着技术的发展, 出现了越来越多的新设备, 操作系统必须对设备驱动进行有效的管理。

目前主流的操作系统, 如Linux、Windows等都具有非常强大的驱动接口, 这些系统中驱动都属于内核的一部分, 当有新设备接入系统时, 要安装相应的驱动程序, 这些驱动程序为内核的一部分。操作系统内核具有最高的权限, 能够操作系统中所有的资源。因为驱动处于内核中, 因此驱动也具有对内核的完全访问权限。为了控制驱动行为, 操作系统开发出许多技术限制驱动操作资源。但是, 由于系统内核庞大, 无法保证其完全抵御恶意驱动对系统的攻击。目前, 操作系统的安全问题很大一部分来自于内核驱动模块。

另一方面, 由于外部设备众多, 操作系统为了兼容这些设备, 必须将设备驱动的代码加入到内核中。图1列出了Linux2.6各版本中的代码组成[1], 可以看出随着版本的变化, 驱动代码越来越多。有研究表明, 软件中bug的数量随着代码量的增加而增加[2], 这样, 很难保证某些驱动中不包含恶意代码。

上世纪80年代, Richard Rashid在卡内基梅隆大学开发Mach系统时提出了微内核的概念[3]。他将内核中大量的代码移到了用户空间, 其中包括驱动代码, 使得内核极为精简, 很容易保证内核的可靠性。在此之后, 微内核系统不断发展, 目前的微内核系统有L4、Minix3[4]等。

我们采用微内核思想设计并实现了一个支持多核的微内核系统VTOS, 目的在于研究微内核系统在多核上的应用, 并且探索使用形式化方法验证内核的可靠性[5,6]。本文首先介绍了VTOS的系统架构, 然后介绍将驱动独立于用户空间所要解决的问题以及解决方法, 最后总结了用户级驱动设计的不足之处。

1 VTOS架构

VTOS运行在x86架构的保护模式下, 能够充分利用CPU的硬件特性保护系统安全。VTOS采用多服务器设计, 其内核部分称为微内核。VTOS采用机制和策略分离的设计:内核只实现那些需要内核级权限的基础机制, 而所有策略都由用户级中的服务器和驱动来实现。下面介绍VTOS结构:

尽管VTOS中所有的进程都是相同的, 但在逻辑上把进程分为4层:内核层、驱动层、服务器层、应用程序层, 如图1所示。

内核处于最底层, 微内核只包含几千行运行在最高特权级上的代码。内核负责处理硬件中断、捕获用户进程异常、处理进程间的通信以及对CPU等资源的管理, 其具有两个任务:系统任务和时钟任务, 它们像正常进程一样被调度运行。时钟任务处理时钟中断、维护系统时间、进行进程调度以及管理定时器;系统任务提供了一些内核调用, 支持用户级服务器和驱动完成相关的工作。原则上, 系统任务只提供那些不能在用户态中实现的特权函数。

图2中, 上面3层都处于用户态中。驱动层位于内核层之上, 这一层中的每个驱动对应一个设备, 例如存储设备、网络设备、打印机等, 此外, 还包括了协议驱动如文件系统、网络协议。每个驱动都是一个用户进程, 像普通的用户进程一样受到CPU和MMU的保护。与普通进程不同的是, 内核允许驱动使用部分的内核调用以完成特权操作。例如:设置中断处理函数、读写设备的I/O端口、进行跨地址空间的拷贝等。这样的设计会造成一些性能损失, 但能够带来更好的权限控制。驱动对设备进行管理, 但与设备的交互需要内核权限, 因此当需要完成这些操作时, 驱动程序将使用内核调用完成。

驱动的上面一层为服务器层, 用户进程通过向这些服务器发送消息来进行系统调用, 服务器会接收到这些消息。当服务器遇到自身不能处理的任务时, 便将任务委托给更底层, 其中驱动管理服务器管理其它服务器和驱动程序, 当有服务器或驱动崩溃时, 它能够重新启动一个新的服务器或驱动, 并且恢复一些信息。

最上层为应用程序层, 不能进行任何特权操作。这一层运行普通的应用程序, 例如:视频播放器、文本编辑器、浏览器等。它们所需的系统服务都由服务器层提供。

在VTOS的4层设计中, 内核层、驱动层和服务器层一起组成操作系统的基础部分。其中内核层和服务器层是固定不变的, 在系统运行时不能动态地往其中加入新的模块, 而驱动层在运行时能够动态进行插入、删除驱动的操作。下面介绍用户级驱动设计。

2 用户级驱动

因为内核可以进行所有的特权操作, 而宏内核中的安全威胁是不可信的第三方驱动使用特权操作。因此, VTOS与其它微内核系统一样将设备驱动移出内核, 作为独立的用户级进程。时钟驱动是一个例外, 由于性能的关系, 它还是存在于内核中。

设备驱动管理着相应的外部设备, 但与外部设备的交互需要内核权限, 因此, 需要仔细考虑驱动需要什么权限以及怎样安全地给予它们这些权限。

2.1 设计原则

2.1.1 最小权限原则

在VTOS中采用最小权限原则来确定每一个驱动所需要的权限。最小权限原则指系统中的每个部分只能访问其完成工作所需要的最少资源。换句话说, 每个程序或者用户只使用完成工作所需要的最小权限, 使用这个原则能够将发生错误造成的损失降到最低, 同时, 它也将各特权程序之间相互通信的可能性降到最低, 避免发生无意的、不适当的额外权限使用。

2.1.2 特权操作类型

VTOS中驱动所需的特权操作分为4种正交类型:CPU、内存、设备I/O、进程间通信。

(1) CPU。一个进程如果处于内核模式中, 就能够使用所有的CPU特权指令, 从而可能绕过其它高级的系统机制, 例如:内核驱动能够重置页表、执行I/O操作等。这些特权指令对系统的运行非常关键, 因此不能暴露给不受信任的代码。此外, 要防止驱动过多使用CPU, 因为低级的驱动代码不受系统调度程序控制, 如果它执行无限循环就会造成系统挂起。

(2) 内存访问。因为驱动经常与系统服务器或者应用程序交换数据, 如果访问非法的内存地址会造成系统错误, 影响系统运行。此外, DMA方式能够绕过CPU和MMU的限制, 内核不能控制DMA的传输。

(3) 设备I/O。内核通过设备I/O来控制设备的运行, 因此应该对设备I/O进行限制。如果多个驱动同时操作一个设备, 就可能影响设备、系统的稳定性。此外, 由于设备中断的处理是异步的, 且需要在最底层被处理, 造成中断处理容易出错。

(4) 进程间通信 (IPC) 。多服务器的微内核设计中, 进程间通信很重要。各个层次间的交互都是通过IPC进行的, 因此, 需要保证IPC的健壮性, 要防止不受信任的代码对IPC机制的破坏。

2.1.3 驱动限制

前面, 我们讨论了系统中关键的特权操作, 由此得出对驱动的一般限制: (1) 驱动不能访问所有CPU资源; (2) 驱动不能访问除了完成自身工作外的其它内存地址; (3) 驱动不能直接操作设备I/O; (4) 驱动不能使用IPC任意访问系统服务。

限制这些驱动后, 驱动就不能直接进行特权操作, 但是, 驱动必须通过一些特权操作才能完成它的工作, 如控制设备的运行等。因此, 系统需要提供一种方法让驱动能够完成它所需要的特权操作。

2.2 请求特权操作

因为用户级驱动不能直接执行特权操作, 所以需要内核提供支持函数进行I/O操作、内存拷贝等, 这些函数要由更高级别的、受系统信任的服务提供, 在VTOS中这个服务为系统任务, 内核支持函数被称为内核调用。驱动需要向系统发送请求, 系统任务将根据驱动的权限对请求进行处理。系统任务循环不断地接收消息、查询内核调用的类型、检查驱动的权限、执行相应的处理函数, 最后返回结果。

系统任务提供许多特权操作内核调用, 这些内核调用只能在内核态中完成, 对驱动最重要的内核调用如表1所示。

表1中所列的为重要的驱动内核调用, 系统任务还提供了其它内核调用。对于一个驱动来说, 它所能使用的内核调用由之前说明的原则进行。使用最小权限原则确定每一个驱动的内核调用权限后, 驱动就不能超过它的权限, 例如, 网卡驱动显然不应该使用进程管理的内核调用, 这样就能保证内核不被不相关的进程使用。

2.3 驱动层与其它层次交互

VTOS通过消息传递机制进行交互。因为驱动负责管理设备, 所以需要特权操作。驱动通过消息机制与系统任务进行交互, 请求系统任务完成所需的特权操作。同时, 驱动也向外提供设备所具有的功能, 例如, 打印机驱动为应用程序提供打印功能、扫描仪驱动为应用程序提供扫描功能等, 因此, 驱动可以和服务器或者应用程序进行交互, 为调用者提供与设备相关的功能。驱动可以决定响应哪个消息请求, 可以只响应特定服务器请求, 也可以响应所有进程发来的请求。

3 结语

本文介绍了微内核操作系统VTOS的总体结构, 并对VTOS中将设备驱动独立于用户空间需要注意的问题进行了详细的介绍。因为驱动独立在用户空间, 不能直接执行内核中的特权操作, 因此能够避免设备驱动为系统带来的安全问题。这样的设计能给操作系统带来很高的可靠性和扩展性, 同时, 驱动完成工作需要特权操作, 在VTOS中, 驱动通过向系统任务发送请求来解决这个问题。

多服务器设计的微内核系统扩展性高, 而且由于内核精巧, 能够利用如形式化验证的方法来保证内核的可靠性。但是, 系统中各部分的通信依赖消息传递机制, 会造成系统性能的下降, 因此, 如何减少系统的性能损失, 还有许多工作要做。

摘要:随着IT技术的不断发展, 出现了越来越多的新设备, 操作系统为了保持对这些新设备以及已有老设备的支持, 其内核就要加入越来越多的驱动代码, 驱动代码的加入使得原本已经复杂的内核更加庞大, 且这些代码可能潜伏着许多漏洞, 威胁系统安全。而微内核系统将设备驱动独立于用户空间, 不再处于内核中, 使得设备驱动不会影响到内核的安全, 这种设计对于减少系统安全威胁帮助很大。

关键词:VTOS微内核系统,操作系统,驱动限制

参考文献

[1]JORRIT HERDER.Building a dependable operating system[M].Ph.D.thesis, 2010.

[2]OSTRAND T J, WEYUKER E J.The distribution of faults in a large industrial software system[J].ACM SIGSOFT Software Engineering Notes, 2002:55-64.

[3]ACCETTA M, BARON R, BOLOSKY W, et al.Mach:a new kernel foundation for unix development[Z].1986.

[4]Minix3[EB/OL].http://www.minix3.org, 2014.

[5]钱振江, 刘苇, 黄皓.操作系统形式化设计与验证综述[J].计算机工程, 2012 (38) .

[6]QIAN Z J, HUANG H, SONG F M.VTOS:research on methodology of“Light-Wegight”formal design and verification for microkernel OS[J].ICICS 2013, LNCS 8233, 2013.

内核驱动 篇2

摘要:本文以64位PCI总线接口芯片PCI9656的设备驱动程序为基础,比较了Linux2.6内核与2.4内核的区别,设计与开发了在Linux2.6内核下PCI9656设备驱动程序,进而研究了2.6内核的内存和中断管理机制。

关键字:Linux2.6;设备驱动程序;PCI9656

1 引言

Linux操作系统因为其高效、安全、可动态加载及源代码开放等特点,深受设备驱动程序开发人员的喜爱。系统内核大部分独立于底层硬件运行,用户无需关心硬件问题,而用户操作是通过一组标准化的调用来完成。设备驱动程序的任务是将这些调用映射到作用于实际硬件设备的特定操作上,该编程接口能够使得驱动程序独立于内核的其他部分来搭建,在需要时才动态加载到内核。这种模块化的特点,使得Linux设备驱动程序的编写过程变得清晰简单。

目前,为满足日益庞大的数据处理需要,基于64位PCI总线接口设备的研究开发显得尤为重要。因而本文将基于PLX公司推出的PCI总线接口芯片PCI9656,设计开发在Linux2.6内核下的设备驱动程序,进而对2.6内核的内存和中断管理机制进行分析研究。

2 Linux2.6与2.4内核的比较

2.1 系统稳定性

为了彻底防止对正在被使用的内核模块进行错误操作,2.6内核在加载和导出内核模块方面都较2.4内核进行了改进,避免了用户执行将导致系统崩溃的操作,例如强制删除模块等。同时,当驱动程序需要在多个文件中包含头文件时,不必定义宏__NO_VERSION__来检查内核的版本。

2.2 统一设备模型

统一设备模型的创建是2.6内核最重要的变化之一。它促进了模块接口的标准化,其目的是更好地控制和管理设备,主要包括:更准确地确定系统设备,更高效的进行电源管理以及改进的系统总线结构管理。

2.3 内核基础设施

2.6内核为了区别以.o为扩展名的常规对象文件,将内核模块的扩展名改为.ko。相对于2.4内核下系统所支持的RAM为4GB而言,2.6内核下系统支持更大数量的RAM,在分页模式下最高可达64GB。同时,2.6内核优化了I/O调度器,确保不会有进程驻留在队列中过长时间等待输入/输出操作,使得I/O操作的响应更为迅速。

2.4 外部设备

在2.4内核中有约束大型系统的限制,比如支持的每一类设备的最大数量为256。而2.6内核则彻底地打破了这些限制,可以支持4095种主要的设备类型,每一个单独的类型又可以支持超过一百万个的子设备。

3 Linux2.6内核下PCI设备驱动程序的设计

3.1 PCI设备驱动程序中核心数据结构

在2.6内核下使用file_operations数据结构,来建立设备驱动程序中的`函数与主设备号(major number)之间的对应关系。该数据结构中包含了指向驱动程序内部大多数函数的指针,描述了虚拟文件系统如何操作一个打开的外围设备。因而file _operations结构是驱动程序向内核其他部分提供的一个统一的标准设备接口。

file结构是设备驱动程序使用的另一个重要的数据结构,指示当前系统中已打开的文件。它在C语言库中定义,在调用内核open函数时创建,并传递给在该设备上进行操作的所有函数,直到最后的close函数。file结构中还包含了指向它所拥有的file_operations结构的指针。

inode结构由内核自动生成,代表已打开文件的描述符,与每个打开的文件一一对应。它包含了两个重要的结构成员:dev_t扩展到32位,其中12位主设备号,20位从设备号,而cdev用于存储一个指向字符设备文件的指针。

3.2 驱动程序与内核和外部设备间的关系

(1) 通过Linux提供的系统调用函数(例如init_module等)进入内核,这些函数在2.6内核版本下总共有两百多个,提供了几乎所有应用程序进入内核所需要执行的操作。

(2) 系统的内核函数都有“sys_”前缀(例如函数sys_init_module),应用程序通过访问设备文件系统来调用这些函数。这一层主要是“devfs”(device filesystem)文件管理机制,它是从普通文件和设备文件抽象出来的一个文件系统层,完成进入具体的设备文件操作之前的准备工作。

(3) 由设备驱动程序提供具体的函数,来完成对硬件设备的各种操作。特别的对于PCI9656来说,就是通过PCI接口对设备的寄存器和存储器进行访问操作,例如调用register_chrdev等函数来初始化芯片内部的状态寄存器和配置寄存器。

3.3 PCI9656芯片的操作流程

PCI总线是目前最常用的外设总线之一,Linux的PCI内核代码为PCI设备驱动程序的开发提供了强大的支持。PCI9656的驱动程序主要包括以下几个方面:设备初始化,为PCI芯片分配内存资源,实现数据的读写功能,中断处理,系统收回内存资源,关闭设备等。

4.Linux2.6内核下内存和中断管理的研究

2.6内核应用了许多新技术来实现对各类外部设备驱动程序的更好支持。下面结合PCI9656驱动程序中的内存和中断管理,进一步分析和研究2.6内核对内存和中断进行的改进和优化。

4.1 内存管理

在Linux内存管理器中,页表保持对进程使用的内存物理页的追踪,它将虚拟页映射到物理页上。系统必须找到映射到该页的每一个进程,将使用较少的页置换出去,这样进程中相应页的页表条目才能被更新。随着在系统中运行的进程数量的增加,将这些页置换出去的工作量也会急剧增加。

★ 故事编写范文

★ 编写工作方案

★ 校史编写范文

★ 教案编写范文

★ Windows Vista 显示器驱动程序模型

★ 车站故事编写范文

★ 写编写童话故事以上

★ 小学教学计划如何编写

★ 故事编写范文1200

内核驱动 篇3

在光电检测和跟踪中,高帧频、高分辨率相机得到了广泛的应用,在提高检测和跟踪精度的同时也对图像存储系统速度及稳定性提出了更高的要求[1]。某些系统要求存储系统放置在设备内部,这对系统的体积提出了更高要求。自行研制的PCIE接口高速图像存储系统满足了体积小、速度快而稳的要求。PCIE采用了点对点串行连接,比起PCI以及更早期的计算机并行总线的共享架构,每个设备都有自己的专用连接,不需要向整个总线申请带宽,而且由于差分信号抗干扰性强,可以把数据传输率提高到极高的频率[2]。PCIE用途广泛、标准化程度高、通用性强。

Linux操作系统代码开源,能轻松移植到多种平台上,占用资源较少,是嵌入式设备的一个好选择[3]。另外,Linux在硬盘读写和网络通信方面,可靠性和性能均十分优越,所以本存储系统选用Linux作为操作系统。而驱动是连接硬件与操作系统的纽带,是图像存储系统的核心和灵魂,是可靠性和性能的关键。因此本文结合实例详细介绍PCIE接口的Linux驱动各组成部分与实现,重点阐明内核多线程直接存储的实现方法。

1 硬件系统

本高速存储系统以Power PC为控制核心,由图像采集单元Frame Grabber、FPGA实现的PCIE接口DMA控制器、m SATA接口固态硬盘(SSD)及千兆网接口几个部分组成。如图1所示,图像采集单元将高速图像数据传给FPGA的FIFO中。FPGA还实现了PCIE接口的DMA控制器,PCIE为2.5 GB/s的X4链路,理论总带宽10 GB/s。当运行在Power PC上的Linux驱动发送DMA开始信号后,将图像数据传入Power PC的内存中,再由应用程序存入到挂载在Power PC处理器SATA接口上的SSD中。千兆网用来实现与远程计算机之间的通信控制和远程显示。

图1 高速存储系统结构框图

2 PCIE驱动

2.1 PCIE驱动实现

PCIE接口DMA控制器是字符型设备,PCIE总线作为设备与主控制器之间的高速通道,在驱动中需实现PCIE设备和字符设备驱动双重功能[4]。在PCIE接口DMA控制器驱动中,不仅要实现PCIE的驱动,还需重点实现文件操作字符设备驱动,即file_operations成员的实现并注册chr_dev。

DMA采用的是主总线DMA传输方式,读写都是相对控制器来说的,即写操作是由PCIE设备到内核内存,读是由内核内存到PCIE设备。PCIE接口DMA控制器的驱动组成如图2所示。驱动主要由驱动加载和卸载、中断处理及字符设备文件操作函数组成。

图2 PCIE驱动主要流程

驱动加载的主要步骤如图2(a)所示,关键实现代码及解析如下,每步均需检测是否完成,如出错则需返回出错信息并退出;成功则置申请的资源位,方便模块卸载时判断并释放相应资源。

字符设备初始化:

register_chrdev(g Drvr Major,g Drvr Name,&PCIe_DMA_file);

PCIe_DMA_file为文件操作结构体,如图2(c)所示,包括读、写、设备控制、打开和释放函数,每个函数需独立实现,如ioctl(设备控制)函数如下:

DMA完成后会自动中断,中断处理函数流程如图2(b)所示,通过DDMACR等寄存器即可完成,释放信号量即可唤醒等待读取数据的应用进程。应用进程读数时会调用copy_to_user函数,将数据由内核空间拷贝到应用空间。

2.2 速度测试及分析

将FPGA产生的模拟图像数据写入SSD,在速率为60 MB/s时即频繁丢帧。这是由于大量数据需从内核空间拷贝到应用空间,频繁在内核模式和用户模式之间切换,占用了太多的资源及时间,加上应用程序常常不能被及时调度更加剧了速度的不稳定性。因此提出了内核多线程多级缓冲直接存储到SSD的方法。

3 内核多线程多级缓冲直接存储的实现

3.1 内核直接存储的优势

常规的驱动在DMA完成时,仅将数据传入驱动的DMA缓冲区中,由于DMA缓冲区需要内存在物理上连续,由于内核的限制,Linux最大只能分配4 MB[5]。因此当DMA完成后,驱动就需要通知应用程序及时将数据取走,以便进行下一次DMA。当传输速率较高时,就需要把大量数据通过copy_to_user函数从内核空间拷贝到用户空间,导致频繁地在内核模式和用户模式之间进行切换。这将占用大量资源和CPU时间从而导致系统性能的大幅下降和DMA速度的降低[6],长时间运行还会因不确定的延迟导致数据丢失。驱动运行在内核空间中,在驱动中直接将数据存入硬盘则避免了这些问题。另外由于内核线程优先级高,实时性更好,速度更稳定可靠。

3.2 内核多线程实现流程

实现原理流程如图3所示。应用程序首先在文件存储路径文件中写入默认路径,然后置DDMACR(设备DMA状态控制器)D1位为1,准备开始存储数据。FPGA检测到此位后开始准备数据,当数据达到一次DMA数据量时,置DDMACR D2数据准备好位,应用程序调用ioctl函数查询此位,数据准备好后开始DMA,应用程序进入睡眠状态。在驱动初始化时申请两个DMA缓存区并设置好DMA写操作初始值,同时初始化两个内核线程,一个写硬盘线程,一个开始下次DMA线程。

图3 内核多线程直接存储流程图

当DMA完成时,进入中断处理程序,清中断后,切换DMA内核缓冲区,最后释放开始下次DMA信号量和写文件信号量。开始下次DMA线程收到开始下次DMA信号量后,检测数据准备好位,为真时启动下次DMA。写文件线程也申请两个大缓存,收到写文件信号量时,将已完成DMA缓冲区的数据缓存到一个大的内核缓存中,大缓存大小是DMA缓存的N倍。大缓存满时,切换到另一个大缓存,已满大缓存写入到默认路径下依自定义格式所建文件中,当存储达到指定次数后,关闭文件并新建另一个文件。如此循环,实现了连续的高速DMA存储。关键细节主要有两级双缓冲的实现和内核中文件操作的实现,将在后文作详细讲解。

3.2.1 两级双缓冲的实现

因为每次DMA的数据量小而中断频率较高,为避免频繁向硬盘写入小文件,提高硬盘存储速度。可在内核内存申请两个较大的缓存来转存DMA数据,大缓存容量是DMA写缓存的N倍,本系统取DMA缓存大小为4 MB,因为大缓存只需内存逻辑地址连续不需要物理地址连续,所以可以使用vmalloc函数分配内存,一次最大可分配1 GB[7]。考虑到系统总内存大小和SSD最佳速度对应的文件大小,大缓存大小取40 MB。

乒乓缓存的数据流图如图4所示,每个周期大小为一次DMA时间间隔,进入中断后通过对标志位的取反来标识周期1和周期2的交换。数据在周期1时以DMA的方式将数据传送到DMA写缓存1,在周期2时通过memcpy将数据转存入大缓存,并用memset函数清零DMA写缓存。反之在周期2时将数据存入DMA写缓存2,周期1进行转存,从而形成乒乓缓存。在N个周期中转存的数据都存入大缓存1,而在另外N个周期中都存入大缓存2,从而形成以N为周期的大周期乒乓缓存,降低了写频率,增大了每次写文件的大小,使得DMA和存储速度均连续且平稳。

图4 两级双缓存数据流图

3.2.2 内核中写硬盘的实现方法

在驱动模块中,用户空间的open、read、write等文件操作函数都是不可以使用的,而应该使用内核中对应的函数。在早期版本中可以使用sys_open、sys_read和sys_write完成文件操作,但是在较新的版本中不再由EXPORT_SYMBOL导出这几个符号[8],因此新版本中不能再使用。分析sys_open可知调用do_sys_open后调用了filp->open,因此使用filp文件指针搭配struct file里的read和write即可完成对文件的读写操作。filp->f_op->read和filp->f_op->write都会对参数buf进行合法性检查,如果不在用户空间则会拒绝访问。因此需要设置FS(取USER_DS、KERNEL_DS两值)来增大寻址范围。这些函数定义在asm/uaccess.h。关键代码如下:

3.3 速度测试

本系统Power PC提供SATA2.0接口,经Linux dd命令无格式写SSD速度最大值约为260 MB/s,写Ext3格式文件最大速度约为115 MB/s,速度降低的主要原因是较低性能的CPU需对文件系统信息生成和写入。对两种方法实现的驱动在同一系统下进行对比测试,测试方法为FPGA自动生成图像数据,保证DMA数据随时准备好。因此系统的存储只与驱动的效率和稳定性有关,为减少对系统的影响,系统每隔一分钟通过gettimeofday函数得到存储一百帧图像所用时间,并将时间存入另一文件中,从而可以计算出存储速度。存储两小时,采用Matlab画出速度曲线如图5和图6所示。可以看出,新方法实现的驱动不仅稳定存储速度达到了100 MB/s以上,而且还十分稳定。而传统驱动稳定速度只有60 MB/s上下,速度波动也剧烈得多。应用中对内核多线程直接存储实现的系统连续写入512×512@200 Hz的14位灰度图像,即速率为100 MB/s。测试5次,每次两小时,均未发生丢帧和错帧现象。而采用常规驱动的本系统在速率为60 MB/s时即出现明显丢帧;当速率提高到80 MB/s时,丢帧会更加频繁,而且基本都是连续丢帧。由此可见采用内核多线程多缓存直接存储的驱动具有明显的优势,存储速度更快也更稳定。

图5 常规方法速度曲线

图6 本文方法速度曲线

4 结语

本文介绍PCIE驱动的一般实现方法和步骤,针对其不足提出了在内核中直接用多线程多缓冲存储的方法。阐述了其实现的原理和流程,并对关键步骤和关键点作了详细分析。该系统的瓶颈主要在于SSD的写入速度,后续实现适合大量数据连续写入的RAID3 SSD阵列后,速度将有较大提升。该存储驱动实现方法显著地提高了存储速度及稳定性,具有较高的工程实践及借鉴意义。

摘要:在Linux系统中,高速存储驱动会将大量数据从内核空间复制到用户空间进行存储,这将导致存储速度下降和剧烈波动。针对该问题,结合自行设计开发的PCIE接口高速图像存储系统,分析Linux下PCIE驱动的一般组成及实现,提出一种在驱动中实现多线程多缓存直接存储的方法。首先图像数据乒乓DMA传输到双DMA缓存中,然后将数据乒乓转存到内核空间双大缓存中,最后由内核线程直接写入SSD。经该方法优化后的SSD存储速度达到100MB/s以上,比常规方法提升了65%以上。在实际工程中,输入512×512@200Hz的14位灰度图像能够实现稳定存储,避免了原来的丢帧现象,且性能稳定,取得了良好的优化效果。

关键词:图像存储,PCIE,DMA,Linux,驱动,多线程

参考文献

[1]Ravi Budruk,Don Anderson,Tom Shanley.PCI Express系统体系结构标准教材[M].田玉敏,译.北京:电子工业出版社,2005.

[2]孙科林,周维超,吴钦章.高速实时光纤图像传输系统的实现[J].光学精密工程,2011,19(9):2230-2233.

[3]博韦,西斯特.深入理解Linux内核[M].陈莉君,张琼声,张宏伟,译.北京:中国电力出版社,2007:557-594.

[4]周小波.嵌入式Linux下PCIE数据采集卡驱动开发[D].成都:电子科技大学,2013.

[5]宋宝华,何昭然,史海滨,等.精通Linux设备驱动程序开发[M].北京:人民邮电出版社,2012.

[6]雷雨,周维超,舒怀亮.基于ATR结构的嵌入式NAND Flash图像记录系统[J].光电工程,2014,41(3):49-54.

[7]杨阿锋,吴帅,刘凯,等.PCIe接口高速数据传输卡的驱动程序开发[J].中国测试技术,2008,34(2):67-69.

内核驱动 篇4

目前, 大规模可编程逻辑器件FPGA的出现, 设计人员可使用FPGA进行产品开发, 不仅成本低、周期短、可靠性高。本文基于FPGA中NIOSⅡ内核设计驱动真彩液晶显示模块, 以NIOSⅡ处理器为核心, 结合FPGA中丰富的IP内核, 可根据成本、研发时间等任意选择FPGA芯片, 增强了产品的可靠性和设计灵活性。在Nios II 9.1 IDE环境下采用C语言完成了系统的软件设计和调试, 主要包括了系统驱动硬件构建和系统应用程序开发两部分。驱动程序直接和底层硬件进行宏定义, NIOSⅡ处理器通过驱动程序实现对控制器的操作。

二、主要器件介绍

主芯片采用Altera公司高性价比的FPGA;CycloneⅣ系列的EP4CE6F17C8N。为256脚BGA封装。支持JTAG调试, AS下载, 内有6272宏单元数, 总比特数270Kbit, 内核供电电压范围1.15V-1.25V, 输入/输出电源电压3.3V, 速度级数为8。可编程逻辑芯片逻辑块数为392, 时钟管理为PLL, 最高工作频率为472.5MHz。采用配置芯片为EPCS16, EP4CE6F17C8N为目前一款高速、高性能的FPGA芯片。NIOSⅡ是Altera特有的基于通用FPGA构架的软CPU内核。

TFT_LCD真彩液晶显示模块使用ILI9325控制芯片, 该芯片支持240RGB×320像素, 可以显示262144真色彩。支持宽视角显示。组合720通道源极驱动和320通道门极驱动。栅源极移动方向可逆。丰富的色彩显示函数。局部驱动函数, 用户指定在LCD屏上部分区域显示。节能功能:8色模式;待机模式;睡眠模式。低功率消耗结构:IOVcc=1.65V-3.3V (interface I/O) , Vcc=2.4-3.3V, Vci=2.5-3.3V。

三、FPGA内部电路硬件设计

在FPGA内部构建SOPC也就是NIOSⅡ的处理器。也就是说在FPGA中根据自己的需要任意搭配一个最节省的处理器, 这个处理器的内核就是NIOSⅡ处理器, 外围设备包括SPI、IO接口等等。本系统所需要的基本组件有CPU、SDRAM控制器、JTAG-UART、SRAM, 因为在TFT-LCD已经带有控制器了ILI9325控制芯片, 所以就不需要以自定义组件的形式在So PC Builder中添加该元件的控制器。

设置生成新元件后就可以将新定制的tft_lcd添加到Nios系统中, 产生Nios II系统模块, 并添加到工程中。保存编译, 通过之后可将编译生成的sof文件下载到FPGA芯片, 至此硬件方面的工作基本完成。为搭建本设计的硬件平台中使用生成的系统模块如图1所示。

四、软件设计

本系统使用Nios II 9.1 IDE环境, 在Nios II9.1 IDE集成开发的环境下采用C语言编写软件代码。TFT_LCD液晶屏显示红、绿、蓝三色彩条。显示图形如图2。以下给出部分软件代码:

五、结论

实践证明, 采用FPGA中NIOSⅡ实现TFT真彩液晶显示模块驱动的设计方案具有很好的显示效果。用户能根据显示屏的大小灵活调整硬件逻辑设计以实现对显示屏的控制, 而不需要改变其原有硬件构成。从长远来看, 基于NiosⅡ的微处理器, 可以通过更改其硬件逻辑配置方便地进行版本升级、方便维护, 减少了设计流程、提高可靠性、降低了成本。

参考文献

[1]EDA先锋工作室吴继华蔡海宁王诚Altera FPGA/CPLD设计 (高级篇) 第2版人民邮电出版社2011.

[2]王刚, 张潋.基于FPGA的SoPC嵌入式系统设计与典型实例[M].北京:电子工业出版社, 2009.

内核驱动 篇5

嵌入式系统的开发都有其特殊的应用场合与特定功能[1],而嵌入式Linux操作系统因其开源和广泛的处理器支持、易于移植而备受行业青睐。AT91RM9200是Atmel公司针对系统控制、通信领域推出的基于ARM920T内核的32位RISC微处理器,它具有小体积,低功耗,低成本及高性能等特点[2],其内部集成了SPI、串口、PIO、以太网、EBI、USB、MCI等多种接口。

在Linux系统中,应用层不可以直接操作硬件[1],需设计驱动程序向下屏蔽硬件特性,实现硬件与用户间的通信。系统平台为在虚拟机中安装Fedora 8,目标系统采用Linux 2.6.21.7内核,定制文件系统建立NFS根文件系统,使用双网卡方式搭建成交叉开发环境,并使用超级终端或minicom作为控制台。

1 设备驱动程序设计

该控制系统框架如图1所示。ARM通过USART1接收外来的控制命令,通过SPI接口和通用PIO口与外部设备通信,达到控制作用。在Linux下,所有的设备以文件的形式来使用。其中Linux已经提供了支持AT91RM9200的SPI驱动,DBGU和UART驱动,只要对其源代码进行一些修改并在编译内核时将其选中就可以直接使用。所以主要集中在PIO口驱动设计中,外部设备使用一个PB29引脚(即IRQ0)作为外部中断信号提供给ARM,另外使用一些I/O引脚对外部设备进行控制。

Linux设备分为3类:字符设备、块设备和网络设备,该系统设计的是模块化字符设备驱动程序。Linux 2.6内核与Linux 2.4内核主要有3点不同:

(1) 内核的API变化,增加了不少新功能;

(2) 提供了sysfs用于描述设备树;

(3) 驱动模块从.o变为.ko。

1.1 驱动程序重要数据结构

打开的设备在内核内部由file结构标识,内核使用file_operations结构访问驱动程序的函数。file_operations结构是一个定义在<Linux/fs.h>中的函数指针数组。下面主要介绍常用的几个成员[3]:

在这些函数指针中,open和release用于设备的打开和关闭,是每个驱动程序必须实现的函数。其他函数根据实际需要来实现,在该项目中实现方式如下:

另一个重要数据结构是file结构体,主要包括以下成员:

它代表一个打开的文件,只出现在内核空间,与用户空间的file是不同的。在open操作时创建,然后传递给file_operations的其他函数指针,直到close。

第三个重要数据结构即inode,其成员包括:dev_t i_rdev和struct cdev * i_cdev,其中i_rdev中包含实际设备号,可以通过下面两个宏函数获取主从设备号:

unsigned int iminor(struct inode *inode);

unsigned int imajor(struct inode *inode);

初始化file_operations结构体后,要将其中定义的各个方法如open,release,write,read,ioctl等一一实现。其函数名即初始化这个file_operations结构体时各成员函数指针。当在用户空间调用open时,内核空间的open方法即相应操作,其他方法同理。

1.2 驱动初始化和卸载清理工作

驱动加载需要进行设备注册等一系列初始化工作,并且在卸载驱动时要释放资源进行一些清理工作以使其不影响内核。所以定义两个函数static int devctl_init()和static void devctl_exit(),然后通过module_init(devctl_init)和module_exit(devctl_exit)来通知内核。为了维护Linux的开源性,调用下面的宏来声明:

MODULE_AUTHOR("SNOW in 2008.5.26");

MODULE_LICENSE("Dual BSD/GPL");

在初始化函数中,首先进行设备的注册。主设备号表示对应的驱动程序,次设备号由内核使用,用于正确确定设备文件所指的设备。可以动态申请或者静态申请设备号。动态申请使用下面的函数:

int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);

dev 是一个只输出的参数,它在函数成功完成时持有分配范围的第一个数;firstminor是请求的第一个要用的次编号;count是请求的连续设备编号的总数;name为设备名,返回值小于0表示分配失败。然后通过major=MAJOR(dev)获取主设备号。如果注册不成功或者卸载驱动时需要取消设备的注册,使用下面的函数实现(其参数含义同上):

void unregister_chrdev_region(dev_t first,unsigned int count);

对于字符型设备还要定义一个cdev结构体变量,并使用cdev_init()初始化,然后调用cdev_add()通知内核添加一个字符设备。同样在卸载时要使用cdev_del()移除,否则用户使用驱动时,有时不能打开设备。因为不使用cdev或者cdev在模块卸载时不删除会导致内核处在一个不稳定状态,在用户层可能无法打开设备文件。

1.3 I/O端口访问

在系统控制要求中,需要访问ARM的I/O端口,包括普通I/O口和复用为IRQ0的PB29引脚,然而Linux中对I/O端口和I/O内存的读写指令中使用的都是虚拟地址,所以在访问前要先将物理寄存器地址映射到I/O内存。有两种方法实现地址映射,一种是使用ioremap为I/O内存区域分配虚拟地址,用iounmap取消,另一种是使用内核已经定义好的虚拟地址。这里主要介绍第二种方式。

对于AT91RM9200利用如下转换函数获取虚拟地址,其中宏AT91_VA_BASE_SYS是系统虚拟基地址:

读写端口对于AT91RM9200还可使用专门函数int at91_set_gpio_value(unsigned pin,int value),并包含头文件asm-arm/arch-at91/gpio.h。一般端口的访问在驱动模块初始化时申请资源,在卸载时释放资源,而对于I/O口的使能则在open方法中实现,相应的禁用在release方法中实现。

1.4 ioctl方法的实现

用户可以通过ioctl方法向内核发送各种命令,必要时传递参数,下面展示一个简单实例。

1.5 中断控制实现

当外部信号的到来时刻不可预测时,使用轮询方式将使得效率极低,需要使用阻塞型中断实现。即没有中断信号到来时阻塞读进程,使其处于睡眠状态,当中断到来唤醒读进程,执行预定处理操作。

首先,在open方法中使用request_irq()安装中断处理程序,在release方法中释放。函数原型如下:

int request_irq(unsigned int irq,irqreturn_t (*handler)(int,void*,struct pt_regs*),unsigned long flags,const char* dev_name,void* dev_id);

void free_irq(unsigned int irq,void *dev_id);

其中:参数irq为中断号;handler为ISR指针;flags为与中断管理有关的各选项字节掩码;dev_name即设备名;dev_id为中断信号线。

其次,ISR为申请中断时使用的参数名,假设为irq0_handler,定义原型如下:

static irqreturn_t irq0_handler(int irq,void* dev_id,struct pt_regs* regs),

中断阻塞即在其内部调用void wake_up_interruptible(wait_queue_head_t* queue)实现,然后返回IRQ_HANDLED;在read方法中调用wait_event_interruptible(queue,condition)来唤醒读进程,这样,当用户程序读设备时,如果没有中断到来,读进程将进入睡眠状态,中断发生被唤醒。

对于中断信号IRQ0,因是PB29复用,要配置为外设A[4],同时还要配置中断源类型,函数分别在#include <asm-arm/arch-at91/gpio.h>,#include <Linux/irq.h>中定义,原型如下:

int at91_set_A_periph(unsigned pin,int use_pullup);

int set_irq_type(unsigned int irq,unsigned int type);

2 编译和调试

驱动程序可静态编译进内核,也可编译成模块动态加载。为便于调试采用动态模块加载方式,Linux 2.6内核下驱动编译方式和Linux 2.4版明显不同,其建立的Makefile只需简单地写入obj-m:=devctl.o(假设源文件为devctl.c),然后执行命令:make –C /usr/local/arm/Linux-2.6.21.7 SUBDIRS=MYMPWD modules,注意内核源文件目录因各自系统而异,然后将生成的.ko文件置于目标系统的/home目录下,使用insmod加载模块,并使用cat /proc/devices命令查看分配到的设备号,使用mknod创建设备节点,卸载模块使用rmmod命令。

为方便调试,可以在适当使用printk打印信息,还可以通过点LED等以便于发现问题。

3 结 语

通过对相关的Linux 2.6内核中驱动源码的深入研究与自我设计实践,不断调试,在此阐述的方法得到实际验证,并已成功使用到某仪器的控制系统中。Linux博大精深,其开源的特点必将吸引更多的开发者投入其中,使其更好发展,应用于更多领域。

参考文献

[1]陈赜.ARM9嵌入式技术及Linux高级实践教程[M].北京:北京航空航天大学出版社,2005.

[2]王田苗.嵌入式系统设计与实例开发——基于ARM微处理器与μC/OS-Ⅱ实时操作系统[M].北京:清华大学出版社,2002.

[3]Jonathan Corbet,Greg Kroah Hartman,Alessandro Rubini.Linux Device Drivers[M].3rd Edition.O′Reilly,2005.

[4]ATMEL Corporation.AT91RM9200 Datasheet[Z].2006.

[5]张晓东,李秀娟,张杰.基于ARM的嵌入式远程监控系统设计[J].现代电子技术,2008,31(6):22-23.

上一篇:铝合金的性能优势论文下一篇:输运性质