# **Linux OS Project 3**
<br/>
<br/>
<br/>
**第1組 111522015 陳萬鈿 110522156 陳明威**
Goal
---
***1**.編寫syscall 功能為取得當前Process使用中的CPU的號碼。*
***2**.編寫syscall 透過此syscall 開始計算Process的context switch 次數。*
***3**.編寫syscall 透過此syscall 取得Process的context switch 次數*
<br/>
Method
---
**1.在看過Process的Kernel後,我們可以找到一個function取得執行Process CPU number,這個function在宣告task_struct結構的標頭檔中,*task_cpu()*,我們只需要透過current取得當前Process task_struct指標傳入,這個function會回傳CPU number。**
<br/>
```C=
static inline unsigned int task_cpu(const struct task_struct *p)
{
return task_thread_info(p)->cpu;
}
```
/include/linux/sched.h
**2&3.在進行context switch時是透過core.c中的schedule函數進行的,他會使用context_switch()進行切換,他帶的參數有prev,next將CPU切給next,我們可以在他切換之前將cswitch加一透過這個方法紀錄Process cotext switch number,因為拿到CPU跟放棄CPU的兩個Process都算是做了context switch 故我們兩者皆計算入內。**
<br/>
```C=
static void __sched notrace __schedule(bool preempt)
{
struct task_struct *prev, *next;
unsigned long *switch_count;
struct rq *rq;
int cpu;
cpu = smp_processor_id();
rq = cpu_rq(cpu);
rcu_note_context_switch();
prev = rq->curr;
/*
* do_exit() calls schedule() with preemption disabled as an exception;
* however we must fix that up, otherwise the next task will see an
* inconsistent (higher) preempt count.
*
* It also avoids the below schedule_debug() test from complaining
* about this.
*/
if (unlikely(prev->state == TASK_DEAD))
preempt_enable_no_resched_notrace();
schedule_debug(prev);
if (sched_feat(HRTICK))
hrtick_clear(rq);
/*
* Make sure that signal_pending_state()->signal_pending() below
* can't be reordered with __set_current_state(TASK_INTERRUPTIBLE)
* done by the caller to avoid the race with signal_wake_up().
*/
smp_mb__before_spinlock();
raw_spin_lock_irq(&rq->lock);
lockdep_pin_lock(&rq->lock);
rq->clock_skip_update <<= 1; /* promote REQ to ACT */
switch_count = &prev->nivcsw;
if (!preempt && prev->state) {
if (unlikely(signal_pending_state(prev->state, prev))) {
prev->state = TASK_RUNNING;
} else {
deactivate_task(rq, prev, DEQUEUE_SLEEP);
prev->on_rq = 0;
/*
* If a worker went to sleep, notify and ask workqueue
* whether it wants to wake up a task to maintain
* concurrency.
*/
if (prev->flags & PF_WQ_WORKER) {
struct task_struct *to_wakeup;
to_wakeup = wq_worker_sleeping(prev, cpu);
if (to_wakeup)
try_to_wake_up_local(to_wakeup);
}
}
switch_count = &prev->nvcsw;
}
if (task_on_rq_queued(prev))
update_rq_clock(rq);
next = pick_next_task(rq, prev);
clear_tsk_need_resched(prev);
clear_preempt_need_resched();
rq->clock_skip_update = 0;
if (likely(prev != next)) {
rq->nr_switches++;
rq->curr = next;
++*switch_count;
prev->cswitch_number++; //被切換者
next->cswitch_number++;//切換者
trace_sched_switch(preempt, prev, next);
rq = context_switch(rq, prev, next); /* unlocks the rq */
cpu = cpu_of(rq);
} else {
lockdep_unpin_lock(&rq->lock);
raw_spin_unlock_irq(&rq->lock);
}
balance_callback(rq);
}
```
<br/>
**而每個Process都會有一個專屬的task_struct記錄著Process的狀態、記憶體位置、Process id(pid), etc.,我們可以在task_strcut中加入一個變數cswitch_number紀錄context_switch次數。**
<br/>
```C=
#endif
/* task state */
int cswitch_number;//新增這個欄位記錄著被切換幾次
int exit_state;
int exit_code, exit_signal;
int pdeath_signal; /* The signal sent when the parent dies */
unsigned long jobctl; /* JOBCTL_*, siglock protected */
/* Used for emulating ABI behavior of previous Linux versions */
unsigned int personality;
```
<br/>
**當調用到start_to_count_number_of_process_switches()這個syscall我們就將cswitch_number歸零,當調用到end_to_count_number_of_process_switches()則回傳cswitch_number。**
<br/>
Kernel & Linux version
---
**Kernel 4.4.1 + ubuntu 16.04**
**( GCC 5.4.0 + htop)**
htop install
```
sudo apt install htop
```
<br/>
Get_CPU_number test_code
---
```c=
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
sleep(20);
unsigned int k = 0;
unsigned int *k1 = &k;
syscall(548,k1);
printf("%d\n",k);
while(1){
};
return 0;
}
```
<br/>
IO-BOUND test_code
---
```c=
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <time.h>
#define ON 1
#define OFF 0
int main(){
time_t start,finish;
int b = 0;
int lock = ON;
int *temp;
int number = 0;
temp = &number;
syscall(326);
start = time(NULL);
while(lock==ON){
sleep(0.01);
printf("[%d ]",b++);
if(difftime(time(NULL),start) == 120){
lock=OFF;
};
}
syscall(327,temp);
printf("\nDuring the past 2 minutes the process make %d times process switches. \n",number);
return 0;
}
```
<br/>
CPU-BOUND test_code
---
```c=
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <time.h>
#define ON 1
#define OFF 0
int main(){
time_t start;
int b = 0;
int lock = ON;
int *temp;
int number = 0;
temp = &number;
syscall(326);
start = time(NULL);
while(lock==ON){
b=b+1;
if(difftime(time(NULL),start) == 120){
lock=OFF;
};
}
syscall(327,temp);
printf("\nDuring the past 2 minutes the process make %d times process switches. \n",number);
return 0;
}
```
<br/>
Syscall_1 code
---
```c=
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/smp.h>
#include <linux/sched.h>
#include <asm/current.h>
SYSCALL_DEFINE1(get_cpu_number,unsigned int* ,cpu){
unsigned int temp = task_cpu(current);
unsigned int *k = &temp;
copy_to_user(cpu,k,sizeof(unsigned int));
return 0;
}
```
Syscall_2 code
---
```c=
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>
#include <asm/current.h>
SYSCALL_DEFINE0(start_get_number){
current->cswitch_number = 0;
return 0;
}
```
Syscall_3 code
---
```c=
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include <asm/current.h>
SYSCALL_DEFINE1(end_get_number,unsigned int *, result){
int *temp;
int csnumber = current->cswitch_number;
temp = &csnumber;
copy_to_user(result,temp,sizeof(int));
return 0;
}
```
Result&Conclusion
---
### **首先是syscall_1的結果**


在測試碼上我們先讓process sleep 20秒,在此期間透過*ps aux*指令列出所有執行中Process,並使用*taskset*指令使此Process只能在特定CPU跑,我們依序對四顆CPU進行測試,測試結果表示我們回傳的CPU number與我們指定的CPU是相符的,這證明我們的syscall是有效的。
### **syscall_2&syscall_3 執行結果**


**經由以上執行結果,我們可以發現兩個Process context switch次數有很大的區別,其中原因在於第一個Process在經過常數+1後做了Printf的動作,因為printf屬於IO在linux中會經由以下流程printf() ---> printf() in the C library ---> write() in C library ---> write() system call in kernel.,因此CPU會切換給kernel來做IO,而第二個Process並不需要進行IO,因此我們可以發現第二個Process的context switch次數非常的少。**
<br/>