iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0
Software Development

打造 OS Kernel:從 OS in 1,000 Lines 到 xv6系列 第 29 篇

Day 29|打造 Kernel Observer:把 Process、Syscall、Scheduler 資訊整合起來

  • 分享至 

  • xImage
  •  

今天希望同一個程式可以有兩種觀察方式:平常只取得統計,需要追查時才開啟少量事件輸出。
如果每個排程與 syscall 都立即印字,工具本身就會大幅改變我們想看的行為。
所以整合的第一步不是增加更多 log,而是能夠關閉它。

把診斷輸出與計數分開

保留前面新增的計數欄位及初始化,但移除以下臨時列印與其呼叫:

  • Day 21 的 obs_created() 與呼叫
  • Day 22 的前兩次 dispatch 列印,保留 scheduled++
  • Day 23 的 finish 列印
  • Day 24 的 sleep/wake 列印,保留計數
  • Day 25 在 exec 中呼叫 viewer 的條件,保留 viewer 函式供除錯
  • Day 26 的 getpid 階段列印,保留原本一次 handler 呼叫與 syscalls++

修改後先重新編譯,避免留下未使用的 static 函式或誤刪必要宣告。
我們不是移除觀察能力,而是把即時輸出從預設路徑拿開。

先支援一個刻意小範圍的 trace

本次只追蹤 getpid,避免提供一個看似通用卻沒有控制輸出量的追蹤系統。
在 struct proc 新增 int trace_getpid;,並在 allocproc() 初始化為 0。
這個旗標由行程自己透過 syscall 設定,fork 子行程不繼承,exec 則保留,讓 wrapper 能追蹤它接著執行的程式。

在 kernel/syscall.h 新增 #define SYS_trace 25,其餘接線如下:

檔案 新增內容
kernel/syscall.c 宣告區 extern uint64 sys_trace(void);
syscalls[] [SYS_trace] = sys_trace,
user/user.h int trace(int);
user/usys.pl entry("trace");

在 kernel/sysproc.c 加入:

uint64
sys_trace(void)
{
  int enabled;
  argint(0, &enabled);
  if (enabled != 0 && enabled != 1)
    return -1;
  myproc()->trace_getpid = enabled;
  return 0;
}

在 kernel/syscall.c 原本 handler 呼叫的下一行加入:

if (num == SYS_getpid && p->trace_getpid)
  printk("trace pid=%d syscall=getpid result=%lu\n",
         p->pid, p->trapframe->a0);

這是 handler 返回後的紀錄,不涵蓋不返回的 exit,也不是全系統 syscall trace。
目前沒有印出任意指標內容或核心位址,避免把觀察工具變成不必要的資料洩漏介面。

做成使用者可操作的命令

新增 user/trace.c,並在 Makefile 的 UPROGS 加入 $U/_trace\:

#include "kernel/types.h"
#include "user/user.h"

int
main(int argc, char **argv)
{
  if (argc < 2) {
    fprintf(2, "usage: trace command [args...]\n");
    exit(1);
  }
  if (trace(1) < 0)
    exit(1);
  exec(argv[1], &argv[1]);
  trace(0);
  fprintf(2, "trace: exec failed\n");
  exit(1);
}

在 xv6 checkout 執行 make TOOLPREFIX=riscv64-linux-gnu- CPUS=1 qemu,於 xv6 Shell 依序執行:

pidprobe
trace pidprobe
pstat

第一個只印出返回值,第二個多出 getpid trace,第三個則提供結構化的自身統計。
若普通 pidprobe 仍有 Day 26 的大量階段紀錄,表示臨時 instrumentation 尚未清乾淨。

統計欄位的契約

欄位 實際意義 不能直接解讀成
scheduled scheduler 交給行程的次數 CPU 使用時間
syscalls 有效 syscall 被分派的次數 成功完成的呼叫數
sleeps 實際進入 SLEEPING sleep 函式呼叫次數
wakes wakeup 使 SLEEPING 轉成 RUNNABLE 所有原因的喚醒總數
yields 核心 yield 次數 使用者主動讓出 CPU 次數
size_bytes 行程的 sz RSS 或實體 RAM 用量

計數器在行程表格重用時歸零,exec 則保留,因為 exec 替換程式映像,不是建立新的 PID。
若要比較單一工作片段,應對同一 PID 做前後差值,不直接拿生命週期累計值比較。

Observer 也會成為被觀察系統的一部分

pstat 需要進入核心,trace 需要輸出,計數更新也會增加指令與鎖的成本。
所以測試時要記錄 trace 是否開啟,不把不同觀察設定下的數字當成等價條件。
這個版本沒有事件 ring buffer、時間戳排序或跨 CPU 原子快照,這些是可以延伸的方向,不是已經完成的功能。

本日新增 trace.c,修改旗標、handler 與 syscall 接線,並清除前面各篇的臨時列印。

day29: integrate process statistics and opt-in syscall tracing

最後一天會把 CPU、等待、記憶體與 syscall 四種行為放進同一個測試程式,用差值檢查 Observer 是否說得出一致的故事。

參考資料


上一篇
Day 28|第一次擴充 xv6 API:實作自己的 pstat System Call
系列文
打造 OS Kernel:從 OS in 1,000 Lines 到 xv6 共 29 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言