免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1162 | 回复: 0
打印 上一主题 下一主题

I/O端口和I/O内存管理 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2005-10-13 14:11 |只看该作者 |倒序浏览

使用I/O端口
       I/O端口有点类似内存位置:可以用和访问内存芯片相同的电信号对它进行读写
。但这两者实际上并不一样;断口操作是直接对外设进行的,和内存相比更不灵活。而
且,有8位的端口,也有16位的端口和32位的端口,不能相互混淆*。
 
 
 
 
       因此,C语言程序必须调用不同的函数来访问大小不同的端口。Linux内核头文件
中(就在与体系结构相关的头文件中)定义了如下一些内联函数。
 
 
 
——————————————————————————————————————

 
注意    从现在开始,如果我只使用unsigned而不进一步指定类型信息的话,那我是在
谈及一个与体系结构相关的定义,此时就不必关心它的准确特性。这些函数基本是可移
植的,因为编译器在赋值时会自动进行强制类型转换(cast)-类型被强制转换成unsigne
d类型防止了编译时出现的警告信息。只要程序员赋值时注意避免溢出,这种强制类型转
换就不会丢失信息。在本章剩余部分我会一直保持这种“不完整的类型定义”的方式。
 
——————————————————————————————————————

 
 
 
unsigned inb(unsigned port);
unsigned inb(unsigned port);
 
void outb(unsigned char byte, unsigned port);
 
按字节(8位宽度)读写端口。port参数在一些平台上定义为unsigned long,而在另一些
平台上定义为unsigned short。不同平台上inb返回值的类型也不相同。
 
 
 
unsigned inw(unsigned port);
 
void outw(unsigned short word, unsigned port);
 
这些函数用于访问16位端口(“字宽度”);Linux的M68k版本不提供,因为该处理器只支
持字节宽度的I/O操作,不支持字宽度或更大宽度的操作。
 
 
 
unsigned inl(unsigned port);
 
void outl(unsigned doubleword, unsigned port);
 
这些函数用于访问32位端口。doubleword参数根据不同平台定义成unsigned long类型或
这些函数用于访问32位端口。doubleword参数根据不同平台定义成unsigned long类型或
unsigned int类型。
 
 
 
       除了每次只能传输一个数据单位的in和out操作,绝大多数处理器还提供了能传
输多个字节,字或long类型数据的特殊指令。这些指令就是所谓的“串指令”,将在本
章稍后处的“串操作”一节中介绍。
 
 
 
       注意这里没有定义64位的I/O操作。即使在64位的体系结构上,I/O端口也只使用
32位的数据通路。
 
 
 
       上面这些函数主要是提供给设备驱动程序使用的,但它们也可以在用户空间使用
