# Linux Project3
### 組員名單
* 110525015 劉松靄
* 111525013 何雋永
* 109502547 楊晴方
### 系統環境
* 作業系統: ubuntu 18.04
* 原本 Kernel 版本:5.4.0-131-generic
* 更新 Kernel 版本 : 6.0.6
### Q1
#### kernel code
```
#include <linux/kernel.h>
#include <linux/syscalls.h>
SYSCALL_DEFINE0(getCpuNumber){
return (current->thread_info).cpu;
}
```
由於 CONFIG_THREAD_INFO_IN_TASK = Y,所以 thread_info 在 task_struct 中,圖示如下。

#### user code
```
#include <linux/kernel.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <stdio.h>
int main(){
unsigned cpu, node;
printf("get_cpu_number: %u\n", syscall(338));
syscall(SYS_getcpu, &cpu, &node, NULL);
printf("Default function: %u\n", cpu);
return 0;
}
```
#### Result
```
jack@jack-ASUSPRO-D840MB-M840MB:~$ ./p3
get_cpu_number: 0
Default function: 0
jack@jack-ASUSPRO-D840MB-M840MB:~$ ./p3
get_cpu_number: 0
Default function: 0
jack@jack-ASUSPRO-D840MB-M840MB:~$ ./p3
get_cpu_number: 4
Default function: 4
```
### Q2
#### kernel code
* ##### task_struct
```
#ifdef CONFIG_RV
/*
* Per-task RV monitor. Nowadays fixed in RV_PER_TASK_MONITORS.
* If we find justification for more monitors, we can think
* about adding more or developing a dynamic method. So far,
* none of these are justified.
*/
union rv_task_monitor rv[RV_PER_TASK_MONITORS];
#endif
int context_switch_count; // <---這行是新增的
/*
* New fields for task_struct should be added above here, so that
* they are included in the randomized portion of task_struct.
*/
randomized_struct_fields_end
/* CPU-specific state of this task: */
struct thread_struct thread;
/*
* WARNING: on x86, 'thread_struct' contains a variable-sized
* structure. It *MUST* be at the end of 'task_struct'.
*
* Do not put anything below here!
*/
};
```
* ##### context_switch
```
rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
prepare_lock_switch(rq, next, rf);
/* Here we just switch the register state and the stack. */
prev->context_switch_count++; // <---這行是新增的
next->context_switch_count++; // <---這行是新增的
switch_to(prev, next, prev);
barrier();
return finish_task_switch(prev);
}
```

