new: kthread机制

This commit is contained in:
fslongjin
2022-09-30 15:30:50 +08:00
parent edef02286e
commit 02a6b3a303
21 changed files with 802 additions and 326 deletions

View File

@ -1,17 +1,21 @@
all: procs.o process.o
CFLAGS += -I .
kernel_process_objs:= $(shell find ./*.c)
ECHO:
@echo "$@"
$(kernel_process_objs): ECHO
gcc $(CFLAGS) -c $@ -o $@.o
procs.o: proc.S
gcc -E proc.S > _proc.s
as $(ASFLAGS) -o procs.o _proc.s
process.o: process.c
gcc $(CFLAGS) -c process.c -o process.o
all: procs.o $(kernel_process_objs)
clean:

305
kernel/process/kthread.c Normal file
View File

@ -0,0 +1,305 @@
#include <common/kthread.h>
#include <common/glib.h>
#include <common/spinlock.h>
#include <sched/sched.h>
#include <debug/bug.h>
static spinlock_t __kthread_create_lock; // kthread创建过程的锁
static struct List kthread_create_list; // kthread创建任务的链表
struct process_control_block *kthreadd_pcb = NULL; // kthreadd守护线程的pcb
// 枚举各个标志位是在第几位
enum KTHREAD_BITS
{
KTHREAD_IS_PER_CPU = 0,
KTHREAD_SHOULD_STOP,
KTHREAD_SHOULD_PARK,
};
/**
* @brief kthread的创建信息仅在创建过程中存在
*
*/
struct kthread_create_info_t
{
// 传递给kthread的信息
int (*thread_fn)(void *data);
void *data;
int node;
// kthreadd守护进程传递给kthread_create的结果
struct process_control_block *result;
struct List list;
};
/**
* @brief kthread信息
* 该结构体将会绑定到pcb的worker_private中
*/
struct kthread_info_t
{
uint64_t flags;
uint32_t cpu;
int result;
int (*thread_fn)(void *);
void *data;
// todo: 将这里改为completion机制
bool exited; // 是否已退出
char *full_name; // 内核线程的名称
};
/**
* @brief 获取pcb中的kthread结构体
*
* @param pcb pcb
* @return struct kthread* kthread信息结构体
*/
static inline struct kthread_info_t *to_kthread(struct process_control_block *pcb)
{
WARN_ON(!(pcb->flags & PF_KTHREAD));
return pcb->worker_private;
}
static struct process_control_block *__kthread_create_on_node(int (*thread_fn)(void *data), void *data,
int node,
const char name_fmt[], va_list args)
{
struct process_control_block *pcb = NULL;
struct kthread_create_info_t *create = kzalloc(sizeof(struct kthread_create_info_t), 0);
if (create == NULL)
return ERR_PTR(-ENOMEM);
create->thread_fn = thread_fn;
create->data = data;
create->node = node;
create->result = NULL;
list_init(&create->list);
spin_lock(&__kthread_create_lock);
list_append(&kthread_create_list, &create->list);
spin_unlock(&__kthread_create_lock);
kdebug("to wakeup kthread daemon..., current preempt=%d, rflags=%#018lx", current_pcb->preempt_count, get_rflags());
while (kthreadd_pcb == NULL) // 若kthreadd未初始化则等待kthreadd启动
;
// 唤醒kthreadd守护进程
process_wakeup_immediately(kthreadd_pcb);
// 等待创建完成
// todo: 使用completion机制以降低忙等时间
while (create->result == NULL)
pause();
// 获取结果
pcb = create->result;
if (!IS_ERR(create->result))
{
// todo: 为内核线程设置名字
}
kfree(create);
return pcb;
}
/**
* @brief 让当前内核线程退出并返回result参数给kthread_stop()函数
*
* @param result 返回值
*/
void kthread_exit(long result)
{
struct kthread_info_t *kt = to_kthread(current_pcb);
kt->result = result;
kt->exited = true;
process_do_exit(0);
}
/**
* @brief 在当前结点上创建一个内核线程
*
* @param thread_fn 该内核线程要执行的函数
* @param data 传递给 thread_fn 的参数数据
* @param node 线程的任务和线程结构都分配在这个节点上
* @param name_fmt printf-style format string for the thread name
* @param arg name_fmt的参数
* @return 返回一个pcb或者是ERR_PTR(-ENOMEM)
*
* 请注意该宏会创建一个内核线程并将其设置为停止状态。您可以使用wake_up_process来启动这个线程。
* 新的线程的调度策略为SCHED_NORMAL并且能在所有的cpu上运行
*
* 当内核线程被唤醒时会运行thread_fn函数并将data作为参数传入。
* 内核线程可以直接返回也可以在kthread_should_stop为真时返回。
*/
struct process_control_block *kthread_create_on_node(int (*thread_fn)(void *data), void *data,
int node,
const char name_fmt[], ...)
{
struct process_control_block *pcb;
va_list args;
va_start(args, name_fmt);
pcb = __kthread_create_on_node(thread_fn, data, node, name_fmt, args);
va_end(args);
return pcb;
}
/**
* @brief 内核线程的包裹程序
* 当内核线程被运行后从kernel_thread_func跳转到这里。
* @param _create 内核线程的创建信息
* @return int 内核线程的退出返回值
*/
static int kthread(void *_create)
{
struct kthread_create_info_t *create = _create;
// 将这几个信息从kthread_create_info中拷贝过来。以免在kthread_create_info被free后数据丢失从而导致错误。
int (*thread_fn)(void *data) = create->thread_fn;
void *data = create->data;
int retval = 0;
struct kthread_info_t *self = to_kthread(current_pcb);
self->thread_fn = thread_fn;
self->data = data;
// todo: 增加调度参数设定
// todo: 当前内核线程继承了kthreadd的优先级以及调度策略需要在这里进行更新
// 设置当前进程为不可被打断
current_pcb->state = PROC_UNINTERRUPTIBLE;
// 将当前pcb返回给创建者
create->result = current_pcb;
// 发起调度使得当前内核线程休眠。直到创建者通过process_wakeup将当前内核线程唤醒
sched();
retval = -EINTR;
// 如果发起者没有调用kthread_stop()则该kthread的功能函数开始执行
if (!(self->flags & (1 << KTHREAD_SHOULD_STOP)))
{
retval = thread_fn(data);
}
kthread_exit(retval);
}
static void __create_kthread(struct kthread_create_info_t *create)
{
pid_t pid = kernel_thread(kthread, create, CLONE_FS | CLONE_SIGNAL);
if (IS_ERR((void *)pid))
{
// todo: 使用complete机制完善这里
create->result = (struct process_control_block *)pid;
}
}
/**
* @brief kthread守护线程
*
* @param unused
* @return int 不应当退出
*/
int kthreadd(void *unused)
{
kinfo("kthread daemon started!");
struct process_control_block *pcb = current_pcb;
kthreadd_pcb = current_pcb;
current_pcb->flags |= PF_NOFREEZE;
for (;;)
{
current_pcb->state = PROC_INTERRUPTIBLE;
// 所有的创建任务都被处理完了
if (list_empty(&kthread_create_list))
sched();
spin_lock(&__kthread_create_lock);
// 循环取出链表中的任务
while (!list_empty(&kthread_create_list))
{
// 从链表中取出第一个要创建的内核线程任务
struct kthread_create_info_t *create = container_of(kthread_create_list.next, struct kthread_create_info_t, list);
list_del_init(&create->list);
spin_unlock(&__kthread_create_lock);
__create_kthread(create);
spin_lock(&__kthread_create_lock);
}
spin_unlock(&__kthread_create_lock);
}
}
/**
* @brief 内核线程调用该函数,检查自身的标志位,判断自己是否应该执行完任务后退出
*
* @return true 内核线程应该退出
* @return false 无需退出
*/
bool kthread_should_stop(void)
{
struct kthread_info_t *self = to_kthread(current_pcb);
if (self->flags & (1 << KTHREAD_SHOULD_STOP))
return true;
return false;
}
/**
* @brief 向kthread发送停止信号请求其结束
*
* @param pcb 内核线程的pcb
* @return int 错误码
*/
int kthread_stop(struct process_control_block *pcb)
{
int retval;
struct kthread_info_t *target = to_kthread(pcb);
target->flags |= (1 << KTHREAD_SHOULD_STOP);
process_wakeup(pcb);
// 等待指定的内核线程退出
// todo: 使用completion机制改进这里
while (target->exited == false)
usleep(5000);
retval = target->result;
// 释放内核线程的页表
process_exit_mm(pcb);
process_release_pcb(pcb);
return retval;
}
/**
* @brief 设置pcb中的worker_private字段只应被设置一次
*
* @param pcb pcb
* @return bool 成功或失败
*/
bool kthread_set_worker_private(struct process_control_block *pcb)
{
if (WARN_ON_ONCE(to_kthread(pcb)))
return false;
struct kthread_info_t *kt = kzalloc(sizeof(struct kthread_info_t), 0);
if (kt == NULL)
return false;
pcb->worker_private = kt;
return true;
}
/**
* @brief 初始化kthread机制(只应被process_init调用)
*
* @return int 错误码
*/
int kthread_mechanism_init()
{
kinfo("Initializing kthread mechanism...");
spin_init(&__kthread_create_lock);
list_init(&kthread_create_list);
// 创建kthreadd守护进程
kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_SIGNAL);
return 0;
}

