# NCU Linux 作業系統 作業一(linux_survey_TT)
## Team 5
+ 108522028 謝明諺
+ 108522041 李文郁
+ 104502509 李唯源
## 開發環境和版本資訊
+ Virtual Machine: Virtual Box
+ Linux Release: Ubuntu 16.04 (32-bit)
+ Kernel Version: Linux-3.9.1 (32-bit no PAE)
## 加入 system call
```shell=
sudo apt-get install libncurses5-dev
cd ~/linux-3.9.1
mkdir hello
cd hello
vim hello.c並寫入自訂的system call function
vim Makefile 並寫入 obj-y := hello.o
cd ~/linux-3.9.1/arch/x86/syscalls
vim syscall_32.tbl 裡加入在最後一行 351 i386 linux_survey_TT sys_linux_survey_TT
cd ~/linux-3.9.1/include/linux
vim syscalls.h 裡加入在最後一行 asmlinkage int sys_linux_survey_TT(int result[]);
重啟動電腦後選擇 3.9.1 kernel version
```
## 編譯linux kernel步驟
```shell=
cd ~/linux-3.9.1
sudo make menuconfig
sudo make -j8
sudo make modules_install
sudo make install
sudo update-grub
```
## 測試結果
### 第一題、第二題、第三題
### result_1.txt
[result1.txt](https://drive.google.com/open?id=1Jv6q74uzN1wNzZydBggwdqwJnJeG9zla)
### result_2.txt
[result2.txt](https://drive.google.com/open?id=1FzKNm1Tj90Y1k5_XPoka_822JNTmFFbB)
### result_3.txt
[result3.txt](https://drive.google.com/open?id=17REvQHIV36HuD85j0bALs0mNMgjVSUDM)
### 第四題、第五題
```
[+] PID = 2723 (parent)
[+] map count = 14
[+] total virtual mem = 2060
[+] total physical mem = 276
[+] PID = 2724 (child)
[+] map count = 14
[+] total virtual mem = 2060
[+] total physical mem = 60
[+] PID = 2724 (child-COW)
[+] map count = 15
[+] total virtual mem = 2192
[+] total physical mem = 296
由結果分析 :
The percentages of the virtual addresses that have physical memory assigned to the program at location 1 : 13%
The percentages of the virtual addresses that have physical memory assigned to the program at location 2 : 3%
The percentages of the virtual addresses that have physical memory assigned to the program at location 3 : 13%
location1跟location2的shared memory大約為74%。
location1跟location3的shared memory大約為40%。
```
## Problem : Copy-On-Write
### 實驗結果
本來預期在child process修改result1那邊發生COW,但由實驗結果我們會發現發生COW的位置跟我們預期的不一樣,在parent process就發生了,所以result2及result3的結果相同,如下圖。

### 原因
我們知道local變數的位置是放在stack上的,而main又接近stack的底部,所以在這支程式被載入到memory的時候由於程式一開始會將程式所需的資訊放入stack中,例如環境變數,所以當我們將result_1定義在比較靠近stack底部的時候,result_1因為PAGE是4k對齊的關係,有一些部分被包含到stack前段的PAGE中,所以我們在parent那邊寫入result1時就發生COW。
```cpp=
int main()
{
int result_1[MEMORY_SIZE]; //這裡已經跟stack底部的一些初始化東西的分到一個PAGE
int result_2[MEMORY_SIZE];
int result_3[MEMORY_SIZE];
if(fork()){ // fork後child跟parent的PAGE還是一樣的
a = syscall(351,result_1); //parent會因為要寫入result_1發生COW
write2file(result_1,"result_1.txt");
}
```
### 正確的COW
若將程式改成如下,則可觀察到不一樣的結果。
```cpp=
int main()
{
int result_3[MEMORY_SIZE];
int result_2[MEMORY_SIZE];
int result_1[MEMORY_SIZE];
```

## 程式碼
+ kernel function code
```cpp=
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/sched.h>
#include<linux/mm.h>
#include<linux/mm_types.h>
#include<asm/io.h>
#include<linux/highmem.h>
int result_count;
int vma_total;
unsigned long total_virtual_memory;
void write2result(int result[],unsigned long addr)
{
/*
* index 0 1 2 3 4 5 6 7 8
* result vma_total pid map_count total_vm total_pm vm_start vm_end phy_start phy_end .....
*/
result[result_count] = (int)addr;
result_count++;
}
static unsigned long virual2physical(struct task_struct *task,unsigned long va);
void print_virual_memory(struct task_struct *task,int result[])
{
struct mm_struct *mm;
struct vm_area_struct *vma;
unsigned long pa = 0;
int tmp,tmp1 = 0;
mm = task->mm;
/* map_count [2] */
write2result(result,mm->map_count);
/* total virtual memory [3] */
write2result(result,(mm->total_vm << (PAGE_SHIFT - 10)));
/* total physical memory [4] */
write2result(result,(get_mm_rss(mm) << (PAGE_SHIFT - 10)));
for(vma = mm->mmap; vma; vma=vma->vm_next){
tmp = (vma->vm_end - vma->vm_start) / 0x1000 ;
while(tmp1 < tmp){
write2result(result,vma->vm_start+tmp1*0x1000);
write2result(result,vma->vm_start+tmp1*0x1000+0x1000);
pa = virual2physical(task,vma->vm_start + tmp1*0x1000);
if (pa != -1){
write2result(result,pa);
write2result(result,pa+0x1000);
} else {
write2result(result,0);
write2result(result,0);
}
tmp1++;
}
tmp1 = 0;
vma_total += tmp;
tmp = 0;
}
}
static unsigned long virual2physical(struct task_struct *task,unsigned long va)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long pa = 0;
unsigned long page_addr = 0;
unsigned long page_offset = 0;
pgd = pgd_offset(task->mm,va);
if(pgd_none(*pgd)){
return -1;
}
pud = pud_offset(pgd,va);
if(pud_none(*pud)){
return -1;
}
pmd = pmd_offset(pud,va);
if(pmd_none(*pmd)){
return -1;
}
pte = pte_offset_kernel(pmd,va);
if(pte_none(*pte)){
return -1;
}
page_addr = pte_val(*pte) & PAGE_MASK;
page_offset = va & ~PAGE_MASK;
pa = page_addr | page_offset;
return pa;
}
asmlinkage void sys_linux_survey_TT(int result[])
{
struct task_struct *task;
result_count = 1;
vma_total = 0;
task = current;
/* task pid [1] */
write2result(result,task->pid);
print_virual_memory(task,result);
result[0] = vma_total;
}
```
+ user program
```cpp=
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#define MEMORY_SIZE 10000
void write_str(FILE *fp,char *str,int result,int result2,int result3)
{
char buf[60];
memset(buf,0,sizeof(buf));
sprintf(buf,str,result,result2,result3);
fwrite(buf,sizeof(char),strlen(buf),fp);
}
void write2file(int result[],char *name)
{
FILE *fp;
int i;
fp = fopen(name,"w");
write_str(fp,"[+] PID = %d \n",result[1],0,0);
write_str(fp,"[+] map count = %d \n",result[2],0,0);
write_str(fp,"[+] total virtual mem = %d \n",result[3],0,0);
write_str(fp,"[+] total physical mem = %d \n",result[4],0,0);
for(i=0;i<result[0];i++){
write_str(fp,"[%2d] v_start : %10p \t, v_end : %10p \n",i+1,result[5+i*4],result[6+i*4]);
write_str(fp,"[%2d] p_start : %10p \t, p_end : %10p \n",i+1,result[7+i*4],result[8+i*4]);
}
fclose(fp);
}
void show_process_mem_info(int result[])
{
int i;
/*
[0] result count
[1] pid
[2] map count
[3] total virtual memory
[4] total physical memory
[5] vm start
[6] vm end
[7] pm start
[8] pm end
*/
printf("[+] PID = %d \n",result[1]);
printf("[+] map count = %d \n",result[2]);
printf("[+] total virtual mem = %d \n",result[3]);
printf("[+] total physical mem = %d \n",result[4]);
for(i=0;i<result[0];i++){
printf("[%2d] v_start : %10p \t, v_end : %10p \n",i+1,result[5+i*4],result[6+i*4]);
printf("[%2d] p_start : %10p \t, p_end : %10p \n",i+1,result[7+i*4],result[8+i*4]);
}
}
int main()
{
int result_1[MEMORY_SIZE];
int result_2[MEMORY_SIZE];
int result_3[MEMORY_SIZE];
int exit_status;
int a;
if(fork()){
a = syscall(351,result_1);
write2file(result_1,"result_1.txt");
wait(&exit_status);
} else {
a = syscall(351,result_2);
write2file(result_2,"result_2.txt");
result_1[0]=123;
a = syscall(351,result_3);
write2file(result_3,"result_3.txt");
}
}
```
+ analysis.py (第五題)
``` cpp=
#!/usr/bin/env python3
f = open('result_1.txt')
f1 = open('result_2.txt')
f2 = open('result_3.txt')
tp1 = 0
tp2 = 0
tp3 = 0
tpbuf1 = []
tpbuf2 = []
tpbuf3 = []
def analysis(f):
tp = 0
tpbuf = []
lines = f.readline()
while lines:
if lines[6] == 'p':
if "nil" not in lines[21:26]:
tp = tp + 1
tpbuf.append(lines[16:26])
lines = f.readline()
f.close()
return tp ,tpbuf
tp1 ,tpbuf1 = analysis(f)
tp2 ,tpbuf2 = analysis(f1)
tp3 ,tpbuf3 = analysis(f2)
num = 0
num1 = 0
for i in range(1,tp1):
for j in range(1,tp2):
if tpbuf1[i] == tpbuf2[j]:
num = num + 1
for i in range(1,tp1):
for j in range(1,tp3):
if tpbuf1[i] == tpbuf3[j]:
num1 = num1 + 1
print('percent: {:.0%}'.format(num/tp2))
print('percent: {:.0%}'.format(num1/tp3))
```