新增rust ffi (#77)

* 引入cargo

* 取消对Cargo.lock的跟踪

* 解决vscode报错问题

* new: rust的代码能够调用c语言的printk_color

* 1、将原本run.sh的工作拆解,变为几个不同的make命令
2、在docker镜像中编译rust

* 更改workflow

* update workflow

* new: 解决workflow无法通过编译的问题
This commit is contained in:
login
2022-11-11 15:35:37 +08:00
committed by GitHub
parent 5e023cf791
commit 2813126e31
271 changed files with 609 additions and 307 deletions

View File

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

View File

@ -0,0 +1,330 @@
#include <common/glib.h>
#include <common/kthread.h>
#include <common/spinlock.h>
#include <debug/bug.h>
#include <sched/sched.h>
#include <time/sleep.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的结果,
// 成功则返回PCB不成功则该值为负数错误码。若该值为NULL意味着创建过程尚未完成
struct process_control_block *result;
struct List list;
};
/**
* @brief 获取pcb中的kthread结构体
*
* @param pcb pcb
* @return struct kthread* kthread信息结构体
*/
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);
BUG_ON(name_fmt == NULL);
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,
// todo: 使用completion优化这里
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))
{
// 为内核线程设置名字
char pcb_name[PCB_NAME_LEN];
va_list get_args;
va_copy(get_args, args);
// 获取到字符串的前16字节
int len = vsnprintf(pcb_name, name_fmt, PCB_NAME_LEN, get_args);
if (len >= PCB_NAME_LEN)
{
//名字过大 放到full_name字段中
struct kthread_info_t *kthread = to_kthread(pcb);
char *full_name = kzalloc(1024, 0);
vsprintf(full_name, name_fmt, get_args);
kthread->full_name = full_name;
}
// 将前16Bytes放到pcb的name字段
process_set_pcb_name(pcb, pcb_name);
va_end(get_args);
}
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;
current_pcb->state &= ~PROC_RUNNING; // 设置当前进程不是RUNNING态
io_mfence();
// 发起调度使得当前内核线程休眠。直到创建者通过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);
io_mfence();
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;
}
/**
* @brief 释放pcb指向的worker private
*
* @param pcb 要释放的pcb
*/
void free_kthread_struct(struct process_control_block *pcb)
{
struct kthread_info_t *kthread = to_kthread(pcb);
if (!kthread)
{
return;
}
pcb->worker_private = NULL;
kfree(kthread->full_name);
kfree(kthread);
}

View File

@ -0,0 +1,28 @@
#pragma once
#if ARCH(X86_64)
#include <arch/x86_64/current.h>
#else
#error Unsupported architecture!
#endif
#include "proc-types.h"
/**
* @brief 增加自旋锁计数变量
*
*/
#define preempt_disable() \
do \
{ \
++(current_pcb->preempt_count);\
} while (0)
/**
* @brief 减少自旋锁计数变量
*
*/
#define preempt_enable() \
do \
{ \
--(current_pcb->preempt_count);\
}while(0)

View File

