---
# System prepended metadata

title: 新增自定義system call part2
tags: [OS, course project]

---

# 新增自定義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