---
# System prepended metadata

title: '**Linux OS Project 3**'

---

# **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/>