@ -0,0 +1,144 @@
#pragma once
#include <common/wait_queue.h>
// 进程最大可拥有的文件描述符数量
#define PROC_MAX_FD_NUM 16
// 进程的内核栈大小 32K
#define STACK_SIZE 32768
// 进程的运行状态
// 正在运行
#define PROC_RUNNING (1 << 0)
// 可被中断
#define PROC_INTERRUPTIBLE (1 << 1)
// 不可被中断
#define PROC_UNINTERRUPTIBLE (1 << 2)
// 挂起
#define PROC_ZOMBIE (1 << 3)
// 已停止
#define PROC_STOPPED (1 << 4)
// 内核代码段基地址
#define KERNEL_CS (0x08)
// 内核数据段基地址
#define KERNEL_DS (0x10)
// 用户代码段基地址
#define USER_CS (0x28)
// 用户数据段基地址
#define USER_DS (0x30)
// 进程初始化时的数据拷贝标志位
#define CLONE_FS (1UL << 0) // 在进程间共享打开的文件
#define CLONE_SIGNAL (1UL << 1)
#define CLONE_VM (1UL << 2) // 在进程间共享虚拟内存空间
#define PCB_NAME_LEN 16
struct thread_struct
{
// 内核层栈基指针
ul rbp; // in tss rsp0
// 内核层代码指针
ul rip;
// 内核层栈指针
ul rsp;
ul fs, gs;
ul cr2;
// 异常号
ul trap_num;
// 错误码
ul err_code;
};
// ========= pcb->flags =========
// 进程标志位
#define PF_KTHREAD (1UL << 0) // 内核线程
#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 进程控制块
*
*/
struct process_control_block
{
// 进程的状态
volatile long state;
// 进程标志:进程、线程、内核线程
unsigned long flags;
int64_t preempt_count; // 持有的自旋锁的数量
long signal;
long cpu_id; // 当前进程在哪个CPU核心上运行
char name[PCB_NAME_LEN];
// 内存空间分布结构体, 记录内存页表和程序段信息
struct mm_struct *mm;
// 进程切换时保存的状态信息
struct thread_struct *thread;
// 连接各个pcb的双向链表
struct List list;
//todo:给pcb中加一个spinlock_t成员
//进程自旋锁
// spinlock_t alloc_lock;
// 地址空间范围
// 用户空间: 0x0000 0000 0000 0000 ~ 0x0000 7fff ffff ffff
// 内核空间: 0xffff 8000 0000 0000 ~ 0xffff ffff ffff ffff
uint64_t addr_limit;
long pid;
long priority; // 优先级
int64_t virtual_runtime; // 虚拟运行时间
// 进程拥有的文件描述符的指针数组
// todo: 改用动态指针数组
struct vfs_file_t *fds[PROC_MAX_FD_NUM];
// 链表中的下一个pcb
struct process_control_block *next_pcb;
// 父进程的pcb
struct process_control_block *parent_pcb;
int32_t exit_code; // 进程退出时的返回码
uint32_t policy; // 进程调度策略标志位
wait_queue_node_t wait_child_proc_exit; // 子进程退出等待队列
/* PF_kTHREAD | PF_IO_WORKER 的进程worker_private不为NULL*/
void *worker_private;
};
// 将进程的pcb和内核栈融合到一起,8字节对齐
union proc_union
{
struct process_control_block pcb;
ul stack[STACK_SIZE / sizeof(ul)];
} __attribute__((aligned(8)));
struct tss_struct
{
unsigned int reserved0;
ul rsp0;
ul rsp1;
ul rsp2;
ul reserved1;
ul ist1;
ul ist2;
ul ist3;
ul ist4;
ul ist5;
ul ist6;
ul ist7;
ul reserved2;
unsigned short reserved3;
// io位图基地址
unsigned short io_map_base_addr;
} __attribute__((packed)); // 使用packed表明是紧凑结构编译器不会对成员变量进行字节对齐。

36
kernel/src/process/proc.S Normal file
View File

@ -0,0 +1,36 @@
#include"../common/asm.h"
/**
* @brief 导出内核线程的执行引导程序
* (kernel_thread)
* rsp
* 7unsigned longpt_regs
*/
ENTRY(kernel_thread_func)
popq %r15
popq %r14
popq %r13
popq %r12
popq %r11
popq %r10
popq %r9
popq %r8
popq %rbx
popq %rcx
popq %rdx
popq %rsi
popq %rdi
popq %rbp
popq %rax
movq %rax, %ds
popq %rax
movq %rax, %es
popq %rax
addq $0x20, %rsp
popfq
addq $0x10, %rsp
movq %rdx, %rdi
callq *%rbx
movq %rax, %rdi
callq process_do_exit

