Browse Source

new: mutex

fslongjin 2 years ago
parent
commit
946bbef392

+ 7 - 1
kernel/common/Makefile

@@ -3,7 +3,7 @@ CFLAGS += -I .
 
 kernel_common_subdirs:=libELF math
 
-all: glib.o printk.o cpu.o bitree.o kfifo.o wait_queue.o
+all: glib.o printk.o cpu.o bitree.o kfifo.o wait_queue.o mutex.o wait.o
 	@list='$(kernel_common_subdirs)'; for subdir in $$list; do \
     		echo "make all in $$subdir";\
     		cd $$subdir;\
@@ -28,3 +28,9 @@ kfifo.o: kfifo.c
 
 wait_queue.o: wait_queue.c
 	gcc $(CFLAGS) -c wait_queue.c -o wait_queue.o
+
+mutex.o: mutex.c
+	gcc $(CFLAGS) -c mutex.c -o mutex.o
+
+wait.o: sys/wait.c
+	gcc $(CFLAGS) -c sys/wait.c -o sys/wait.o

+ 120 - 0
kernel/common/mutex.c

@@ -0,0 +1,120 @@
+#include <common/mutex.h>
+#include <mm/slab.h>
+#include <sched/sched.h>
+
+/**
+ * @brief 初始化互斥量
+ *
+ * @param lock mutex结构体
+ */
+void mutex_init(mutex_t *lock)
+{
+    atomic_set(&lock->count, 1);
+    spin_init(&lock->wait_lock);
+    list_init(&lock->wait_list);
+}
+
+static void __mutex_sleep()
+{
+    current_pcb->state = PROC_UNINTERRUPTIBLE;
+    sched_cfs();
+}
+
+static void __mutex_acquire(mutex_t *lock)
+{
+}
+/**
+ * @brief 对互斥量加锁
+ *
+ * @param lock mutex结构体
+ */
+void mutex_lock(mutex_t *lock)
+{
+    bool lock_ok = 0;
+
+    while (lock_ok == false)
+    {
+        spin_lock(&lock->wait_lock);
+        if (likely(mutex_is_locked(lock)))
+        {
+            struct mutex_waiter_t *waiter = (struct mutex_waiter_t *)kmalloc(sizeof(struct mutex_waiter_t), 0);
+            if (waiter == NULL)
+            {
+                kerror("In mutex_lock: no memory to alloc waiter. Program's behaviour might be indetermined!");
+                spin_unlock(&lock->wait_lock);
+                return;
+            }
+            memset(waiter, 0, sizeof(struct mutex_waiter_t));
+            waiter->pcb = current_pcb;
+            list_init(&waiter->list);
+            list_append(&lock->wait_list, &waiter->list);
+
+            spin_unlock(&lock->wait_lock);
+
+            __mutex_sleep();
+        }
+        else
+        {
+            atomic_dec(&lock->count);
+            spin_unlock(&lock->wait_lock);
+            lock_ok = true;
+        }
+    }
+}
+
+/**
+ * @brief 对互斥量解锁
+ *
+ * @param lock mutex结构体
+ */
+void mutex_unlock(mutex_t *lock)
+{
+    if (unlikely(!mutex_is_locked(lock)))
+        return;
+    
+    spin_lock(&lock->wait_lock);
+    struct mutex_waiter_t *wt = NULL;
+    if (mutex_is_locked(lock))
+    {
+        if (!list_empty(&lock->wait_list))
+            wt = container_of(list_next(&lock->wait_list), struct mutex_waiter_t, list);
+
+        atomic_inc(&lock->count);
+        if (wt != NULL)
+            list_del(&wt->list);
+    }
+
+    spin_unlock(&lock->wait_lock);
+
+    if (wt != NULL)
+    {
+        process_wakeup(wt->pcb);
+        kfree(wt);
+    }
+}
+
+/**
+ * @brief 尝试对互斥量加锁
+ *
+ * @param lock mutex结构体
+ *
+ * @return 成功加锁->1, 加锁失败->0
+ */
+int mutex_trylock(mutex_t *lock)
+{
+    if (mutex_is_locked(lock))
+        return 0;
+
+    spin_lock(&lock->wait_lock);
+    if (mutex_is_locked(lock))
+    {
+        spin_unlock(&lock->wait_lock);
+        return 0;
+    }
+    else
+    {
+        atomic_dec(&lock->count);
+        spin_unlock(&lock->wait_lock);
+        return 1;
+    }
+}