View File

@ -58,6 +58,7 @@ struct thread_struct
#define PF_NEED_SCHED (1UL << 1) // 进程需要被调度
#define PF_VFORK (1UL << 2) // 标志进程是否由于vfork而存在资源共享
#define PF_KFORK (1UL << 3) // 标志在内核态下调用fork临时标记do_fork()结束后会将其复位)
#define PF_NOFREEZE (1UL << 4) // 当前进程不能被冻结
/**
* @brief 进程控制块
@ -101,6 +102,9 @@ struct process_control_block
int32_t exit_code; // 进程退出时的返回码
wait_queue_node_t wait_child_proc_exit; // 子进程退出等待队列
/* PF_kTHREAD | PF_IO_WORKER 的进程worker_private不为NULL*/
void *worker_private;
};
// 将进程的pcb和内核栈融合到一起,8字节对齐

View File

@ -6,6 +6,7 @@
#include <common/string.h>
#include <common/compiler.h>
#include <common/elf.h>
#include <common/kthread.h>
#include <common/time.h>
#include <common/sys/wait.h>
#include <driver/video/video.h>
@ -21,6 +22,7 @@
#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>
@ -278,7 +280,7 @@ static int process_load_elf_file(struct pt_regs *regs, char *path)
uint64_t pa = alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys;
struct vm_area_struct *vma = NULL;
int ret = mm_create_vma(current_pcb->mm, virt_base, PAGE_2M_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma);
// 防止内存泄露
if (ret == -EEXIST)
free_pages(Phy_to_2M_Page(pa), 1);
@ -578,7 +580,7 @@ ul process_do_exit(ul code)
* @return int
*/
int kernel_thread(unsigned long (*fn)(unsigned long), unsigned long arg, unsigned long flags)
pid_t kernel_thread(int (*fn)(void*), void* arg, unsigned long flags)
{
struct pt_regs regs;
barrier();
@ -617,50 +619,9 @@ int kernel_thread(unsigned long (*fn)(unsigned long), unsigned long arg, unsigne
void process_init()
{
kinfo("Initializing process...");
initial_mm.pgd = (pml4t_t *)get_CR3();
initial_mm.code_addr_start = memory_management_struct.kernel_code_start;
initial_mm.code_addr_end = memory_management_struct.kernel_code_end;
initial_mm.data_addr_start = (ul)&_data;
initial_mm.data_addr_end = memory_management_struct.kernel_data_end;
initial_mm.rodata_addr_start = (ul)&_rodata;
initial_mm.rodata_addr_end = (ul)&_erodata;
initial_mm.bss_start = (uint64_t)&_bss;
initial_mm.bss_end = (uint64_t)&_ebss;
initial_mm.brk_start = memory_management_struct.start_brk;
initial_mm.brk_end = current_pcb->addr_limit;
initial_mm.stack_start = _stack_start;
initial_mm.vmas = NULL;
initial_tss[proc_current_cpu_id].rsp0 = initial_thread.rbp;
// ========= 在IDLE进程的顶层页表中添加对内核地址空间的映射 =====================
// 由于IDLE进程的顶层页表的高地址部分会被后续进程所复制为了使所有进程能够共享相同的内核空间
// 因此需要先在IDLE进程的顶层页表内映射二级页表
uint64_t *idle_pml4t_vaddr = (uint64_t *)phys_2_virt((uint64_t)get_CR3() & (~0xfffUL));
for (int i = 256; i < 512; ++i)
{
uint64_t *tmp = idle_pml4t_vaddr + i;
barrier();
if (*tmp == 0)
{
void *pdpt = kmalloc(PAGE_4K_SIZE, 0);
barrier();
memset(pdpt, 0, PAGE_4K_SIZE);
barrier();
set_pml4t(tmp, mk_pml4t(virt_2_phys(pdpt), PAGE_KERNEL_PGT));
}
}
barrier();
flush_tlb();
/*
kdebug("initial_thread.rbp=%#018lx", initial_thread.rbp);
kdebug("initial_tss[0].rsp1=%#018lx", initial_tss[0].rsp1);
@ -672,14 +633,19 @@ void process_init()
// 初始化进程的循环链表
list_init(&initial_proc_union.pcb.list);
// 临时设置IDLE进程的的虚拟运行时间为0防止下面的这些内核线程的虚拟运行时间出错
current_pcb->virtual_runtime = 0;
barrier();
kernel_thread(initial_kernel_thread, 10, CLONE_FS | CLONE_SIGNAL); // 初始化内核线程
barrier();
kthread_mechanism_init(); // 初始化kthread机制
initial_proc_union.pcb.state = PROC_RUNNING;
initial_proc_union.pcb.preempt_count = 0;
initial_proc_union.pcb.cpu_id = 0;
initial_proc_union.pcb.virtual_runtime = (1UL << 60);
// 将IDLE进程的虚拟运行时间设置为一个很大的数值
current_pcb->virtual_runtime = (1UL << 60);
}
@ -712,6 +678,7 @@ unsigned long do_fork(struct pt_regs *regs, unsigned long clone_flags, unsigned
io_mfence();
// 将当前进程的pcb复制到新的pcb内
memcpy(tsk, current_pcb, sizeof(struct process_control_block));
tsk->worker_private = NULL;
io_mfence();
// 初始化进程的循环链表结点
@ -719,9 +686,17 @@ unsigned long do_fork(struct pt_regs *regs, unsigned long clone_flags, unsigned
io_mfence();
// 判断是否为内核态调用fork
if (current_pcb->flags & PF_KTHREAD && stack_start != 0)
if ((current_pcb->flags & PF_KTHREAD) && stack_start != 0)
tsk->flags |= PF_KFORK;
if (tsk->flags & PF_KTHREAD)
{
// 对于内核线程设置其worker私有信息
retval = kthread_set_worker_private(tsk);
if (IS_ERR_VALUE(retval))
goto copy_flags_failed;
tsk->virtual_runtime = 0;
}
tsk->priority = 2;
tsk->preempt_count = 0;
@ -815,10 +790,17 @@ struct process_control_block *process_get_pcb(long pid)
*
* @param pcb 进程的pcb
*/
void process_wakeup(struct process_control_block *pcb)
int process_wakeup(struct process_control_block *pcb)
{
BUG_ON(pcb == NULL);
if (pcb == current_pcb || pcb == NULL)
return -EINVAL;
// 如果pcb正在调度队列中则不重复加入调度队列
if (pcb->state == PROC_RUNNING)
return 0;
pcb->state = PROC_RUNNING;
sched_enqueue(pcb);
return 0;
}
/**
@ -826,10 +808,13 @@ void process_wakeup(struct process_control_block *pcb)
*
* @param pcb 进程的pcb
*/
void process_wakeup_immediately(struct process_control_block *pcb)
int process_wakeup_immediately(struct process_control_block *pcb)
{
pcb->state = PROC_RUNNING;
sched_enqueue(pcb);
if (pcb->state == PROC_RUNNING)
return 0;
int retval = process_wakeup(pcb);
if (retval != 0)
return retval;
// 将当前进程标志为需要调度缩短新进程被wakeup的时间
current_pcb->flags |= PF_NEED_SCHED;
}
@ -1159,6 +1144,17 @@ void process_exit_thread(struct process_control_block *pcb)
{
}
/**
* @brief 释放pcb
*
* @param pcb
* @return int
*/
int process_release_pcb(struct process_control_block *pcb)
{
kfree(pcb);
return 0;
}
/**
* @brief 申请可用的文件句柄
*

View File

@ -52,7 +52,6 @@
*
*/
// 设置初始进程的tss
#define INITIAL_TSS \
{ \
@ -73,17 +72,15 @@
.io_map_base_addr = 0 \
}
#define GET_CURRENT_PCB \
"movq %rsp, %rbx \n\t" \
"andq $-32768, %rbx\n\t"
/**
* @brief 切换进程上下文
* 先把rbp和rax保存到栈中然后将rsp和rip保存到prev的thread结构体中
* 然后调用__switch_to切换栈配置其他信息最后恢复下一个进程的rax rbp。
*/
/**
* @brief 切换进程上下文
* 先把rbp和rax保存到栈中然后将rsp和rip保存到prev的thread结构体中
* 然后调用__switch_to切换栈配置其他信息最后恢复下一个进程的rax rbp。
*/
#define switch_proc(prev, next) \
do \
@ -134,14 +131,14 @@ struct process_control_block *process_get_pcb(long pid);
*
* @param pcb 进程的pcb
*/
void process_wakeup(struct process_control_block *pcb);
int process_wakeup(struct process_control_block *pcb);
/**
* @brief 将进程加入到调度器的就绪队列中,并标志当前进程需要被调度
*
* @param pcb 进程的pcb
*/
void process_wakeup_immediately(struct process_control_block *pcb);
int process_wakeup_immediately(struct process_control_block *pcb);
/**
* @brief 使当前进程去执行新的代码
@ -185,7 +182,17 @@ void process_exit_notify();
* @return int
*/
int kernel_thread(unsigned long (*fn)(unsigned long), unsigned long arg, unsigned long flags);
pid_t kernel_thread(int (*fn)(void*), void* arg, unsigned long flags);
int process_fd_alloc(struct vfs_file_t *file);
/**
* @brief 释放pcb
*
* @param pcb
* @return int
*/
int process_release_pcb(struct process_control_block *pcb);
/**
* @brief 切换页表
@ -213,4 +220,3 @@ extern struct mm_struct initial_mm;
extern struct thread_struct initial_thread;
extern union proc_union initial_proc_union;
extern struct process_control_block *initial_proc[MAX_CPU_NUM];
int process_fd_alloc(struct vfs_file_t *file);