實作一個回傳物理位址的系統呼叫

前言

這是 2023 NCU Linux Project 1 的 Demo,Demo 完後又花了一小段時間把報告補的更完整了一點

這篇文章會教大家自己新增一個 system call,其作用是將傳入的 virtual address 轉換為 physical address 後回傳

因此文章中會簡單寫一下 linux 中的 page 與 page table,並帶大家簡單操作一次 page table

測試的環境如下:

1
2
3
OS: Ubuntu 22.04
ARCH: X86_64
Source Version: 6.6

但因為是用 QEMU 跑,照理說應該不太會有環境的問題

Build Linux Kernel

首先要先把 kernel Build 起來,這邊有錄一個 Demo 的影片:Linux HW1 Demo

下面的範例 code 跟影片中的 code 有些許不一樣,但操作起來的步驟是一樣的

先下載 kernel 的 source code:

1
2
3
4
5
6
7
# download the kernel code
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz
tar -xvf linux-6.6.tar.xz
cd linux-6.6
# Install required dependencies
sudo apt update && sudo apt install make gcc libncurses-dev flex bison
make allnoconfig

接著將 config 設定好

1
2
3
4
5
6
7
8
# Initialize kernel config
make menuconfig
64-bit kernel -> Enable
Executable file formats -> Enable all
Device Drivers > Character devices > Serial drivers and 8250/16550 and compatible serial support -> Enable
Device Drivers > Character devices > Console on 8250/16550 and compatible serial port -> Enable
General Setup > Initial RAM filesystem and RAM disk (initramfs/initrd) support -> Enable
Process type and features -> Linux guest support -> Support for running PVH guests -> Enable

下面是輸入 make menuconfig 後會出現的選單,把上面列出來的選項都勾起來:

image.png
image.png
image.png
image.png

然後開始編譯 kernel

1
2
3
# Install required dependencies
sudo apt install libssl-dev libelf-dev
make -j <num_cpu>

Build Root FS

由於 kernel 只寫好的 filesystem 的運作邏輯,例如要如何操作 ext4 的 filesystem,filesystem 本體是需要我們自己 mount 進去 kernel 的

這邊我們使用 busybox,它提供了一些常用的指令,像是 cdls 等等

首先下載 busybox 並編譯:

1
2
3
4
5
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
make menuconfig # Select build static binary
make install

接著要製作等等要 mount 進 kernel 的 filesystem 本體,就是幾個簡單的資料夾

1
2
3
4
5
6
7
8
9
10
11
12
13
cd _install
mkdir -p lib lib64 proc sys etc etc/init.d
cat > ./etc/init.d/rcS << EOF
#!/bin/sh
# Mount the /proc and /sys filesystems
mount -t proc none /proc
mount -t sysfs none /sys
# Populate /dev
/sbin/mdev -s
EOF

chmod +x etc/init.d/rcS
find . | cpio -o --format=newc | gzip > ../../linux-6.6/rootfs.img.gz

image.png

Run Kernel

可以先簡單測一下 kernel 能不跑得起來

1
2
sudo apt install qemu qemu-kvm
qemu-system-x86_64 -kernel vmlinux -nographic -initrd rootfs.img.gz -append "root=/dev/ram rdinit=/sbin/init console=ttyS0"

沒問題的話我們就開始新增 system call 了

Add System Call

修改 syscall_64.tbl

首先我們要新增自己的 system call,打開 arch/x86/entry/syscalls/syscall_64.tbl

在第 377 行後面新增我們自己的 system call:

1
454 common  my_get_physical_addresses   sys_my_get_physical_addresses   

image

這行有四個部分,每項之間由空白或 tab 隔開,它們代表的意義是:

  • 454
    system call number,在使用系統呼叫時要使用這個數字
  • common
    支援的 ABI, 只能是 64x32common,分別表示「只支援 amd64」、「只支援 x32」或「都支援」
  • my_get_physical_addresses
    system call 的名字
  • sys_my_get_physical_addresses
    system call 對應的實作,kernel 中通常會用 sys 開頭來代表 system call 的實作

syscall_64.tbl 這個檔案會在 compile 階段被讀取後轉為 header file

檔案位於 arch/x86/include/generated/asm/syscalls_64.h

image

實作自己的 system call

接下來要新增對應的 system call 實作,我們這裡要實作的是回傳 physical address 的 system call,所以先講一下要怎麼做到這件事

在 Linux 內部的記憶體地址映射過程為邏輯地址 –> 線性地址–> 實體地址 (PA),實體地址最簡單:在匯流排中傳輸的數位信號,而線性地址和邏輯地址所表示的意涵則是種轉換規則,線性地址規則如下:

image

這部分由 MMU 完成,其中在 IA32 架構下,涉及到主要的暫存器有 CR0, CR3。機器指令中出現的是邏輯地址,邏輯地址規則如下:

image

在 Linux 中的邏輯地址對應於線性地址,也就是說 Intel 為了相容過往架構,把硬體設計搞得很複雜,Linux 核心的實作則予以簡化,並且在支援其他處理器架構時,儘量保持該原則。

page in linux

page 在 v6.6.5 linux 中定義在 mm_type.h 的第 74 行:

> (可以點開) `struct page` definition <
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
struct page {
unsigned long flags; /* Atomic flags, some possibly
* updated asynchronously */
/*
* Five words (20/40 bytes) are available in this union.
* WARNING: bit 0 of the first word is used for PageTail(). That
* means the other users of this union MUST NOT use the bit to
* avoid collision and false-positive PageTail().
*/
union {
struct { /* Page cache and anonymous pages */
/**
* @lru: Pageout list, eg. active_list protected by
* lruvec->lru_lock. Sometimes used as a generic list
* by the page owner.
*/
union {
struct list_head lru;

/* Or, for the Unevictable "LRU list" slot */
struct {
/* Always even, to negate PageTail */
void *__filler;
/* Count page's or folio's mlocks */
unsigned int mlock_count;
};

/* Or, free page */
struct list_head buddy_list;
struct list_head pcp_list;
};
/* See page-flags.h for PAGE_MAPPING_FLAGS */
struct address_space *mapping;
union {
pgoff_t index; /* Our offset within mapping. */
unsigned long share; /* share count for fsdax */
};
/**
* @private: Mapping-private opaque data.
* Usually used for buffer_heads if PagePrivate.
* Used for swp_entry_t if PageSwapCache.
* Indicates order in the buddy system if PageBuddy.
*/
unsigned long private;
};
struct { /* page_pool used by netstack */
/**
* @pp_magic: magic value to avoid recycling non
* page_pool allocated pages.
*/
unsigned long pp_magic;
struct page_pool *pp;
unsigned long _pp_mapping_pad;
unsigned long dma_addr;
union {
/**
* dma_addr_upper: might require a 64-bit
* value on 32-bit architectures.
*/
unsigned long dma_addr_upper;
/**
* For frag page support, not supported in
* 32-bit architectures with 64-bit DMA.
*/
atomic_long_t pp_frag_count;
};
};
struct { /* Tail pages of compound page */
unsigned long compound_head; /* Bit zero is set */
};
struct { /* ZONE_DEVICE pages */
/** @pgmap: Points to the hosting device page map. */
struct dev_pagemap *pgmap;
void *zone_device_data;
/*
* ZONE_DEVICE private pages are counted as being
* mapped so the next 3 words hold the mapping, index,
* and private fields from the source anonymous or
* page cache page while the page is migrated to device
* private memory.
* ZONE_DEVICE MEMORY_DEVICE_FS_DAX pages also
* use the mapping, index, and private fields when
* pmem backed DAX files are mapped.
*/
};

/** @rcu_head: You can use this to free a page by RCU. */
struct rcu_head rcu_head;
};

union { /* This union is 4 bytes in size. */
/*
* If the page can be mapped to userspace, encodes the number
* of times this page is referenced by a page table.
*/
atomic_t _mapcount;

/*
* If the page is neither PageSlab nor mappable to userspace,
* the value stored here may help determine what this page
* is used for. See page-flags.h for a list of page types
* which are currently stored here.
*/
unsigned int page_type;
};

/* Usage count. *DO NOT USE DIRECTLY*. See page_ref.h */
atomic_t _refcount;

#ifdef CONFIG_MEMCG
unsigned long memcg_data;
#endif

/*
* On machines where all RAM is mapped into kernel address space,
* we can simply calculate the virtual address. On machines with
* highmem some memory is mapped into kernel virtual memory
* dynamically, so we need a place to store that address.
* Note that this field could be 16 bits on x86 ... ;)
*
* Architectures with slow multiplication can define
* WANT_PAGE_VIRTUAL in asm/page.h
*/
#if defined(WANT_PAGE_VIRTUAL)
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */

#ifdef CONFIG_KMSAN
/*
* KMSAN metadata for this page:
* - shadow page: every bit indicates whether the corresponding
* bit of the original page is initialized (0) or not (1);
* - origin page: every 4 bytes contain an id of the stack trace
* where the uninitialized value was created.
*/
struct page *kmsan_shadow;
struct page *kmsan_origin;
#endif

#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
int _last_cpupid;
#endif
} _struct_page_alignment;