計算由 running 轉成 ready(把 cpu 給別人)和從 ready 轉 running(把 cpu 拿回來)的數目
* ##### syscall
```
#include <linux/kernel.h>
#include <linux/syscalls.h>
SYSCALL_DEFINE0(start_to_count_number_of_process_switches){
current->context_switch_count = 0;
return 0;
}
SYSCALL_DEFINE0(stop_to_count_number_of_process_switches){
return current->context_switch_count;
}
```
#### user code
* ##### IO_bound
```
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <linux/kernel.h>
#include <unistd.h>
#include <sys/time.h>
#define ON 1
#define OFF 0
#define start_to_count_number_of_process_switches 339
#define stop_to_count_number_of_process_switches 340
int main(){
int cnt = 0, b = 0;
int __switch = ON;
long start_sec, end_sec;
struct timeval tv;
syscall(start_to_count_number_of_process_switches);
gettimeofday(&tv, NULL);
start_sec = tv.tv_sec;
while(__switch==ON){
sleep(0.01);
printf("[%d ]", b++);
gettimeofday(&tv, NULL);
end_sec = tv.tv_sec;
if(end_sec-start_sec >= 120)
__switch = OFF;
}
cnt = syscall(stop_to_count_number_of_process_switches);
printf("\nDuring the past 2 minutes the process makes %d times process switches.\n", cnt);
return 0;
}
```
* ##### Results
```
[2025675 ][2025676 ][2025677 ][2025678 ][2025679 ][2025680 ]
During the past 2 minutes the process makes 4050978 times process switches.
```
* ##### CPU_bound
```
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <linux/kernel.h>
#include <unistd.h>
#include <sys/time.h>
#define ON 1
#define OFF 0
#define start_to_count_number_of_process_switches 339
#define stop_to_count_number_of_process_switches 340
int main(){
int cnt = 0, b = 0;
int __switch = ON;
long start_sec, end_sec;
struct timeval tv;
syscall(start_to_count_number_of_process_switches);
gettimeofday(&tv, NULL);
start_sec = tv.tv_sec;
while(__switch==ON){
b = b+1;
gettimeofday(&tv, NULL);
end_sec = tv.tv_sec;
if(end_sec-start_sec >= 120)
__switch = OFF;
}
cnt = syscall(stop_to_count_number_of_process_switches);
printf("\nDuring the past 2 minutes the process makes %d times process switches.\n", cnt);
return 0;
}
```
* ##### Results
```
During the past 2 minutes the process makes 2182 times process switches.
```
### 補充的知識點
#### 1. fork vs. vfork vs. clone
fork
```
#ifdef __ARCH_WANT_SYS_FORK
SYSCALL_DEFINE0(fork)
{
#ifdef CONFIG_MMU
struct kernel_clone_args args = {
.exit_signal = SIGCHLD,
};
return kernel_clone(&args);
#else
/* can not support in nommu mode */
return -EINVAL;
#endif
}
#endif
```
vfork
```
#ifdef __ARCH_WANT_SYS_VFORK
SYSCALL_DEFINE0(vfork)
{
struct kernel_clone_args args = {
.flags = CLONE_VFORK | CLONE_VM,
.exit_signal = SIGCHLD,
};
return kernel_clone(&args);
}
#endif
```
clone
```
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
int __user *, parent_tidptr,
int __user *, child_tidptr,
unsigned long, tls)
{
struct kernel_clone_args args = {
.flags = (lower_32_bits(clone_flags) & ~CSIGNAL),
.pidfd = parent_tidptr,
.child_tid = child_tidptr,
.parent_tid = parent_tidptr,
.exit_signal = (lower_32_bits(clone_flags) & CSIGNAL),
.stack = newsp,
.tls = tls,
};
return kernel_clone(&args);
}
```
#### 2. pid 0 1 2 的 process
* pid 0 是 swapper process 是 pid 1、2 的 parent
* pid 1 是 init process,是所有 user space process 的祖先
* pid 2 是 kthreadd process,是所有 kernel space process 的祖先
所有的 process 只有 pid 0 是由靜態生成的,其他皆透過 kernel_clone 產生 (早期的版本是 do_fork)
#### 3. do while(0)
##### 如下所示,是 switch_to 的 macro,do while(0) 就是只執行一次,那幹嘛多此一舉 (?
```
#define switch_to(prev, next, last) \
do { \
((last) = __switch_to_asm((prev), (next))); \
} while (0)
```
##### 考慮以下情形
```
只寫重點
void a(){
cout << "a\n";
}
void b(){
cout << "b\n";
}
#define hello a();b();
int main() {
if(false)
hello
return 0;
}
```
##### 期望輸出
```
甚麼都沒有
```
##### 實際輸出
```
b
```
##### 原因如下
```
在擴展的時候 main 會變成下面那樣,所以無論如何 b() 都會執行到
if(false)
a();
b();
return 0;
```
##### 那如果我們把 code 框起來 {} 呢
```
#define hello {a();b();}
那 main 會變成下面那樣
if(false) {
a();
b();
}
return 0;
```
##### ok 看起來可以正確運行,接著考慮分號
```
#define hello {a();b();}
有 else 就出事,因為平常寫程式會加分號。
if(false)
hello <- 這邊就不能有分號QQ
```
##### 最終版本 do while(0)
```
#define hello
do {
a();b();
} while(0)
這樣就一定要加分號,不加還會報錯。
```