---
# System prepended metadata

title: '**Linux OS Project 1**'

---

# **Linux OS Project 1**
<br/>
<br/>
<br/>


**第1組 111522015 陳萬鈿 110522156 陳明威**


Goal
---

<br/>

Linux的虛擬記憶體儲存方式是，將資料分段存入3GB大小的虛擬位置中，
此專案的目的是建立一條system call，功能包含解決以下問題，
"*建立三條threads，並確認各個分段位置，在不同的thread中是否共享分段位置*"。**



<br/>


Method
---

<br/>

目標是確認不同thread各個分段是否共享相同位置，故想法是先在kernel中加入一條system call，功能為將虛擬位置轉換為實體位置，並將不同thread各個分段的虛擬位置傳入並傳回實體位置，最後進行比對判斷是否相同。
<br/>

Kernel & Linux version
---

**Kernel 4.4.1 + ubuntu 16.04**
<br/>

Compile
---
https://blog.kaibro.tw/2016/11/07/Linux-Kernel%E7%B7%A8%E8%AD%AF-Ubuntu/
透過以上步驟，逐步建立syscall並編譯
編譯前要裝的套件
sudo apt install build-essential libncurses-dev libssl-dev libelf-dev bison flex -y

每次編譯完成改syscall後，只需要重開機選擇原本的kernel輸入以下指令即可
```
sudo make 
sudo make modules_install
sudo make install
```

<br/>

Test code
---

```C=
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#define mask  0x00000000ffffffff
char label[100] = {"Stack Lib Heap Bss Data Code "};

sem_t s1,s2;

int data = 0;
int bss ;
int code(){
	return 0;
}

void* thread_1(){

	sem_wait(&s1);
	int index = 0;
	int stack = 54088;
	int *heap = malloc(sizeof(int));
	unsigned long address[6] = 
	{(unsigned long)&stack,
	(unsigned long)&printf,
	(unsigned long)heap,
	(unsigned long)&bss,
	(unsigned long)&data,
	(unsigned long)&code};

	unsigned long temp =0;
	unsigned long *p2 = &temp;
	printf("I'm THREAD_1 !\n\n");
	for(int i = 0 ;i < 6;i++){
		unsigned long *p1 = &address[i];
		long a = syscall(546,p1,p2);
		while(1){
			printf("%c",label[index]);
			index++;
			if(label[index] ==' '){index++; break;}};
		printf(" vaddr: %#lx\t paddr: %#lx\n",*p1,*p2 & mask );
	}
	printf("\n");
	sem_post(&s2);
}
void* thread_2(){

	sem_wait(&s2);
        int index = 0;
        int stack = 54088;
        int *heap = malloc(sizeof(int));
        unsigned long address[6] =
        {(unsigned long)&stack,
        (unsigned long)&printf,
        (unsigned long)heap,
        (unsigned long)&bss,
        (unsigned long)&data,
        (unsigned long)&code};

        unsigned long temp =0;
        unsigned long *p2 = &temp;
        printf("I'm THREAD_2 !\n\n");
        for(int i = 0 ;i < 6;i++){
                unsigned long *p1 = &address[i];
                long a = syscall(546,p1,p2);
                while(1){
                        printf("%c",label[index]);
                        index++;
                        if(label[index] ==' '){index++; break;}};
                printf(" vaddr: %#lx\t paddr: %#lx\n",*p1,*p2 & mask );
        }
}

int main(){
	
	sem_init(&s1,0,0);
	sem_init(&s2,0,0);
	pthread_t t_1,t_2;
	pthread_create(&t_1,NULL,thread_1,NULL);
	pthread_create(&t_2,NULL,thread_2,NULL);
	
        int index = 0;
        int stack = 54088;
        int *heap = malloc(sizeof(int));
        unsigned long address[6] =
        {(unsigned long)&stack,
        (unsigned long)&printf,
        (unsigned long)heap,
        (unsigned long)&bss,
        (unsigned long)&data,
        (unsigned long)&code};

        unsigned long temp =0;
        unsigned long *p2 = &temp;
        printf("I'm MAIN thread !\n\n");
        for(int i = 0 ;i < 6;i++){
                unsigned long *p1 = &address[i];
                long a = syscall(546,p1,p2);
                while(1){
                        printf("%c",label[index]);
                        index++;
                        if(label[index] ==' '){index++; break;}};
                printf(" vaddr: %#lx\t paddr: %#lx\n",*p1,*p2 & mask );
        }
	printf("\n");
	sem_post(&s1);
	pthread_join(t_1,NULL);
        pthread_join(t_2,NULL);


	return 0;
}
```

