1. 程式人生 > 其它 >作業系統真相還原 第八章 記憶體管理系統

作業系統真相還原 第八章 記憶體管理系統

第八章 記憶體管理系統

點陣圖bitmap及其函式的實現

點陣圖簡介

點陣圖:

bitmap,常用的資料結構,廣泛用於資源管理,大量資源的管理可以採用點陣圖的方式。redis也有bitmap。

位(bit)是最小的單位,節省空間,可以用位來對映(map)一個被管理的資源單位,0表示此資源單位未使用,1表示使用。所以此種資料結構被稱為bitmap。

一個位圖結構相當於一組資源的對映,點陣圖中的每一位都與被管理的資源單位一一對映,很適合用於管理大容量的資源。

點陣圖本質是一個二進位制串,可以使用位元組陣列來實現。一個位元組有8位,對映8個資源單位。

點陣圖的定義與實現

點陣圖結構

#ifndef __LIB_KERNEL_BITMAP_H
#define __LIB_KERNEL_BITMAP_H
#include "global.h"
#define BITMAP_MASK 1
struct bitmap {
   uint32_t btmp_bytes_len;
/* 在遍歷點陣圖時,整體上以位元組為單位,細節上是以位為單位,所以此處點陣圖的指標必須是單位元組 */
   uint8_t* bits;
};

void bitmap_init(struct bitmap* btmp);
bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx);
int bitmap_scan(struct bitmap* btmp, uint32_t cnt);
void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value);
#endif

具體實現

#include "bitmap.h"
#include "stdint.h"
#include "string.h"
#include "print.h"
#include "interrupt.h"
#include "debug.h"

/* 將點陣圖btmp初始化 */
void bitmap_init(struct bitmap* btmp) {
   memset(btmp->bits, 0, btmp->btmp_bytes_len);   
}

/* 判斷bit_idx位是否為1,若為1則返回true,否則返回false */
bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx) {
   uint32_t byte_idx = bit_idx / 8;    // 向下取整用於索引陣列下標
   uint32_t bit_odd  = bit_idx % 8;    // 取餘用於索引陣列內的位
   return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd));
}

/* 在點陣圖中申請連續cnt個位,成功則返回其起始位下標,失敗返回-1 */
int bitmap_scan(struct bitmap* btmp, uint32_t cnt) {
   uint32_t idx_byte = 0;	 // 用於記錄空閒位所在的位元組
/* 先逐位元組比較,蠻力法 */
   while (( 0xff == btmp->bits[idx_byte]) && (idx_byte < btmp->btmp_bytes_len)) {
/* 1表示該位已分配,所以若為0xff,則表示該位元組內已無空閒位,向下一位元組繼續找 */
      idx_byte++;
   }

   ASSERT(idx_byte < btmp->btmp_bytes_len);
   if (idx_byte == btmp->btmp_bytes_len) {  // 若該記憶體池找不到可用空間		
      return -1;
   }

 /* 若在點陣圖陣列範圍內的某位元組內找到了空閒位,
  * 在該位元組內逐位比對,返回空閒位的索引。*/
   int idx_bit = 0;
 /* 和btmp->bits[idx_byte]這個位元組逐位對比 */
   while ((uint8_t)(BITMAP_MASK << idx_bit) & btmp->bits[idx_byte]) { 
	 idx_bit++;
   }
	 
   int bit_idx_start = idx_byte * 8 + idx_bit;    // 空閒位在點陣圖內的下標
   if (cnt == 1) {
      return bit_idx_start;
   }

   uint32_t bit_left = (btmp->btmp_bytes_len * 8 - bit_idx_start);   // 記錄還有多少位可以判斷
   uint32_t next_bit = bit_idx_start + 1;
   uint32_t count = 1;	      // 用於記錄找到的空閒位的個數

   bit_idx_start = -1;	      // 先將其置為-1,若找不到連續的位就直接返回
   while (bit_left-- > 0) {
      if (!(bitmap_scan_test(btmp, next_bit))) {	 // 若next_bit為0
	 count++;
      } else {
	 count = 0;
      }
      if (count == cnt) {	    // 若找到連續的cnt個空位
	 bit_idx_start = next_bit - cnt + 1;
	 break;
      }
      next_bit++;          
   }
   return bit_idx_start;
}

