這系列主要是我對WASM研究的筆記,可能內容比較簡略。總共包括:git
現代的內存尋址機制,都引入了名爲「分段」的概念:不一樣級別的程序、程序的不一樣數據類型,存放在不一樣的「段」上面,而後再定義在「段」上的偏移量。也就是說,現代程序看到的地址都不是線性的,而是分段過的地址,形如:segmentSelector:offset
。這些段和偏移量組成的空間,就是邏輯內存空間;這些二元組,就是邏輯地址。程序員
段標識符是由一個16位長的字段組成,又稱爲段選擇符(Selector),由處理器提供段寄存器來存放段標識符,段寄存器有6種:github
cs
代碼段寄存器,指向包含程序指令的段;ss
棧寄存器,指向包含當前程序的段;ds
數據段寄存器,指向包含靜態數據或者全局數據段;es
, fs
, gs
稱爲附加段寄存器,做通常用途,能夠指向任意的數據段段寄存器存放的並非段基地址也不是段描述符。段的詳細信息須要經過選擇器從描述符表(Descriptor Table,段表)中獲得。 這樣作能夠加快查詢速度,也能夠進行權限控制,只有訪問級別夠的程序才能成功拿到段基地址和長度web
16位選擇器具體組成:編程
ldtr
,DT相似於一個數組,每項佔8個字節段表描述符的結構比較複雜,不過最重要的是段的段基(BASE
, 32bit)和段界(LIMIT
, 20bit)數組
經過段描述符獲得BASE
以後,再與邏輯地址偏移量offset
相加,就獲得了線性地址:緩存
當段描述符中的G = 1時, 分頁機制啓用。markdown
分頁就是人爲地在邏輯上將連續的內存空間,按照固定大小切分紅一段一段。對於線性內存來講,這樣切分出來的固定大小叫作頁();對於物理內存來講,這樣切分出來的固定大小叫作頁幀()。 分頁機制將線性內存分爲若干頁,將物理內存分爲若干幀,並創建從頁到幀的映射關係。這個映射關係,是一個「多對一」的映射。 線性地址的轉換分兩步完成,每一步都基於一種都基於一種轉換表,第一種轉換表稱爲頁目錄錶轉換,第二種轉換稱爲頁錶轉換。使用這種二級模式的目的在於減小每一個進程頁表所需的RAM的數量。就像咱們看書有個書目錄同樣,方便快捷。具體轉換以下圖所示:架構
內存分段與分頁功能重合,所以不少新架構或OS傾向於使用Flat Segmentation,如x86-64和Linuxide
在Linux中細分了四種段:
全部的用戶進程都是使用同一個用戶代碼段描述符和用戶數據段描述符,它們是__USER_CS
和__USER_DS
,也就是每一個進程處於用戶態時,它們的CS寄存器和DS寄存器中的值是相同的。當任何進程或者中斷異常進入內核後,都是使用相同的內核代碼段描述符和內核數據段描述符,它們是__KERNEL_CS
和__KERNEL_DS
。這裏要明確記得,內核數據段實際上就是內核態堆棧段。 邏輯地址是由段選擇符(16位) + 段內偏移量offset(32位)得來。以前也說到,只有處於用戶態,CS和DS寄存器中的值都是__USER_CS
和__USER_DS
。只要處於內核態,CS和DS寄存器中的值都是__KERNEL_CS
和__KERNEL_DS
。在咱們編程過程當中,實際上提供的地址都是一個偏移量,系統會自動將這個偏移量與CS中的段選擇符進行結合。也就是咱們使用的邏輯地址實際上只使用了offset這一段,段選擇符都爲空。以前也說了這四個段描述符的BASE都爲0x00000000,也得出當邏輯地址經過這樣的分段機制轉爲線性地址後,實際上並無變化,也就是邏輯地址=線性地址(其實這兩個地址都是offset的值)。
除了內存分段管理以外,應用程序也有段的概念,主要是描述的程序對數據的組織。通常Linux程序擁有下面幾個段:
wasm的棧能夠簡化爲:
從 往低位增加而堆從往高位增加,由於棧先放置因此須要在編譯的時候給一個最大值。
棧空間能夠經過下面方式設置:
clang \
--target=wasm32 \
-O3 \
-flto \
-nostdlib \
-Wl,--no-entry \
-Wl,--export-all \
-Wl,--lto-O3 \
-Wl,-z,stack-size=$[8 * 1024 * 1024] \ # Set maximum stack size to 8MiB
-o add.wasm \
add.c
複製代碼
// llvm 源碼: // <https://github.com/llvm-mirror/lld/blob/master/wasm/Driver.cpp#L355> Config->InitialMemory = args::getInteger(Args, OPT_initial_memory, 0); Config->GlobalBase = args::getInteger(Args, OPT_global_base, 1024); Config->MaxMemory = args::getInteger(Args, OPT_max_memory, 0); Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", WasmPageSize); //... 複製代碼
refer: dassur.ma/things/c-to…
If the effective address of a memory access is a multiple of the alignment attribute value of the memory access, the memory access is considered aligned, otherwise it is considered misaligned. Aligned and misaligned accesses have the same behavior
若是一個內存的訪問有效地址(Effective address)是儲存器訪問的對齊屬性的倍數,那麼此次儲存器訪問就被稱爲是對齊的,不然是不對齊。對齊與不對齊的訪問具備相同的行爲,可是對齊會提升CPU的處理速度。
wasm32 的對齊屬性是32,wasm64的native 對齊屬性就是64
也便是當前訪問的真實地址(相對於offset來講)
i32.const 3 ;; address_operand = 3
i64.const 1234 ;; value
i64.store16 1 3 ;; alignment=1, offset=3, effective_address = 3 + 3 = 6
複製代碼
上述是對齊的,可是若是是:
i32.const 3 ;; address_operand = 3
i64.const 1234 ;; value
i64.store16 2 3 ;; alignment=2
複製代碼
那麼將會對不齊: