# 新增自定義system call part2
如題,本次HW3也是要將自定義的system call加到kernel中,其主要步驟參考為前次HW2自己的筆記。[1]
- system call需求:收集process control block中的內容,並在不同shell之下輸出到終端。
- 環境:kernel v4.4.1[2]
### 1. system call實作

略圖,以下為實際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(¤t->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(¤t->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

定義結構。

### 3. Makefile擴充
#### 3.1 增加剛實作的source code的依賴規則

使用+=附增上去,而非用:=去取代。
#### 3.2 在kernel Makefile新增檢索路徑

### 4. 修改system call table (handler table)

### 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;
}
```

圖一. user, system time皆為0

圖二. user time有值

圖三 system time有值
### bash執行

### sh執行

---
### 錯誤紀錄

原本是用timespec_to_ns去轉換stime,後來看到是u64類型,所以直接捨用。

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