+ 69 - 0
kernel/common/mutex.h

@@ -0,0 +1,69 @@
+#pragma once
+
+#include <common/atomic.h>
+#include <common/spinlock.h>
+#include <common/glib.h>
+#include <process/process.h>
+
+/**
+ * @brief Mutex - 互斥锁
+ *
+ * - 同一时间只有1个任务可以持有mutex
+ * - 不允许递归地加锁、解锁
+ * - 只允许通过mutex的api来操作mutex
+ * - 在硬中断、软中断中不能使用mutex
+ */
+typedef struct
+{
+
+    atomic_t count; // 锁计数。1->已解锁。 0->已上锁,且有可能存在等待者
+    spinlock_t wait_lock;
+    struct List wait_list;
+} mutex_t;
+
+/**
+ * @brief 在mutex上的等待者的结构体
+ *
+ */
+struct mutex_waiter_t
+{
+    struct List list;
+    struct process_control_block *pcb;
+};
+
+/**
+ * @brief 初始化互斥量
+ *
+ * @param lock mutex结构体
+ */
+void mutex_init(mutex_t *lock);
+
+/**
+ * @brief 对互斥量加锁
+ *
+ * @param lock mutex结构体
+ */
+void mutex_lock(mutex_t *lock);
+
+/**
+ * @brief 对互斥量解锁
+ *
+ * @param lock mutex结构体
+ */
+void mutex_unlock(mutex_t *lock);
+
+/**
+ * @brief 尝试对互斥量加锁
+ *
+ * @param lock mutex结构体
+ *
+ * @return 成功加锁->1, 加锁失败->0
+ */
+int mutex_trylock(mutex_t *lock);
+
+/**
+ * @brief 判断mutex是否已被加锁
+ *
+ * @return 已加锁->1, 未加锁->0
+ */
+#define mutex_is_locked(lock) ((atomic_read(&(lock)->count) == 1) ? 0 : 1)

+ 15 - 0
kernel/common/sys/wait.c

@@ -0,0 +1,15 @@
+#include <syscall/syscall_num.h>
+#include <syscall/syscall.h>
+
+/**
+ * @brief 等待指定pid的子进程退出
+ *
+ * @param pid 子进程的pid
+ * @param stat_loc 返回的子进程结束状态
+ * @param options 额外的控制选项
+ * @return pid_t
+ */
+pid_t waitpid(pid_t pid, int *stat_loc, int options)
+{
+    return (pid_t)enter_syscall_int(SYS_WAIT4, (uint64_t)pid, (uint64_t)stat_loc, options, 0, 0, 0, 0, 0);
+}

+ 12 - 0
kernel/common/sys/wait.h

@@ -0,0 +1,12 @@
+#pragma once
+#include "types.h"
+
+/**
+ * @brief 等待指定pid的子进程退出
+ *
+ * @param pid 子进程的pid
+ * @param stat_loc 返回的子进程结束状态
+ * @param options 额外的控制选项
+ * @return pid_t
+ */
+pid_t waitpid(pid_t pid, int *stat_loc, int options);

+ 4 - 1
kernel/ktest/Makefile

@@ -2,7 +2,7 @@
 CFLAGS += -I .
 
 
-all: ktest.o bitree.o kfifo.o
+all: ktest.o bitree.o kfifo.o mutex.o
 
 ktest.o: ktest.c
 	gcc $(CFLAGS) -c ktest.c -o ktest.o
@@ -12,3 +12,6 @@ bitree.o: test-bitree.c
 
 kfifo.o: test-kfifo.c
 	gcc $(CFLAGS) -c test-kfifo.c -o test-kfifo.o
+
+mutex.o: test-mutex.c
+	gcc $(CFLAGS) -c test-mutex.c -o test-mutex.o

+ 2 - 1
kernel/ktest/ktest.h

@@ -3,10 +3,11 @@
 
 uint64_t ktest_test_bitree(uint64_t arg);
 uint64_t ktest_test_kfifo(uint64_t arg);
