---
# System prepended metadata

title: linux project1

---

# linux project 1
___




## <font color="#F7A004">系統環境</font>
虛擬機:virtual box
作業系統:ubantu 22.04  (這邊怕版本不相容下載舊版)
kernel:Linux 5.15.137

_____
## <font color="#F7A004">編譯過程</font>
### step 1 :kenrel_syscall_前置處理
```c=
#進入 linux-5.15.137
cd usr/src/linux-5.15.137/  

# 建立一個資料夾 mycall
mkdir mycall      

# 在新建立好的資料夾中建立一個system call
vim mycall/my_get_physical_addresses.c

# 建立Makefile
vim mycall/Makefile

# 將my_get_physical_addresses.o編入kernel
obj-y := my_get_physical_addresses.o 

# 編輯原系統中的Makefile
core-y	+= kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ mycall/

# 將系統呼叫對應的函數加入到系統呼叫的標頭檔中
# 開啟檔案`include/linux/syscalls.h`
vim  include/linux/syscalls.h

# 並在 #endif前加上
asmlinkage long sys_my_get_physical_addresses(unsigned long __user *usr_ptr);

# 將syscall加入到kernel的syscall table 並開啟檔案
vim arch/x86/entry/syscalls/syscall_64.tbl

# 直接加入底部
548	common    my_get_physical_addresses	sys_my_get_physical_addresses

```

