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