refactor: mount system reworked
[lunaix-os.git] / lunaix-os / kernel / signal.c
index 49f1e534f6c65d06c4d0ba916847867e66cc491c..5f0c868789d9af36d6286dfae989b9a385e28546 100644 (file)
@@ -1,32 +1,47 @@
+#include <lunaix/lunistd.h>
+#include <lunaix/lxsignal.h>
 #include <lunaix/process.h>
 #include <lunaix/sched.h>
 #include <lunaix/signal.h>
+#include <lunaix/spike.h>
+#include <lunaix/status.h>
 #include <lunaix/syscall.h>
 
 extern struct scheduler sched_ctx; /* kernel/sched.c */
 
+void __USER__
+default_sighandler_term(int signum)
+{
+    _exit(signum);
+}
+
 void* default_handlers[_SIG_NUM] = {
     // TODO: 添加默认handler
+    [_SIGINT] = default_sighandler_term,  [_SIGTERM] = default_sighandler_term,
+    [_SIGKILL] = default_sighandler_term, [_SIGSEGV] = default_sighandler_term,
+    [_SIGINT] = default_sighandler_term,
 };
 
+volatile isr_param __temp_save;
 // Referenced in kernel/asm/x86/interrupt.S
 void*
 signal_dispatch()
 {
-    // if (!(SEL_RPL(__current->intr_ctx.cs))) {
-    //     // 同特权级间调度不进行信号处理
-    //     return 0;
-    // }
-
     if (!__current->sig_pending) {
         // 没有待处理信号
         return 0;
     }
 
     int sig_selected =
-      31 - __builtin_clz(__current->sig_pending & ~__current->sig_mask);
+      31 - __builtin_clz(__current->sig_pending &
+                         ~(__current->sig_mask | __current->sig_inprogress));
 
-    __current->sig_pending = __current->sig_pending & ~__SIGNAL(sig_selected);
+    __SIGCLEAR(__current->sig_pending, sig_selected);
+
+    if (sig_selected == 0) {
+        // SIG0 is reserved
+        return 0;
+    }
 
     if (!__current->sig_handler[sig_selected] &&
         !default_handlers[sig_selected]) {
@@ -44,7 +59,29 @@ signal_dispatch()
     struct proc_sig* sig_ctx =
       (struct proc_sig*)(ustack - sizeof(struct proc_sig));
 
-    sig_ctx->prev_context = __current->intr_ctx;
+    /*
+        这是一个相当恶心的坑。
+        问题是出在原本的sig_ctx->prev_context = __current->intr_ctx的上面
+        这个语句会被gcc在编译时,用更加高效的 rep movsl 来代替。
+
+        由于我们采用按需分页,所以在很多情况下,用户栈实际被分配的空间不允许我们进行完整的
+        注入,而需要走page fault handler进行动态分页。
+
+        竞态条件就出现在这里!
+
+        假若我们的__current->intr_ctx注入了一半,然后产生page-fault中断,
+        那么这就会导致我们的__current->intr_ctx被这个page-fault中断导致的
+        上下文信息覆盖。那么当page-fault handler成功分配了一个页,返回,
+        拷贝也就得以进行。遗憾的是,只不过这次拷贝的内容和前面的拷贝是没有任何的关系
+        (因为此时的intr_ctx已经不是之前的intr_ctx了!)
+        而这就会导致我们保存在信号上下文中的进程上下文信息不完整,从而在soft_iret时
+        触发#GP。
+
+        解决办法就是先吧intr_ctx拷贝到一个静态分配的区域里,然后再注入到用户栈。
+    */
+    __temp_save = __current->intr_ctx;
+    sig_ctx->prev_context = __temp_save;
+
     sig_ctx->sig_num = sig_selected;
     sig_ctx->signal_handler = __current->sig_handler[sig_selected];
 
@@ -53,15 +90,58 @@ signal_dispatch()
         sig_ctx->signal_handler = default_handlers[sig_selected];
     }
 
-    __current->sig_mask |= __SIGNAL(sig_selected);
+    __SIGSET(__current->sig_inprogress, sig_selected);
 
     return sig_ctx;
 }
 
+int
+signal_send(pid_t pid, int signum)
+{
+    if (signum < 0 || signum >= _SIG_NUM) {
+        __current->k_status = EINVAL;
+        return -1;
+    }
+
+    struct proc_info* proc;
+    if (pid > 0) {
+        proc = get_process(pid);
+        goto send_single;
+    } else if (!pid) {
+        proc = __current;
+        goto send_grp;
+    } else if (pid < -1) {
+        proc = get_process(-pid);
+        goto send_grp;
+    } else {
+        // TODO: send to all process.
+        //  But I don't want to support it yet.
+        __current->k_status = EINVAL;
+        return -1;
+    }
+
+send_grp:
+    struct proc_info *pos, *n;
+    llist_for_each(pos, n, &proc->grp_member, grp_member)
+    {
+        __SIGSET(pos->sig_pending, signum);
+    }
+    return 0;
+
+send_single:
+    if (PROC_TERMINATED(proc->state)) {
+        __current->k_status = EINVAL;
+        return -1;
+    }
+    __SIGSET(proc->sig_pending, signum);
+    return 0;
+}
+
 __DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx)
 {
     __current->intr_ctx = sig_ctx->prev_context;
-    __current->sig_mask &= ~__SIGNAL(sig_ctx->sig_num);
+    __current->flags &= ~PROC_FINPAUSE;
+    __SIGCLEAR(__current->sig_inprogress, sig_ctx->sig_num);
     schedule();
 }
 
@@ -90,15 +170,53 @@ __DEFINE_LXSYSCALL3(int,
 
 __DEFINE_LXSYSCALL2(int, signal, int, signum, sighandler_t, handler)
 {
-    if (signum < 0 || signum >= _SIG_NUM) {
+    if (signum <= 0 || signum >= _SIG_NUM) {
         return -1;
     }
 
-    if (((1 << signum) & _SIGNAL_UNMASKABLE)) {
+    if ((__SIGNAL(signum) & _SIGNAL_UNMASKABLE)) {
         return -1;
     }
 
     __current->sig_handler[signum] = (void*)handler;
 
     return 0;
+}
+
+void
+__do_pause()
+{
+    __current->flags |= PROC_FINPAUSE;
+
+    while ((__current->flags & PROC_FINPAUSE)) {
+        sched_yieldk();
+    }
+
+    __current->k_status = EINTR;
+}
+
+__DEFINE_LXSYSCALL(int, pause)
+{
+    __do_pause();
+    return -1;
+}
+
+__DEFINE_LXSYSCALL2(int, kill, pid_t, pid, int, signum)
+{
+    return signal_send(pid, signum);
+}
+
+__DEFINE_LXSYSCALL1(int, sigpending, sigset_t, *sigset)
+{
+    *sigset = __current->sig_pending;
+    return 0;
+}
+
+__DEFINE_LXSYSCALL1(int, sigsuspend, sigset_t, *mask)
+{
+    sigset_t tmp = __current->sig_mask;
+    __current->sig_mask = (*mask) & ~_SIGNAL_UNMASKABLE;
+    __do_pause();
+    __current->sig_mask = tmp;
+    return -1;
 }
\ No newline at end of file