有關 list_head 的解說可以閱讀 你所不知道的 C 語言: linked list 和非連續記憶體

struct page 本身就會佔有一定的記憶體空間,而在 How many page flags do we really have? 一文中有提到,於一個 4GB 的系統中將會有一百萬個 page 結構體實例,因此 struct page 內的每一個 byte 都需要做嚴格的把控

為了減少 struct page 本身使用的記憶體空間,設計上使用了 union,整個 struct page 使用了兩個大的 union 以節省記憶體空間

struct 的詳細內容可以看看這篇:linux内核那些事之struct page

或是查一下 struct page 應該就蠻多不錯的文章可以看了

page table in linux

一般來說,x86 的架構使用 2-level 的 page table(10-10-12),而 x86-64 的架構則使用 4-level(9-9-9-9-12) 或 5-level(pgd_tpud_t 間多了一層 p4d_t) 的 page table,但也有 3-level 的,這可以透過 config 內的 CONFIG_PGTABLE_LEVELS 設定,基本上是 base on 處理器架構在設定的

以下是一個 4-level page table 的例子:

image

圖源:關於Linux記憶體尋址與頁表處理的一些細節
(圖很小,可以用新分頁打開來看一下)

每一個 Process 都會有自己的 Page Table,存在它自己的 kernel space,Page table 的 Base address 會被存在 CR3 裡面,這是一個 register,又被稱為 PDBR(page directory base register),存的是實體位址,但 task_struct->mm->pgd 內儲存的則是 Process Global Directory 的虛擬位址

在 context switch 發生時,CR3 會載入新的 Process 的 Page Table,而將值寫入 CR3 時系統會自動刷新 TLB 的內容

因此要做虛擬位址與實體位址的轉換,只需要照著 Page Table 一層一層查下去就好,也就是 pgd_t -> p4d_t -> pud_t -> pmd_t -> pte_t 這樣查下去

查表的話可以使用對應的 function 來查,例如我要使用 pgd_tp4d_t,那我可以這樣寫:

1
2
3
4
5
pgd_t *pgd;
p4d_t *p4d;

pgd = pgd_offset(current->mm, vaddr);
p4d = p4d_offset(pgd, vaddr);

要用 p4d_tpud_t 同理:

1
2
pud_t *pud;
pud = pud_offset(p4d, vaddr);

我們可以看一下這些 offset function 的實作,以 v6.6.5 為例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// include/linux/pgtable.h line 91

#ifndef pte_offset_kernel
static inline pte_t *pte_offset_kernel(pmd_t *pmd, unsigned long address)
{
return (pte_t *)pmd_page_vaddr(*pmd) + pte_index(address);
}
#define pte_offset_kernel pte_offset_kernel
#endif

// ...

// include/linux/pgtable.h line 119
/* Find an entry in the second-level page table.. */
#ifndef pmd_offset
static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)
{
return pud_pgtable(*pud) + pmd_index(address);
}
#define pmd_offset pmd_offset
#endif

