2

我有一些数据存储在闪存中,我需要使用 C 指针访问这些数据才能使非 Linux 图形驱动程序工作(我认为这个要求与 DMA 相关,不确定)。调用 read 有效,但我不想在 FLASH 和非 Linux 驱动程序之间有中间 RAM 缓冲区。

然而,仅仅创建一个指针并将我想要的地址存储在它上面就会让 Linux 发出一个关于我的无效访问的异常。

void *ptr = 0xdeadbeef;
int a = *ptr; // invalid access!

我在这里想念什么?有人可以向我指出一种材料来让我清楚地了解这些概念吗?

我正在阅读有关mmap的信息,但我不确定这是否是我需要的。

4

3 回答 3

4

您遇到的问题是 linux 在虚拟地址空间中运行您的程序。因此,您在代码中直接使用的每个地址(如 0xdeadbeef)都是一个虚拟地址,由内存管理单元转换为物理地址,该物理地址不一定与您的虚拟地址相同。这允许轻松分离多个独立进程和其他东西,如分页等。

现在的问题是,在您的情况下,没有物理地址映射到虚拟地址 0xdeadbeef 导致内核中止执行。

您已经找到的调用 mmap 要求内核将特定文件(从特定偏移量)分配给进程的虚拟地址。请注意,mmap 的返回地址可能是完全不同的地址。所以不要对你得到的虚拟地址做任何假设。

因此,存在 mmap 和 /dev/mem 的示例,其中内存设备的偏移量是物理地址。在内核能够将文件从您提供的偏移量分配给进程的虚拟地址之后,您可以像直接访问一样访问内存区域。

在您不再需要该区域后,请不要忘记对该区域进行 munmap。否则你会导致类似于内存泄漏的事情。

/dev/mem 方法的一个问题是运行该进程的用户需要访问该设备。这可能会引入安全问题(例如,三星最近在其手持设备中引入了此类安全漏洞)

一种更安全的方法是我发现的一篇文章(The Userspace I/O HOWTO)中描述的方法,因为您仍然可以控制用户进程可访问的内存区域。

于 2013-01-30T14:10:02.173 回答
1

您需要以不同的方式访问内存。基本上你需要打开 /dev/mem 并使用 mmap()。(正如你所建议的)。简单的例子:

int openMem(unsigned int address, unsigned int size)
{
int             mmapFD;
int             page_size;
unsigned int    page_start_address;

/* Minimum page size for the mmapped region. */
mask = size - 1;

/* Get the page size. */
page_size = (int) sysconf(_SC_PAGE_SIZE);

/* We have to map shared memory to beginning of memory page so adjust 
 * memory address accordingly. */
page_start_address = address - (address % page_size);

/* Open the file that will be mapped. */

if((mmapFD = open("/dev/mem", (O_RDWR | O_SYNC))) == -1)
{
    printf("Opening shared memory device failed\n");
    return -1;
}

mmap_base_address = mmap(0, size, (PROT_READ|PROT_WRITE), MAP_SHARED, mmapFD, (off_t)page_start_address & ~mask);
if(mmap_base_address == MAP_FAILED)
{
    printf("Mapping memory failed\n");
    return -1;
}
return 0;
}

unsigned int *getAddress(unsigned int address)
{
unsigned int    log_address;

log_address = (int)((off_t)mmap_base_address + ((off_t)address & mask));

return (unsigned int*)log_address;
}

...

result = openMem(address, 0x10000);

if (result < 0)
    return result;

target_address =  getValue(address);

*(unsigned int*)target_address = value;

这会将“值”设置为“地址”。

于 2013-01-30T14:20:49.403 回答
0

你需要打电话ioremap- 比如:

void *myaddr = ioremap(0xdeadbeef, size);

其中 size 是您的内存区域的大小。您可能希望对第一个参数使用页面对齐的地址,例如0xdeadb000- 但我希望您的实际设备无论如何都不在“0xdeadbeef”。

编辑:对 ioremap 的调用必须从驱动程序完成!

于 2013-01-30T14:11:46.417 回答