我正在尝试使用 /dev/mem 读取映射到 PCIe 设备的一些物理地址。PCIe 设备映射到0x387ffa000000:
bash# lspci -s 1a:00.0 -v | grep Memory
Memory at 387ffa000000 (64-bit, prefetchable) [size=32M]
Run Code Online (Sandbox Code Playgroud)
所以我根据我在 SO 找到的内容编写了一些程序,它使用了mmap ():
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage: %s <phys_addr> <offset>\n", argv[0]);
return 0;
}
off_t offset = strtoul(argv[1], NULL, 0);
size_t len = strtoul(argv[2], NULL, 0);
size_t pagesize = sysconf(_SC_PAGE_SIZE);
off_t page_base = (offset / pagesize) * pagesize;
off_t page_offset = offset - page_base;
int fd = open("/dev/mem", O_SYNC);
if (fd < 0) {
perror("Can't open");
return 1;
}
unsigned char *mem = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, page_base);
if (mem == MAP_FAILED) {
perror("Can't map memory");
return 1;
}
size_t i;
for (i = 0; i < len; ++i) printf("%02x ", (int)mem[page_offset + i]);
printf("\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
虽然这有效:
bash# ./mem_read.out 0x387ffa000000 0x10
00 1f 00 18 00 05 07 08 00 00 00 00 00 00 00 00
Run Code Online (Sandbox Code Playgroud)
我只想尝试使用read ()/ write () 调用而不是 mmap() 进行相同的操作。
但是当我尝试这个时:
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage: %s <phys_addr> <size>\n", argv[0]);
return 1;
}
off_t offset = strtoul(argv[1], NULL, 0);
size_t len = strtoul(argv[2], NULL, 0);
if (!len) {
printf("size argument can't be 0\n");
return 1;
}
int fd = open("/dev/mem", O_SYNC);
if (fd < 0) {
perror("Can't open");
return 1;
}
if (lseek(fd, offset, SEEK_SET) < 0) {
perror("Can't seek");
return 1;
}
char* buff = (char*)malloc(len * sizeof(char) + 1);
if (read(fd, buff, len) < 0) {
perror("Can't read");
return 1;
}
printf("%s\n", buff);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它失败了:
bash# ./mem_read.out 0x387ffa000000 0x10
Can't read: Bad address
Run Code Online (Sandbox Code Playgroud)
/dev/mem当我使用常规文本文件代替时,相同的代码可以工作。
有谁知道为什么前者有效而后者无效?
只是为了提供额外的上下文,当 时打印“错误地址” errno == EFAULT,由以下方法返回read():
buf 超出了您可访问的地址空间。
这对我来说毫无意义。