【问题标题】:represent memory in c在 c 中表示内存
【发布时间】:2012-06-01 02:36:26
【问题描述】:

我正在尝试用 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% 对指针感到满意,所以这可能是错误的。

对不起,如果这已经在某个地方讨论过,但我找不到任何与我需要的东西相关的东西(我的关键字非常广泛)

【问题讨论】:

  • 检查 malloc 失败 - if(mem[idx] == NULL) ...

标签: c pointers memory-management


【解决方案1】:

从逻辑上而不是位级别开始。

每个页面有 4,194,304 字节。

然后,在算术上,要将线性地址转换为(页,偏移量)对,除以 4,194,304 得到页码,然后取余数得到页的偏移量。

page = address / PAGESIZE;
offset = address % PAGESIZE;

由于您想高效地执行此操作并且这些是 2 的幂,您可以用 PAGESIZE 的以 2 为底的对数(即 22)右移代替除以 PAGESIZE:

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) 进行与运算。

【讨论】:

    【解决方案2】:

    如果PAGESIZE 是 2 的幂,则它只设置了 1 位。因此,将其与另一个值进行与运算只能在结果中设置零或一个位。两个可能的值。但是您将它用作数组索引。

    此外,您的 memWrite(uint32_t idx, uint32_t ofs, uint32_t val) 函数始终与 val 的值进行“与”运算。因此,例如,如果valuint32_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】:

      这会做你想做的事。我用过更小的尺寸。为了清楚起见,我省略了错误检查。它使用您使用索引器数组的方案。

      #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();
      }
      

      【讨论】:

        【解决方案4】:

        我认为memOfs 的值计算不正确。例如,PAGESIZE 表示的十进制值4194304 是十六进制的0x400000,这意味着在按位与运算后,您只能得到原始地址的第 22 位,而不是低 22 位。将该值添加到 4MB 页面数组指针实际上会使您超出堆上已分配数组的末尾。将偏移量计算的掩码更改为0x3FFFFF,然后将其与原始内存地址按位与,以便计算到页面的正确偏移量。比如:

        memIdx = memAdd >> PAGEEXP;
        memOfs = memAdd & 0x3FFFFF;  //value of memOfs will be between 0 and 4194303
        

        【讨论】:

        • 感谢您的回答。不敢相信我犯了一个简单的错误,比如试图 AND 一点。还对如何构建我的程序提供了一些很好的见解!
        猜你喜欢
        • 1970-01-01
        • 2011-12-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-06
        相关资源
        最近更新 更多