---
# System prepended metadata

title: Linux-Project3
tags: [linux]

---

# Linux-Project3

###### tags: `linux`

第9組 
111522030 李孟潔
111522061 鄭伊涵
111526003 張友安

## Outline
[ToC]

## Project3
- https://staff.csie.ncu.edu.tw/hsufh/COURSES/FALL2022/linux_project_3.html

## Enviroment
* VMware
* Ubuntu 16.04 Lts
* Linux Kernel 4.15.1

## Q1 Kernel Space
### 新增System call
```c=
core-y          += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ get_CPUnumber/
```  
### 新增system call到system call table
```c=
333     common  get_CPU_number          sys_get_CPU_number
```
### 定義system call的prototype
```c=
asmlinkage long sys_get_CPU_number(int* number);
```
### System call -get_CPU_number
```c=
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/sched.h>

SYSCALL_DEFINE1(get_CPU_number, unsigned int*, number){
	unsigned int cpuNum = 10;
	cpuNum = task_cpu(current);
	copy_to_user(number, &cpuNum, sizeof(unsigned int));
	printk("cpu number in kernel: %u\n", cpuNum);
	return 0;
}
```

## Q1 User space
```c=
#include <syscall.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>

int main(){
	unsigned int init_num = 5678;
	unsigned int* cpu_number = &init_num;
	syscall(333, cpu_number);
	printf("cpu number in user: %u\n", *cpu_number);
	return 0;
}
```
### Process
* **設計流程及說明：**
    在user space先初始化我們用來存放cpu number的變數為任意數字，以確保之後透過system call取得的cpu number是否有正卻執行(未初始化預設為0)，接著呼叫我們所編譯的system call(333)，該system call透過呼叫task_cpu取得當前執行的cpu number，其取得途徑如下：
    * 1.	透過參數current取得當前process的task_struct，current在kernel中被定義為get_current()，而該函式可取得當前process的thread_info，再以此取得task_struct。
    * 2.	將current作為參數引入task_cpu後，會根據是否定義CONFIG_THREAD_INFO_IN_TASK來決定從task_struct或是從task_thread_info取得cpu number。

## Q1 Result of get_CPU_number program

以下是我們執行10次的結果，可以看到cpu 0和cpu 1都有被使用到。
```c=
cya@ubuntu:~/Desktop$ gcc test.c -o test
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 0
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 0
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 1
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 0
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 0
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 0
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 1
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 0
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 0
cya@ubuntu:~/Desktop$ ./test
cpu number in user: 1
```
### Kernel message in dmesg:
在syscall裡printk以確認結果是否符合預期
```c=
[446089.547747] cpu number in kernel: 0
[446090.895565] cpu number in kernel: 0
[446091.858955] cpu number in kernel: 1
[446092.628468] cpu number in kernel: 0
[446093.720960] cpu number in kernel: 0
[446094.690959] cpu number in kernel: 0
[446095.422937] cpu number in kernel: 1
[446096.009711] cpu number in kernel: 0
[446096.674835] cpu number in kernel: 0
[446097.295066] cpu number in kernel: 1
```

## Kernel Space
### /linux-4.15.1/include/linux/sched.h
在此檔案中找到struct task_struct，並且在thread_struct前面加上long cs_counter。
會在thread_struct前面加上cs_counter，而不是在最後一行加的原因，是task_struct的最後一段warning有表明thread_struct包含variable-sized structure，所以thread_struct必須是task_struct的最後一行。
```c=
/* manually add*/
        long cs_counter;
```

### /linux-4.15.1/kernel/sched/core.c
在此檔案中找到schedule函式(asmlinkage __visible void __sched schedule(void))，在struct task_struct *tsk = current; 後面加一行tsk->cs_counter++; 用來表示在這個process作context switch時，會將值+1。
```c=
/*manually add*/
tsk -> cs_counter++;
```
### 新增System call
若要新增多個system call, 不同的system call的code必須存放在不同的資料夾，若是放在相同的資料夾，則會無法新增第二個system call。
因此我們在syscall_start此資料夾裡存放start_to_count的system call，在syscall_stop此資料夾裡存放stop_to_count的system call。
```c=
core-y          += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ syscall_start/ syscall_stop/
```
### 新增system call到system call table
```c=
333     common  start_to_count          sys_start_to_count
334     common  stop_to_count           sys_stop_to_count
```

### 定義system call的prototype
```c=
asmlinkage long sys_start_to_count(void);
asmlinkage long sys_stop_to_count(void);
```