![582116085_1369947167873949_5067527698500628642_n](https://hackmd.io/_uploads/r1BLpfweZx.png)



遇到的問題:
1. 一開始編譯遇到的一個問題就是syscall的位置明明寫對了，但卻始終編譯不了
2.  vim 模式 不熟悉，但使用nano常常誤改程式
3.  看不懂終端錯誤原因，尤其linker出錯，沒想到是core設定出錯

- **因此下個專題在解決終端錯誤前，要重新檢驗各階段到底在幹嘛?他們的目的是甚麼**





:::danger
mycall必須放在: core-y	+= kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ 的後面 --->>>
core-y	+= kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ mycall/，在編譯通過前我都是把牠們分開導致編譯通過不了!
:::



### step 2 調整設定
``` c=
make localmodconfig

# 查看邏輯核心數量
nproc

# make 時輸入剛剛看到的核心數量
make -j8

# 安裝kernel
sudo make modules_install -j8
sudo make install -j8

# 更新作業系統的bootloader成新的kernel
sudo update-grub

# 重新啟動
reboot

# 查看有沒有成功更新kernel
uname -r
```
重啟後不知道原因，系統會有閃頻問題，但不影響實作
## <font color="#F7A004">修改system call </font>
:::success
目標:實作system call，使完成malloc中lazy allocation 的機制
:::

```c=
#include <linux/mm.h>        // struct mm_struct
#include <linux/sched.h>     // current 宏
#include <linux/syscalls.h>  // SYSCALL_DEFINE 宏
#include <linux/uaccess.h>   // __user 宏
#include <asm/pgtable.h>     // 頁表相關的結構和宏 (pgd_t, pmd_t, pte_t)

SYSCALL_DEFINE1(get_phy, void __user *, user_addr)
{
    unsigned long vaddr = (unsigned long)user_addr;
    unsigned long phys_addr = 0;
    
    // 獲取當前行程的記憶體管理結構
    struct mm_struct *mm = current->mm;
    
    pgd_t *pgd;
    p4d_t *p4d;
    pud_t *pud;
    pmd_t *pmd;
    pte_t *ptep;
    pte_t pte;

    printk(KERN_INFO "SYS_GET_PHY: 檢查虛擬地址 0x%lx\n", vaddr);

    // 嘗試獲取 MMU 讀鎖，以防止頁表在讀取過程中被修改
    if (!mmap_read_trylock(mm))
        return -EAGAIN; 

    /* 步驟 1: 5 級頁表遍歷 (唯讀) */
    
    // 檢查 Page Global Directory (PGD)
    pgd = pgd_offset(mm, vaddr);
    if (pgd_none(*pgd) || pgd_bad(*pgd))
        goto out_unlock;

    // 檢查 Page 4th Directory (P4D)
    p4d = p4d_offset(pgd, vaddr);
    if (p4d_none(*p4d) || p4d_bad(*p4d))
        goto out_unlock;

    // 檢查 Page Upper Directory (PUD)
    pud = pud_offset(p4d, vaddr);
    if (pud_none(*pud) || pud_bad(*pud))
        goto out_unlock;

    // 檢查 Page Middle Directory (PMD)
    pmd = pmd_offset(pud, vaddr);
    if (pmd_none(*pmd) || pmd_bad(*pmd))
        goto out_unlock;

    /* 步驟 2: 找到 Page Table Entry (PTE) */
    ptep = pte_offset_map(pmd, vaddr);
    if (!ptep)
        goto out_unlock;

    pte = *ptep;
    
    /* 步驟 3: 檢查 PTE 是否有效 (Present) */
    if (pte_present(pte)) {
        // 提取 PFN (Page Frame Number) 並轉換為實體地址
        phys_addr = (unsigned long)(pte_pfn(pte) << PAGE_SHIFT);
        
        // 加上頁面內的偏移量
        phys_addr |= (vaddr & ~PAGE_MASK);
    }

    // 解除 PTE 映射
    pte_unmap(ptep);

out_unlock:
    // 解除 MMU 讀鎖
    mmap_read_unlock(mm);
    // 返回實體地址
    return (long)phys_addr;
}

```

```
```
)
下圖為四級頁表轉換示意圖(linux可採用3,4,5級轉換)
![page_table](https://hackmd.io/_uploads/HJtf2Dwlbx.png)

### 系統架構圖:
1. 系統在context switch時會將cr3占存器時設成下一個progess的pgd entry，並且current指標總是指向當前task_struct結構體
![hWJh8f8](https://hackmd.io/_uploads/B1n-gX_xWe.png)

3. 下方為task strcut 中的mm_strcut結構體，可以看到他在裡面分存取了pgd的位置，因此我們可以找到mm_struct的結構，再去找到vm_area_struct
```c=
struct mm_struct {
	struct {
		/*
		 * Fields which are often written to are placed in a separate
		 * cache line.
		 */
		struct {
			/**
			 * @mm_count: The number of references to &struct
			 * mm_struct (@mm_users count as 1).
			 *
			 * Use mmgrab()/mmdrop() to modify. When this drops to
			 * 0, the &struct mm_struct is freed.
			 */
			atomic_t mm_count;
		} ____cacheline_aligned_in_smp;

		struct maple_tree mm_mt;

		unsigned long mmap_base;	/* base of mmap area */
		unsigned long mmap_legacy_base;	/* base of mmap area in bottom-up allocations */
#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
		/* Base addresses for compatible mmap() */
		unsigned long mmap_compat_base;
		unsigned long mmap_compat_legacy_base;
#endif
		unsigned long task_size;	/* size of task vm space */
		pgd_t * pgd;
```

3 而malloc就是拿取當前task_的vm_area，當系統第一次時Page_table時，pte_entry的valid_invaild bit by default會是0，因此當前進程第一次存取整個page_table時，系統會產生page_fault，因而造成interupt，此時交接給page_fault_handler去處理，去設置valid_bit為一
![image](https://hackmd.io/_uploads/B1RDPUue-g.png)



4.當正式要寫入memory前，會再跑一次頁表pgd->p4d->pud->pmd-pte進行轉換

5.，因為pte_valid_bit已設為一，因此可以直接找到實體記憶體位置!
## <font color="#F7A004">測試結果</font>
![582721741_1332781291660040_6193217228839204498_n](https://hackmd.io/_uploads/SJxcP7ul-e.png)

**觀察上圖可以看到，在寫入memory前虛擬記憶體已經配置好，但實體記憶體尚未分配，而分配後有觀察到記憶體分配出連續的空間**


## <font color="#F7A004">user program</font>
:::success
目標:將syscall封包，可以在python的user_spcae使用
:::
需先創建檔案，用來封包syscall，我們叫他my_get_phy,c
```c=
//  my_get_phy.c
#include <unistd.h>

#define sys_get_phy 548 // 你設定的 syscall number

// Python 會用 ctypes 呼叫這個函數
unsigned long my_get_physical_addresses(void *virtual_addr) {
    return (unsigned long)syscall(SYS_get_phy, virtual_addr);
}
```
接著我們必須編譯他
```c=
gcc -Wall -O2 -fPIC -shared -o lib_my_get_phy.so my_get_phy.c
```
### python interface
```python
import ctypes

# -----------------------------
# C syscall wrapper
# -----------------------------
lib = ctypes.CDLL('./lib_my_get_phy.so')
lib.my_get_physical_addresses.argtypes = [ctypes.c_void_p]
lib.my_get_physical_addresses.restype = ctypes.c_ulong

def virt2phys(addr):
    """VA -> PA"""
    return lib.my_get_physical_addresses(ctypes.c_void_p(addr))

# -----------------------------
# sbrk(0) break
# -----------------------------
libc = ctypes.CDLL("libc.so.6")
libc.sbrk.restype = ctypes.c_void_p
libc.sbrk.argtypes = [ctypes.c_long]

def get_heap_break():
    return libc.sbrk(0)

PAGE_SIZE = 4096

print("=== 初始 program break ===")
break_before = get_heap_break()
print(f"program break: {hex(break_before)}\n")

malloc_size = 4096  # 每次 malloc 4KB
buffers = []

# 循環 malloc 觀察 break 變化
for i in range(50):
    buf = ctypes.create_string_buffer(malloc_size)
    buffers.append(buf)
    break_now = get_heap_break()

    # 代表 heap 變大了
    if break_now != break_before:
        print(f"malloc {i+1}: break = {hex(break_now)}")
        print(f"  -> program break 增加了 {break_now - break_before} bytes")

        # 列出新增 break 範圍的 VA -> PA
        start_va = break_before
        end_va = break_now
        print("\n=== 新增 heap VA->PA ===")
        for va in range(start_va, end_va, PAGE_SIZE):
            try:
                pa = virt2phys(va)
                if pa == 0:
                    pa_str = "未分配"
                else:
                    pa_str = f"0x{pa:x}"  # 十六進位格式
            except Exception:
                pa_str = "未分配"
            print(f"VA: 0x{va:x} -> PA: {pa_str}")
        print()
        break_before = break_now

```
#### 測試結果:
![581786627_1728363234503937_2194650492205630891_n](https://hackmd.io/_uploads/ByuIomdlbg.png)
![582204628_25326257273657230_6449595408017005402_n](https://hackmd.io/_uploads/SkRNh7uxZl.png)
**可以看到上圖驗證了lazy allocation ，當我們要了一個很大的空間，但卻沒有實際去配置他時，他僅僅只在虛擬內存空間作分配，直到寫入後，頁表轉換為她才會去配置實際的記憶體，因此可以節省實體記憶體的開銷**
## <font color="#F7A004">Reference</font>
>[一文读懂 Linux 内存分配全过程](https://zhuanlan.zhihu.com/p/452139283) 
> [奔跑吧 CH 3.1 進程的誕生](https://hackmd.io/@PIFOPlfSS3W_CehLxS3hBQ/S14tx4MqP)
>[add a system call教學網址 by 助教](https://hackmd.io/aist49C9R46-vaBIlP3LDA?both )
[Linux記憶體管理by Joseph](https://hackmd.io/@sysprog/linux-memory)
[肝下 50 万字的《Linux 内核精通》笔记，你的底层原理水平将从入门到入魔
](https://xie.infoq.cn/article/29552089e9a4bdaaca916c10d)