#ifndef pud_offset
static inline pud_t *pud_offset(p4d_t *p4d, unsigned long address)
{
return p4d_pgtable(*p4d) + pud_index(address);
}
#define pud_offset pud_offset
#endif

static inline pgd_t *pgd_offset_pgd(pgd_t *pgd, unsigned long address)
{
return (pgd + pgd_index(address));
};

基本上就和上面的圖一樣,找到對應的 Directory,加上對應的 index,如此一層層下去

p4d 比較特別,只有在特定的架構上有實作,如 arch/x86/include/asm/pgtable.h

1
2
3
4
5
6
7
/* to find an entry in a page-table-directory. */
static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
{
if (!pgtable_l5_enabled())
return (p4d_t *)pgd;
return (p4d_t *)pgd_page_vaddr(*pgd) + p4d_index(address);
}

一般的架構下參數傳進去會直接被 return:

1
2
3
4
5
6
// include/asm-generic/pgtable-nop4d.h line 35

static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
{
return (p4d_t *)pgd;
}

也就是說 pgd == p4d

而當在 3-level 的硬體架構上,則沒有 pudp4d,因此參數傳進 pud_offset 也是會直接被 return:

1
2
3
4
5
6
// include/asm-generic/pgtable-nopud.h line 42
static inline pud_t *pud_offset(p4d_t *p4d, unsigned long address)
{
return (pud_t *)p4d;
}
#define pud_offset pud_offset

也就是 pgd == p4d == pud,其他的也是同理

開始實作 system call

打開 include/linux/syscalls.h

在第 942 行後新增

1
asmlinkage long sys_my_get_physical_addresses(void *);

image

新增一個檔案叫 project1.c,路徑是 kernel/project1.c

> (可以點開) 範例 code <
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <linux/syscalls.h>
// #define DEBUG

SYSCALL_DEFINE1(my_get_physical_addresses, void *, addr_p)
{
unsigned long vaddr = (unsigned long)addr_p;
pgd_t *pgd;
p4d_t *p4d;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long paddr = 0;
unsigned long page_addr = 0;
unsigned long page_offset = 0;

pgd = pgd_offset(current->mm, vaddr);
#ifdef DEBUG
printk("pgd_val = 0x%lx\n", pgd_val(*pgd));
printk("pgd_index = %lu\n", pgd_index(vaddr));
#endif
if (pgd_none(*pgd)) {
printk("not mapped in pgd\n");
return 0;
}

p4d = p4d_offset(pgd, vaddr);
#ifdef DEBUG
printk("p4d_val = 0x%lx\n", p4d_val(*p4d));
printk("p4d_index = %lu\n", p4d_index(vaddr));
#endif
if (p4d_none(*p4d)) {
printk("not mapped in p4d\n");
return 0;
}

pud = pud_offset(p4d, vaddr);
#ifdef DEBUG
printk("pud_val = 0x%lx\n", pud_val(*pud));
printk("pud_index = %lu\n", pud_index(vaddr));
#endif
if (pud_none(*pud)) {
printk("not mapped in pud\n");
return 0;
}

pmd = pmd_offset(pud, vaddr);
#ifdef DEBUG
printk("pmd_val = 0x%lx\n", pmd_val(*pmd));
printk("pmd_index = %lu\n", pmd_index(vaddr));
#endif
if (pmd_none(*pmd)) {
printk("not mapped in pmd\n");
return 0;
}

pte = pte_offset_kernel(pmd, vaddr);
#ifdef DEBUG
printk("pte_val = 0x%lx\n", pte_val(*pte));
printk("pte_index = %lu\n", pte_index(vaddr));
#endif
if (pte_none(*pte)) {
printk("not mapped in pte\n");
return 0;
}

/* Page frame physical address mechanism | offset */
page_addr = pte_val(*pte) & PAGE_MASK;
page_offset = vaddr & ~PAGE_MASK;
paddr = page_addr | page_offset;
#ifdef DEBUG
printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);
printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);
#endif

return paddr;
}

virt_to_phys 只能轉 kernel space 的 virtual address,因此必須從頭用 page table 查找

