feat: signal support (tested!)
authorMinep <zelong56@gmail.com>
Sun, 19 Jun 2022 17:56:28 +0000 (18:56 +0100)
committerMinep <zelong56@gmail.com>
Sun, 19 Jun 2022 17:56:28 +0000 (18:56 +0100)
feat: signal demo.

lunaix-os/includes/lunaix/signal.h
lunaix-os/includes/lunaix/spike.h
lunaix-os/kernel/asm/x86/interrupt.S
lunaix-os/kernel/asm/x86/interrupts.c
lunaix-os/kernel/lxinit.c
lunaix-os/kernel/sched.c
lunaix-os/kernel/signal.c

index 4a4cf0b725234ba2f65ad2fc3276f3fecc1a4041..d1f7041ebd284257b8ce8730f3c155a6d56d426e 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __LUNAIX_SIGNAL_H
 #define __LUNAIX_SIGNAL_H
 
+#include <lunaix/syscall.h>
+
 #define _SIG_NUM 8
 
 #define _SIG_PENDING(bitmap, sig) ((bitmap) & (1 << (sig)))
 #define _SIGSEGV 0
 #define _SIGALRM 1
 #define _SIGCHLD 2
-#define _SIGCLD SIGCHLD
+#define _SIGCLD _SIGCHLD
 #define _SIGINT 3
 #define _SIGKILL 4
 #define _SIGSTOP 5
 #define _SIGCONT 6
 
-#define _SIGNAL_UNMASKABLE ((1 << _SIGKILL) | (1 << _SIGSTOP))
+#define __SIGNAL(num) (1 << (num))
+#define __SET_SIGNAL(bitmap, num) (bitmap = bitmap | __SIGNAL(num))
+
+#define _SIGNAL_UNMASKABLE (__SIGNAL(_SIGKILL) | __SIGNAL(_SIGSTOP))
 
 #define _SIG_BLOCK 1
 #define _SIG_UNBLOCK 2
@@ -23,4 +28,6 @@
 typedef unsigned int sigset_t;
 typedef void (*sighandler_t)(int);
 
+__LXSYSCALL2(int, signal, int, signum, sighandler_t, handler);
+
 #endif /* __LUNAIX_SIGNAL_H */
index 1ce7481aabfb752242dff6ab7e018c3618fba07b..48a19045dbb5b801d5e901856b9c391f8ed4ec81 100644 (file)
@@ -15,7 +15,7 @@
 // 获取v最近的最小k倍数
 #define ROUNDDOWN(v, k) ((v) & ~((k)-1))
 
-#define __USER__ __attribute__((section("usrtext")))
+#define __USER__ __attribute__((section(".usrtext")))
 
 inline static void
 spin()
index e9419837ab5b6c07ea36b9a23173b4aec3d15ba4..48dff2faff1850a4be0b7aca45926d76ab357118 100644 (file)
         .skip 16
 #endif
 
+.section .bss
+    .align 16
+    lo_tmp_stack:
+        .skip 128
+    tmp_stack:
+
 .section .text
     isr_template FAULT_DIVISION_ERROR
     isr_template FAULT_GENERAL_PROTECTION, no_error_code=0
 
         addl $8, %esp
 
-#ifdef __ASM_INTR_DIAGNOSIS
         pushl %eax
+#ifdef __ASM_INTR_DIAGNOSIS
         movl 4(%esp), %eax
         movl %eax, debug_resv
-        popl %eax
 #endif
+        # 处理TSS.ESP的一些边界条件。如果是正常iret(即从内核模式*优雅地*退出)
+        # 那么TSS.ESP0应该为iret进行弹栈后,%esp的值。
+        # 所以这里的边界条件是:如返回用户模式,iret会额外弹出8个字节(ss,esp)
+        movl 8(%esp), %eax
+        andl $3, %eax
+        setnz %al
+        shll $3, %eax
+        addl $16, %eax
+        addl %esp, %eax
+        movl %eax, (_tss + 4)
+        popl %eax
         iret
 
     .global switch_to
     switch_to:
         # 约定
-        # arg1: 目标进程PCB地址 (next)
-        popl %ecx               # next
-
-        call signal_dispatch    # kernel/signal.c
-        movl %eax, %edx
+        # arg1: 目标进程PCB地址 (next
 
+        popl %ecx               # next
         movl __current, %eax    
         movl 88(%eax), %ebx     # __current->pagetable
         movl 88(%ecx), %eax     # next->pagetable
     1:
         movl %ecx, __current    # __current = next
 
-        test %edx, %edx         # do we have signal to handle?
+        # 我们已经处在了新的地址空间,为了避免影响其先前的栈布局
+        # 需要使用一个临时的栈空间
+        movl $tmp_stack, %esp
+        call signal_dispatch    # kernel/signal.c
+
+        test %eax, %eax         # do we have signal to handle?
         jz 1f
-        movl %edx, %eax
         jmp handle_signal
     1:
         leal 8(%ecx), %eax
         # 注意2:handle_signal在调用之前,须确保proc_sig已经写入用户栈!
         leal 8(%eax), %ebx      # arg1 in %eax: addr of proc_sig structure in user stack
 
-        pushl 72(%ebx)          # proc_sig->prev_context.ss
+        pushl $UDATA_SEG        # proc_sig->prev_context.ss
         pushl %eax              # esp
         pushl 64(%ebx)          # proc_sig->prev_context.eflags
         pushl $UCODE_SEG        # cs
index 688799f29212f26548502b1fa9e0a6bcf89a024b..864c7da58af3df7f09a292feee299df532474e43 100644 (file)
@@ -1,4 +1,5 @@
 #include <arch/x86/interrupts.h>
+#include <arch/x86/tss.h>
 #include <hal/apic.h>
 #include <hal/cpu.h>
 #include <lunaix/mm/page.h>
index 57e2bda22421da102c8b063bca8adfd2f1fd58c4..1485c3411e4d95266642876f8481f0b9b3168173 100644 (file)
@@ -5,6 +5,7 @@
 #include <lunaix/mm/kalloc.h>
 #include <lunaix/mm/vmm.h>
 #include <lunaix/proc.h>
+#include <lunaix/signal.h>
 #include <lunaix/spike.h>
 #include <lunaix/syslog.h>
 #include <lunaix/timer.h>
@@ -18,6 +19,12 @@ LOG_MODULE("INIT")
 #define WAIT_DEMO
 #define IN_USER_MODE
 
+void __USER__
+sigchild_handler(int signum)
+{
+    kprintf(KINFO "SIGCHLD received\n");
+}
+
 void __USER__
 _lxinit_main()
 {
@@ -31,6 +38,8 @@ _lxinit_main()
     }
 #endif
 
+    signal(_SIGCHLD, sigchild_handler);
+
     int status;
 #ifdef WAIT_DEMO
     // 测试wait
@@ -57,7 +66,7 @@ _lxinit_main()
 
     waitpid(-1, &status, WNOHANG);
 
-    for (size_t i = 0; i < 5; i++) {
+    for (size_t i = 0; i < 10; i++) {
         pid_t pid = 0;
         if (!(pid = fork())) {
             sleep(i);
index 1a9743dd9752b182e6e151ef645a91c1d76eea68..50f5f67ceaa6cf209d885fd7e9b78ebaa3ef8635 100644 (file)
@@ -49,8 +49,15 @@ run(struct proc_info* proc)
     }
     proc->state = PROC_RUNNING;
 
-    // XXX: 我们需要这一步吗?
-    // tss_update_esp(__current->intr_ctx.esp);
+    /*
+        将tss.esp0设置为上次调度前的esp值。
+        当处理信号时,上下文信息是不会恢复的,而是保存在用户栈中,然后直接跳转进位于用户空间的sig_wrapper进行
+          信号的处理。当用户自定义的信号处理函数返回时,sigreturn的系统调用才开始进行上下文的恢复(或者说是进行
+          另一次调度。
+        由于这中间没有进行地址空间的交换,所以第二次跳转使用的是同一个内核栈,而之前默认tss.esp0的值是永远指向最顶部
+        这样一来就有可能会覆盖更早的上下文信息(比如嵌套的信号捕获函数)
+    */
+    tss_update_esp(proc->intr_ctx.registers.esp);
 
     apic_done_servicing();
 
@@ -258,6 +265,8 @@ terminate_proc(int exit_code)
     __current->state = PROC_TERMNAT;
     __current->exit_code = exit_code;
 
+    __SET_SIGNAL(__current->parent->sig_pending, _SIGCHLD);
+
     schedule();
 }
 
index 4c0bb56ad27b307c1e1b44641d47e28db1b88799..49f1e534f6c65d06c4d0ba916847867e66cc491c 100644 (file)
@@ -26,7 +26,7 @@ signal_dispatch()
     int sig_selected =
       31 - __builtin_clz(__current->sig_pending & ~__current->sig_mask);
 
-    __current->sig_pending = __current->sig_pending & ~(1 << sig_selected);
+    __current->sig_pending = __current->sig_pending & ~__SIGNAL(sig_selected);
 
     if (!__current->sig_handler[sig_selected] &&
         !default_handlers[sig_selected]) {
@@ -34,7 +34,7 @@ signal_dispatch()
         return 0;
     }
 
-    uintptr_t ustack = __current->ustack_top;
+    uintptr_t ustack = __current->ustack_top & ~0xf;
 
     if ((int)(ustack - USTACK_END) < (int)sizeof(struct proc_sig)) {
         // 用户栈没有空间存放信号上下文
@@ -53,12 +53,15 @@ signal_dispatch()
         sig_ctx->signal_handler = default_handlers[sig_selected];
     }
 
+    __current->sig_mask |= __SIGNAL(sig_selected);
+
     return sig_ctx;
 }
 
 __DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx)
 {
     __current->intr_ctx = sig_ctx->prev_context;
+    __current->sig_mask &= ~__SIGNAL(sig_ctx->sig_num);
     schedule();
 }