(预处理定义和内联声明没有用#ifdef __KERNEL__保护)。但是如果要在用户空间代码中
使用inb及其相关函数,必须满足下面这些条件:
 
 
 
l        编译该程序时必须带-O选项来强制内联函数的展开。
l        编译该程序时必须带-O选项来强制内联函数的展开。
 
 
 
l        必须用ioperm或iopl来获取对端口进行I/O操作的权限。ioperm用来获取对指
定端口的操作权限,而iopl用来获取对整个I/O空间的操作权限。这两个函数都是Intel
平台提供的。
 
 
 
l        必须以root身份运行该程序才能调用ioperm。或者,该程序的某个祖先已经以
root身份获取了对端口操作的权限。
 
 
 
       示例程序misc-progs/inp.c和misc-progs/outp.c是在用户空间通过命令行读写8
位端口的一个小工具。我已经在我的PC上成功运行过。但由于缺少ioperm函数的原因,
它们不能在其它平台上运行。如果你想冒险,可以将它们设置上SUID位,那么不用显式
地获取特权就可以使用硬件了。
 
平台相关性
       如果你考虑移植问题,你会发现I/O指令是所有计算机指令中与处理器最密切相
关的部分。因此,大部分与I/O端口有关的源代码都与平台相关。
关的部分。因此,大部分与I/O端口有关的源代码都与平台相关。
 
 
 
       Linux系统,尽管是可移植的,但处理器的特性不是完全透明的。大部分硬件驱
动程序在平台间是不可移植的,而且在同一模块中驱动程序所涵盖的平台一般不超过两
到三种。
 
 
 
       回头看看前面的函数列表,你可以看到一处不兼容的地方,数据类型,参数类型
根据各平台体系结构上的不同要相应地使用不同的数据类型。例如,port参数在x86平台
(处理器只支持64KB字节的I/O空间)上定义为unsigned short,但在Alpha平台上定义为u
nsigned long。Alpha平台上端口是和内存在同一地址空间内的一些特定区域,它在设计
上就不存在I/O地址空间,它的端口是被当作为不能被高速缓冲的内存区域。
 
 
 
       I/O数据类型是核心中一个仍需要整理的部分,尽管现在能正常工作。对这些不
够明确的类型最好的解决方法是定义一个与体系结构有关的port_t数据类型而对数据项
则使用u8,u16和u32这些数据类型(参见第10章“合理使用数据类型”的“分配确定的空
间大小给数据项”一节)。但还没人真正注意过这个问题,因为这个问题主要是个书写技
巧上的问题。
巧上的问题。
 
 
 
       其他一些与平台相关的问题来源于处理器结构根本上的差异,因此也无法避免。
因为本书我假定你不会在不了解底层硬件的情况下为特定的系统写驱动程序,所以我不
会详细讨论这些差异。下面是可以支持的体系结构的总结:
 
 
 
X86
 
       该体系结构支持本章提到的所有函数。
 
 
 
Alpha
 
支持前面所有函数,但不同的Alpha平台上端口I/O操作的实现也有不同。串操作是用C语
言实现的,在文件arch/alpha/lib/io.c中定义。但遗憾的是,2.0系列的核心在2.0.29
版之前还只开放word和long类型数据的串操作;因此,模块中无法使用insb和outsb函数
。在2.0.30和2.1.3版中这个问题已经改正过来了。
 
 
 
 
Sparc
 
Sparc不提供特殊的I/O指令。I/O空间是通过内存映射获得,在页表中设置了标志。在头
文件中inb和其它函数都被定义为空函数来避免第一次把驱动程序移植到Sparc体系结构
上时编译器为此报告错误。
 
 
 
M68k
 
只支持inb,outb和它们相应的暂停式版本(见下节)。68000上没有定义串操作,也没定
义readb,writeb和相关函数。
 
 
 
Mips
 
支持前面所有函数。但串操作是用汇编语言写的紧凑循环(tight loop)实现的,因为Mip
s处理器不提供机器一级的串I/O操作。
 
 
 
 
PowerPC
 
       除了串I/O操作,其它函数都能支持。
 
 
 
       感兴趣的读者可以从io.h文件获得更多信息,除了我在本章介绍的函数,一些与
体系结构相关的函数有时也由该文件定义。
 
 
 
       值得提及的是,Alpha处理器并不为端口提供不同的地址空间,虽然AXP机器一般
带有ISA和PCI插槽,而且这两种总线都为内存和I/O操作提供了不同的信号线。利用特别
的接口芯片将指定的内存地址引用转换成对I/O端口的访问,基于Alpha的PC可以实现一
个与Intel系列兼容的I/O抽象层。
 
 
 
       Alpha平台上的I/O操作在“Alpha参考手册”中详细介绍了,该手册可从DEC公司
免费获得,手册详尽地阐述了I/O问题,介绍了AXP处理器是如何将虚拟地址空间划分为
免费获得,手册详尽地阐述了I/O问题,介绍了AXP处理器是如何将虚拟地址空间划分为
“类内存”和“不类内存”区域;后者用于内存映射的I/O。
 
暂停式(pausing)I/O
       一些平台-特别是在i386上-当处理器和总线间数据得传输太快是会带来问题。
问题是源于相对ISA总线处理器的时钟频率太快了,当设备卡太慢时,这个问题就容易暴
露出来;解决该问题的方法是,如果后面又跟着一条I/O指令,就在该条I/O指令后添加
一小段延迟。如果你的设备会丢失数据,或者你担心它会丢失数据,你可以用暂停式的I
/O操作取代通常的I/O操作。暂停式I/O函数很象前面列出的那些I/O函数,但它们的名字
都以_p结尾;例如inb_p,outb_p等等。对所有支持的体系结构,如果定义了不暂停的I/
O函数,那么也会定义相应的暂停式的I/O函数,虽然有些平台上它们会被扩展成相同的
代码
[color="#ffffff"]
新的I/O内存接口由下面这些函数组成:
 
 
 
 
unsigned readb(address);
 
unsigned readw(address);
 
unsigned readl(address);
 
这些宏用于从I/O内存中取得8位,16位和32位的数据值。1.2版的Linux不提供。使用宏
的优点是对参数的类型不作要求;参数address在使用前会被强制类型转换,因为这个值
“不清楚是整数还是指针,但我们两者都能接受”(见 asm-alpha/io.h)。读和写函数都
不会检查address参数的合法性,因为使用它就是为了能和使用指针一样快(我们已经知
道有时它们实际上就是被扩展成指针操作)。
 
 
 
unsigned writeb(unsigned value, address);
 
unsigned writew(unsigned value, address);
 
unsigned writel(unsigned value, address);
 
       和前面的函数类似的,这些函数(宏)用于写8位,16位和32位的数据项。
       和前面的函数类似的,这些函数(宏)用于写8位,16位和32位的数据项。
 
 
 
memset_io(address,value,count);
 
当你要对I/O调用memset进行操作时,这个函数可以满足你的需要,并且也保留了memset
原来的语义。
 
 
 
memcpy_fromio(dest, source, nbytes);
 
memcpy_toio(dest, source, nbytes);
 
这些函数用于成块传输I/O内存的数据,和memcpy_tofs的功能有些相似。它们是和上面
这些函数一起引入Linux中的,1.2版的Linux不提供。与示例代码一起发布的sysdep.h头
文件修正了函数的版本相关性问题,为1.2版以后的所有内核提供了这些函数的定义。
 
 
 
       和I/O端口函数一样的,这些函数在能支持的体系结构间的移植性现在也很有限
。一些平台根本不提供这些函数;一些平台上它们是被扩展为指针操作的宏,而在另一
。一些平台根本不提供这些函数;一些平台上它们是被扩展为指针操作的宏,而在另一
些平台上它们则是真正的函数。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/9747/showart_51773.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP