当前位置:Linux教程 - Linux文化 - xwindow的显示原理

xwindow的显示原理


>>> 此贴的回复 >> 13.2 mmap设备操作

内存映射是现代 Unix 系统最有趣的特征之一。对于驱动程序来说,内存映射可以提供给用户程序直接访问设备内存的能力。

看一下X Window系统服务器的虚拟内存区域,就可以看到 mmap 用法的一个明显的例子:

cat /proc/731/maps 08048000-08327000 r-xp 00000000 08:01 55505 /usr/X11R6/bin/XF86_SVGA 08327000-08369000 rw-p 002de000 08:01 55505 /usr/X11R6/bin/XF86_SVGA 40015000-40019000 rw-s fe2fc000 08:01 10778 /dev/mem 40131000-40141000 rw-s 000a0000 08:01 10778 /dev/mem 40141000-40941000 rw-s f4000000 08:01 10778 /dev/mem ...

X 服务器的VMA的整个列表是很长的,但是这里我们对大部分的项都不感兴趣。然而,确实可以看到 /dev/mem 的三个独立的映射,它可以使我们对 X 服务器怎样与显示卡协同工作有一些了解。第一个映射显示了映射到 fe2fc000 的一个 16KB 区域,这个地址远远高于系统上最高的 RAM 地址,它是 PCI 外围设备(显示卡)的一段内存区域,它是该卡的控制区域。中间的映射位于 a0000,它是在 640KB ISA 空洞中的标准位置。最后的 /dev/mem 映射是位于 f4000000 位置的一个相当大的区域,而且是显示内存本身。这些区域也可以在 /proc/iomem 中看到:

000a0000-000bffff : Video RAM area f4000000-f4ffffff : Matrox Graphics, Inc. MGA G200 AGP fe2fc000-fe2fffff : Matrox Graphics, Inc. MGA G200 AGP

映射一个设备,意味着使用户空间的一段地址关联到设备内存上。无论何时,只要程序在分配的地址范围内进行读取或者写入,实际上就是对设备的访问。在 X 服务器的例子中,使用 mmap 可以既快速又简单地访问显示卡的内存。对于象这样的性能要求比较严格的应用来说,直接访问能给我们提供很大不同。

正如读者所怀疑的,并不是所有的设备都能进行 mmap 抽象;例如,象串口设备和其它面向流的设备,就无法实现这种抽象。mmap 的另一个限制是映射都是以 PAGE_SIZE 为单位的。内核只能在页表一级上处理虚拟地址;因此,被映射的区域必须是 PAGE_SIZE 的整数倍,而且必须位于起始于 PAGE_SIZE 整数倍地址的物理内存内。如果区域的大小不是页大小的整数倍,内核可通过生成一个稍微大一些的区域来调节页面大小粒度。

这些限制对于驱动程序来说并不是很大的问题,因为程序访问设备的动作总是依赖于设备的,它需要知道如何使得被映射的内存区域有意义,所以 PAGE_SIZE 对齐不是一个问题。在 ISA 设备用于某些非 x86 平台时存在一个比较大的限制,因为它们对于 ISA 硬件的开发并不一样。例如,某些 Alpha 计算机将 ISA内存看成是不能直接映射的、离散的8位、16位或者32位项的集合。这种情况下,根本不能使用 mmap。不能直接将ISA地址映射到 Alpha 地址的原因,是由于两种系统间不兼容的数据传输规范导致的。早期的 Alpha 处理器只能进行 32 位和 64 位的内存访问,而 ISA 只能进行 8 位和 16 位的数据传输,并且没有透明地将一个协议映射到另一个之上的方法。

在能够使用 mmap 的情况下,使用 mmap 还有另外一个优势。例如,我们已经讨论了 X 服务器,它可以与显示内存进行大量的数据交换。相对于 lseek/write 实现来说,将图形显示映射到用户空间可以显著提高吞吐量,另一个典型的例子是受程序控制的 PCI 设备。大多数 PCI 外围设备都将它们自己的控制寄存器映射到内存地址上,而苛刻的应用程序可能更喜欢直接访问寄存器,而不是重复调用 ioctl 来完成它的工作。

mmap 方法是 file_operations 结构中的一员,并且在执行 mmap系统调用时就会调用该方法。在调用实际方法之前,内核会完成很多工作,而且该方法的原型与其系统调用的原型具有很大区别。这与其它系统调用如 ioctl 和 poll 不同,在调用它们之前内核不需要做太多的工作。