# **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 訊號結束程序,這種分頁錯誤會導致行程直接關閉。