System call 
---
```C=
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>
#include <linux/mm_types.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/pgtable.h>
#include <asm/current.h>

SYSCALL_DEFINE2(v_to_p,unsigned long* ,vaddr,unsigned long* ,paddr){
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;

	unsigned long t1;
	unsigned long t2;
	unsigned long *va = &t1;
	unsigned long *pa = &t2;


	unsigned long p_index=0,p_offset=0;
	
	copy_from_user(va,vaddr,sizeof(unsigned long));
	printk("va : %lx",t1);
	pgd = pgd_offset(current->mm,t1);
	if (pgd_none(*pgd) || pgd_bad (*pgd)){
		return -1;
	}
	pud = pud_offset(pgd,t1);
	if(pud_none(*pud) || pud_bad(*pud)){
		return -1;
	}
	pmd = pmd_offset(pud,t1);
	if(pmd_none(*pmd) || pmd_bad(*pmd)){
		return -1;
	}
	pte = pte_offset_kernel(pmd,t1);
	if(!pte){
		return -1;
	}        

	p_index = pte_val(*pte) & PAGE_MASK;
	p_offset = t1 & ~PAGE_MASK;
	t2 = p_index | p_offset;
	printk("pa :%lx",t2);
	copy_to_user(paddr,pa,sizeof(unsigned long));
	return 0;


}
```

Result
--- 
![](https://i.imgur.com/mZ8Zxt1.png)



<br/>

由以上結果可比對得知各個thread的stack_segment和heap_segment並不共享實體位置，但Bss、Data、Code、Lib段都是共享實體位置。

<br/>

關於linux的virtual address 轉 physical address
---

<br/>

x86_64架構 在64位元的linux系統中，虛擬位置是用64位元表示0x0000,0000,0000,0000 -> 0xffff,ffff,ffff,ffff 
但實際只使用後48個位元，詳細的介紹可參考連結
https://stackoverflow.com/questions/25852367/x86-64-canonical-address

並採用4層地址映射

<br/>

![](https://i.imgur.com/eAwphJi.png)

<br/>

![](https://i.imgur.com/Mb8ntaF.png)


<br/>

要找到Page table的位置，我們首先要從linux儲存porcess資料的結構task_struct，
存取其中的mm point，mm指標儲存mm_struct位置，而mm_struct是用來儲存該process虛擬位置資料的結構，在該結構中我們就可以找到pgd的初始位置，使用pgd_offset即可存取pgd page中的entry，接著可以透過entry中的資料加上offset使用pud_offset存取pud page entry，依此類推最後可得到pte entry中的實體位置再將後面的offset 12 bits 清成0，加上正確的offset可得到正確的實體位置。

task_struct
https://blog.csdn.net/bit_clearoff/article/details/54292300
https://elixir.bootlin.com/linux/v4.4.1/source/arch/x86/include/asm/current.h#L8
mm_struct
https://blog.csdn.net/qq_26768741/article/details/54375524
https://elixir.bootlin.com/linux/v4.4.1/source/arch/x86/include/asm/pgtable.h
<br/>

Virtual address spaces
---

<br/>

由觀察執行結果中的虛擬位置，可以得到此process在virtual address spaces的位置，上面的結果是三個執行緒的code_segment取值為code funtion的實體位置，在這裡我們稍作修改將thread_1 code_segment 取thread_1 function虛擬位置，thread_2 main 同理，可得到以下結果。

<br/>

![](https://i.imgur.com/Yio2ntz.png)

<br/>

接著可以畫出下面的圖片


![](https://i.imgur.com/PV8Q0xN.png)



<br/>

Conclusion
---

<br/>

在本次的專案中可以發現，main thread、thread_1、thread_2 都是共享data、bss、lib、code這些分段位置，而stack，heap則各自配置不同的記憶體位置，此次專案中的加分題需要找出各個分段的起始到結束位置和大小，我認為可以從mm_struct儲存的start_code、end_code、start_data、end_data等等提出虛擬位置，回傳後做運算進而算出size。


可能有幫助的連結
https://blog.csdn.net/menogen/article/details/24664833 刪kernel
https://www.twblogs.net/a/5d122e90bd9eee1ede04cb76 mm結構
https://blog.csdn.net/lijzheng/article/details/23618365 proc/pid/maps文件詳解
https://hackmd.io/@RinHizakura/S1wfy6nQO 查詢process使用哪個sycall/如何定義SYSCALL_DEFINE
https://www.linuxquestions.org/questions/slackware-14/difference-between-make-mrproper-and-make-clean-473144/ 清理上次編譯一堆文件
https://blog.csdn.net/qq_30624591/article/details/88544739 copy_to_user使用方式
https://blog.gtwang.org/linux/linux-tmux-terminal-multiplexer-tutorial/ tmux使用方式
https://elixir.bootlin.com/linux/v4.4.1/source/arch/x86/include/asm 查kernel標頭檔資料位置/內容結構
https://www.cnblogs.com/muahao/p/10297852.html 四層分頁表詳解



