Unix V6 on PDP-11 architecture - Introduction === ###### tags: `unix_v6` `kernel` ## 目錄 [TOC] ## Unix V6 Kenneth Lane Thompson, Dennis MacAlistair Ritchie 開發 貝爾實驗室 1976 年發布。 以下的 Unix V6 source 皆是以在 PDP-11/40 的機器上運行的為準。 ## PDP-11 architecture 16-bit 架構,RISC,指令和 data 都是以 16 bit 為單位進行處理。 In PDP-11, `one word = 16 bits = 2 bytes` bus 的地址寬度為 18 bits,所以 memory 最高可以安裝 2^18^ = 256KB 上去 (範圍: 0x00000 ~ 0x3FFFF) PDP-11/40 的 I/O address, registers 會被 mapped 到 physical memory 的最上方 8 KB 的位置 (以 memory 256KB 為例,即 0x3e000 ~ 0x3FFFF) ### PDP-11 registers register 的長度皆為 16 bit #### General registers 1. r0, r1 - 用於運算 - 函數的回傳值 2. r2, r3, r4 - local 暫存、處理 3. r5 - frame pointer or environment pointer 4. r6 - stack pointer (sp) 5. r7 - program counter (pc) #### PSW *[PSW]: PSW (program status word) 類似 ARM 的 CPSR | bit | 15-14 | 13-12 | 7-5 | 4 | 3 | 2 | 1 | 0 | | --------- | --------- | --------- | --------- | ------- | ---- | ---- | ---- | ---- | | functions | current mode | previous mode | 處理器的優先級 | trap 位 | N | Z | V | C | mode (15-14 & 13-12 bits): 00 for user mode, 11 for kernel mode ## Process - process 狀態 1. Executable 2. Sleep - CPU 有兩種 mode,user mode and kernel mode,process 切換 mode 時,所對應的實體記憶體位置也會變,即 user mode 和 kernel mode 有不同的實體記憶體區域 - 隨著 process 增多,memory 會不足,這時會將休眠狀態或是重要度較低的 process 移出 memory 到 swap 裡。 ### Process structure 一個 active process 會被分配到兩個 structure,`struct proc, struct user` > One struct proc and one struct user are allocated per active process. - struct proc - ![](https://i.imgur.com/qbBWD2b.png) - 一個 process 對應一個 struct proc,一個系統上的所有 process 就是 proc\[\] - proc\[\] 常駐在 memory 中 - proc\[\] 的長度定義在 macro NPROC `#define NPROC 50` (參考:unixV6 param.h) - struct user - ![](https://i.imgur.com/kGwIKck.png) - struct user 是用來記錄 process 打開的目錄、文件等等訊息 - 由於 kernel 只需要當前執行的 process 所對應到的 struct user,所以當其他的 process 被 swapped out 時,他所對應的 struct user 也會被 swapped out - 而當前所執行的 process,其所對應到的 struct user,kernel 可透過 global variable, `_u` 來存取,可參考 `conf/m40.s` ```assembly= /* conf/m40.s */ .global _u _u = 140000 // Note: is octal! ``` ### Process in memory - 首先先看一下在 pdp11 上,physical 和 virtual address 的長度容量差別: | Address Space | address length | capacity | | ------------- | ------------- | -------- | | virtual address | 16 bits | 2^16^ = 64K | | physical address | 18 bits | 2^18^ = 256K | - 每個 process 都有一個自己的 virtual address space,使用 virtual address 而不直接使用 physical address 有以下的好處: 1. program 可以使用以任意地址為起點的 memory space 2. 管理方便,避免某 process 訪問到其他 process 的 memory space 3. 提高 memory 使用效率 Process Layout in the virtual memory (地址長度 16 bits,容量 64K): ``` Address (virtual) Layout 0x0000 ←-------------┌────────────────┐---------╮ | | ↓ | | | .TEXT | "Text Segment" | | size=user.u_tsize | | ↑ |────────────────|---------╯ | | | | | | | | |────────────────|--╮--------------------------╮ (│ struct user |) ↓ | (│----------------|) "PPDA", size=USIZE | (| kernel stack |) ↑ | 0x2000N (Align 8KB) ←|────────────────|--╯------╮ | | .DATA | ↓ | |----------------| | | .BSS | "Data Area" | |----------------| size=user.u_dsize | | | | | HEAP | | | | ↑ | |────────────────|---------╯ ↓ | ↓ | "Data Segment" | | size = proc.p_size | | = USIZE + user.u_dsize | | + user.u_ssize | | ↑ | | | | | | | | | | ↑ | | |────────────────|---------╮ | | | ↓ | | | "Stack Area" | | STACK | size=user.u_ssize | | | | | | ↑ | 0xFFFF ←-------------└────────────────┘---------╯-------------------╯ ``` :::danger PPDA 實際上並不存在 virtual memory 中,只在 physical memroy 上,所以 user space 無法存取 PPDA,可見下圖 ::: ![](https://i.imgur.com/cUvGxMG.png) :::info 以下講的 size,單位皆為 64 bytes,例如: size = 2,則大小總共為 64\*2 = 128 bytes ::: #### Text Segment - READ-ONLY,用來儲存 process 要被執行的 machine code - 若某個 process 被執行多次,則共享同一個 Text Segment - size = user.u_tsize #### Data Segment - 跟 Text Segment 不一樣,不會被多個 process 共享 - Data Segment 的 physical address 儲存在 proc.p_addr 裡,其長度則是儲存在 proc.p_size 裡 - proc.p_size = USIZE + user.u_dsize + user.u_ssize - Data Segment 包含了 1. PPDA (pre-process data area) - 由 struct user 和 kernel stack 所組成 - 每個 process 都有其自己的一段 virtual memory,即都有屬於自己的 PPDA - 而 PPDA 儲存於 physical memory 中,**並沒有被 map 到 virtual memory 上**。 - 其在 physical memroy 中,位於 virtual memory mapped 到 physical memory 位置的上方 (較低位置處)。 - size = USIZE,USIZE 定義在 param.h 裡 `#define USIZE 16` ,所以其大小為 16\*64(bytes) = 1024 bytes = 1K 2. Data Area, size = user.u_dsize = .Data + .BSS + Heap size 1. .Data - static, global variables **with initialization**. 2. .BSS- static, global variables **without initialization**. 3. Heap - dynamic variables,可以往高位置的地方上長,以增加空間。 3. Stack Area (Stack) - local varaibles, data - 在 virtual address 中的最高位置,會往低位置上長 - size = user.u_ssize,以增加空間。 ## Address Translation *[MMU]: Memory Management Unit *[APR]: Active Page Register - MMU 透過 APR 去做 address translation ``` virtual_address → MMU → physical_address ⎛⎞ ⎧‾‾‾‾‾‾‾‾‾⏉‾‾‾‾‾‾‾‾‾⎫ | PAR ⎮ PDR | ⎩_________⏊_________⎭ APR ``` ### APR - PAR & PDR #### APR ![](https://i.imgur.com/jCkh8Lo.png) - APR 有兩種,kernel mode 和 user mode ,要使用哪種是根據 PSW register 的 \[15-14\] bits (即當前狀態) 來決定的 (00->kernel, 11->user) - APR 的值保存在 struct user 中,當輪到某一個 process 執行時,則會把其相對應的 struct user 欄位裡的 APR 值 load 出來存進 APR register - 一組 APR 由 8 個 (即 APR0, APR1, ..., APR7) registers,**每一個對應到一個 page**,然後 APR 總共又有兩個 (kernel mode & user mode) - APR 由 PAR(Page Address Register) 和 PDR(Page Description Register) 所組成 *[PAR]: Page Address Register *[PDR]: Page Description Register #### PAR 用來記錄 base address | bits | 11 - 0 | | ---- | ------ | | description | base address (64 bytes 為單位) | #### PDR 用來記錄 page 裡總共有幾個 block,access rights 等等。 | bits | 14-8 | 6 | 3 | 2-1 | | ---- | ---- | - | - | --- | | description | block 數目 | update flag, 用來表示是否更新的 flag | page address 的分配方式 (1 : 0) = (Decending : Accending) | page access rights (00 : 01 : 11) = (未分配 : READ_ONLY : READ&WRITE) | ### How to translate? ![](https://i.imgur.com/Mg50rEf.png) #### Step 1. 先由 virtual_address \[15:13\] bits 算出是哪一個 APR (也就是第幾個 PAR ) **假設算出個結果是 N,即第 N+1 個** Note: \[15:13\] 3 bits, 2^3^ = 8,剛好夠表示 APR0 ~ APR7,共八個 #### Step 2. 找出是哪一個 APR 後,找出其對應的 PAR (Ex: 假設是第N+1個 APR,即 APR\[N\],那對應的也是 PAR\[N\]) 計算出 nBlock_baseAddr ~ nBlock_baseAddr = PAR\[N\]~\[11:0\]~ + virtual_address~\[12:6\]~ Note: nBlock_baseAddr 為 physical address 上第 N 個 Block 的 base address #### Step 3. 有了 virtual address 是在 physical address 第幾個 block 的 **base address** 後,我們 append offset (virutal_address~\[5:0\]~)後,即可得到實際的 physical address (total 18 bits) **再次提醒,physical address length = 18 bits, virtual = 16 bits** ### How to access current process's "struct user"? Recall: 前面有提到可以透過 global variable `_u` 來獲 current process's "struct user" ```assembly= /* conf/m40.s */ .global _u _u = 140000 // Note: is octal! ``` ![](https://i.imgur.com/q6grOZ3.png) Address: 140000~octal~ = 1 110 000 000 000 000~bin~ 最高三位為 6 代表他所對應的是 (kernel mode) APR\[6\] `_u` 所指到的是 kernel space 裡 page6 的起始位置(因為後面都是0) 將這個位置作為 current process 的 Data Segment (including PPDA, Data Area, Stack Area) 即將 proc.p_addr = **PAR\[6\]~\[11:0\]~** << 6,可以這麼做的原因是因為他的後面 offset 全部是 0 而 PPDA 的位置是在 proc.p_addr,剛好 PPDA 的起始就是 current process's struct user,這樣我們就成功得到了 struct user 的 physical address 了。