# Linux Operating System
## Content
[TOC]
## 1.Topic
* [1. ](/Rmodl8SeQkCjmaP1MHCm3Q)Write a new system call "void * my_get_physical_addresses(void *)".
>it can use it to get the physical address of a virtual address of a process,and the return value of this system call is physicall address.[color=#20e7ea]
>
* [2. ](/VCgDCdYQT86h1XuxFIuKsA)Using the new system call to show how the following memory areas are shared by these threads.
>show memory areas include code segments, data segments, BSS segments, heap segments, libraries, stack segments, and thread local storages,and figure of memory.[color=#20e7ea]
## 2.Add New Syscall
**新增 get_physical_addresses 資料夾**
```
cd /usr/src/linux-5.15.137
sudo mkdir get_physical_address
cd get_physical_address
```

**建立 get_physical_address.c**
```
sudo vim get_physical_address.c
```
>get_physical_address.c
>

**建立 Makefile**
```
sudo vim Makefile
```
>Makefile
>

**回到 linux-5.15.137 改 Makefile**
```
cd ..
sudo vim Makefile
```
>在 Makefile 的 core-y 最後面補上 my_get_physical_address
```C=
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ hello/ get_physical_address/
```

**修改 syscall_64.tbl 檔**
```
sudo vim arch/x86/entry/syscalls/syscall_64.tbl
```
>添加 system call
```C=
450 common get_physical_address sys_get_physical_address
```

**編輯 syscalls.h 檔**
```
sudo vim include/linux/syscalls.h
```
>syscalls.h 新增內容
```C=
asmlinkage long sys_get_physical_address(void);
```

## 3.Compile Kernel
**cd 到 linux-5.15.137 資料夾**
```
cd /usr/src/linux-5.15.137
```
**使用12核心編譯 kernel**
```
sudo make -j12
```

**kernel 安裝上 unubntu22.04**
```
sudo make modules_install -j12
```

```
sudo make install -j12
```

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

**重新啟動, 並選擇新 kernel version**
```
reboot
```

**查看版本是否有更新**
```
uname -mrs
```

## 4.Test Syscall
**建立程式碼**
```
sudo vim get_physical_address.c
```
>get_physical_address.c
```C=
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define SYS_get_physical_address 450
int main(){
void* ptr = malloc(512);
long ret = syscall(SYS_get_physical_address, ptr);
printf("The physical address is %lx", ret);
return 0;
}
```
**編譯 get_physical_address.c 並執行**
```
gcc -o get_physical_address get_physical_address.c
```
```
./get_physical_address
```
**使用 dmesg 查看 kernel 中訊息**
```
sudo dmseg
```

## 5.Kernel Space
**過程**

>上圖為linux kernel中task_struct結構體, 從中取得mm_strcut中的pgd及mmap, 而mmap則紀錄process使用的VMA(virtual memory area), 透過mm取得目前address
* 64-bits Virtual address 轉成 Physical address

>64bits的linux kernel 5.15.137架構是使用5層的page table來進行位址轉換(其中p4d是從v4.11之後才出現), 分別是Page Global Directory (PGD), P4D, Page Upper Directory (PUD), Page Middle Directory (PMD), Page Table (PTE),且每一級的table有三個Description marco - size, shift, mask, 並且在Kernel中結構各定義為pgd_t, p4d_t, pmd_t, pte_t
## 6.User Space
**程式碼執行畫面**

* 其中main thread和其他三個sub thread的data, bss, code segments是在相同的physical address起始位置上, 而stack, thread local, heap segment的address則是不一樣的
**記憶體配置結果圖**

## 7.Kernel Space & User Space Code
### ==Kernel Space Code==
```C=
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/mm_types.h>
#include <asm/pgtable.h>
#include <linux/slab.h>
SYSCALL_DEFINE1(get_physical_address,unsigned long , virtual_address) {
pgd_t *pgd;
p4d_t *p4d;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
struct task_struct *task;
struct mm_struct *mm;
unsigned long phys_addr = 0;
unsigned long addr = 0;
unsigned long offset = 0;
// Get the task structure of the current process
task = current;
// Get the memory descriptor of the current process
mm = get_task_mm(task);
pgd = pgd_offset(mm, virtual_address);
printk(KERN_INFO "PGD: %lx\n", pgd);
p4d = p4d_offset(pgd, virtual_address);
printk(KERN_INFO "P4D: %lx\n", p4d);
pud = pud_offset(p4d, virtual_address);
printk(KERN_INFO "PUD: %lx\n", pud);
pmd = pmd_offset(pud, virtual_address);
printk(KERN_INFO "PMD: %lx\n", pmd);
pte = pte_offset_kernel(pmd, virtual_address);
printk(KERN_INFO "PTE: %lx\n", pte);
addr = pte_val(*pte) & PAGE_MASK;
offset = virtual_address & ~PAGE_MASK;
phys_addr = addr | offset;
printk(KERN_INFO "phys_addr: %lx\n", phys_addr);
return phys_addr;
}
```
下方為linux kernel中pgtable.h相關的Macro
```C=
#define pgd_offset(mm, address) pgd_offset_pgd((mm)->pgd, (address))
#define pte_offset_kernel
#define PAGE_MASK (~(PAGE_SIZE-1))
#ifdef __ASSEMBLY__
#define PAGE_SIZE (1 << PAGE_SHIFT)
#else
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_SHIFT 12
```
### ==User Space Code==
``` c=
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>
#include <stdlib.h>
#define SYS_get_physical_address 450
extern void *func1(void *);
extern void *func2(void *);
extern int main();
void * my_get_physical_addresses(void *ptr1){
syscall(SYS_get_physical_address, ptr1);
}
struct data_
{
int id ;
char name[16] ;
} ;
typedef struct data_ sdata ;
static __thread sdata tx ; //thread local variable
int a=123; //global variable
int d;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
void hello(int pid)
{
pthread_mutex_lock ( &mutex1 ); //lock
int b=10; //local varialbe
b=b+pid;
//global variable
int* c = malloc(sizeof(int));
printf("(data segment)1.In thread %d \nthe value of gloable varialbe a is %d, the offset of the logical address of a is %p, ", pid, a, &a);
printf("the physical address of global variable a is %p\n", my_get_physical_addresses(&a) );
//local variable
printf("(stack segment)2.the value of local varialbe b is %d, the offset of the logical address of b is %p, ", b, &b);
printf("the physical address of local variable b is %p\n", my_get_physical_addresses(&b));
//thread local variable
printf("(thread local segment)3.the offset of the logical address of thread local varialbe tx is %p, ", &tx);
printf("the physical address of thread local variable tx is %p\n", my_get_physical_addresses(&tx));
//function
printf("(code segment)4.the offset of the logical address of function hello is %p, ", hello);
printf("the physical address of function hello is %p\n", my_get_physical_addresses(hello));
printf("(code segment)5.the offset of the logical address of function func1 is %p, ", func1);
printf("the physical address of function func1 is %p\n", my_get_physical_addresses(func1));
printf("(code segment)6.the offset of the logical address of function func2 is %p, ", func2);
printf("the physical address of function func2 is %p\n", my_get_physical_addresses(func2));
printf("(code segment)7.he offset of the logical address of function main is %p, ", main);
printf("the physical address of function main is %p\n", my_get_physical_addresses(main));
//library function
printf("(libraries)8.the offset of the logical address of library function printf is %p, ", printf);
printf("the physical address of library function printf is %p\n", my_get_physical_addresses(printf));
printf("(heap)9.the offset of the logical address of c is %p, ",c);
printf("the physical address of heap is %p\n",my_get_physical_addresses(c));
printf("(BSS)10.the value of initializied global variable d is %d, the offset of the logical address of b is %p, ", d, &d);
printf("the physical address of initializied global variable (in BSS) d is %p\n", my_get_physical_addresses(&d));
printf("====================================================================================================================\n");
pthread_mutex_unlock( &mutex1 ); //unlock
free(c);
}
void *func1(void *arg)
{
char *p = (char*) arg ;
int pid ;
pid = syscall( __NR_gettid );
tx.id = pid ;
strcpy(tx.name,p) ;
printf("I am thread with ID %d executing func1().\n",pid);
hello(pid);
while(1)
{
//printf("(%d)(%s)\n",tx.id,tx.name) ;
sleep(1) ;
}
}
void *func2(void *arg)
{
char *p = (char*) arg ;
int pid ;
pid = syscall( __NR_gettid );
tx.id = pid ;
strcpy(tx.name,p) ;
printf("I am thread with ID %d executing func2().\n",pid);
hello(pid);
while(1)
{
//printf("(%d)(%s)\n",tx.id,tx.name) ;
sleep(2) ;
}
}
int main()
{
pthread_t id[2];
char p[2][16] ;
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("I am main thread with ID %d.\n", pid);
hello(pid);
while(1)
{
//printf("(%d)(%s)\n",tx.id,tx.name) ;
sleep(5) ;
}
}
```
## 8.Encounter Problem and Solution
* 問題一 : 在 system call 裡加入了copy_to_user() 和 copy_from_user() 這兩個函式出錯(出現 Segmentation Fault or Killed)
>呼叫這兩個函式時,需要傳遞的是兩個 pointer , 是為了可以讓 kernel space 讀到位於 user space 的位址,但由於我們只需傳遞一個 virtual_address 變數,因此最後沒有使用到這兩個函式
>
>dmesg所產生詳細的問題
>
>
>參考自 : [Linux内核和用户空间数据交互copy_to_user和copy_from_user](https://blog.csdn.net/qq_30624591/article/details/88544739)
* 問題二 : 虛擬機磁碟空間不足
>根據本次的經驗,我們的磁碟的容量用了約40GB,因此建議虛擬機的磁碟空間至少要大於40GB
## Reference
-get_task_mm函式
https://zhuanlan.zhihu.com/p/41788388
-copy_to_user and copy_from_user
[copy_to_user(void __user *to, const void *from, unsigned long n)](https://elixir.free-electrons.com/linux/v3.9/source/arch/x86/lib/usercopy_32.c#L658)
[copy_from_user(void *to, const void __user *from, unsigned long n)](https://elixir.free-electrons.com/linux/v3.9/source/arch/x86/lib/usercopy_32.c#L658)
-mm_struct, Task結構
https://hackmd.io/@eugenechou/H1LGA9AiB
-PGD, P4D, PUD, PMD, PTE, linux轉換頁表
https://hackmd.io/@combo-tw/Linux-%E8%AE%80%E6%9B%B8%E6%9C%83/%2F%40combo-tw%2FBJlTwJUABB
https://github.com/monjjjjj/linux_project1_multithread/blob/main/README.md
-address 轉換表圖片來源
https://www.chegg.com/homework-help/questions-and-answers/given-virtual-memory-areas-used-process-write-system-call-sysgetpageinfo-report-current-st-q46483922