我正在尝试用C编写一个指令集模拟器来模拟运行ARM的机器。 我需要能够有效地表示4GB内存,经过一些挖掘后,我们开始创建一个1024个指针数组的解决方案,每个指针指向一个4MB的块,它在第一次使用时动态分配
#define MEMSIZE 1024 //1024 * 2Mb = 4Gb
#define PAGESIZE 4194304 //4 Mb
#define PAGEEXP 22 //2^PAGEEXP = PAGESIZE
uint32_t* mem[MEMSIZE];
我的问题是如何访问某个内存地址?
我所尝试的是将地址分解为索引和偏移量,如下所示,但这似乎只对索引和偏移量返回0。 (memAdd是我试图访问的地址)
memIdx = memAdd >> PAGEEXP;
memOfs = memAdd & PAGESIZE;
我拥有地址后用于读/写的函数如下:
void memWrite(uint32_t idx, uint32_t ofs, uint32_t val)
{
if(mem[idx] == 0)
mem[idx] = malloc(PAGESIZE);
*(mem[idx] + ofs) = *(mem[idx] + ofs) & val;
}
uint32_t memRead(uint32_t idx, uint32_t ofs)
{
if(mem[idx] == 0)
return 0;
else
return *(mem[idx] + ofs);
}
这些似乎在我的脑海中然而我仍然不是100%使用指针,所以这可能是错误的。
很抱歉,如果已经在某处讨论了这个问题,但我找不到任何与我需要相关的内容(我的关键字相当广泛)
答案 0 :(得分:4)
开始逻辑地查看它而不是位级别。
每页有4,194,304字节。
然后,算术上,将线性地址转换为(页面,偏移)对,除以4,194,304得到页码,然后取余数得到页面的偏移量。
page = address / PAGESIZE;
offset = address % PAGESIZE;
由于你想有效地做到这一点并且这些是2的幂,你可以用PAGESIZE替换除以右移的PAGESIZE的基数为2的对数,即22:
page = address >> PAGEEXP;
因此,您的部分代码是正确的。但是,要获得偏移量,您要做的就是屏蔽除了刚刚移出页码的所有位。为此,您必须与PAGESIZE - 1
进行AND。
offset = address & (PAGESIZE - 1);
这是因为在二进制文件中,你开始的是一个看起来像这样的数字(其中p是页码位,o是偏移位):
address = ppppppppppoooooooooooooooooooooo
您想要自己获取页码和偏移号。你显然想要向右移22位来获得页码:
page = addresss >> 22 = 0000000000000000000000pppppppppp
但是如果你和页面大小(00000000010000000000000000000000,二进制),你在答案中只有一个最多一个1位,它只会告诉你页码是奇数还是偶数。没用。
你想要的东西比它少一点,那就是二进制00000000001111111111111111111111,因此:
ppppppppppoooooooooooooooooooooo
& 00000000001111111111111111111111
-----------------------------------
= 0000000000oooooooooooooooooooooo
这就是你获得偏移量的方法。
这是一般规则:如果N是2的整数幂,则N除以与log(N)/ log(2)的右移相同,并且这种除法的余数由下式给出:与(N-1)进行AND运算。
答案 1 :(得分:2)
这将做你想要的。我用的是较小的尺码。为清楚起见,我省略了错误检查。它使用您使用索引器数组的方案。
#include <cstdlib>
#include <cstdio>
#include <stdint.h>
#define NUMPAGE 1024
#define NUMINTSPERPAGE 4
uint32_t* buf;
uint32_t* idx[NUMPAGE];
void InitBuf()
{
buf = (uint32_t*) calloc(NUMPAGE, NUMINTSPERPAGE * sizeof uint32_t );
for ( size_t i = 0; i < NUMPAGE; i++ )
{
idx[i] = &buf[i * NUMINTSPERPAGE * sizeof uint32_t];
}
}
void memWrite(size_t i, size_t ofs, uint32_t val)
{
idx[i][ofs] = val;
}
uint32_t memRead(size_t i, size_t ofs)
{
return idx[i][ofs];
}
int main()
{
InitBuf();
uint32_t val = 1243;
memWrite(1, 2, val);
printf("difference = %ld", val - memRead(1, 2));
getchar();
}
答案 2 :(得分:2)
如果PAGESIZE
是2的幂,则它只有1位。因此,将其与另一个值进行对比只能在结果中设置零或一位。两个可能的值。但是你将它用作数组索引。
此外,您的memWrite(uint32_t idx, uint32_t ofs, uint32_t val)
函数的值始终为val
的值。因此,例如,如果val
为uint32_max
,则对此函数的任何调用都将无效。
最后,您不仅没有检查malloc()
的失败结果,也没有初始化返回的内存块。
尝试这样的方法(遗憾的是我无法测试它,我现在没有编译器方便)。
enum { SIM_PAGE_BITS = 22 }; // 2^22 = 4MiB
enum { SIM_MEM_PAGES = 1024 }; // 1024 * 4MiB = 4GiB
enum { SIM_PAGE_SIZE = (1<<SIM_PAGE_BITS) };
enum { SIM_PAGE_MASK = SIM_PAGE_SIZE-1 };
enum { UNINITIALISED_MEMORY_CONTENT = 0 };
enum { WORD_BYTES = sizeof(uint32_t)/sizeof(unsigned char) };
#define PAGE_OFFSET(addr) (SIM_PAGE_MASK & (uint32_t)addr)
// cast to unsigned type to avoid sign extension surprises if addr<0
#define PAGE_NUM(addr) (((uint32_t)addr) >> SIM_PAGE_BITS)
#define IS_UNALIGNED(addr) (addr & (WORD_BYTES-1))
unsigned char* mem[MEMSIZE];
uint32_t memRead(uint32_t addr) {
if (IS_UNALIGNED(addr)) return handle_unaligned_read(addr);
const uint32_t page = PAGE_NUM(addr);
if (mem[page]) {
const unsigned char *p = mem[page] + PAGE_OFFSET(addr);
return *(uint32_t*)p;
} else {
return UNINITIALISED_MEMORY_CONTENT;
}
}
void memWrite(uint32_t addr, uint32_t val) {
if (IS_UNALIGNED(addr)) return handle_unaligned_write(addr, val);
const uint32_t page = PAGE_NUM(addr);
if (!mem[page]) {
if (val == UNINITIALISED_MEMORY_CONTENT) {
return;
}
mem[page] = malloc(SIM_PAGE_SIZE);
if (!mem[page]) {
handle_out_of_memory();
}
// If UNINITIALISED_MEMORY_CONTENT is always 0 we can
// use calloc instead of malloc then memset.
memset(mem[page], UNINITIALISED_MEMORY_CONTENT, SIM_PAGE_SIZE);
}
const unsigned char *p = mem[page] + PAGE_OFFSET(addr);
*(uint32_t*)p = val;
}
答案 3 :(得分:0)
我不相信正确计算memOfs
的值。例如,4194304
表示的十进制值PAGESIZE
是十六进制的0x400000
,这意味着在按位与运算之后,您只得到原始地址的第22位,而不是低22位。将该值添加到4MB页面数组指针实际上会使您超出堆上已分配数组的末尾。将偏移计算的掩码更改为0x3FFFFF
,然后使用原始内存地址按位与AND,以计算页面中的正确偏移量。例如:
memIdx = memAdd >> PAGEEXP;
memOfs = memAdd & 0x3FFFFF; //value of memOfs will be between 0 and 4194303