尝试mmap PCI设备BAR-0时触发"Bad file descriptor"错误求助
解决mmap映射PCI BAR-0时的Bad File Descriptor错误
首先,你的代码里有一个致命错误直接导致了这个问题,同时还有几个其他需要注意的点,我逐一给你说明:
1. 文件描述符类型错误(最可能的直接原因)
你用uint32_t pci_dev来存储open()的返回值,但open()返回的是int类型——当打开文件失败时,它会返回-1。由于uint32_t是无符号整数,-1会被转换成0xFFFFFFFF,这时候你判断if (pci_dev < 0)永远不会成立,程序直接跳过了错误处理,带着无效的文件描述符进入mmap()调用,自然就会抛出Bad file descriptor错误。
修复方法:把pci_dev的类型改成int:
int pci_dev; // 替换原来的uint32_t pci_dev;
2. mmap权限不匹配
你打开文件时用了O_RDWR | O_SYNC,但mmap()只指定了PROT_WRITE权限,这会导致权限不匹配。应该同时指定读和写权限:
mmap_base = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, pci_dev, 0);
3. /sys resource文件的映射限制
/sys下的resource0是虚拟文件,它本身存储的是PCI BAR的物理地址信息,而不是直接可映射的内存区域。很多情况下,直接mmap这个文件并不是正确的做法,正确的流程应该是:
- 读取
resource0文件,获取BAR的物理起始地址和大小 - 打开
/dev/mem设备 - 以获取到的物理地址作为偏移量,mmap
/dev/mem的对应区域
示例代码片段(读取物理地址):
// 读取resource0获取物理地址 FILE *res_file = fopen(file, "r"); if (!res_file) PRINT_ERROR; uint64_t phys_addr, size; fscanf(res_file, "%lx %lx", &phys_addr, &size); fclose(res_file); // 打开/dev/mem int mem_fd = open("/dev/mem", O_RDWR | O_SYNC); if (mem_fd < 0) PRINT_ERROR; // 确保物理地址是页对齐的(mmap的offset必须是页大小的整数倍) uint64_t aligned_phys = phys_addr & ~(uint64_t)(MAP_SIZE - 1); uint64_t offset = phys_addr - aligned_phys; // 映射内存 void *mmap_base = mmap(NULL, size + offset, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, aligned_phys); if (mmap_base == MAP_FAILED) PRINT_ERROR; // 实际的BAR起始地址是映射后的地址加上偏移 void *bar_base = (char *)mmap_base + offset;
4. 权限问题
映射PCI资源或物理内存需要root权限,确保你用sudo运行你的程序,否则即使代码正确也会因为权限不足失败。
最后,修改后的完整基础代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include "../include/eeprom.h" #include "../include/pci.h" #include "../include/i2c.h" #include "../include/types.h" #define PRINT_ERROR \ do { \ fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \ __LINE__, __FILE__, errno, strerror(errno)); \ exit(1);\ } while(0) #define MAP_SIZE 4096 #define MAP_MASK (MAP_SIZE - 1) int main (int argc, char* argv[]) { int pci_dev; // 修复类型为int void *mmap_base; char *file = "/sys/bus/pci/devices/0000:00:16.0/resource0"; // 修正初始化语法,去掉多余花括号 printf ("Path to BAR-0: %s\n", file); // Open Bar-0 of PCI device pci_dev = open (file, O_RDWR | O_SYNC); if (pci_dev < 0) { PRINT_ERROR; } // Map BAR-0 from physical memory to virtual address space mmap_base = mmap (NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, pci_dev, 0); if (mmap_base == MAP_FAILED) // 用标准的MAP_FAILED判断错误,mmap不会返回NULL { PRINT_ERROR; } printf ("Mapped on address %p of size %d Byte\n", mmap_base, MAP_SIZE); // 程序结束时解除映射并关闭文件 munmap(mmap_base, MAP_SIZE); close(pci_dev); return 0; }
另外注意:原代码里char *file = {"/sys/..."};的初始化语法是错误的,已经修正为char *file = "/sys/...";。
内容的提问来源于stack exchange,提问作者A.Z.
相关产品推荐
相关产品推荐

