---
# System prepended metadata

title: LinuxOS Project3
tags: [Course - Linux OS]

---

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;
}
```
### 測試結果
![](https://i.imgur.com/QqpfXIz.png)
:::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;  

 };
```
![](https://i.imgur.com/cTMEPlU.png)

要注意的是新增的部分要放在`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
![](https://i.imgur.com/Os2yeGg.png)
* CPU Bound
![](https://i.imgur.com/T5QErfM.png)
* CPU Bound (With another CPU-bound program running.)
![](https://i.imgur.com/wbjkcpi.png)

## 參考資料
* 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