+uint64_t ktest_test_mutex(uint64_t arg);
 
 /**
  * @brief 开启一个新的内核线程以进行测试
- * 
+ *
  * @param func 测试函数
  * @param arg 传递给测试函数的参数
  * @return pid_t 测试内核线程的pid

+ 1 - 1
kernel/ktest/ktest_utils.h

@@ -18,7 +18,7 @@
         printk("[ kTEST ] file:%s, Line:%d\t", __FILE__, __LINE__); \
         printk(__VA_ARGS__);                                        \
         printk("\n");                                               \
-    } while (0);
+    } while (0)
 
 /**
  * @brief 测试用例函数表

+ 93 - 0
kernel/ktest/test-mutex.c

@@ -0,0 +1,93 @@
+#include "ktest_utils.h"
+#include <common/mutex.h>
+#include <common/time.h>
+#include <common/sys/wait.h>
+#include <process/process.h>
+
+static mutex_t mtx;
+
+/**
+ * @brief 测试是否能够加锁
+ *
+ * @param arg0
+ * @param arg1
+ * @return long
+ */
+static long ktest_mutex_case0(uint64_t arg0, uint64_t arg1)
+{
+    assert(mutex_is_locked(&mtx) == 0);
+    mutex_lock(&mtx);
+    assert(mutex_is_locked(&mtx) == 1);
+    mutex_unlock(&mtx);
+    assert(mutex_is_locked(&mtx) == 0);
+    assert(mutex_trylock(&mtx) == 1);
+    mutex_unlock(&mtx);
+    assert(mutex_is_locked(&mtx) == 0);
+}
+
+/**
+ * @brief 测试用例1的辅助线程
+ *
+ * @param arg
+ * @return long
+ */
+static unsigned long ktest_mutex_case1_pid1(uint64_t arg)
+{
+    kTEST("ktest_mutex_case1_subproc start.");
+    assert(mutex_is_locked(&mtx) == 1);
+    mutex_lock(&mtx);
+    assert(atomic_read(&mtx.count) == 0);
+    assert(list_empty(&mtx.wait_list));
+
+    mutex_unlock(&mtx);
+    kTEST("ktest_mutex_case1_subproc exit.");
+    return 0;
+}
+
+static long ktest_mutex_case1(uint64_t arg0, uint64_t arg1)
+{
+    if (!assert(mutex_is_locked(&mtx) == 0))
+        goto failed;
+
+    // 加锁
+    mutex_lock(&mtx);
+    // 启动另一个线程
+    pid_t pid = kernel_thread(ktest_mutex_case1_pid1, 0, 0);
+    // 等待100ms
+    usleep(100000);
+    while (list_empty(&mtx.wait_list))
+        ;
+
+    // 当子线程加锁后,计数应当为0
+    assert(atomic_read(&mtx.count) == 0);
+    struct mutex_waiter_t *wt = container_of(list_next(&mtx.wait_list), struct mutex_waiter_t, list);
+    assert(wt->pcb->pid == pid);
+
+    mutex_unlock(&mtx);
+
+    int stat = 1;
+    waitpid(pid, &stat, 0);
+    assert(stat == 0);
+    return 0;
+failed:;
+    kTEST("mutex test case1 failed.");
+    return -1;
+}
+
+static ktest_case_table kt_mutex_func_table[] = {
+    ktest_mutex_case0,
+    ktest_mutex_case1,
+};
+uint64_t ktest_test_mutex(uint64_t arg)
+{
+    kTEST("Testing mutex...");
+    mutex_init(&mtx);
+
+    for (int i = 0; i < sizeof(kt_mutex_func_table) / sizeof(ktest_case_table); ++i)
+    {
+        kTEST("Testing case %d", i);
+        kt_mutex_func_table[i](i, 0);
+    }
+    kTEST("mutex Test done.");
+    return 0;
+}

+ 9 - 2
kernel/process/process.c

@@ -6,6 +6,7 @@
 #include <common/compiler.h>
 #include <common/libELF/elf.h>
 #include <common/time.h>
+#include <common/sys/wait.h>
 #include <driver/video/video.h>
 #include <driver/usb/usb.h>
 #include <exception/gate.h>
@@ -416,8 +417,14 @@ ul initial_kernel_thread(ul arg)
     usb_init();
 
     // 对一些组件进行单元测试
-    ktest_start(ktest_test_bitree, 0);
-    ktest_start(ktest_test_kfifo, 0);
+    uint64_t tpid[] = {
+        ktest_start(ktest_test_bitree, 0),
+        ktest_start(ktest_test_kfifo, 0),
+        ktest_start(ktest_test_mutex, 0),
+    };
+    // 等待测试进程退出
+    for(int i=0;i<sizeof(tpid)/sizeof(uint64_t);++i)
+        waitpid(tpid[i], NULL, NULL);
 
     // 准备切换到用户态
     struct pt_regs *regs;