接下來要把這個檔案新增到 makefile 裡面,修改 kernel/Makefile

1
2
3
4
5
6
7
8
9
obj-y     = fork.o exec_domain.o panic.o \
cpu.o exit.o softirq.o resource.o \
sysctl.o capability.o ptrace.o user.o \
signal.o sys.o umh.o workqueue.o pid.o task_work.o \
extable.o params.o \
kthread.o sys_ni.o nsproxy.o \
notifier.o ksysfs.o cred.o reboot.o \
async.o range.o smpboot.o ucount.o regset.o ksyms_common.o \
project1.o

Build Test Binary

接下來要寫一個 user program 來使用這個 system call,在 kernel 資料夾的外面新增一個檔案叫 project1.c

image

> (可以點開) 範例 code <
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>
#include <stdlib.h>

extern void *func1(void *);
extern void *func2(void *);
extern int main();

void *my_get_physical_addresses(void *vaddr)
{
return syscall(454, vaddr);
}

struct data_
{
int id;
char name[16];
};
typedef struct data_ sdata;
static __thread sdata tx; // thread local variable

int a = 123; // global variable
int *c; // heap variable

void hello(int pid)
{
int b = 10; // local variable

b = b + pid;
// global variable
printf("[PID %d]: In thread %d, the value of global variable a is %d, the offset of the logical address of a is %p\n", pid, pid, a, &a);
printf("[PID %d]: the physical address of global variable a is %p\n", pid, my_get_physical_addresses(&a));

// local variable
printf("[PID %d]: the value of local variable b is %d, the offset of the logical address of b is %p\n", pid, b, &b);
printf("[PID %d]: the physical address of local variable b is %p\n", pid, my_get_physical_addresses(&b));

// heap variable
printf("[PID %d]: the value of heap variable c is %d, the offset of the logical address of c is %p\n", pid, *c, c);
printf("[PID %d]: the physical address of heap variable c is %p\n", pid, my_get_physical_addresses(c));

// thread local variable
printf("[PID %d]: the offset of the logical address of thread local variable tx is %p\n", pid, &tx);
printf("[PID %d]: the physical address of thread local variable tx is %p\n", pid, my_get_physical_addresses(&tx));

// function
printf("[PID %d]: the offset of the logical address of function hello is %p\n", pid, hello);
printf("[PID %d]: the physical address of function hello is %p\n", pid, my_get_physical_addresses(hello));
printf("[PID %d]: the offset of the logical address of function func1 is %p\n", pid, func1);
printf("[PID %d]: the physical address of function func1 is %p\n", pid, my_get_physical_addresses(func1));
printf("[PID %d]: the offset of the logical address of function func2 is %p\n", pid, func2);
printf("[PID %d]: the physical address of function func2 is %p\n", pid, my_get_physical_addresses(func2));
printf("[PID %d]: the offset of the logical address of function main is %p\n", pid, main);
printf("[PID %d]: the physical address of function main is %p\n", pid, my_get_physical_addresses(main));

// library function
printf("[PID %d]: the offset of the logical address of library function printf is %p\n", pid, printf);
printf("[PID %d]: the physical address of library function printf is %p\n", pid, my_get_physical_addresses(printf));
}

void *func1(void *arg)
{
char *p = (char *)arg;
int pid;
pid = syscall(__NR_gettid);
tx.id = pid;
strcpy(tx.name, p);
printf("[PID %d]: I am thread with ID %d executing func1().\n", pid, pid);
hello(pid);
}

void *func2(void *arg)
{
char *p = (char *)arg;
int pid;
pid = syscall(__NR_gettid);
tx.id = pid;
strcpy(tx.name, p);
printf("[PID %d]: I am thread with ID %d executing func2().\n", pid, pid);
hello(pid);
}

