From cb6237403a039a1eebc703c3cf0fdb8be4bcb5fd Mon Sep 17 00:00:00 2001 From: aLinChe <1129332011@qq.com> Date: Wed, 26 Nov 2025 00:21:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(syscall):=20=E4=BF=AE=E5=A4=8Dcputime,=20sy?= =?UTF-8?q?s=5Frt=5Fsigtimedwait=20and=20sys=5Frt=5Fsigreturn=20(#1406)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. **修复:** 进程 CPU 时间统UserUContext 计精度,将统计基准调整为扣除 IRQ 和 Steal 时间后的净时间(accounted_cputime),避免将硬件中断处理时间错误计入进程的 utime/stime 2. **修复:** `sys_rt_sigtimedwait`: - 修复了信号等待逻辑,确保即使 等待信号集 为空也能正确进入 do_kernel_rt_sigtimedwait,符合 Linux 行为。 - 修复潜在的无限睡眠问题:当线程被非目标信号或非超时事件唤醒时,将正确返回 EINTR,避免进程一直挂起。 - 修复了bitflags错误移除的bug,应该使用remove()。 3. **修复:** `sys_rt_sigreturn:`: - 当从信号处理函数返回用户态时,内核错误地使用 sysretq 来处理 sys_rt_sigreturn 的返回,%rcx 和 %r11 这两个寄存器被意外破坏。 - 修复:如果待恢复的 %rcx / %r11 与 sysretq 的行为冲突,则强制跳转到 .L_syscall_must_use_iret 分支,使用 iretq 指令精确恢复完整的上下文。 TODO: 目标线程的 TGID 没有正确设置为 leader 的 PID,导致还有两个sys_rt_sigtimedwait的测例无法通过。 ```sh Value of: tgkill(getpid(), tid, kSigno) Expected: not -1 (success) Actual: -1 (of type int), with errno PosixError(errno=3 No such process) ``` --------- Signed-off-by: aLinChe <1129332011@qq.com> --- kernel/src/arch/x86_64/asm/entry.S | 11 ++++++++ kernel/src/ipc/syscall/sys_rt_sigtimedwait.rs | 25 ++++++++++--------- kernel/src/sched/cputime.rs | 16 ++++++------ .../syscall/gvisor/blocklists/sigreturn_test | 2 -- .../gvisor/blocklists/sigtimedwait_test | 3 +++ user/apps/tests/syscall/gvisor/whitelist.txt | 1 + 6 files changed, 36 insertions(+), 22 deletions(-) delete mode 100644 user/apps/tests/syscall/gvisor/blocklists/sigreturn_test create mode 100644 user/apps/tests/syscall/gvisor/blocklists/sigtimedwait_test diff --git a/kernel/src/arch/x86_64/asm/entry.S b/kernel/src/arch/x86_64/asm/entry.S index 8df6566cf..9c2c5732c 100644 --- a/kernel/src/arch/x86_64/asm/entry.S +++ b/kernel/src/arch/x86_64/asm/entry.S @@ -407,6 +407,11 @@ ENTRY(syscall_64) popq %rax addq $0x10, %rsp // 弹出变量FUNC和errcode + cmpq (%rsp), %rcx + jne .L_syscall_must_use_iret + cmpq 0x10(%rsp), %r11 + jne .L_syscall_must_use_iret + popq %rcx // pop rip到rcx addq $0x8, %rsp // 弹出cs @@ -415,3 +420,9 @@ ENTRY(syscall_64) swapgs sysretq + +// 适用于 sigreturn, ptrace, 或任何修改了上下文的情况 +// 此时栈结构完美符合 iretq 要求: [RIP, CS, RFLAGS, RSP, SS] +.L_syscall_must_use_iret: + swapgs + iretq diff --git a/kernel/src/ipc/syscall/sys_rt_sigtimedwait.rs b/kernel/src/ipc/syscall/sys_rt_sigtimedwait.rs index 818bd3909..0ac548c41 100644 --- a/kernel/src/ipc/syscall/sys_rt_sigtimedwait.rs +++ b/kernel/src/ipc/syscall/sys_rt_sigtimedwait.rs @@ -58,19 +58,12 @@ pub fn do_kernel_rt_sigtimedwait( let reader = UserBufferReader::new(uthese, size_of::(), from_user)?; let sigset = reader.read_one_from_user::(0)?; // 移除不可屏蔽的信号(SIGKILL 和 SIGSTOP) - let sigset_val: SigSet = SigSet::from_bits(sigset.bits()).ok_or(SystemError::EINVAL)?; - let kill_stop_mask: SigSet = - SigSet::from_bits_truncate((Signal::SIGKILL as u64) | (Signal::SIGSTOP as u64)); - let result = sigset_val & !kill_stop_mask; - - result + let mut sigset_val = SigSet::from_bits(sigset.bits()).ok_or(SystemError::EINVAL)?; + sigset_val.remove(SigSet::from(Signal::SIGKILL)); + sigset_val.remove(SigSet::from(Signal::SIGSTOP)); + sigset_val }; - // 如果信号集合为空,直接返回 EINVAL(根据 POSIX 标准) - if these.is_empty() { - return Err(SystemError::EINVAL); - } - // 构造等待/屏蔽语义:与Linux一致 // - 等待集合 these // - 临时屏蔽集合 = 旧blocked ∪ these(将这些信号作为masked的常规语义,但仍由本系统调用专门消费) @@ -134,13 +127,21 @@ pub fn do_kernel_rt_sigtimedwait( } } - // 第四步:释放中断,然后真正进入调度睡眠(窗口期内,线程保持可中断阻塞,发送侧会唤醒) + // 第四步:检查是否有其他未屏蔽的待处理信号打断,若被其他信号唤醒了,必须返回 EINTR 让内核去处理那个信号 + pcb.recalc_sigpending(None); + if pcb.has_pending_signal_fast() { + drop(preempt_guard); + restore_saved_sigmask(); + return Err(SystemError::EINTR); + } + // 第五步:释放中断,然后真正进入调度睡眠(窗口期内,线程保持可中断阻塞,发送侧会唤醒) // 计算剩余等待时间 let remaining_time = if let Some(deadline) = deadline { let now = PosixTimeSpec::now(); let remaining = deadline.total_nanos() - now.total_nanos(); if remaining <= 0 { + drop(preempt_guard); restore_saved_sigmask(); return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); } diff --git a/kernel/src/sched/cputime.rs b/kernel/src/sched/cputime.rs index f8bec7fcc..0339b499f 100644 --- a/kernel/src/sched/cputime.rs +++ b/kernel/src/sched/cputime.rs @@ -88,15 +88,15 @@ impl CpuTimeFunc { return; } - if user_tick { - pcb.account_utime(cputime); - } else { - pcb.account_stime(cputime); - } - pcb.add_sum_exec_runtime(cputime); + let accounted_cputime = cputime - other; + + if user_tick { + pcb.account_utime(accounted_cputime); + } else { + pcb.account_stime(accounted_cputime); + } + pcb.add_sum_exec_runtime(accounted_cputime); - let cputime_ns = TICK_NESC as u64 * ticks; - let accounted_cputime = cputime_ns - other; // 检查并处理CPU时间定时器 let mut itimers = pcb.itimers_irqsave(); // 处理 ITIMER_VIRTUAL (仅在用户态tick时消耗时间) diff --git a/user/apps/tests/syscall/gvisor/blocklists/sigreturn_test b/user/apps/tests/syscall/gvisor/blocklists/sigreturn_test deleted file mode 100644 index 89bd7355d..000000000 --- a/user/apps/tests/syscall/gvisor/blocklists/sigreturn_test +++ /dev/null @@ -1,2 +0,0 @@ -# 还在修复中 -SigIretTest.CheckRcxR11 diff --git a/user/apps/tests/syscall/gvisor/blocklists/sigtimedwait_test b/user/apps/tests/syscall/gvisor/blocklists/sigtimedwait_test new file mode 100644 index 000000000..d46c39df5 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/blocklists/sigtimedwait_test @@ -0,0 +1,3 @@ +# getpid() 好像有点问题,还在修 +SigtimedwaitTest.IgnoredUnmaskedSignal +SigtimedwaitTest.IgnoredMaskedSignal diff --git a/user/apps/tests/syscall/gvisor/whitelist.txt b/user/apps/tests/syscall/gvisor/whitelist.txt index bd5bca749..69760d340 100644 --- a/user/apps/tests/syscall/gvisor/whitelist.txt +++ b/user/apps/tests/syscall/gvisor/whitelist.txt @@ -57,6 +57,7 @@ sigaction_test sigprocmask_test sigaltstack_test sigreturn_test +sigtimedwait_test # 其他测试 itimer_test