You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

尝试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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:41:57