int main()
{
pthread_t id[2];
char p[2][16];
c = (int *)malloc(sizeof(int));
*c = 456;

strcpy(p[0], "Thread1");
pthread_create(&id[0], NULL, func1, (void *)p[0]);

strcpy(p[1], "Thread2");
pthread_create(&id[1], NULL, func2, (void *)p[1]);

int pid;
pid = syscall(__NR_gettid);
tx.id = pid;
strcpy(tx.name, "MAIN");
printf("[PID %d]: I am main thread with ID %d.\n", pid, pid);
hello(pid);

pthread_join(id[0], NULL);
pthread_join(id[1], NULL);
free(c);
}

然後編譯它

1
gcc -static project1.c -o project1

然後將這個執行檔複製到 busybox-1.36.1/_install 內,並重新製作 image:

1
2
3
cd busybox-1.36.1/_install
cp ../../project1 .
find . | cpio -o --format=newc | gzip > ../../linux-6.6/rootfs.img.gz

Run User Program

linux-6.6 下再執行一次 qemu:

1
2
cd linux-6.6
qemu-system-x86_64 -kernel vmlinux -nographic -initrd rootfs.img.gz -append "root=/dev/ram rdinit=/sbin/init console=ttyS0"

image

按 enter 可以開始下指令,可以先 ls 看看:

image

這裡面就有我們編譯好的執行檔了,直接執行它:

image

輸出:

