LinuxOS Project3
=
###### tags: `Course - Linux OS`
## 小組名單 - 第20組
* 108502532 丁麒源
* 108502533 廖宥霖
* 108502530 曹鈞翔
## Kernel and OS version
:::info
Kernel: Linux-5.15.68
OS: Ubuntu-22.04 64bit (WSL)
:::
## Question 1
* Write a new system call ==int get_CPU_number()== so that a process can use it to get the number of the CPU that executes it.
```c=
//prototype of the new system call is as follows:
int get_CPU_number()
```
* You need to write a program to prove the effectiveness of your new system call.
### `cpu` in `task_struct`
We find `cpu` in `task_struct` in `sched.h` (line 753)
```c=
#ifdef CONFIG_THREAD_INFO_IN_TASK
/* Current CPU: */
unsigned int cpu;
#endif
```
### Add new system call
Use `cpu` which found in `task_struct`
```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(get_CPU_number)
{
unsigned int cpu_number = -1;
cpu_number = current->cpu;
return cpu_number;
}
```
### Linux built-in system call `getcpu(2)`
`getcpu` - determine CPU and NUMA node on which the calling thread is running.
```c=
#define _GNU_SOURCE/* See feature_test_macros(7) */
#include <sched.h>
int getcpu(unsigned int *cpu, unsigned int *node);
/*On success, 0 is returned. On error, -1 is returned.*/
```
### Test Program
Compare the return value of new system call and built-in system call.
```c=
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <syscall.h>
#define __NR_GET_CPU_NUMBER 449
int main()
{
unsigned int newcpu, cpu, node;
newcpu = syscall(__NR_GET_CPU_NUMBER);
if(newcpu==-1)
printf("new syscall error\n");
else
printf("CPU #%u is runnig this program (found by new syscall)\n", newcpu);
if(syscall(SYS_getcpu, &cpu, &node, NULL)==-1)
printf("getcpu error\n");
else
printf("CPU #%u of node #%u is runnig this program (found by builtin syscall)\n", cpu, node);
return 0;
}
```
### 測試結果

:::success
The number of CPU we found by two system call are the same. And the number is between 0 to 16 depend on our test computer.
:::
## Question 2
* Write a new system call ==start_to_count_number_of_process_switches()== so that a process can use it to begin to count the number of process switches the process makes. Besides, write another new system call ==int stop_to_count_number_of_process_switches()== so that a process can use it to stop to count the number of process switches the process makes and return the number of process switches the process makes.
```c=
//prototype of the new system call is as follows:
void start_to_count_number_of_process_switches()
int stop_to_count_number_of_process_switches()
```
* You need to write a CPU-bound program and I/O-bound program to counter the number of process switches the programs make.
### Add new field in `task_struct`
為了計算process switch的次數,我們在`include/linux/sched.h`中的`task_struct`結構中新增一個欄位
```c=
struct task_struct {
unsigned int num_of_process_switches;
};
```