### SystemCall-start to count
此system call乃將cs_counter的值歸零
```c=
#include <linux/sched.h>
#include <asm/current.h>
#include <linux/printk.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>

SYSCALL_DEFINE0(start_to_count){
	printk("num: %ld\n", current->cs_counter);
	current -> cs_counter = 0;
	return 0;
}
```
### SystemCall-stop to count
此system call會回傳當前process的context switch次數(cs_counter)。
```c=
#include <linux/sched.h>
#include <asm/current.h>
#include <linux/printk.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>

SYSCALL_DEFINE0(stop_to_count){
	printk("%ld\n", current -> cs_counter);
	return  current -> cs_counter;
}
```




  

## User Space
### I/O-bound program
```c=
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <syscall.h>

#define ON 1
#define OFF 0

int main()
{
	long ret,a;
	int b = 0;
	int button = ON;
	unsigned long diff;
	struct timeval start;
	struct timeval end;
    
	ret = syscall(333);		//start to count
	gettimeofday(&start, NULL);
    
	while(button == ON)
	{
		sleep(5);
		printf("[%d ]\n",b++);
		gettimeofday(&end, NULL);
		diff = ((end.tv_sec) - (start.tv_sec));
		if(diff / 60 >= 2)
		{
			button = OFF;
			break;
		}
	}

	a = syscall(334);		//stop to count
	printf("During the past 2 minutes the process makes %ld times process switches.\n",a);

	return 0;
}
```
### Process
* **設計流程**
    * 首先，第一個是I/O-bound的user space program。我們會在迴圈簡單印出一個遞增的數字，每次迴圈都會讓程式先sleep 5秒。
    * 在程式正式進入迴圈之前，我們會先叫我們所編譯的system call(333)，該system call是正式開始計算程式context switches的次數(start to count)。
    * 我們用gettimeofday去得到我們想要的區間程式執行的時間，如果程式執行2分鐘，就會讓迴圈停止。
    * 迴圈結束，我們會叫另一個我們編譯的system call(334)，回傳我們在task_struct所新增的cs_counter欄位。
### Result of I/O-bound program
![](https://i.imgur.com/PsvsMkL.png)
* **說明**
    * 因為是I/O-bound，所以會有輸出。進行的輸出共有24次，也代表程式進入sleep也是24次。
    * system call最終回傳的context switches次數也是24次。根據我們的觀察，程式在呼叫sleep時會讓程式進入TASK_INTERRUPTIBLE的狀態，然後呼叫schedule_timeout再進入schedule執行context switches。
    * 所以在此程式中，迴圈進行了24次，sleep也呼叫了24次，所以context switches進行了24次。
    * 以下是sleep在kernel中的code。
```c=
static inline void sleep(unsigned sec)
{
	current->state = TASK_INTERRUPTIBLE;
	schedule_timeout(sec * HZ);
}
```
### CPU-bound program
```c=
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>

#define ON 1
#define OFF 0

int main()
{
	long ret,a;
	int b = 0;
	int button = ON;
	unsigned long diff;
	struct timeval start;
	struct timeval end;
	
	ret = syscall(333);
	gettimeofday(&start, NULL);

	while(button == ON)
	{
		b = b + 1;
		gettimeofday(&end, NULL);
		diff = ((end.tv_sec) - (start.tv_sec));
		if(diff / 60 >= 2)
		{
			button = OFF;
			break;
		}
	}

	a = syscall(334);
	printf("During the past 2 minutes the process makes %ld times process switches.\n",a);

	return 0;
}
```
### Process
* **設計流程**
    * 跟上面I/O-bound很相似，只是不需要有output以及sleep在迴圈裡，而是進行CPU的運算(b=b+1)。
    * 其餘取得程式執行時間，以及呼叫system call的部分都與I/O-bound一樣。先叫start to count，結束時叫stop to count，並且回傳context switches的次數。
### Result of CPU-bound program
![](https://i.imgur.com/eulU575.png)
* **說明**
    * 跟上面I/O-bound相比，CPU-bound的context switches次數就多很多。
    * 根據我們的觀察，我們是假設在做CPU運算時，所占用的CPU資源會比較多，因為花費大部分時間在CPU上，scheduler為了平均分配每個process的CPU資源，所以會一直進行context switches，導致CPU-bound的次數相對上面的多出很多。



## Reference
[Task_struct](https://elixir.bootlin.com/linux/v4.15.1/source/include/linux/sched.h#L520)
[Task_struct](https://elixir.bootlin.com/linux/v4.15.1/source/kernel/sched/core.c#L3427)
[Warning of thread_struct](https://elixir.bootlin.com/linux/v4.15.1/source/include/linux/sched.h#L1101)
[IO & CPU](https://stackoverflow.com/questions/868568/what-do-the-terms-cpu-bound-and-i-o-bound-mean)
