diff --git a/kernel/common/Makefile b/kernel/common/Makefile index f2c73461..81506e41 100644 --- a/kernel/common/Makefile +++ b/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 \ No newline at end of file diff --git a/kernel/common/mutex.c b/kernel/common/mutex.c new file mode 100644 index 00000000..69fb8c89 --- /dev/null +++ b/kernel/common/mutex.c @@ -0,0 +1,120 @@ +#include +#include +#include + +/** + * @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; + } +} \ No newline at end of file diff --git a/kernel/common/mutex.h b/kernel/common/mutex.h new file mode 100644 index 00000000..e2377ecc --- /dev/null +++ b/kernel/common/mutex.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include +#include + +/** + * @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) diff --git a/kernel/common/sys/wait.c b/kernel/common/sys/wait.c new file mode 100644 index 00000000..9b1d1278 --- /dev/null +++ b/kernel/common/sys/wait.c @@ -0,0 +1,15 @@ +#include +#include + +/** + * @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); +} \ No newline at end of file diff --git a/kernel/common/sys/wait.h b/kernel/common/sys/wait.h new file mode 100644 index 00000000..8afb9a42 --- /dev/null +++ b/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); \ No newline at end of file diff --git a/kernel/ktest/Makefile b/kernel/ktest/Makefile index 6791ab98..df7ec2af 100644 --- a/kernel/ktest/Makefile +++ b/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 \ No newline at end of file diff --git a/kernel/ktest/ktest.h b/kernel/ktest/ktest.h index 936f81ae..5a3e6997 100644 --- a/kernel/ktest/ktest.h +++ b/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 diff --git a/kernel/ktest/ktest_utils.h b/kernel/ktest/ktest_utils.h index b01f4d43..ff67e4e7 100644 --- a/kernel/ktest/ktest_utils.h +++ b/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 测试用例函数表 diff --git a/kernel/ktest/test-mutex.c b/kernel/ktest/test-mutex.c new file mode 100644 index 00000000..1a17f783 --- /dev/null +++ b/kernel/ktest/test-mutex.c @@ -0,0 +1,93 @@ +#include "ktest_utils.h" +#include +#include +#include +#include + +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; +} \ No newline at end of file diff --git a/kernel/process/process.c b/kernel/process/process.c index ba38adaf..9cd33b6c 100644 --- a/kernel/process/process.c +++ b/kernel/process/process.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -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