《Linux Device Driver》——与硬件通信

设备驱动程序是软件概念和硬件概念电路之间的一个抽象层。

I/O端口和I/O内存

每一种外设都是通过读写寄存器进行控制。
在硬件层,内存区域和I/O区域没有概念上的区别:它们都通过地址总线和控制总发送电平信号(比如读信号和写信号),再通过数据总线读写数据。
在这里插入图片描述
在这里插入图片描述


I/O寄存器和常规内存

1.I/O寄存器与RAM的最主要区别就是I/O操作具有边际效应(个人理解就是副作用):读取某个地址时可能导致这个地址的内容发生变化,比如很多中断寄存器的值一经读取,便自动清零。内存操作就不存在:内存写操作的唯一结果就是在指定位置存储一个数值;而内存读操作仅仅返回指定位置的一次写入的数值。
2.编译器可以将数据缓存在CPU寄存器而不写入内存,即使存储数据,读写操作也能在高速缓存中进行而不用访问物理内存。
3.常规内存可以优化,但对寄存器进行优化会导致致命错误。驱动程序必须确保不使用高速缓存,在访问寄存器时不发生读或写指令的重新排序。
4.由硬件自身缓存引起的问题很好解决:只要在把底层硬件配置成(自动配置或是linux初始化代码完成)在访问I/O区域(不管是内存还是端口)时禁止硬件缓存即可。
5.由编译器优化和硬件重新排序引起的问题的解决办法:对硬件或其他处理器必须以特定顺序执行的操作之间设置内存屏障。linux有四个宏解决所有可能的排序问题。

 #include<linux/kernel.h>

void barrier(void);
//对barrier的调用可防止在屏障前后的编译器优化,但硬件能完成自己的重新排序。
 #include<asm/system.h>

void rmb(void); /*保证任何出现于屏障前的读在执行任何后续的读之前完成*/
void wmb(void); /*保证任何出现于屏障前的写在执行任何后续的写之前完成*/
void mb(void); /*保证任何出现于屏障前的读写操作在执行任何后续的读写操作之前完成*/

void read_barrier_depends(void); /*一种特殊的、弱些的读屏障形式。rmb 阻止屏障前后的所有读指令的重新排序,read_barrier_depends 只阻止依赖于其他读指令返回的数据的读指令的重新排序。区别微小, 且不在所有体系中存在。除非你确切地理解它们的差别, 并确信完整的读屏障会增加系统开销,否则应当始终使用 rmb。*/
/*以上指令是barrier的超集*/

void smp_rmb(void); 
void smp_read_barrier_depends(void); 
void smp_wmb(void); 
void smp_mb(void); 
/*仅当内核为 SMP 系统编译时插入硬件屏障; 否则, 它们都扩展为一个简单的屏障调用。*/

设备驱动程序中使用内存屏障的典型形式:

writel(dev->registers.addr, io_destination_address);
writel(dev->registers.size, io_size);
writel(dev->registers.operation, DEV_READ);
wmb();/*类似一条分界线,上面的写操作必然会在下面的写操作前完成,但是上面的三个写操作的排序无法保证*/
writel(dev->registers.control, DEV_GO);

内存屏障影响性能,所以应当只在确实需要它们的地方使用。不同的类型对性能的影响也不同,因此要尽可能地使用需要的特定类型。值得注意的是大部分处理同步的内核原语,例如自旋锁和atomic_t,也可作为内存屏障使用。

某些体系允许赋值和内存屏障组合,以提高效率。它们定义如下:

#define set_mb(var, value)do {var = value; mb();}while 0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值