因为项目需要,需要在Linux userspace 读写访问实际物理地址。
一)用户空间可以直接通过打开 /dev/mem 设备文件,然后mmap() 影射进行访问
static int read_type()
{
void * map_base;
FILE *f;
int type,fd;
#define READ_REG32(reg) ( *((volatile int *) (reg)) )
#define ALLOC_SIZE (1024)
fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd) {
printf("Success to open /dev/mem fd=%08x\n", fd);
}
else {
printf("Fail to open /dev/mem fd=%08x\n", fd);
}
map_base = mmap(0, ALLOC_SIZE, PROT_READ, MAP_PRIVATE, fd, 0x35004000);
type = READ_REG32(map_base + 0x20);
close(fd);
munmap(map_base, ALLOC_SIZE);
printf("reg32[%08x] = value[%08x] \n", map_base, type);
type = (type & ( 1 << 27 )) >> 27 ;
printf("reg32[%08x] = value[%08x] \n", map_base, type);
return type;
}
mmap,通过内核建立一个虚拟地址到物理地址的映射,然后通过这一虚拟地址就可以在用户空间访问真实的物理地址了
mmap() 其中有几个参数需要说明:
- PROT_READ 区域可读;
- PROT_WRITE 区域可写;
- MAP_SHARED 对映射区域的写入数据会复制回文件内, 而且允许其他映射该文件的进程共享。
在Android 4.4上实际运行的结果:
- 编译生成的用户空间可执行程序,需要root user权限才能运行。即使程序本身已经是 rwx-rwx-rwx权限。
- mmap()中映射的缓冲区必须是 PAGE size 的整数倍。 如果不是,会产生segmentation fault。
- 实际访问的物理地址如果不是恰好是PAGE size 的整数倍,必须要对mmap()返回的映射基地址加上对应的偏移量,得到最终的影射后地址。
二)用户空间驱动程序
如果用户空间可以访问实际物理地址,则可以更进一步实现用户空间的硬件驱动程序。
用户空间驱动的优点:
- 完整的 C 库可以连接. 驱动可以进行许多奇怪的任务, 不用依靠外面的程序(实现使用策略的工具程序, 常常随着驱动自身发布).
- 程序员可以在驱动代码上运行常用的调试器, 而不必走调试一个运行中的内核的弯路.
- 如果一个用户空间驱动挂起了, 你可简单地杀掉它. 驱动的问题不可能挂起整个系统, 除非被控制的硬件真的疯掉了.
- 用户内存是可交换的, 不象内核内存. 一个不常使用的却有很大一个驱动的设备不会占据别的程序可以用到的 RAM, 除了在它实际在用时.
- 一个精心设计的驱动程序仍然可以, 如同内核空间驱动, 允许对设备的并行存取.
- 如果你必须编写一个封闭源码的驱动, 用户空间的选项使你容易避免不明朗的许可的情况和改变的内核接口带来的问题.
用户空间的设备驱动的方法有几个主要缺点:
- 中断在用户空间无法用. 在某些平台上有对这个限制的解决方法, 例如在 IA32 体系上的 vm86 系统调用.
- 只可能通过内存映射 /dev/mem 来使用 DMA, 而且只有特权用户可以这样做.
- 存取 I/O 端口只能在调用 ioperm 或者 iopl 之后. 此外, 不是所有的平台支持这些系统调用, 而存取/dev/port可能太慢而无效率. 这些系统调用和设备文件都要求特权用户.
- 响应时间慢, 因为需要上下文切换在客户和硬件之间传递信息或动作.
更不好的是,
- 如果驱动已被交换到硬盘, 响应时间会长到不可接受. 使用 mlock 系统调用可能会有帮助, 但是常常的你将需要锁住许多内存页, 因为一个用户空间程序依赖大量的库代码. mlock, 也, 限制在授权用户上.
- 最重要的设备不能在用户空间处理, 包括但不限于, 网络接口和块设备.
如你所见, 用户空间驱动不能做的事情毕竟太多. 感兴趣的应用程序还是存在: 例如, 对 SCSI 扫描器设备的支持( 由 SANE 包实现 )和 CD 刻录器 ( 由 cdrecord 和别的工具实现 ). 在两种情况下, 用户级别的设备情况依赖 "SCSI gneric" 内核驱动, 它输出了低层的 SCSI 功能给用户程序, 因此它们可以驱动它们自己的硬件.
一种在用户空间工作的情况可能是有意义的, 当你开始处理新的没有用过的硬件时. 这样你可以学习去管理你的硬件, 不必担心挂起整个系统. 一旦你完成了, 在一个内核模块中封装软件就会是一个简单操作了.