1229
kernel/src/process/process.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,226 @@
/**
* @file process.h
* @author longjin
* @brief 进程
* @date 2022-01-29
*
* @copyright Copyright (c) 2022
*
*/
#pragma once
#include <common/cpu.h>
#include <common/glib.h>
#include <syscall/syscall.h>
#include "ptrace.h"
#include <common/errno.h>
#include <filesystem/VFS/VFS.h>
#include <common/wait_queue.h>
#include <mm/mm-types.h>
#if ARCH(I386) || ARCH(X86_64)
#include <arch/x86_64/current.h>
#else
#error Unsupported architecture!
#endif
#include "proc-types.h"
// 设置初始进程的PCB
#define INITIAL_PROC(proc) \
{ \
.state = PROC_UNINTERRUPTIBLE, \
.flags = PF_KTHREAD, \
.preempt_count = 0, \
.signal = 0, \
.cpu_id = 0, \
.mm = &initial_mm, \
.thread = &initial_thread, \
.addr_limit = 0xffffffffffffffff, \
.pid = 0, \
.priority = 2, \
.virtual_runtime = 0, \
.fds = {0}, \
.next_pcb = &proc, \
.parent_pcb = &proc, \
.exit_code = 0, \
.wait_child_proc_exit = 0, \
.worker_private = NULL, \
.policy = SCHED_NORMAL \
}
/**
* @brief 任务状态段结构体
*
*/
// 设置初始进程的tss
#define INITIAL_TSS \
{ \
.reserved0 = 0, \
.rsp0 = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)), \
.rsp1 = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)), \
.rsp2 = (ul)(initial_proc_union.stack + STACK_SIZE / sizeof(ul)), \
.reserved1 = 0, \
.ist1 = 0xffff800000007c00, \
.ist2 = 0xffff800000007c00, \
.ist3 = 0xffff800000007c00, \
.ist4 = 0xffff800000007c00, \
.ist5 = 0xffff800000007c00, \
.ist6 = 0xffff800000007c00, \
.ist7 = 0xffff800000007c00, \
.reserved2 = 0, \
.reserved3 = 0, \
.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。
*/
#define switch_proc(prev, next) \
do \
{ \
__asm__ __volatile__("pushq %%rbp \n\t" \
"pushq %%rax \n\t" \
"movq %%rsp, %0 \n\t" \
"movq %2, %%rsp \n\t" \
"leaq switch_proc_ret_addr(%%rip), %%rax \n\t" \
"movq %%rax, %1 \n\t" \
"pushq %3 \n\t" \
"jmp __switch_to \n\t" \
"switch_proc_ret_addr: \n\t" \
"popq %%rax \n\t" \
"popq %%rbp \n\t" \
: "=m"(prev->thread->rsp), "=m"(prev->thread->rip) \
: "m"(next->thread->rsp), "m"(next->thread->rip), "D"(prev), "S"(next) \
: "memory"); \
} while (0)
/**
* @brief 初始化系统的第一个进程
*
*/
void process_init();
/**
* @brief fork当前进程
*
* @param regs 新的寄存器值
* @param clone_flags 克隆标志
* @param stack_start 堆栈开始地址
* @param stack_size 堆栈大小
* @return unsigned long
*/
unsigned long do_fork(struct pt_regs *regs, unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size);
/**
* @brief 根据pid获取进程的pcb
*
* @param pid
* @return struct process_control_block*
*/
struct process_control_block *process_get_pcb(long pid);
/**
* @brief 将进程加入到调度器的就绪队列中
*
* @param pcb 进程的pcb
*/
int process_wakeup(struct process_control_block *pcb);
/**
* @brief 将进程加入到调度器的就绪队列中,并标志当前进程需要被调度
*
* @param pcb 进程的pcb
*/
int process_wakeup_immediately(struct process_control_block *pcb);
/**
* @brief 使当前进程去执行新的代码
*
* @param regs 当前进程的寄存器
* @param path 可执行程序的路径
* @param argv 参数列表
* @param envp 环境变量
* @return ul 错误码
*/
ul do_execve(struct pt_regs *regs, char *path, char *argv[], char *envp[]);
/**
* @brief 释放进程的页表
*
* @param pcb 要被释放页表的进程
* @return uint64_t
*/
uint64_t process_exit_mm(struct process_control_block *pcb);
/**
* @brief 进程退出时执行的函数
*
* @param code 返回码
* @return ul
*/
ul process_do_exit(ul code);
/**
* @brief 当子进程退出后向父进程发送通知
*
*/
void process_exit_notify();
/**
* @brief 初始化内核进程
*
* @param fn 目标程序的地址
* @param arg 向目标程序传入的参数
* @param flags
* @return int
*/
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
int process_fd_alloc(struct vfs_file_t *file);
int process_release_pcb(struct process_control_block *pcb);
/**
* @brief 切换页表
* @param prev 前一个进程的pcb
* @param next 下一个进程的pcb
*
*/
#define process_switch_mm(next_pcb) \
do \
{ \
asm volatile("movq %0, %%cr3 \n\t" ::"r"(next_pcb->mm->pgd) \
: "memory"); \
} while (0)
// flush_tlb();
// 获取当前cpu id
#define proc_current_cpu_id (current_pcb->cpu_id)
extern unsigned long head_stack_start; // 导出内核层栈基地址定义在head.S
extern ul _stack_start;
extern void ret_from_intr(void); // 导出从中断返回的函数定义在entry.S
extern struct tss_struct initial_tss[MAX_CPU_NUM];
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];
/**
* @brief 给pcb设置名字
*
* @param pcb 需要设置名字的pcb
* @param pcb_name 保存名字的char数组
*/
void process_set_pcb_name(struct process_control_block *pcb, const char *pcb_name);

View File

@ -0,0 +1,37 @@
#ifndef __PTRACE_H__
#define __PTRACE_H__
/*
*/
struct pt_regs
{
unsigned long r15;
unsigned long r14;
unsigned long r13;
unsigned long r12;
unsigned long r11;
unsigned long r10;
unsigned long r9;
unsigned long r8;
unsigned long rbx;
unsigned long rcx;
unsigned long rdx;
unsigned long rsi;
unsigned long rdi;
unsigned long rbp;
unsigned long ds;
unsigned long es;
unsigned long rax;
unsigned long func;
unsigned long errcode;
unsigned long rip;
unsigned long cs;
unsigned long rflags;
unsigned long rsp;
unsigned long ss;
};
#endif