---
# System prepended metadata

title: Linux OS Project 3 Write Up

---

# Linux OS Project 3 Write Up
第24組，組員：黃詩云、羅傳郡、林孟玄
## Kernel 與 Ubuntu 版本
Linux Kernel：6.0.6
Ubuntu：18.04
## 第一題
### User Space Code
呼叫我們所新增的system call得到我們的cpu number, 再用linux內建的getcpu函式做驗證
```C=
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <syscall.h>
#include <sys/sysinfo.h>

int main(){

    int cpu=-1;
    cpu=syscall(336);
    printf("cpu number (syscall) %d\n", cpu);

    unsigned node;
    syscall(SYS_getcpu, cpu, node, NULL);
    printf("cpu number (default) %u\n", cpu);

    return 0;
}
```
### Kernel Space Code
從/usr/src/linux-6.0.8/include/linux/sched.h找到task_struct, 接著在task_struct中找到struct thread_info

再到/arch/x86/include/asm/thread_info.h中的struct thread_info找到u32 cpu這個屬性

最後用current->thread_info.cpu得到我們想要的資訊
```C=
#include <linux/kernel.h>
#include <linux/syscalls.h>

SYSCALL_DEFINE0(getcpunumber){
    int cpu_number=current->thread_info.cpu;
    return cpu_number;
}
```
## 執行結果截圖
![](https://imgur.com/HmQksxC.png)




## 第二題
### User Space Code
linux kernel裡process進入wait state分為兩種情況，一種情況是主動睡眠就是sleep這種方式進入，另一種是被動，通常是等待某種資源。

這兩種情況，process都會讓出CPU，但是主動睡眠是可以被信號喚醒的，喚醒後就會進入ready state，這時候還是沒有真正得到CPU來運行，等到沒有更高優先級的process，就會進入running state

所以我們利用sleep去模擬處理I/O的時間


I/O intensive
```C=
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <syscall.h>
#include <sys/time.h>

void main(){
    int total=0;
    int count=0;
    struct timeval t;
    gettimeofday(&t, NULL);
    int start_time=t.tv_sec;
    int current_time;
  
    syscall(337);
    while(1)
    {
        sleep(0.01);
        printf("%d\n", count++);
        gettimeofday(&t, NULL);
        current_time=t.tv_sec;
        if((current_time-start_time)>=120)
        {    
            break;
        }
    }
    total=syscall(338);
    printf("the total of context switch %d\n", total);
}
```
cpu intensive
```C=
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <syscall.h>
#include <sys/time.h>
                   
void main()
{            
    int total;
    int count=0;                
    struct timeval t;
    gettimeofday(&t, NULL);
    int start_time=t.tv_sec;                             
    int current_time;
    
    syscall(337);
    while(1)
    {  
        printf("%d\n", count++);
        gettimeofday(&t, NULL);
        current_time=t.tv_sec;     
        if((current_time-start_time)>=120)
        {    
            break;
        }
    }
    total=syscall(338);
    printf("the total of context switch %d\n", total);
}
```


### Kernel Space Code
我們先去usr/src/linux-6.0.6/kernel/sched/core.c找到static void __sched notrace __schedule, 接著在呼叫context_switch這個函式之前新增我們計算次數的變數next->process_switches_count++
再來新增兩個system call分別為開始計算以及結束計算
```C=
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include <linux/init_task.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/slab.h>

SYSCALL_DEFINE0(startcountswitch){
    current->process_switches_count=0;
    return;
}
```
```C=
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include <linux/init_task.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/slab.h>

SYSCALL_DEFINE0(stopcountswitch){
    return current->process_switches_count;
}
```
## 執行結果截圖
I/O intensive
![](https://imgur.com/SaImBMf.png)
CPU intensive
![](https://imgur.com/eiXRgiR.png)

從結果得知I/O intensive遠比CPU intensive的context switch還多


## 
## 補充
### 1.sleep()

```C=
unsigned int sleep(unsigned int seconds);
```
sleep()可以讓Process主動主動睡眠
以下是由kernel實作的code
```C=
asmlinkage long sys_nanosleep(struct timespec __user *rqtp, struct timespec __user *rmtp)
{
	struct timespec t;
	unsigned long expire;
	long ret;

	expire = timespec_to_jiffies(&t) + (t.tv_sec || t.tv_nsec);
	current->state = TASK_INTERRUPTIBLE;
	expire = schedule_timeout(expire);

}
```
算出超過的時間, state改為TASK_INTERRUPTIBLE, 再呼叫schedule_timeout
接著新增一個timer, 然後呼叫system call, Process進到waiting state

等到一定的時間後，Process會wake。

waiting state是TASK_INTERRUPTIBLE, 可中斷
這種狀態的Process可以被喚醒, 而TASK_UNINTERRUPTIBLE是不能被信號喚醒的。
```C=
fastcall signed long __sched schedule_timeout(signed long timeout)
{
	struct timer_list timer;
	unsigned long expire;
	// 算出超過時間
	expire = timeout + jiffies;

	init_timer(&timer);
	// 超過時間
	timer.expires = expire;
	timer.data = (unsigned long) current;
	
	timer.function = process_timeout;
	// 增加timer
	add_timer(&timer);
	
	schedule();
	// 删除timer
	del_singleshot_timer_sync(&timer);
    
	timeout = expire - jiffies;

 out:
	return timeout < 0 ? 0 : timeout;
}
```
process_timeout函式
```C=
static void process_timeout(unsigned long __data)
{
	wake_up_process((task_t *)__data);
}
```

### 2.gettimeofdate()
gettimeofdate()的實作方式先傳入struct timeval *tv和struct timezone *tz, 不過現在第二個參數填NULL即可, 接著函式裡會呼叫一個system call, sys_gettimeofday
```C=
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
	int ret = sys_gettimeofday(tv, tz);

	if (ret < 0) {
		SET_ERRNO(-ret);
		ret = -1;
	}
	return ret;
}
```

```C=
asmlinkage long sys_gettimeofday(struct __kernel_old_timeval __user *tv,
				struct timezone __user *tz);
```
### 3. Process切換
當中斷發生時會呼叫schedule()函式, 接著schedule()函式會呼叫next = pick_next_task(rq, prev), 所做的工作就是排班演算法, 選擇要執行的下一個Process

接著, 根據排班演算法得到要執行的Process後，呼叫context_switch(rq, prev, next), 完成進程上下文切換

其中, switch_to(prev,next, prev), 會保存Process當時的變數值到暫存器
```C=
#define switch_to(prev, next, last)                             
do {                                                            
    unsigned long ebx, ecx, edx, esi, edi;                      
                                                                
    asm volatile("pushfl\n\t"               /* save    flags */ 
             "pushl %%ebp\n\t"              /* save    EBP   */ 
             "movl %%esp,%[prev_sp]\n\t"    /* save    ESP   */ 
             "movl %[next_sp],%%esp\n\t"    /* restore ESP   */ 
             "movl $1f,%[prev_ip]\n\t"      /* save    EIP   */ 
             "pushl %[next_ip]\n\t"         /* restore EIP   */ 
             __switch_canary                                    
             __retpoline_fill_return_buffer                     
             "jmp __switch_to\n"            /* regparm call  */ 
             "1:\t"                                             
             "popl %%ebp\n\t"               /* restore EBP   */ 
             "popfl\n"                      /* restore flags */ 
                                                                
                                                                
             : [prev_sp] "=m" (prev->thread.sp),                
               [prev_ip] "=m" (prev->thread.ip),                
               "=a" (last),                                     
                                                                
               
               "=b" (ebx), "=c" (ecx), "=d" (edx),              
               "=S" (esi), "=D" (edi)                           
                                                                
               __switch_canary_oparam                           
                                                                
               
             : [next_sp]  "m" (next->thread.sp),                
               [next_ip]  "m" (next->thread.ip),                
                                                                
             
               [prev]     "a" (prev),                           
               [next]     "d" (next)                            
                                                                
               __switch_canary_iparam                           
                                                                
          
            "memory");                                          
} while (0)
```