/* 將點陣圖btmp的bit_idx位設定為value */
void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value) {
   ASSERT((value == 0) || (value == 1));
   uint32_t byte_idx = bit_idx / 8;    // 向下取整用於索引陣列下標
   uint32_t bit_odd  = bit_idx % 8;    // 取餘用於索引陣列內的位

/* 一般都會用個0x1這樣的數對位元組中的位操作,
 * 將1任意移動後再取反,或者先取反再移位,可用來對位置0操作。*/
   if (value) {		      // 如果value為1
      btmp->bits[byte_idx] |= (BITMAP_MASK << bit_odd);
   } else {		      // 若為0
      btmp->bits[byte_idx] &= ~(BITMAP_MASK << bit_odd);
   }
}

bitmap_init

初始化點陣圖,把點陣圖的每個位元組填充為0,即每個位都是0。

C 庫函式 void *memset(void *str, int c, size_t n) 複製字元 c(一個無符號字元)到引數 str 所指向的字串的前 n 個字元。返回一個指向儲存區 str 的指標。

bitmap_scan_test

判斷bit_idx位是否為1,即對映的記憶體是否已被分配

java版

public static void main(String[] args) {
  int b = 0b1110_1111;
  char[] carr = {(char) b, '0'};
  bitmap_scan_test(carr, 4);
}

static int bitmap_scan_test(char[] carr, int bitIdx) {
  int byteIdx = bitIdx / 8;
  int bitOdd = bitIdx % 8;
  int a = carr[byteIdx];
  int b = BIT_MASK<< bitOdd;
  System.out.println("十進位制:" + a + "," + b);
  System.out.println("二進位制:" + Integer.toBinaryString(a) + "," + Integer.toBinaryString(b));
  int result = a & b;
  System.out.println("結果:" + result);
  return ( result);
}

執行結果

十進位制:239,16
二進位制:11101111,10000
結果:0

BIT_MASK<< bitOdd後,只剩下要比較的位為1,其他為都是0, return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd));返回值:此位為0時,結果為0;此為為1時,結果不一定為1,只是大於0。

bitmap_scan

在點陣圖中申請連續cnt個位

找到連續的記憶體,不支援不連續的記憶體分配。

記憶體管理系統

記憶體池規劃

分為實體記憶體池,虛擬記憶體池。通過頁表來做虛擬和物理的對映。記憶體池中記憶體單位為頁,即4k。

實體記憶體池分為核心實體記憶體池,使用者核心記憶體池。

每個使用者程序有自己的一個虛擬記憶體池,使用者程序的頁表把虛擬記憶體池的虛擬記憶體地址對映到使用者實體記憶體地址,不能對映到核心實體記憶體池。

虛擬記憶體池和實體記憶體池:

/* 用於虛擬地址管理 */
struct virtual_addr {
   struct bitmap vaddr_bitmap; // 虛擬地址用到的點陣圖結構 
   uint32_t vaddr_start;       // 虛擬地址起始地址
};
/* 記憶體池結構,生成兩個例項用於管理核心記憶體池和使用者記憶體池 */
struct pool {
   struct bitmap pool_bitmap;	 // 本記憶體池用到的點陣圖結構,用於管理實體記憶體
   uint32_t phy_addr_start;	 // 本記憶體池所管理實體記憶體的起始地址
   uint32_t pool_size;		 // 本記憶體池位元組容量
};

struct pool kernel_pool, user_pool;      // 生成核心記憶體池和使用者記憶體池
struct virtual_addr kernel_vaddr;	 // 此結構是用來給核心分配虛擬地址

申請記憶體時,就是使用點陣圖,然後先從虛擬記憶體池申請虛擬記憶體地址,然後從實體記憶體池中申請實體記憶體地址,然後用頁目錄表頁面對映虛擬地址和實體地址。