C 中的 memset 和 (&, >>) 符号

memset and (&, >>) symbols in C

我发现了一个项目,我无法理解一段代码。Virtual memory

#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define TLB_SIZE 16
#define PAGE_SIZE 256
#define FRAME_SIZE 256
#define PHYSICAL_MEMORY_SIZE PAGE_SIZE *FRAME_SIZE

int logicalAddress = 0;
int offsetNumber = 0;
int pageNumber = 0;
int physicalAddress = 0;
int Frame = 0;
int Value = 0;
int Hit = 0;
int tlbIndex = 0;
int tlbSize = 0;

unsigned pageNumberMask = 65280;  // 1111111100000000
unsigned offsetMask = 255;        // 11111111

int tlbHitCount = 0;
float tlbHitRate = 0;
int addressCount = 0;
int pageFaultCount = 0;
float pageFaultRate = 0;

struct tlbTable {
  unsigned int pageNum;
  unsigned int frameNum;
};

int main(int argc, char *argv[]) {
  // Check to see if user inputs addresses.txt
  if (argc != 2) {
    fprintf(stderr, "Usage ./VirtualMem_Manager <Filename.txt> \n");
    exit(1);
  }
  // Open addresses.txt, BACKING_STORE.bin, and
  // Create Output.txt to store program results
  FILE *addresses = fopen(argv[1], "r");
  FILE *BACKINGSTORE = fopen("BACKING_STORE.bin", "rb");
  FILE *Output = fopen("addressOutput.txt", "w");

  int physicalMemory[PHYSICAL_MEMORY_SIZE];
  char Buffer[256];
  int Index;

  // Declare and initialize pageTable[] array to -1
  int pageTable[PAGE_SIZE];
  memset(pageTable, -1, 256 * sizeof(int));

  // Declare and initialize tlb[] structure to -1
  struct tlbTable tlb[TLB_SIZE];
  memset(pageTable, -1, 16 * sizeof(char));

  // Read each address from addresses.txt
  while (fscanf(addresses, "%d", &logicalAddress) == 1) {
    addressCount++;

    // set the page number and offset for each logical address
    pageNumber = logicalAddress & pageNumberMask;
    pageNumber = pageNumber >> 8;
    offsetNumber = logicalAddress & offsetMask;
    Hit = -1;

    // Check to see if the page number is already in the tlb
    // If it is in tlb, then it is tlb hit
    for (Index = 0; Index < tlbSize; Index++) {
      if (tlb[Index].pageNum == pageNumber) {
        Hit = tlb[Index].frameNum;
        physicalAddress = Hit * 256 + offsetNumber;
      }
    }

    if (!(Hit == -1)) {
      tlbHitCount++;
    }
    // This "else if" loop is the tlb miss
    // Gets the physical page number from page table
    else if (pageTable[pageNumber] == -1) {
      fseek(BACKINGSTORE, pageNumber * 256, SEEK_SET);
      fread(Buffer, sizeof(char), 256, BACKINGSTORE);
      pageTable[pageNumber] = Frame;

      for (Index = 0; Index < 256; Index++) {
        physicalMemory[Frame * 256 + Index] = Buffer[Index];
      }
      pageFaultCount++;
      Frame++;

      // FIFO algorithm for the tlb
      if (tlbSize == 16) tlbSize--;

      for (tlbIndex = tlbSize; tlbIndex > 0; tlbIndex--) {
        tlb[tlbIndex].pageNum = tlb[tlbIndex - 1].pageNum;
        tlb[tlbIndex].frameNum = tlb[tlbIndex - 1].frameNum;
      }

      if (tlbSize <= 15) tlbSize++;

      tlb[0].pageNum = pageNumber;
      tlb[0].frameNum = pageTable[pageNumber];
      physicalAddress = pageTable[pageNumber] * 256 + offsetNumber;
    } else {
      physicalAddress = pageTable[pageNumber] * 256 + offsetNumber;
    }

    // Gets the value from the bin file provided
    Value = physicalMemory[physicalAddress];
    // print the addresses and value to Output.txt
    fprintf(Output, "Virtual Address: %d Physical Address: %d Value: %d \n",
            logicalAddress, physicalAddress, Value);
  }

  // The statistics of the program
  pageFaultRate = pageFaultCount * 1.0f / addressCount;
  tlbHitRate = tlbHitCount * 1.0f / addressCount;

  // Close files provided for the project
  fclose(addresses);
  fclose(BACKINGSTORE);

  // Print the statistics of the program to Output.txt
  fprintf(Output, "Number of Addresses: %d\n", addressCount);
  fprintf(Output, "Number of Page Faults: %d\n", pageFaultCount);
  fprintf(Output, "Page Fault Rate: %f\n", pageFaultRate);
  fprintf(Output, "TLB Hits: %d\n", tlbHitCount);
  fprintf(Output, "TLB Hit Rate %f\n", tlbHitRate);

  // Close Output.txt
  fclose(Output);

  return 0;
}

