# **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的結果** ![](https://i.imgur.com/XjrVtSG.png) ![](https://i.imgur.com/XQ50asT.png) 在測試碼上我們先讓process sleep 20秒,在此期間透過*ps aux*指令列出所有執行中Process,並使用*taskset*指令使此Process只能在特定CPU跑,我們依序對四顆CPU進行測試,測試結果表示我們回傳的CPU number與我們指定的CPU是相符的,這證明我們的syscall是有效的。 ### **syscall_2&syscall_3 執行結果** ![](https://i.imgur.com/a0nlrYy.png) ![](https://i.imgur.com/9pj4Prc.png) **經由以上執行結果,我們可以發現兩個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/>