典型例子:
writel(dev->registers.addr, io_destination_address);
writel(dev->registers.size, io_size);
writel(dev->registers.operation, DEV_READ);
wmb( ); /* 在开始执行操作(最后一个写操作)之前,先将各相关控制寄存器设置好(前三个写操作)。其实wmb就是一条分界线,它保证其之前的三个写操作执行完了之后,才执行其后的写操作,但是前面三个写操作顺序则无法保证 */
writel(dev->registers.control, DEV_GO);
|
内存屏障影响系统性能,所以只能在确实需要它们的地方使用。不同类型的屏障也有不同的性能特性,因此,应当尽可能使用最合适的屏障类型。
值得注意的是大部分处理同步的内核原语,例如自旋锁和atomic_t操作,也具有内存屏障的功能。还有就是有些外设总线(如PCI 总线)有它们自己的缓冲问题; 我们在后面章节遇到时讨论这些问题。
某些体系允许将赋值和内存屏障组合在一起,以提高效率。它们如下定义:
#define set_mb(var, value) do {var = value; mb();} while 0 #define set_wmb(var,,value) do {var = value; wmb();} while 0 #define set_rmb(var,,value) do {var = value; rmb();} while 0
|
在合适的地方,为了更快的完成任务,<asm/system.h> 使用体系特定的(architecture-specific)指令来定义这些宏。注意,很少体系支持set_rmb。使用do...while 结构来定义宏是标准C的惯用方法(在内核源码中非常常见),它使被扩展的宏可在所有上下文环境中作为一个正常的C语句被执行。
2、使用I/O 端口
I/O 端口是驱动用来和很多设备通讯的方法。
2.1、分配I/O 端口
在驱动还没独占设备之前,不应对端口进行操作。内核提供了一个注册接口,以允许驱动声明其需要的端口:
#include <linux/ioport.h>
/* request_region告诉内核:要使用first开始的n个端口。参数name为设备名。如果分配成功返回值是非NULL;否则无法使用需要的端口(/proc/ioports包含了系统当前所有端口的分配信息,若request_region分配失败时,可以查看该文件,看谁先用了你要的端口) */ struct resource *request_region(unsigned long first, unsigned long n, const char *name);
/* 用完I/O端口后(可能在模块卸载时),应当调用release_region将I/O端口返还给系统。参数start和n应与之前传递给request_region一致 */ void release_region(unsigned long start, unsigned long n);
/* check_region用于检查一个给定的I/O端口集是否可用。如果给定的端口不可用,check_region返回一个错误码。不推荐使用该函数,因为即便它返回0(端口可用),它也不能保证后面的端口分配操作会成功,因为检查和后面的端口分配并不是一个原子操作。而request_region通过加锁来保证操作的原子性,因此是安全的 */ int check_region(unsigned long first, unsigned long n);
|
文章评论(0条评论)
登录后参与讨论