> (可以點開) 輸出 <
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
[PID 26]: I am thread with ID 26 executing func1().
[PID 26]: In thread 26, the value of global variable a is 123, the offset of the logical address of a is 0x4e0110
[PID 26]: the physical address of global variable a is 0x80000000017d9110
[PID 26]: the value of local variable b is 36, the offset of the logical address of b is 0x7fd051a94194
[PID 25]: I am main thread with ID 25.
[PID 27]: I am thread with ID 27 executing func2().
[PID 27]: In thread 27, the value of global variable a is 123, the offset of the logical address of a is 0x4e0110
[PID 27]: the physical address of global variable a is 0x80000000017d9110
[PID 27]: the value of local variable b is 37, the offset of the logical address of b is 0x7fd051293194
[PID 27]: the physical address of local variable b is 0x80000000017bd194
[PID 27]: the value of heap variable c is 456, the offset of the logical address of c is 0xffe770
[PID 27]: the physical address of heap variable c is 0x80000000017c0770
[PID 27]: the offset of the logical address of thread local variable tx is 0x7fd0512935e0
[PID 25]: In thread 25, the value of global variable a is 123, the offset of the logical address of a is 0x4e0110
[PID 25]: the physical address of global variable a is 0x80000000017d9110
[PID 25]: the value of local variable b is 35, the offset of the logical address of b is 0x7ffe9eda32f4
[PID 25]: the physical address of local variable b is 0x80000000017c12f4
[PID 25]: the value of heap variable c is 456, the offset of the logical address of c is 0xffe770
[PID 25]: the physical address of heap variable c is 0x80000000017c0770
[PID 25]: the offset of the logical address of thread local variable tx is 0xffd360
[PID 26]: the physical address of local variable b is 0x80000000017bf194
[PID 26]: the value of heap variable c is 456, the offset of the logical address of c is 0xffe770
[PID 26]: the physical address of heap variable c is 0x80000000017c0770
[PID 26]: the offset of the logical address of thread local variable tx is 0x7fd051a945e0
[PID 26]: the physical address of thread local variable tx is 0x80000000017bf5e0
[PID 26]: the offset of the logical address of function hello is 0x4017ed
[PID 26]: the physical address of function hello is 0x25eb7ed
[PID 26]: the offset of the logical address of function func1 is 0x401af7
[PID 26]: the physical address of function func1 is 0x25ebaf7
[PID 27]: the physical address of thread local variable tx is 0x80000000017bd5e0
[PID 27]: the offset of the logical address of function hello is 0x4017ed
[PID 27]: the physical address of function hello is 0x25eb7ed
[PID 27]: the offset of the logical address of function func1 is 0x401af7
[PID 27]: the physical address of function func1 is 0x25ebaf7
[PID 27]: the offset of the logical address of function func2 is 0x401b78
[PID 27]: the physical address of function func2 is 0x25ebb78
[PID 27]: the offset of the logical address of function main is 0x401bf9
[PID 27]: the physical address of function main is 0x25ebbf9
[PID 27]: the offset of the logical address of library function printf is 0x40bbb0
[PID 25]: the physical address of thread local variable tx is 0x80000000017cb360
[PID 25]: the offset of the logical address of function hello is 0x4017ed
[PID 25]: the physical address of function hello is 0x25eb7ed
[PID 25]: the offset of the logical address of function func1 is 0x401af7
[PID 25]: the physical address of function func1 is 0x25ebaf7
[PID 25]: the offset of the logical address of function func2 is 0x401b78
[PID 25]: the physical address of function func2 is 0x25ebb78
[PID 25]: the offset of the logical address of function main is 0x401bf9
[PID 25]: the physical address of function main is 0x25ebbf9
[PID 25]: the offset of the logical address of library function printf is 0x40bbb0
[PID 27]: the physical address of library function printf is 0x25f5bb0
[PID 26]: the offset of the logical address of function func2 is 0x401b78
[PID 26]: the physical address of function func2 is 0x25ebb78
[PID 26]: the offset of the logical address of function main is 0x401bf9
[PID 26]: the physical address of function main is 0x25ebbf9
[PID 26]: the offset of the logical address of library function printf is 0x40bbb0
[PID 26]: the physical address of library function printf is 0x25f5bb0
[PID 25]: the physical address of library function printf is 0x25f5bb0
> (可以點開) 將順序手動整理後的版本: <
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
[PID 26]: I am thread with ID 26 executing func1().
[PID 26]: In thread 26, the value of global variable a is 123, the offset of the logical address of a is 0x4e0110
[PID 26]: the physical address of global variable a is 0x80000000017d9110
[PID 26]: the value of local variable b is 36, the offset of the logical address of b is 0x7fd051a94194
[PID 26]: the physical address of local variable b is 0x80000000017bf194
[PID 26]: the value of heap variable c is 456, the offset of the logical address of c is 0xffe770
[PID 26]: the physical address of heap variable c is 0x80000000017c0770
[PID 26]: the offset of the logical address of thread local variable tx is 0x7fd051a945e0
[PID 26]: the physical address of thread local variable tx is 0x80000000017bf5e0
[PID 26]: the offset of the logical address of function hello is 0x4017ed
[PID 26]: the physical address of function hello is 0x25eb7ed
[PID 26]: the offset of the logical address of function func1 is 0x401af7
[PID 26]: the physical address of function func1 is 0x25ebaf7
[PID 26]: the offset of the logical address of function func2 is 0x401b78
[PID 26]: the physical address of function func2 is 0x25ebb78
[PID 26]: the offset of the logical address of function main is 0x401bf9
[PID 26]: the physical address of function main is 0x25ebbf9
[PID 26]: the offset of the logical address of library function printf is 0x40bbb0
[PID 26]: the physical address of library function printf is 0x25f5bb0
=====================================================================================================================
[PID 27]: I am thread with ID 27 executing func2().
[PID 27]: In thread 27, the value of global variable a is 123, the offset of the logical address of a is 0x4e0110
[PID 27]: the physical address of global variable a is 0x80000000017d9110
[PID 27]: the value of local variable b is 37, the offset of the logical address of b is 0x7fd051293194
[PID 27]: the physical address of local variable b is 0x80000000017bd194
[PID 27]: the value of heap variable c is 456, the offset of the logical address of c is 0xffe770
[PID 27]: the physical address of heap variable c is 0x80000000017c0770
[PID 27]: the offset of the logical address of thread local variable tx is 0x7fd0512935e0
[PID 27]: the physical address of thread local variable tx is 0x80000000017bd5e0
[PID 27]: the offset of the logical address of function hello is 0x4017ed
[PID 27]: the physical address of function hello is 0x25eb7ed
[PID 27]: the offset of the logical address of function func1 is 0x401af7
[PID 27]: the physical address of function func1 is 0x25ebaf7
[PID 27]: the offset of the logical address of function func2 is 0x401b78
[PID 27]: the physical address of function func2 is 0x25ebb78
[PID 27]: the offset of the logical address of function main is 0x401bf9
[PID 27]: the physical address of function main is 0x25ebbf9
[PID 27]: the offset of the logical address of library function printf is 0x40bbb0
[PID 27]: the physical address of library function printf is 0x25f5bb0
=====================================================================================================================
[PID 25]: I am main thread with ID 25.
[PID 25]: In thread 25, the value of global variable a is 123, the offset of the logical address of a is 0x4e0110
[PID 25]: the physical address of global variable a is 0x80000000017d9110
[PID 25]: the value of local variable b is 35, the offset of the logical address of b is 0x7ffe9eda32f4
[PID 25]: the physical address of local variable b is 0x80000000017c12f4
[PID 25]: the value of heap variable c is 456, the offset of the logical address of c is 0xffe770
[PID 25]: the physical address of heap variable c is 0x80000000017c0770
[PID 25]: the offset of the logical address of thread local variable tx is 0xffd360
[PID 25]: the physical address of thread local variable tx is 0x80000000017cb360
[PID 25]: the offset of the logical address of function hello is 0x4017ed
[PID 25]: the physical address of function hello is 0x25eb7ed
[PID 25]: the offset of the logical address of function func1 is 0x401af7
[PID 25]: the physical address of function func1 is 0x25ebaf7
[PID 25]: the offset of the logical address of function func2 is 0x401b78
[PID 25]: the physical address of function func2 is 0x25ebb78
[PID 25]: the offset of the logical address of function main is 0x401bf9
[PID 25]: the physical address of function main is 0x25ebbf9
[PID 25]: the offset of the logical address of library function printf is 0x40bbb0
[PID 25]: the physical address of library function printf is 0x25f5bb0

