# 新增自定義system call part2 如題,本次HW3也是要將自定義的system call加到kernel中,其主要步驟參考為前次HW2自己的筆記。[1] - system call需求:收集process control block中的內容,並在不同shell之下輸出到終端。 - 環境:kernel v4.4.1[2] ### 1. system call實作 ![](https://hackmd.io/_uploads/BJWnJmEmp.png) 略圖,以下為實際code ``` #include<linux/sched.h> #include<linux/uaccess.h> #include<linux/types.h> #include<linux/string.h> #include<linux/kernel.h> struct prinfo { long state; /* curent state of process */ long nice; /* process nice value */ pid_t pid; /* process id */ pid_t parent_pid; /* process id of parent */ pid_t real_parent_pid; /* process id of real_parent */ pid_t youngest_child_pid; /* pid of youngest cgild */ unsigned long start_time; /* process start time */ long user_time; /* CPU time spent in user mode */ long sys_time; /* CPU time spent in systme mode */ long uid; /* user id of process owner */ char comm[16]; /* name of program executed */ }; asmlinkage long sys_getPrInfo(struct prinfo __user *info) { struct prinfo kernel_info; memset(&kernel_info, 0, sizeof(struct prinfo)); kernel_info.state=current->state; kernel_info.nice=task_nice(current); kernel_info.pid=current->pid; kernel_info.parent_pid=current->parent->pid; kernel_info.real_parent_pid=current->real_parent->pid; struct task_struct *youngest_child; youngest_child = list_first_entry_or_null(&current->children, struct task_struct, sibling); if( youngest_child ) { kernel_info.youngest_child_pid = youngest_child->pid; } else { kernel_info.youngest_child_pid = 0; } kernel_info.start_time=current->start_time; kernel_info.user_time=cputime_to_nsecs(current->utime); kernel_info.sys_time=cputime_to_nsecs(current->stime); printk(KERN_INFO "the value of utime is %lu\n", (unsigned long)current->utime); printk(KERN_INFO "the value of stime is %lu\n", (unsigned long)current->stime); kernel_info.uid=__kuid_val(task_uid(current)); get_task_comm(kernel_info.comm, current->comm); if(copy_to_user(info, &kernel_info, sizeof(struct prinfo))) { return -EFAULT; } return 0; } ``` - **asmlinkage long sys_getPrInfo(struct prinfo __user \*info)** 從user level passing argument時需要加上 **__user**。 - **memset(&kernel_info, 0, sizeof(struct prinfo));** 記得初始化字串,以防亂碼。 - **kernel_info.state=current->state;** current是kernel中的一種macro,用以指向當前執行的process 其在x86的實現於*arch/x86/include/asm/current.h*。[3][4] - **kernel_info.nice=task_nice(current);** 因為nice值不屬於task_struct的內容,所以透過task_nice()獲取該process的nice value。[5][6] - **kernel_info.real_parent_pid=current->real_parent->pid;** 一般情況下real_parent跟parent都一樣,除非遇到某些情形會導致parent被調換。 - **youngest_child = list_first_entry_or_null(&current->children, struct task_struct, sibling);** 因為採用雙向link list所以在新的children被生成的時候會將他排到list第一個元素的位置,用list_first_entry_or_null獲取該link list第一個元素。[7][8] - **kernel_info.user_time=cputime_to_nsecs(current->utime);** 獲取時間後轉換成nsec單位。[9][10] - **printk(KERN_INFO "the value of utime is %lu\n", (unsigned long)current->utime);** 因為測試過程中,time輸出都是0,所以用printk確認其正確值。 - **kernel_info.uid=__kuid_val(task_uid(current));** 從kernel level獲取uid可透過task_uid(),但此時獲取的值的類型是kuid_t,需要透過__kuid_val再轉換一次。[13][14][15] - **get_task_comm(kernel_info.comm, current);** 獲取comm。也可以透過current->comm來存取,但直接操作current的值是不太理想的方法,畢竟盡量不要去動到kernel內部。[16] - **if(copy_to_user(info, &kernel_info, sizeof(struct prinfo)))** 最後從kernel level傳遞到user level。 ### 2. 增加system call header ![](https://hackmd.io/_uploads/rJEHgmNm6.png) 定義結構。 ![](https://hackmd.io/_uploads/r1IDgmV76.png) ### 3. Makefile擴充 #### 3.1 增加剛實作的source code的依賴規則 ![](https://hackmd.io/_uploads/rymslXV76.png) 使用+=附增上去,而非用:=去取代。 #### 3.2 在kernel Makefile新增檢索路徑 ![](https://hackmd.io/_uploads/SJ7axQVXa.png) ### 4. 修改system call table (handler table) ![](https://hackmd.io/_uploads/BkRMW747p.png) ### 5. 重新編譯安裝 ``` $sudo make bzImage -j6 $sudo make modules -j6 $sudo make modules_install $sudo make install $sudo reboot ``` ### 6. 測試 test.c ``` #include<stdio.h> #include<sys/syscall.h> #include<sys/types.h> #include<string.h> struct prinfo { long state; /* curent state of process */ long nice; /* process nice value */ pid_t pid; /* process id */ pid_t parent_pid; /* process id of parent */ pid_t real_parent_pid; /* process id of real_parent */ pid_t youngest_child_pid; /* pid of youngest cgild */ unsigned long start_time; /* process start time */ long user_time; /* CPU time spent in user mode */ long sys_time; /* CPU time spent in systme mode */ long uid; /* user id of process owner */ char comm[16]; /* name of program executed */ }; int main(void) { struct prinfo info; memset(&info, 0, sizeof(struct prinfo)); syscall(327, &info); int max_name_len=20; printf("%-*s: %s\n", max_name_len, "Program Name", info.comm); printf("%-*s: %ld\n", max_name_len, "State",info.state); printf("%-*s: %ld\n", max_name_len, "Nice value", info.nice); printf("%-*s: %d\n", max_name_len, "PID", (int)info.pid); printf("%-*s: %d\n", max_name_len, "Parent PID", (int)info.parent_pid); printf("%-*s: %d\n", max_name_len, "Real Parent PID", (int)info.real_parent_pid); printf("%-*s: %d\n", max_name_len, "Youngest Child PID", (int)info.youngest_child_pid); double sec_start_time=info.start_time/1000000000; printf("%-*s: %.5f\n", max_name_len, "Start time", sec_start_time); printf("%-*s: %ld\n", max_name_len, "User time", info.user_time); printf("%-*s: %ld\n", max_name_len, "System time", info.sys_time); printf("%-*s: %ld\n", max_name_len, "UID", info.uid); return 0; } ``` ![image.png](https://hackmd.io/_uploads/ryIRjMV76.png) 圖一. user, system time皆為0 ![image.png](https://hackmd.io/_uploads/ByBx3zV7p.png) 圖二. user time有值 ![image.png](https://hackmd.io/_uploads/SyLz3GE7p.png) 圖三 system time有值 ### bash執行 ![image.png](https://hackmd.io/_uploads/r1n16GNQT.png) ### sh執行 ![image.png](https://hackmd.io/_uploads/ByJZAMVXp.png) --- ### 錯誤紀錄 ![image.png](https://hackmd.io/_uploads/Bkfclf4Qp.png) 原本是用timespec_to_ns去轉換stime,後來看到是u64類型,所以直接捨用。 ![image.png](https://hackmd.io/_uploads/rkeZ8xNQT.png) list_entry_or_null若是沒有對象則會回傳NULL,所以要新增檢查條件,以防執行時killed。 --- 1.https://hackmd.io/JN-if3EpTRm-2P6Gx1wcQQ 2.https://hackmd.io/yHcqeiGnR8KhS6PMJWtq3A?view 3.https://stackoverflow.com/questions/12434651/what-is-the-current-in-linux-kernel-source 4.http://linux.laoqinren.net/kernel/sched/current/ 5.https://linuxtv.org/downloads/v4l-dvb-internals/device-drivers/ch01s03.html 6.https://zh.wikipedia.org/zh-tw/Nice%E5%80%BC 7.https://dri.freedesktop.org/docs/drm/core-api/kernel-api.html 8.https://stackoverflow.com/questions/33092164/how-to-obtain-youngest-childs-pid-from-task-struct 9.https://stackoverflow.com/questions/22602133/i-want-to-get-execution-time-from-task-struct 10.https://lore.kernel.org/lkml/20170207140646.GA25935@lerouge/t/ 11.https://www.coins.tsukuba.ac.jp/~yas/coins/os2-2019/2020-01-10/ 12.https://zhuanlan.zhihu.com/p/435713456 13.https://stackoverflow.com/questions/15108482/how-to-get-user-of-current-process 14.https://www.kernel.org/doc/html/v4.15/security/credentials.html 15.https://stackoverflow.com/questions/14097389/how-to-get-userid-when-writing-linux-kernel-module 16.chrome-extension://bocbaocobfecmglnmeaeppambideimao/pdf/viewer.html?file=http%3A%2F%2Fwww.wenqujingdian.com%2FPublic%2Feditor%2Fattached%2Ffile%2F20180326%2F20180326205151_91573.pdf