要注意的是新增的部分要放在`randomized_struct_fields_end`之前
### Add new code in __scedule
在`kernel/sched/core.c`的`__scedule`中紀錄了與process switch相關的做法
```c= !
static void __sched notrace __schedule(unsigned int sched_mode)
{
struct task_struct *prev, *next;
unsigned long *switch_count;
unsigned long prev_state;
struct rq_flags rf;
struct rq *rq;
int cpu;
... 略...
rq_lock(rq, &rf);
smp_mb__after_spinlock();
/* Promote REQ to ACT */
rq->clock_update_flags <<= 1;
update_rq_clock(rq);
switch_count = &prev->nivcsw;
/*
* We must load prev->state once (task_struct::state is volatile), such
* that:
*
* - we form a control dependency vs deactivate_task() below.
* - ptrace_{,un}freeze_traced() can change ->state underneath us.
*/
prev_state = READ_ONCE(prev->__state);
if (!(sched_mode & SM_MASK_PREEMPT) && prev_state)
{
if (signal_pending_state(prev_state, prev))
{
WRITE_ONCE(prev->__state, TASK_RUNNING);
}
else
{
prev->sched_contributes_to_load =
(prev_state & TASK_UNINTERRUPTIBLE) &&
!(prev_state & TASK_NOLOAD) &&
!(prev->flags & PF_FROZEN);
if (prev->sched_contributes_to_load)
rq->nr_uninterruptible++;
deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK);
if (prev->in_iowait)
{
atomic_inc(&rq->nr_iowait);
delayacct_blkio_start();
}
}
switch_count = &prev->nvcsw;
}
next = pick_next_task(rq, prev, &rf);
clear_tsk_need_resched(prev);
clear_preempt_need_resched();
#ifdef CONFIG_SCHED_DEBUG
rq->last_seen_need_resched_ns = 0;
#endif
if (likely(prev != next))
{
rq->nr_switches++;
/*
* RCU users of rcu_dereference(rq->curr) may not see
* changes to task_struct made by pick_next_task().
*/
RCU_INIT_POINTER(rq->curr, next);
/*
* The membarrier system call requires each architecture
* to have a full memory barrier after updating
* rq->curr, before returning to user-space.
*
* Here are the schemes providing that barrier on the
* various architectures:
* - mm ? switch_mm() : mmdrop() for x86, s390, sparc, PowerPC.
* switch_mm() rely on membarrier_arch_switch_mm() on PowerPC.
* - finish_lock_switch() for weakly-ordered
* architectures where spin_unlock is a full barrier,
* - switch_to() for arm64 (weakly-ordered, spin_unlock
* is a RELEASE barrier),
*/
++*switch_count;
migrate_disable_switch(rq, prev);
psi_sched_switch(prev, next, !task_on_rq_queued(prev));
trace_sched_switch(sched_mode & SM_MASK_PREEMPT, prev, next);
/*Add by user*/
next->num_of_process_switches++;
/* Also unlocks the rq: */
rq = context_switch(rq, prev, next, &rf);
}
else
{
rq->clock_update_flags &= ~(RQCF_ACT_SKIP | RQCF_REQ_SKIP);
rq_unpin_lock(rq, &rf);
__balance_callbacks(rq);
raw_spin_rq_unlock_irq(rq);
}
}
```
其中可以注意到`rq = context_switch(rq, prev, next, &rf);`這行指令
我們在其上加入`next->num_of_process_switches++;`
就可以計算當前process swap out的次數。
而在上面的nivcsw和nvcsw,則分別代表了struct rusage中
自願context switch和強制context switch
若是將這兩個數相加,應該可以得到近似num_of_process_switches的答案
### struct rusage
```c=
struct rusage {
struct timeval ru_utime; /* user CPU time used */
struct timeval ru_stime; /* system CPU time used */
long ru_maxrss; /* maximum resident set size */
long ru_ixrss; /* integral shared memory size */
long ru_idrss; /* integral unshared data size */
long ru_isrss; /* integral unshared stack size */
long ru_minflt; /* page reclaims (soft page faults) */
long ru_majflt; /* page faults (hard page faults) */
long ru_nswap; /* swaps */
long ru_inblock; /* block input operations */
long ru_oublock; /* block output operations */
long ru_msgsnd; /* IPC messages sent */
long ru_msgrcv; /* IPC messages received */
long ru_nsignals; /* signals received */
long ru_nvcsw; /* voluntary context switches */
long ru_nivcsw; /* involuntary context switches */
};
```
```c=
#include <sys/time.h>
#include <sys/resource.h>
int getrusage(int who, struct rusage *usage);
/*On success, 0 is returned. On error, -1 is returned.*/
```
__`ru_nvcsw`__:
The number of times a context switch resulted due to a process voluntarily giving up the processor before its time slice was completed (usually to await availability of a resource).
__`ru_nivcsw`__:
The number of times a context switch resulted due to a higher priority process becoming runnable or because the current process exceeded its time slice.
### Add new 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(start_to_count_number_of_process_switches){
current->num_of_process_switches=0;
}
```
```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(stop_to_count_number_of_process_switches){
return current->num_of_process_switches;
}
```
### I/O bound程式
```c=
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <syscall.h>
#include <sys/time.h>
#include <sys/resource.h>
#define __NR_start_to_count_number_of_process_switches 450
#define __NR_stop_to_count_number_of_process_switches 451
#define ON 1
#define OFF 0
void main()
{
int a,b;
a=0;
b=0;
int myswitch = ON;
struct rusage rup;
struct timeval tv;
gettimeofday(&tv,NULL);
int start_time=tv.tv_sec;
int current_time;
printf("start time: %d\n", start_time);
syscall(__NR_start_to_count_number_of_process_switches);
while(myswitch==ON)
{
sleep(0.01);
printf("[%d ]",b++);
gettimeofday(&tv,NULL);
current_time=tv.tv_sec;
if ((current_time-start_time)>=120){
myswitch=OFF;
}
}
a=syscall(__NR_stop_to_count_number_of_process_switches);
getrusage(RUSAGE_SELF, &rup);
long total_switch_time = rup.ru_nvcsw + rup.ru_nivcsw;
printf("\n%d times process switches are made.(current->num_of_process_switches)\n",a);
printf("%ld times process switches are made.(RUSAGE)\n",total_switch_time);
}
```
### CPU bound程式
```c=
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <syscall.h>
#include <sys/time.h>
#include <sys/resource.h>
#define __NR_start_to_count_number_of_process_switches 450
#define __NR_stop_to_count_number_of_process_switches 451
#define ON 1
#define OFF 0
void main()
{
int a;
int myswitch=ON;
float b=0;
struct rusage rup;
struct timeval tv;
gettimeofday(&tv,NULL);
int start_time=tv.tv_sec;
int current_time;
printf("start time: %d\n", start_time);
syscall(__NR_start_to_count_number_of_process_switches);
while(myswitch==ON)
{
b=b+1;
gettimeofday(&tv,NULL);
current_time=tv.tv_sec;
if ((current_time-start_time)>=120){
myswitch=OFF;
}
}
a=syscall(__NR_stop_to_count_number_of_process_switches);
getrusage(RUSAGE_SELF, &rup);
long total_switch_time = rup.ru_nvcsw + rup.ru_nivcsw;
printf("\n%d times process switches are made.(current->num_of_process_switches)\n",a);
printf("%ld times process switches are made.(RUSAGE)\n",total_switch_time);
}
```
### 測試結果
* I/O Bound

* CPU Bound

* CPU Bound (With another CPU-bound program running.)

## 參考資料
* linux task_struct - cpu
https://elixir.bootlin.com/linux/v5.15.68/source/include/linux/sched.h
* linux built-in getcpu(2)
https://man7.org/linux/man-pages/man2/getcpu.2.html
* process switches - getrusage
https://linux.die.net/man/2/getrusage
https://unix.stackexchange.com/questions/39342/how-to-see-how-many-context-switches-a-process-makes
https://www.cnblogs.com/iihcy/p/5105839.html
* switch_to
https://blog.csdn.net/wh8_2011/article/details/49611699
https://blog.csdn.net/chengonghao/article/details/51334705