]> Zhao Yanbai Git Server - kernel.git/commitdiff
添加通过TSC统计进程精确运行时间的能力
authorzhaoyanbai <zhaoyanbai@126.com>
Sat, 15 Aug 2026 00:43:58 +0000 (08:43 +0800)
committerzhaoyanbai <zhaoyanbai@126.com>
Sat, 15 Aug 2026 00:43:58 +0000 (08:43 +0800)
include/system.h
include/task.h
kernel/ap.c
kernel/fork.c
kernel/hpet.c
kernel/sched.c
kernel/system.c

index 40b982848c39868b6b6c4fede2d57b0971b8d57d..e8231d81177120e0725e18932aeb2cb4044e56b5 100644 (file)
@@ -68,6 +68,11 @@ void kfree(void* addr);
 #define disableIRQ() cli()
 #define enableIRQ() sti()
 
+extern uint64_t tsc_khz;  // TSC频率(KHZ:次/毫秒)
+uint64_t rdtsc();
+
+void udiv64(uint64_t dividend, uint64_t divisor, uint64_t* quotient, uint64_t* remainder);
+
 #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
 
 #define INT_STACK_SIZE PAGE_SIZE
index e143b6004c9d23a04cb5252e3e8517747eb3adca..731239dd3f03b866b671ef252a323f04218d8838 100644 (file)
@@ -88,9 +88,11 @@ typedef union task_union {
         int ticks_left;  // 时间片剩余
 
         // 仅用于统计
-        uint32_t st_ticks;
-        uint32_t sched_cnt;       // 被调度换上CPU的次数
-        uint32_t sched_keep_cnt;  // 时间片到了,但是没有被换出,又重新执行的次数
+        uint64_t st_ticks;
+        uint64_t st_last_exec_tsc;  // 上次被调度上CPU执行的TSC时间
+        uint64_t st_runtime_tsc;    // 总的运行时间TSC时间
+        uint32_t sched_cnt;         // 被调度换上CPU的次数
+        uint32_t sched_keep_cnt;    // 时间片到了,但是没有被换出,又重新执行的次数
 
         uint64_t magic;  // 栈溢出标志
     };
@@ -123,6 +125,7 @@ void task_set_ready(task_t* t);
 void task_set_wait();  // 只有当前进程可以调用
 
 void task_init_lists(task_t* t);
+void task_init_stats(task_t* t);
 
 #endif  // ASM
 
index 288c2bdc9137d4630e33978b95eaf0f97ce34c60..4a10353e7764ee221cd5481f558a2f18386823b8 100644 (file)
@@ -309,10 +309,66 @@ const char* task_state(unsigned int state) {
 
     return s[state];
 }