这三行我看不懂

pageNumber = logicalAddress & pageNumberMask;
pageNumber = pageNumber >> 8;
offsetNumber = logicalAddress & offsetMask;

据我了解,papeNumber 是 logicalAddress 和 pageNuberMask 存储在一起的值。和这个字符并加入他们。然后我们在 8(>> 8).

上进行偏移

为什么使用 memset 很危险。

出于性能和内存原因,pageNumberpage 中的偏移量存储在单个 unsigned 值中。

我会在评论中用c++14位表示加上0b前缀来说明。

unsigned logicalAddress = someValue;           // <- 0bPPPPPPPPFFFFFFFF

其中 P 表示 pageNumber 的位和 offsetNumberF 位;

pageNumber = logicalAddress & pageNumberMask;  // <- 0bPPPPPPPP00000000
pageNumber = pageNumber >> 8;                  // <-         0bPPPPPPPP
offsetNumber = logicalAddress & offsetMask;    // <-         0bFFFFFFFF

关于memset正确使用不会有危险

(为了便于说明,此答案假设地址为 32 位,这似乎与问题中的代码一致。)

pageNumber = logicalAddress & pageNumberMask;
pageNumber = pageNumber >> 8;
offsetNumber = logicalAddress & offsetMask;

逻辑地址分为三组位:在此代码中忽略的 16 位高位,8 位用于页码,8 位低位用于页面内的偏移量。

pageNumberMask是一个已经准备好页码位为1,其他地方为0的值。然后操作 logicalAddress & pageNumberMask 执行按位与,它产生一个值,其中唯一设置的位是在 logicalAddress 的页码位中设置的那些位。换句话说,它产生一个高位和偏移位被清除的值,有效地隔离页码位。

然后 pageNumber >> 8 将这些页码位向下移动到低位,因此结果是页码。该页码随后可用于索引数组。

类似地,offsetMask 是一个准备值,偏移位为 1,其他位置为 0,而 logicalAddress & offsetMask 仅提取偏移位。

请注意 logicalAddress & pageNumberMask 只有在页码位以上的位可能被设置时才有用。这是因为 pageNumber >> 8 无论如何都会删除低位,因此使用 & 操作清除它们是没有意义的。所以这段代码将页码位与高位分开,但不使用那些高位。这可能是因为它是一个简化的教学示例,实际部署代码会使用高位。

And why is using memset dangerous.

memset是一个很容易被误用的强大套路。给定不正确的参数,它会修改任何可访问的内存,编译器通常无法检测到它被错误使用,因此无法提供警告或错误消息。

代码中的缺陷

问题中的代码有很多缺陷,所以不是很好的教学范例:

  • struct tlbTable tlb[TLB_SIZE]; 后跟 memset(pageTable, -1, 16*sizeof(char)); 看起来像一个错误。 pageTable 之前已初始化,这可能是为了初始化 tlb。而且尺码不对; tlb有16个元素(TLB_SIZE是16),每个都是几个字节。那一行大概应该是memset(tlb, -1, sizeof tlb);,前面的memset应该是memset(pageTable, -1, sizeof pageTable);.
  • 为各种参数定义了预处理器宏,例如 PAGE_SIZE 的 256,但随后部分代码会忽略这些并使用硬编码常量,如 Frame*256 + Index。这是错误的秘诀。