整理成表格

  • logical address:

    identifier / logical address main thread thread 1 thread 2 位址一樣
    global variable a 0x4e0110 0x4e0110 0x4e0110
    local variable b 0x7ffe9eda32f4 0x7fd051a94194 0x7fd051293194
    heap variable c 0xffe770 0xffe770 0xffe770
    thread local variable tx 0xffd360 0x7fd051a945e0 0x7fd0512935e0
    logical address of function hello 0x4017ed 0x4017ed 0x4017ed
    logical address of function func1 0x401af7 0x401af7 0x401af7
    logical address of function func2 0x401b78 0x401b78 0x401b78
    logical address of function main 0x401bf9 0x401bf9 0x401bf9
    logical address of library function printf 0x40bbb0 0x40bbb0 0x40bbb0
  • physical address:

    identifier / physical address main thread thread 1 thread 2 位址一樣
    global variable a 0x80000000017d9110 0x80000000017d9110 0x80000000017d9110
    local variable b 0x80000000017c12f4 0x80000000017bf194 0x80000000017bd194
    heap variable c 0x80000000017c0770 0x80000000017c0770 0x80000000017c0770
    thread local variable tx 0x80000000017cb360 0x80000000017bf5e0 0x80000000017bd5e0
    logical address of function hello 0x25eb7ed 0x25eb7ed 0x25eb7ed
    logical address of function func1 0x25ebaf7 0x25ebaf7 0x25ebaf7
    logical address of function func2 0x25ebb78 0x25ebb78 0x25ebb78
    logical address of function main 0x25ebbf9 0x25ebbf9 0x25ebbf9
    logical address of library function printf 0x25f5bb0 0x25f5bb0 0x25f5bb0

hackmd 的排版讓表格不太好看,所以這邊截一下圖:

image

把 memory layout 簡單畫出來:

image

字很醜不好意思

References

  • Linux kernel on QEMU
  • Minimal kernel on QEMU
  • embed linux bootstrap
  • initrd
  • Page table
  • Get physical memory
  • ppodds Linux Kernel Project 1
  • linux内核那些事之struct page
  • How to Add a System Call
  • How many page table levels does Linux kernel use? 4 or 5?
  • 關於Linux記憶體尋址與頁表處理的一些細節
  • 學習ARM64頁表轉換流程
  • 自由工作者-萊昂筆記-頁表
  • 分頁架構
  • Linux 核心設計: 記憶體管理