+
+static char* format_runtime_tsc(uint64_t tsc) {
+    uint64_t runtime_tsc = tsc;
+    uint64_t _ms = 0;
+    uint64_t _us = 0;
+    uint64_t _ns = 0;
+    uint64_t _remainder = 0;
+    udiv64(runtime_tsc, tsc_khz, &_ms, &_remainder);
+    udiv64(_remainder * 1000, tsc_khz, &_us, &_remainder);
+    udiv64(_remainder * 1000, tsc_khz, &_ns, NULL);
+
+    uint64_t total_seconds = 0;
+    uint64_t days = 0;
+    uint64_t hours = 0;
+    uint64_t minutes = 0;
+    uint64_t seconds = 0;
+
+    // total_seconds = ms / 1000;
+    // ms = ms % 1000
+    udiv64(_ms, 1000, &total_seconds, &_ms);
+
+    // days = total_seconds / (60 * 60 * 24);
+    udiv64(total_seconds, 60 * 60 * 24, &days, &total_seconds);
+    udiv64(total_seconds, 60 * 60, &hours, &total_seconds);
+    udiv64(total_seconds, 60, &minutes, &seconds);
+
+    static char buffer[64];
+    char tmp[16];
+
+    buffer[0] = 0;
+
+    if (days > 0) {
+        sprintf(tmp, "%lud", days);
+        strcat(buffer, tmp);
+    }
+    if (hours > 0) {
+        sprintf(tmp, "%uh", (uint32_t)hours);
+        strcat(buffer, tmp);
+    }
+    if (minutes > 0) {
+        sprintf(tmp, "%um", (uint32_t)minutes);
+        strcat(buffer, tmp);
+    }
+    if (seconds > 0) {
+        sprintf(tmp, "%us", (uint32_t)seconds);
+        strcat(buffer, tmp);
+    }
+
+    uint32_t _ms32 = (uint32_t)_ms;
+    uint32_t _us32 = (uint32_t)_us;
+    _us32 /= 100;  // 保留1位小数
+    sprintf(tmp, ".%03u.%u", _ms32, _us32);
+    strcat(buffer, tmp);
+
+    return (char*)buffer;
+}
 void print_all_tasks() {
     extern task_t* monitor_tasks[];
 
-    ap_printl(MPL_TASK_TITLE, "         NAME      STATE LT/PI REASON     TICKS     SCHED     KEEP");
+    ap_printl(MPL_TASK_TITLE, "         NAME      STATE LT/PI REASON     TICKS     RUNTIME");
 
     for (int i = 0; i < 10; i++) {
         task_t* p = monitor_tasks[i];
@@ -321,7 +377,9 @@ void print_all_tasks() {
             continue;
         }
 
-        ap_printl(MPL_TASK_0 + p->pid, "%08x %-6s:%u %s %02d/%02u %-10s %-9u %-9u %-9u",
+        char* runtime_str = format_runtime_tsc(p->st_runtime_tsc);
+
+        ap_printl(MPL_TASK_0 + p->pid, "%08x %-6s:%u %s %02d/%02u %-10s %-9lu %16s",
                   p,                     //
                   p->name,               //
                   p->pid,                //
@@ -330,8 +388,7 @@ void print_all_tasks() {
                   p->priority,           //
                   p->reason,             //
                   p->st_ticks,           //
-                  p->sched_cnt,          //
-                  p->sched_keep_cnt      //
+                  runtime_str            //
         );
     }
 }
index dd36f00e6cb2759d02f071c72b46c864acb1acdb..b51ab4751538d95cf96c971f703066cc6b9ecc5d 100644 (file)
@@ -32,6 +32,7 @@ int do_fork(pt_regs_t* regs, unsigned long flags) {
     tsk->state = TASK_INITING;
 
     task_init_lists(tsk);
+    task_init_stats(tsk);
 
     unsigned long iflags;
     irq_save(iflags);
@@ -85,9 +86,6 @@ int do_fork(pt_regs_t* regs, unsigned long flags) {
     tsk->priority = current->priority;
 
     tsk->ticks_left = TASK_TICKS_PER_QUANTUM;
-    tsk->st_ticks = 0;
-    tsk->sched_cnt = 0;
-    tsk->sched_keep_cnt = 0;
 
     // for switch_to
     tsk->eip = child_regs->eip;
index 665bf94c5d7b1b10878ff6d4c8f23473e0b26c9d..7a0d2e0fdff481d647dcde93986d728683773c1b 100644 (file)
@@ -194,6 +194,38 @@ bool hpet_calibration_end(uint32_t timn) {
     return false;
 }
 
+void hpet_calibrate_tsc(uint32_t hz) {
+    const uint32_t timn = 0;
+
+    hpet_disable();
+
+    hpet_prepare_calibration(timn, hz);
+
+    hpet_enable();
+
+    uint64_t tsc_t0 = rdtsc();
+
+    while (!hpet_calibration_end(timn)) {
+        asm("pause");
+    }
+
+    uint64_t tsc_t1 = rdtsc();
+
+    assert(tsc_t1 > tsc_t0);
+
+    uint64_t tsc_delta = tsc_t1 - tsc_t0;
+
+    // HPET精确等待T秒,T = 1/hz,则T秒内TSC的计数为tsc_delta
+    // tsc_dela = tsc频率(次/秒) * T秒
+    // 所以 tcs频率(HZ:次/秒)= tsc_delta / T秒 = tsc_delta * hz
+    // 所以 tcs频率(KHZ:次/毫秒) = tsc_delta * hz / 1000
+    //
+    // tsc_khz = tsc_delta * hz / 1000;
+    udiv64(tsc_delta * hz, 1000, &tsc_khz, NULL);
+
+    printk("TSC frequency: %u KHz\n", tsc_khz);
+}
+
 void hpet_init() {
     assert(hpet_use_phys_addr_index < sizeof(hpet_phys_addrs) / sizeof(hpet_phys_addrs[0]));
     hpet_phys_addr = hpet_phys_addrs[hpet_use_phys_addr_index];
@@ -219,4 +251,7 @@ void hpet_init() {
     printk("HPET Configuration: 0x%08x%08x\n", (uint32_t)(config >> 32), (uint32_t)config);
     printk("HPET enabled: %s\n", (config & (1ULL << 0)) ? "Y" : "N");
     printk("HPET legacy replacement: %s\n", (config & (1ULL << 1)) ? "Y" : "N");
+
+    // 用HPET等待 1秒*1000/10=100ms 的时间来校准TSC频率
+    hpet_calibrate_tsc(10);
 }
index ddbb8aaa4609f81eea134e46469096df0b086353..4720a5a6e394d1fed0f55153e2a296caf0889c34 100644 (file)
@@ -56,14 +56,13 @@ void init_root_task() {
     root_task.reason = "root";
     root_task.priority = TASK_PRIORITY_MAX;
     root_task.ticks_left = 1;
-    root_task.st_ticks = 0;
     root_task.vma_list = NULL;
-    root_task.sched_cnt = 0;
-    root_task.sched_keep_cnt = 0;
     root_task.magic = TASK_MAGIC;
     strcpy(root_task.name, "root");
 
     task_init_lists(&root_task);
+    task_init_stats(&root_task);
+    root_task.st_last_exec_tsc = rdtsc();
 
     list_add(&root_task.list, &all_tasks);
 
@@ -316,7 +315,16 @@ void schedule() {
     clear_need_schedule();
 
     if (prev != next) {
+        uint64_t tsc = rdtsc();
+
+        prev->st_runtime_tsc += tsc - prev->st_last_exec_tsc;
+        prev->st_last_exec_tsc = 0;
+
+        assert(next->st_last_exec_tsc == 0);
+        next->st_last_exec_tsc = tsc;
+
         next->sched_cnt++;
+
         context_switch(prev, next);
     } else {
         next->sched_keep_cnt++;
@@ -373,6 +381,15 @@ void task_init_lists(task_t* t) {
     INIT_LIST_HEAD(&t->waitq_list);
 }
 
+void task_init_stats(task_t* t) {
+    assert(t != NULL);
+    t->st_ticks = 0;
+    t->st_last_exec_tsc = 0;
+    t->st_runtime_tsc = 0;
+    t->sched_cnt = 0;
+    t->sched_keep_cnt = 0;
+}
+
 ///
 
 static volatile bool _need_schedule = false;
index 45ee42377be3cae7ad3a3e496a6cb83a04a93242..04c5fba5d24387df7185d5cc4f1ee7fc2d68d785 100644 (file)
@@ -234,6 +234,47 @@ void io_mfence() {
     asm volatile("mfence" ::: "memory");
 }
 
+uint64_t tsc_khz = 0;  // TSC频率(KHZ:次/毫秒)
+
+uint64_t rdtsc() {
+    uint32_t lo, hi;
+
+    asm volatile("rdtsc;" : "=a"(lo), "=d"(hi)::"memory");
+
+    uint64_t tsc = ((uint64_t)hi << 32) | lo;
+
+    return tsc;
+}
+
+void udiv64(uint64_t dividend, uint64_t divisor, uint64_t* quotient, uint64_t* remainder) {
+    uint64_t _quotient = 0;
+    uint64_t _remainder = 0;
+
+    // 整体算法类似10进的除法,只不过换成二进制的数来算
+    // 10进制需要猜能乘几,而二进制不用猜,只有0和1,所以直接比较大小即可
+    for (int i = 63; i >= 0; i--) {
+        // 以下两行可以看成类似10进制除法的被除数从高往低试除数时,被除数被逐渐试的高位数
+        _remainder <<= 1;
+        _remainder |= (dividend >> i) & 1;
+
+        // 如果这部分高位数能比被除数大,则商的对应位为1,否则为0
+        if (_remainder >= divisor) {
+            _remainder -= divisor;
+            _quotient |= (1ULL << i);
+        } else {
+            // 商为0的比特位其实什么都不用做
+        }
+    }
+
+    if (quotient) {
+        *quotient = _quotient;
+    }
+
+    if (remainder) {
+        *remainder = _remainder;
+    }
+}
+
 paddr_t get_rcba_paddr() {
     uint32_t rcba = pci_get_rcba();