Browse Source

使用内核线程来刷新屏幕 (#57)

* 修改了test-idr的错误

* new: 修复切换双缓冲的时候的卡顿问题

Signed-off-by: guanjinquan <[email protected]>
Co-authored-by: guanjinquan <[email protected]>
Co-authored-by: fslongjin <[email protected]>
login 2 years ago
parent
commit
efa38a7d5d

+ 2 - 1
.vscode/settings.json

@@ -144,7 +144,8 @@
         "ktest_utils.h": "c",
         "kthread.h": "c",
         "lockref.h": "c",
-        "compiler_attributes.h": "c"
+        "compiler_attributes.h": "c",
+        "timer.h": "c"
     },
     "C_Cpp.errorSquiggles": "Enabled",
     "esbonio.sphinx.confDir": ""

+ 2 - 1
kernel/driver/pci/pci.c

@@ -439,7 +439,8 @@ void pci_init()
         {
             if (ptr->Status & 0x10)
             {
-                kinfo("[ pci device %d ] class code = %d\tsubclass=%d\tstatus=%#010lx\tcap_pointer=%#010lx\tbar5=%#010lx", i, ptr->Class_code, ptr->SubClass, ptr->Status, ((struct pci_device_structure_general_device_t *)ptr)->Capabilities_Pointer, ((struct pci_device_structure_general_device_t *)ptr)->BAR5);
+                kinfo("[ pci device %d ] class code = %d\tsubclass=%d\tstatus=%#010lx\tcap_pointer=%#010lx\tbar5=%#010lx, vendor=%#08x, device id=%#08x", i, ptr->Class_code, ptr->SubClass, ptr->Status, ((struct pci_device_structure_general_device_t *)ptr)->Capabilities_Pointer, ((struct pci_device_structure_general_device_t *)ptr)->BAR5,
+                                                                                                                                                            ptr->Vendor_ID, ptr->Device_ID);
                 uint32_t tmp = pci_read_config(ptr->bus, ptr->device, ptr->func, ((struct pci_device_structure_general_device_t *)ptr)->Capabilities_Pointer);
             }
             else

+ 71 - 27
kernel/driver/video/video.c

@@ -1,17 +1,17 @@
 #include "video.h"
-#include <mm/mm.h>
+#include <common/kprint.h>
+#include <common/kthread.h>
 #include <common/printk.h>
+#include <common/spinlock.h>
+#include <common/time.h>
 #include <driver/multiboot2/multiboot2.h>
-#include <time/timer.h>
-#include <common/kprint.h>
+#include <driver/uart/uart.h>
+#include <exception/softirq.h>
 #include <mm/mm.h>
 #include <mm/slab.h>
-#include <common/spinlock.h>
-#include <exception/softirq.h>
-#include <driver/uart/uart.h>
-#include <common/time.h>
-
-
+#include <process/process.h>
+#include <sched/sched.h>
+#include <time/timer.h>
 
 uint64_t video_refresh_expire_jiffies = 0;
 uint64_t video_last_refresh_pid = -1;
@@ -19,10 +19,11 @@ uint64_t video_last_refresh_pid = -1;
 struct scm_buffer_info_t video_frame_buffer_info = {0};
 static struct multiboot_tag_framebuffer_info_t __fb_info;
 static struct scm_buffer_info_t *video_refresh_target = NULL;
+static struct process_control_block *video_daemon_pcb = NULL;
+static spinlock_t daemon_refresh_lock;
 
 #define REFRESH_INTERVAL 15UL // 启动刷新帧缓冲区任务的时间间隔
 
-
 /**
  * @brief VBE帧缓存区的地址重新映射
  * 将帧缓存区映射到地址SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE处
@@ -37,22 +38,53 @@ void init_frame_buffer()
     int reserved;
 
     video_frame_buffer_info.vaddr = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + FRAME_BUFFER_MAPPING_OFFSET;
-    mm_map_proc_page_table(global_CR3, true, video_frame_buffer_info.vaddr, __fb_info.framebuffer_addr, video_frame_buffer_info.size, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false, true, false);
+    mm_map_proc_page_table(global_CR3, true, video_frame_buffer_info.vaddr, __fb_info.framebuffer_addr,
+                           video_frame_buffer_info.size, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false, true, false);
 
     flush_tlb();
     kinfo("VBE frame buffer successfully Re-mapped!");
 }
 
 /**
- * @brief 刷新帧缓冲区
- *
+ * @brief video守护进程, 按时刷新帧缓冲区
+ * @param unused
+ * @return int
+ */
+int video_refresh_daemon(void *unused)
+{
+    // 初始化锁, 这个锁只会在daemon中使用
+    spin_init(&daemon_refresh_lock);
+    
+    for (;;)
+    {
+        if (clock() >= video_refresh_expire_jiffies)
+        {
+            video_refresh_expire_jiffies = cal_next_n_ms_jiffies(REFRESH_INTERVAL << 1);
+
+            if (likely(video_refresh_target != NULL))
+            {
+                spin_lock(&daemon_refresh_lock);
+                memcpy((void *)video_frame_buffer_info.vaddr, (void *)video_refresh_target->vaddr,
+                       video_refresh_target->size);
+                spin_unlock(&daemon_refresh_lock);
+            }
+        }
+        video_daemon_pcb->flags &= ~PROC_RUNNING;
+        sched();
+    }
+
+    return 0;
+}
+
+/**
+ * @brief 唤醒video的守护进程
  */
 void video_refresh_framebuffer(void *data)
 {
-    video_refresh_expire_jiffies = cal_next_n_ms_jiffies(REFRESH_INTERVAL << 1);
-    if (unlikely(video_refresh_target == NULL))
+    if (unlikely(video_daemon_pcb == NULL))
         return;
-    memcpy((void *)video_frame_buffer_info.vaddr, (void *)video_refresh_target->vaddr, video_refresh_target->size);
+
+    process_wakeup(video_daemon_pcb);
 }
 
 /**
@@ -62,17 +94,22 @@ void video_refresh_framebuffer(void *data)
  * true ->高级初始化:增加double buffer的支持
  * @return int
  */
-int video_reinitialize(bool level)
+int video_reinitialize(bool level) // 这个函数会在main.c调用, 保证 video_init() 先被调用
 {
     if (level == false)
         init_frame_buffer();
     else
     {
+        // 计算开始时间
+        video_refresh_expire_jiffies = cal_next_n_ms_jiffies(10 * REFRESH_INTERVAL);
+
+        // 创建video守护进程
+        video_daemon_pcb = kthread_run(&video_refresh_daemon, NULL, CLONE_FS | CLONE_SIGNAL);
+        video_daemon_pcb->virtual_runtime = 0; // 特殊情况, 最高优先级, 以后再改
+
         // 启用屏幕刷新软中断
         register_softirq(VIDEO_REFRESH_SIRQ, &video_refresh_framebuffer, NULL);
 
-        video_refresh_expire_jiffies = cal_next_n_ms_jiffies(10 * REFRESH_INTERVAL);
-
         raise_softirq(VIDEO_REFRESH_SIRQ);
     }
     return 0;
@@ -88,12 +125,16 @@ int video_set_refresh_target(struct scm_buffer_info_t *buf)
 {
 
     unregister_softirq(VIDEO_REFRESH_SIRQ);
-    int counter = 100;
-    while ((get_softirq_pending() & (1 << VIDEO_REFRESH_SIRQ)) && counter > 0)
-    {
-        --counter;
-        usleep(1000);
-    }
+    // todo: 在completion实现后,在这里等待其他刷新任务完成,再进行下一步。
+
+    // int counter = 100;
+
+    // while ((get_softirq_pending() & (1 << VIDEO_REFRESH_SIRQ)) && counter > 0)
+    // {
+    //     --counter;
+    //     usleep(1000);
+    // }
+    // kdebug("buf = %#018lx", buf);
     video_refresh_target = buf;
     register_softirq(VIDEO_REFRESH_SIRQ, &video_refresh_framebuffer, NULL);
     raise_softirq(VIDEO_REFRESH_SIRQ);
@@ -134,14 +175,17 @@ int video_init()
     video_frame_buffer_info.height = __fb_info.framebuffer_height;
     io_mfence();
 
-    video_frame_buffer_info.size = video_frame_buffer_info.width * video_frame_buffer_info.height * ((video_frame_buffer_info.bit_depth + 7) / 8);
+    video_frame_buffer_info.size =
+        video_frame_buffer_info.width * video_frame_buffer_info.height * ((video_frame_buffer_info.bit_depth + 7) / 8);
     // 先临时映射到该地址,稍后再重新映射
     video_frame_buffer_info.vaddr = 0xffff800003000000;
-    mm_map_phys_addr(video_frame_buffer_info.vaddr, __fb_info.framebuffer_addr, video_frame_buffer_info.size, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false);
+    mm_map_phys_addr(video_frame_buffer_info.vaddr, __fb_info.framebuffer_addr, video_frame_buffer_info.size,
+                     PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false);
 
     io_mfence();
     char init_text2[] = "Video driver initialized.";
     for (int i = 0; i < sizeof(init_text2) - 1; ++i)
         uart_send(COM1, init_text2[i]);
+
     return 0;
 }

+ 1 - 1
kernel/ktest/test-idr.c

@@ -33,7 +33,7 @@ static long ktest_idr_case0(uint64_t arg0, uint64_t arg1)
     idr_init(&k_idr);
     assert(k_idr.id_free_cnt == 0);
 
-    assert(idr_pre_get(&k_idr, 0) == 1);
+    assert(idr_pre_get(&k_idr, 0) == 0);
     assert(k_idr.id_free_cnt == IDR_FREE_MAX);
 
     for (int i = 1; i < 64; i++)

+ 19 - 11
kernel/lib/libUI/screen_manager.c

@@ -1,12 +1,12 @@
 #include "screen_manager.h"
-#include <common/string.h>
-#include <driver/multiboot2/multiboot2.h>
 #include <common/kprint.h>
 #include <common/spinlock.h>
-#include <mm/mm.h>
-#include <mm/slab.h>
+#include <common/string.h>
+#include <driver/multiboot2/multiboot2.h>
 #include <driver/uart/uart.h>
 #include <driver/video/video.h>
+#include <mm/mm.h>
+#include <mm/slab.h>
 
 extern struct scm_buffer_info_t video_frame_buffer_info;
 static struct List scm_framework_list;
@@ -234,35 +234,43 @@ int scm_enable_alloc()
  */
 int scm_enable_double_buffer()
 {
-    if (__scm_double_buffer_enabled == true)
+    if (__scm_double_buffer_enabled == true) // 已经开启了双缓冲区了, 直接退出
         return 0;
     __scm_double_buffer_enabled = true;
-    if (list_empty(&scm_framework_list))
+    if (list_empty(&scm_framework_list)) // scm 框架链表为空
         return 0;
 
     // 逐个检查已经注册了的ui框架,将其缓冲区更改为双缓冲
     struct scm_ui_framework_t *ptr = container_of(list_next(&scm_framework_list), struct scm_ui_framework_t, list);
+    // 这里的ptr不需要特判空指针吗 问题1
     do
     {
         if (ptr->buf == &video_frame_buffer_info)
         {
-            uart_send_str(COM1, "##init double buffer##");
+            uart_send_str(COM1, "##init double buffer##\n");
             struct scm_buffer_info_t *buf = __create_buffer(SCM_BF_DB | SCM_BF_PIXEL);
             if ((uint64_t)(buf) == (uint64_t)-ENOMEM)
                 return -ENOMEM;
-            uart_send_str(COM1, "##to change double buffer##");
-            if (ptr->ui_ops->change(buf) != 0)
+            uart_send_str(COM1, "##to change double buffer##\n");
+
+            if (ptr->ui_ops->change(buf) != 0)  // 这里的change回调函数不会是空指针吗 问题2
             {
+
                 __destroy_buffer(buf);
                 kfree(buf);
+
             }
+
         }
-    } while (list_next(&ptr->list) != &scm_framework_list);
+
+    } while (list_next(&ptr->list) != &scm_framework_list); // 枚举链表的每一个ui框架
+
+
     // 设置定时刷新的对象
     video_set_refresh_target(__current_framework->buf);
     // 通知显示驱动,启动双缓冲
     video_reinitialize(true);
-    uart_send_str(COM1, "##initialized double buffer##");
+    uart_send_str(COM1, "##initialized double buffer##\n");
     return 0;
 }
 

+ 26 - 20
kernel/lib/libUI/textui.c

@@ -1,11 +1,11 @@
 #include "textui.h"
 
-#include "screen_manager.h"
 #include "driver/uart/uart.h"
-#include <common/string.h>
-#include <common/printk.h>
+#include "screen_manager.h"
 #include <common/atomic.h>
 #include <common/errno.h>
+#include <common/printk.h>
+#include <common/string.h>
 
 struct scm_ui_framework_t textui_framework;
 static spinlock_t __window_id_lock = {1};
@@ -19,6 +19,7 @@ static struct textui_vline_chromatic_t __initial_vlines[INITIAL_VLINES] = {0};
 static struct textui_window_t __initial_window = {0}; // 初始窗口
 static struct textui_private_info_t __private_info = {0};
 static struct List __windows_list;
+static spinlock_t change_lock;
 
 /**
  * @brief 初始化window对象
@@ -29,7 +30,8 @@ static struct List __windows_list;
  * @param vlines_ptr 虚拟行数组指针
  * @param cperline 每行最大的字符数
  */
-static int __textui_init_window(struct textui_window_t *window, uint8_t flags, uint16_t vlines_num, void *vlines_ptr, uint16_t cperline)
+static int __textui_init_window(struct textui_window_t *window, uint8_t flags, uint16_t vlines_num, void *vlines_ptr,
+                                uint16_t cperline)
 {
     memset((window), 0, sizeof(struct textui_window_t));
     list_init(&(window)->list);
@@ -56,12 +58,12 @@ static int __textui_init_window(struct textui_window_t *window, uint8_t flags, u
  * @param vline 虚拟行对象指针
  * @param chars_ptr 字符对象数组指针
  */
-#define __textui_init_vline(vline, chars_ptr)                      \
-    do                                                             \
-    {                                                              \
-        memset(vline, 0, sizeof(struct textui_vline_chromatic_t)); \
-        (vline)->index = 0;                                        \
-        (vline)->chars = chars_ptr;                                \
+#define __textui_init_vline(vline, chars_ptr)                                                                          \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        memset(vline, 0, sizeof(struct textui_vline_chromatic_t));                                                     \
+        (vline)->index = 0;                                                                                            \
+        (vline)->chars = chars_ptr;                                                                                    \
     } while (0)
 
 int textui_install_handler(struct scm_buffer_info_t *buf)
@@ -78,7 +80,7 @@ int textui_uninstall_handler(void *args)
 
 int textui_enable_handler(void *args)
 {
-    uart_send_str(COM1, "textui_enable_handler");
+    uart_send_str(COM1, "textui_enable_handler\n");
     return 0;
 }
 
@@ -91,16 +93,16 @@ int textui_change_handler(struct scm_buffer_info_t *buf)
 {
     memcpy((void *)buf->vaddr, (void *)(textui_framework.buf->vaddr), textui_framework.buf->size);
     textui_framework.buf = buf;
+    
     return 0;
 }
 
-struct scm_ui_framework_operations_t textui_ops =
-    {
-        .install = &textui_install_handler,
-        .uninstall = &textui_uninstall_handler,
-        .change = &textui_change_handler,
-        .enable = &textui_enable_handler,
-        .disable = &textui_disable_handler,
+struct scm_ui_framework_operations_t textui_ops = {
+    .install = &textui_install_handler,
+    .uninstall = &textui_uninstall_handler,
+    .change = &textui_change_handler,
+    .enable = &textui_enable_handler,
+    .disable = &textui_disable_handler,
 };
 
 /**
@@ -166,7 +168,8 @@ static int __textui_new_line(struct textui_window_t *window, uint16_t vline_id)
  * @param character
  * @return int
  */
-static int __textui_putchar_window(struct textui_window_t *window, uint16_t character, uint32_t FRcolor, uint32_t BKcolor)
+static int __textui_putchar_window(struct textui_window_t *window, uint16_t character, uint32_t FRcolor,
+                                   uint32_t BKcolor)
 {
     if (textui_is_chromatic(window->flags)) // 启用彩色字符
     {
@@ -246,7 +249,8 @@ int textui_putchar_window(struct textui_window_t *window, uint16_t character, ui
         if (window->vlines.chromatic[window->vline_operating].index <= 0)
         {
             window->vlines.chromatic[window->vline_operating].index = 0;
-            memset(window->vlines.chromatic[window->vline_operating].chars, 0, sizeof(struct textui_char_chromatic_t) * window->chars_per_line);
+            memset(window->vlines.chromatic[window->vline_operating].chars, 0,
+                   sizeof(struct textui_char_chromatic_t) * window->chars_per_line);
             --(window->vline_operating);
             if (unlikely(window->vline_operating < 0))
                 window->vline_operating = window->vlines_num - 1;
@@ -295,6 +299,8 @@ int textui_putchar(uint16_t character, uint32_t FRcolor, uint32_t BKcolor)
  */
 int textui_init()
 {
+    spin_init(&change_lock);
+
     spin_init(&__window_id_lock);
     __window_max_id = 0;
     list_init(&__windows_list);

+ 1 - 1
kernel/main.c

@@ -165,7 +165,7 @@ void system_initialize()
 
     process_init();
     // 启用double buffer
-    scm_enable_double_buffer();
+    // scm_enable_double_buffer();  // 因为时序问题, 该函数调用被移到 initial_kernel_thread
     io_mfence();
 
     // fat32_init();

+ 24 - 19
kernel/process/process.c

@@ -1,29 +1,29 @@
 #include "process.h"
 
-#include <common/printk.h>
-#include <common/kprint.h>
-#include <common/stdio.h>
-#include <common/string.h>
 #include <common/compiler.h>
 #include <common/elf.h>
+#include <common/kprint.h>
 #include <common/kthread.h>
-#include <common/time.h>
+#include <common/printk.h>
+#include <common/spinlock.h>
+#include <common/stdio.h>
+#include <common/string.h>
 #include <common/sys/wait.h>
-#include <driver/video/video.h>
+#include <common/time.h>
+#include <common/unistd.h>
+#include <debug/bug.h>
+#include <debug/traceback/traceback.h>
+#include <driver/disk/ahci/ahci.h>
 #include <driver/usb/usb.h>
+#include <driver/video/video.h>
 #include <exception/gate.h>
-#include <filesystem/fat32/fat32.h>
 #include <filesystem/devfs/devfs.h>
+#include <filesystem/fat32/fat32.h>
 #include <filesystem/rootfs/rootfs.h>
 #include <mm/slab.h>
-#include <common/spinlock.h>
+#include <sched/sched.h>
 #include <syscall/syscall.h>
 #include <syscall/syscall_num.h>
-#include <sched/sched.h>
-#include <common/unistd.h>
-#include <debug/traceback/traceback.h>
-#include <debug/bug.h>
-#include <driver/disk/ahci/ahci.h>
 
 #include <ktest/ktest.h>
 
@@ -372,7 +372,7 @@ ul do_execve(struct pt_regs *regs, char *path, char *argv[], char *envp[])
     // 独立的地址空间才能使新程序正常运行
     if (current_pcb->flags & PF_VFORK)
     {
-        kdebug("proc:%d  creating new mem space", current_pcb->pid);
+        // kdebug("proc:%d  creating new mem space", current_pcb->pid);
         // 分配新的内存空间分布结构体
         struct mm_struct *new_mms = (struct mm_struct *)kmalloc(sizeof(struct mm_struct), 0);
         memset(new_mms, 0, sizeof(struct mm_struct));
@@ -479,7 +479,9 @@ exec_failed:;
 #pragma GCC optimize("O0")
 ul initial_kernel_thread(ul arg)
 {
-    // kinfo("initial proc running...\targ:%#018lx", arg);
+    kinfo("initial proc running...\targ:%#018lx", arg);
+
+    scm_enable_double_buffer();
 
     ahci_init();
     fat32_init();
@@ -582,7 +584,7 @@ ul process_do_exit(ul code)
  * @return int
  */
 
-pid_t kernel_thread(int (*fn)(void*), void* arg, unsigned long flags)
+pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
 {
     struct pt_regs regs;
     barrier();
@@ -794,13 +796,16 @@ struct process_control_block *process_get_pcb(long pid)
  */
 int process_wakeup(struct process_control_block *pcb)
 {
+    // kdebug("pcb pid = %#018lx", pcb->pid);
+
     BUG_ON(pcb == NULL);
     if (pcb == current_pcb || pcb == NULL)
         return -EINVAL;
     // 如果pcb正在调度队列中,则不重复加入调度队列
-    if (pcb->state == PROC_RUNNING)
+    if (pcb->state & PROC_RUNNING)
         return 0;
-    pcb->state = PROC_RUNNING;
+
+    pcb->state |= PROC_RUNNING;
     sched_enqueue(pcb);
     return 0;
 }
@@ -812,7 +817,7 @@ int process_wakeup(struct process_control_block *pcb)
  */
 int process_wakeup_immediately(struct process_control_block *pcb)
 {
-    if (pcb->state == PROC_RUNNING)
+    if (pcb->state & PROC_RUNNING)
         return 0;
     int retval = process_wakeup(pcb);
     if (retval != 0)

+ 2 - 2
kernel/sched/sched.c

@@ -69,10 +69,10 @@ void sched_cfs()
     current_pcb->flags &= ~PF_NEED_SCHED;
     struct process_control_block *proc = sched_cfs_dequeue();
     // kdebug("sched_cfs_ready_queue[proc_current_cpu_id].count = %d", sched_cfs_ready_queue[proc_current_cpu_id].count);
-    if (current_pcb->virtual_runtime >= proc->virtual_runtime || current_pcb->state != PROC_RUNNING) // 当前进程运行时间大于了下一进程的运行时间,进行切换
+    if (current_pcb->virtual_runtime >= proc->virtual_runtime || !(current_pcb->state & PROC_RUNNING)) // 当前进程运行时间大于了下一进程的运行时间,进行切换
     {
 
-        if (current_pcb->state == PROC_RUNNING) // 本次切换由于时间片到期引发,则再次加入就绪队列,否则交由其它功能模块进行管理
+        if (current_pcb->state & PROC_RUNNING) // 本次切换由于时间片到期引发,则再次加入就绪队列,否则交由其它功能模块进行管理
             sched_cfs_enqueue(current_pcb);
         // kdebug("proc->pid=%d, count=%d", proc->pid, sched_cfs_ready_queue[proc_current_cpu_id].count);
         if (sched_cfs_ready_queue[proc_current_cpu_id].cpu_exec_proc_jiffies <= 0)

+ 2 - 3
kernel/time/timer.h

@@ -7,9 +7,9 @@
 uint64_t volatile timer_jiffies = 0; // 系统时钟计数
 
 // 计算接下来n毫秒对应的系统时间片
-#define cal_next_n_ms_jiffies(expire_ms) (timer_jiffies + 1000*expire_ms)
+#define cal_next_n_ms_jiffies(expire_ms) (timer_jiffies + 1000 * (expire_ms))
 // 计算接下来n微秒对应的系统时间片
-#define cal_next_n_us_jiffies(expire_us) (timer_jiffies + expire_us)
+#define cal_next_n_us_jiffies(expire_us) (timer_jiffies + (expire_us))
 
 void timer_init();
 
@@ -62,5 +62,4 @@ void timer_func_add(struct timer_func_list_t *timer_func);
  */
 void timer_func_del(struct timer_func_list_t *timer_func);
 
-
 uint64_t clock();

+ 1 - 0
run.sh

@@ -174,6 +174,7 @@ if [ $flag_can_run -eq 1 ]; then
         -drive id=disk,file=bin/disk.img,if=none \
         -device ahci,id=ahci \
         -device ide-hd,drive=disk,bus=ahci.0    \
+        -net nic,model=virtio \
         -usb    \
         -device qemu-xhci,id=xhci,p2=8,p3=4 \
         -machine accel=${qemu_accel}