---
# System prepended metadata

title: '**Linux OS Project 2**'

---

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


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


Goal
---
*Write a program using the system call you wrote in Project 1 to check how memory areas are shared by two processes that execute this program simultaneously.
The memory areas include code segments, data segments, BSS segments, heap segments, libraries, stack segments.*

<br/>


Method
---
**寫一個program test.c，透過使用tmux執行test.c編譯後的執行檔，比對彼此的segment實體位置來確認是否相同。**
<br/>
**Project_1 syscall link :**          https://hackmd.io/ISR9bPY8RjuH2r8RijH__A
<br/>

Kernel & Linux version
---

**Kernel 4.4.1 + ubuntu 16.04**
**( GCC 5.4.0 + Tmux 2.1)**

tmux install
```
sudo apt install tmux
```


<br/>



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

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

int main(){
	
	int k = 0;
	unsigned long arr[8];
	unsigned long arr1[8];

        unsigned long temp =0;
        unsigned long *p2 = &temp;


        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};
	printf("I'm process pid : %d !!!\n\n" , getpid());
        for(int i = 0 ;i < 6;i++){
                unsigned long *p1 = &address[i];
                long a = syscall(547,p1,p2);
                while(1){
                        printf("%c",label[index]);
                        index++;
                        if(label[index] ==' '){index++; break;}};
                printf(" vaddr: %#lx\t paddr: %#lx\n",*p1,*p2 );
        }
	printf("\n\n");

	long a = syscall(546 , arr1, arr1);
	
	arr1[7] =0;
        printf("virtual address :");
	for(int i = 0 ; i < 8 ; i = i +2){
		printf("%d : start : %lx  end : %lx\n", k ,arr1[i]  , arr1[i+1]);
		k++;
	}
	for(int i = 0 ; i < 8 ;i++){
		unsigned long *p1 = &arr1[i];
		long b = syscall(547,p1,p2);
		arr1[i] = temp;
	}
	k=0;
	arr1[7] =0;
        printf("\n")
        printf("physical address :");
        for(int i = 0 ; i < 8 ; i = i +2){
                printf("%d : start : %lx  end : %lx\n", k ,arr1[i] ,arr1[i+1]);
		k++;
        }

	printf("\n");


	sleep(1000);
	return 0;
}
```
<br/>

Syscall code
---
```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 ){

	struct mm_struct *mm;
	struct vm_area_struct* vma;	
	unsigned long arr[7]; 
		
	mm = current->mm;
	vma = mm->mmap;
	copy_from_user(arr,vaddr,sizeof(unsigned long)*7);

	arr[6] = mm->start_stack;
	arr[0] = mm->start_code;
	arr[1] = mm->end_code;
	arr[2] = mm->start_data;
	arr[3] = mm->end_data;
	arr[4] = mm->start_brk;
	arr[5] = mm->brk;


	copy_to_user(paddr,arr,sizeof(unsigned long)*7);


	return 0;


}
```

compiling
---

**在command line 依序輸入以下指令**
```
gcc -fpie test.c -o main
```
<br/>

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


從mm_struct中抓到的位置分別對應為
0 : code_start/code_end address 
1 : data_start/data_end address
2 : heap_start/heap_end address
3 : stack_start address
<br/>

Conclusion
---

<br/>

在上次的專案中找出各個thread的分段位置中有出現**錯誤**，我發現lib segment的virtual address要比heap segment來的高，但是結果卻顯示與code segment的位置非常接近，這是因為lib segment取值是取到printf@plt位置，會導致這個結果的原因是gcc compiler設定上問題，我的compiler預設並沒有開啟pie，因此編譯後的檔案是non-pie，而linux x86_64的二進制文件地址默認在0x400000，這個地址與code segment很近才會有先前的結果。
![](https://i.imgur.com/4ckOGmG.png)
[PIE-PIC](https://leimao.github.io/blog/PIC-PIE/)
[ASLR](https://leimao.github.io/blog/PIC-PIE/)
[得到錯誤地址的原因](https://stackoverflow.com/questions/28119365/what-are-the-differences-comparing-pie-pic-code-and-executable-on-64-bit-x86-pl )
[PLT](https://www.cnblogs.com/sword03/p/9385660.html)



這次加入了一個新的syscall將上次加分題的功能實做出來，編譯過程與上次相同，從task_struct-> mm_struct中取值，回傳後再透過Project_1 syscall轉換為physical address。


透過以上結果我們可以判斷不同的process執行相同的program，共享的實體位置只有code segment和lib segment共享，其他皆沒有共享。

<br/>

---
## demo 提問
**1.在sleep 時 process 狀態**

透過tmux開兩個process進sleep(),再分割一個下指令: $ps a可看到當中STAT(目前狀態)皆為S+([ps man_page](https://man7.org/linux/man-pages/man1/ps.1.html#PROCESS_STATE_CODES))
![ps](https://i.imgur.com/0ZydEmg.png)

**2.sleep時如何關閉 process? ctrl+c在做什麼?kill在做什麼?**

終止正在執行之程式(ctrl+c)，按下後Terminal會發送一個[**SIGINT中斷訊號**]給Shell，Shell再把SIGINT轉發給ping process，最後ping process收到就會自己停掉。

暫停正在執行的程式(ctrl+z)，發送[**SIGTSTP(暫停訊號)**]給正在跑的process。回復執行下指令:**fg**
[Day23-Signal 訊號（一）]: https://ithelp.ithome.com.tw/articles/10226301

**3.signal?**
是 OS 控制程序執行的媒介，作業系統中 process 間通訊的一種有限制的方式。它是一種異步的通知機制，用來提醒 process 一個事件已經發生。當一個 signal 傳送給一個 process，作業系統中斷了行程正常的控制流程，此時，任何非原子操作都將被中斷。例如用戶要終止某一執行中的應用程式，OS 就要將用戶的這個意圖，利用訊號正確地傳遞到指定的應用程序。這類要求 process 結束的訊號稱為 termination signals。
**SIGTERM**
Termination
SIGTERM 信號是用於導致程序終止的通用信號。 與 SIGKILL 不同，此信號可以被阻止、處理和忽略。

**SIGINT**
Interrupt
User 輸入 INTR character（Ctrl + C）時產生

**SIGQUIT**
和SIGINT類似, 但由QUIT字符(通常是Ctrl-\)來控制. process在收到SIGQUIT退出時會產生core文件, 在這個意義上類似於一個程序錯誤信號。

**SIGKILL**
信號用於立即終止程序。 它不能被處理或忽略，也無法阻止此信號。

**SIGHUP**
Hang-up
hong up，本信號在用戶終端連接(正常或非正常)結束時發出, 通常是在終端的控制進程結束時, 通知同一session內的各個作業, 這時它們與控制終端不再關聯。

**4.mmap system call 在做什麼?**

mmap system call 是linux中可用來將文件映射到虛擬記憶體位置，如此一來不同的process透過對這段記憶體位置的讀寫達到共享的目的，這樣也可以方便操作
文件不需要透過read write函數對文件進行讀寫，當呼叫此函數時會在process的虛擬記憶體位置找到連續的空間，並分配vm_area_struct結構的空間，再將此空間鏈結到原本process的vm_area_struct串列中。

**5.copy on write(COW)?**
如果有多個呼叫者（callers）同時請求相同資源（如記憶體或磁碟上的資料儲存），他們會共同取得相同的指標指向相同的資源，直到某個呼叫者試圖修改資源的內容時，系統才會真正複製一份專用副本（private copy）給該呼叫者，而其他呼叫者所見到的最初的資源仍然保持不變。這過程對其他的呼叫者都是透明的。此作法主要的優點是如果呼叫者沒有修改該資源，就不會有副本（private copy）被建立，因此多個呼叫者只是讀取操作時可以共享同一份資源。

**6.page fault哪些原因?** 
沒有實際建立虛擬記憶體與實體記憶體的映射關係，當CPU沒有辦法存取實體記憶體的時候就會發生分頁錯誤(page fault)。

觸發 Page Fault 的原因主要歸類為幾大類：

1.使用共享記憶體的區域，但是還不存在虛擬位址與實體位址的對應，而產生的分頁錯誤，這種錯誤只要在分頁表中建立對應關係就好了。
2.訪問的地址在實體記憶體中不存在，需要從硬碟或是 swap分割槽讀入才能使用，這種效能影響比較大，因為磁碟讀取的速度真的太慢了。
3.訪問的記憶體位址非法，缺頁錯誤會進而引發 SIGSEGN 訊號結束程序，這種分頁錯誤會導致行程直接關閉。