执行一个信号处理程序是件相当复杂的任务,因为在用户态和内核态之间切换时需要谨慎地处理栈中的内容。 信号处理程序是用户态进程所定义的函数,并包含在用户态的代码段中。handle_signal( )函数运行在内核态而信号处理程序运行在用户态,这就意味着在当前进程恢复 “正常”执行之前,它必须首先执行用户态的信号处理程序。此外,当内核打算恢复进程的正常执行时,内核态堆栈不再包含被中断程序的硬件上下文,因为每当从内核态向用户态转换时,内核态堆栈被清空。而另外一个复杂性是因为信号处理程序可以调用系统调用,在这种情况下,执行了系统调用的服务例程以后,控制权必须返回到信号处理程序而不是到被中断程序的代码。Linux所采用的解决方法是: 一个非阻塞的信号发送给一个进程。当中断或异常发生时,进程切换到内核态。正要返回到用户态前,内核执行do_signal()函数,这个函数又依次处理信号( 通过调用handle_signal() )和建立用户态堆栈(通过调用setup_frame()或setup_rt_frame() )。当进程又切换到用户态时,因为信号处理程序的起始地址被强制放进程序计数器中,因此开始执行信号处理程序。当处理程序终止时,setup_frame() 或setup_rt_frame()函数放在用户态堆栈中的返回代码就被执行。这个代码调用sigreturn()系统调用,它的服务例程把正常程序的用户态堆栈硬件上下文拷贝到内核态堆栈,并把用户态堆栈恢复回到它原来的状态(通过调用restore_sigcontext() )。当这个系统调用结束时,普通进程因此能恢复自己的执行。
调用用顺序:
do_signal()->handle_signal()->setup_rt_frame()用来设置用户态堆栈
在执行完signal handler后,进程将跳转到__kernel_rt_sigreturn
如上图,ret_to_user是系统调用、中断、异常触发处理完成后都会调用的函数,调用路径(arm64):
ret_to_user -> work_pending -> do_notify_resume(arch/arm64/kernel/signal.c) -> do_signal -> get_signal、handle_signal -> setup_rt_frame。
setup_rt_frame里面会将注册处理函数压入到用户线程栈上,待返回到用户态,注册函数即被调用执行。
get_signal里面会调用判断信号的关联行为,如果需要结束进程,侧会调用do_group_exit销毁进程关联资源,这时候不会走到handle_signal调用户注册函数;如果信号已被捕获,则返回true,提示需要走handle_signal调用用户注册函数。
信号句柄执行完毕
sys_sigreturn()函数从sigframe的uc_mcontext中拷贝进程硬件上下文至内核栈中,并把帧数据从用户栈中删除;通过调用restore_sigcontext()函数实现这两步。
(在setup_frame->setup_sigframe中把当前进程硬件上下文内容保存到帧数据中,即保存到uc_mcontext中,一并放入用户栈,信号句柄执行完毕再放回内核栈)
setup_rt_frame:SignalSetup
// SignalSetup implements Context.SignalSetup. (Compare to Linux's // arch/x86/kernel/signal.c:__setup_rt_frame().) func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt *SignalStack, sigset linux.SignalSet) error { sp := st.Bottom // "The 128-byte area beyond the location pointed to by %rsp is considered // to be reserved and shall not be modified by signal or interrupt // handlers. ... leaf functions may use this area for their entire stack // frame, rather than adjusting the stack pointer in the prologue and // epilogue." - AMD64 ABI // // (But this doesn't apply if we're starting at the top of the signal // stack, in which case there is no following stack frame.) if !(alt.IsEnabled() && sp == alt.Top()) { sp -= 128 } // Allocate space for floating point state on the stack. // // This isn't strictly necessary because we don't actually populate // the fpstate. However we do store the floating point state of the // interrupted thread inside the sentry. Simply accounting for this // space on the user stack naturally caps the amount of memory the // sentry will allocate for this purpose. fpSize, _ := c.fpuFrameSize() sp = (sp - usermem.Addr(fpSize)) & ^usermem.Addr(63) // Construct the UContext64 now since we need its size. uc := &UContext64{ // No _UC_FP_XSTATE: see Fpstate above. // No _UC_STRICT_RESTORE_SS: we don't allow SS changes. Flags: _UC_SIGCONTEXT_SS, Stack: *alt, MContext: SignalContext64{ R8: c.Regs.R8, R9: c.Regs.R9, R10: c.Regs.R10, R11: c.Regs.R11, R12: c.Regs.R12, R13: c.Regs.R13, R14: c.Regs.R14, R15: c.Regs.R15, Rdi: c.Regs.Rdi, Rsi: c.Regs.Rsi, Rbp: c.Regs.Rbp, Rbx: c.Regs.Rbx, Rdx: c.Regs.Rdx, Rax: c.Regs.Rax, Rcx: c.Regs.Rcx, Rsp: c.Regs.Rsp, Rip: c.Regs.Rip, Eflags: c.Regs.Eflags, Cs: uint16(c.Regs.Cs), Ss: uint16(c.Regs.Ss), Oldmask: sigset, }, Sigset: sigset, } // TODO: Set SignalContext64.Err, Trapno, and Cr2 based on // the fault that caused the signal. For now, leave Err and Trapno // unset and assume CR2 == info.Addr() for SIGSEGVs and SIGBUSes. if linux.Signal(info.Signo) == linux.SIGSEGV || linux.Signal(info.Signo) == linux.SIGBUS { uc.MContext.Cr2 = info.Addr() } // "... the value (%rsp+8) is always a multiple of 16 (...) when control is // transferred to the function entry point." - AMD64 ABI ucSize := binary.Size(uc) if ucSize < 0 { // This can only happen if we've screwed up the definition of // UContext64. panic("can't get size of UContext64") } // st.Arch.Width() is for the restorer address. sizeof(siginfo) == 128. frameSize := int(st.Arch.Width()) + ucSize + 128 frameBottom := (sp-usermem.Addr(frameSize)) & ^usermem.Addr(15) - 8 sp = frameBottom + usermem.Addr(frameSize) st.Bottom = sp // Prior to proceeding, figure out if the frame will exhaust the range // for the signal stack. This is not allowed, and should immediately // force signal delivery (reverting to the default handler). if act.IsOnStack() && alt.IsEnabled() && !alt.Contains(frameBottom) { return syscall.EFAULT } // Adjust the code. info.FixSignalCodeForUser() // Set up the stack frame. infoAddr, err := st.Push(info) if err != nil { return err } ucAddr, err := st.Push(uc) if err != nil { return err } if act.HasRestorer() { // Push the restorer return address. // Note that this doesn't need to be popped. if _, err := st.Push(usermem.Addr(act.Restorer)); err != nil { return err } } else { // amd64 requires a restorer. return syscall.EFAULT } // Set up registers. c.Regs.Rip = act.Handler c.Regs.Rsp = uint64(st.Bottom) c.Regs.Rdi = uint64(info.Signo) c.Regs.Rsi = uint64(infoAddr) c.Regs.Rdx = uint64(ucAddr) c.Regs.Rax = 0 c.Regs.Ds = userDS c.Regs.Es = userDS c.Regs.Cs = userCS c.Regs.Ss = userDS // Save the thread's floating point state. c.sigFPState = append(c.sigFPState, c.x86FPState) // Signal handler gets a clean floating point state. c.x86FPState = newX86FPState() return nil }
sys_rt_sigreturn:SignalRestore
// SignalRestore implements Context.SignalRestore. (Compare to Linux's // arch/x86/kernel/signal.c:sys_rt_sigreturn().) func (c *context64) SignalRestore(st *Stack, rt bool) (linux.SignalSet, SignalStack, error) { // Copy out the stack frame. var uc UContext64 if _, err := st.Pop(&uc); err != nil { return 0, SignalStack{}, err } var info SignalInfo if _, err := st.Pop(&info); err != nil { return 0, SignalStack{}, err } // Restore registers. c.Regs.R8 = uc.MContext.R8 c.Regs.R9 = uc.MContext.R9 c.Regs.R10 = uc.MContext.R10 c.Regs.R11 = uc.MContext.R11 c.Regs.R12 = uc.MContext.R12 c.Regs.R13 = uc.MContext.R13 c.Regs.R14 = uc.MContext.R14 c.Regs.R15 = uc.MContext.R15 c.Regs.Rdi = uc.MContext.Rdi c.Regs.Rsi = uc.MContext.Rsi c.Regs.Rbp = uc.MContext.Rbp c.Regs.Rbx = uc.MContext.Rbx c.Regs.Rdx = uc.MContext.Rdx c.Regs.Rax = uc.MContext.Rax c.Regs.Rcx = uc.MContext.Rcx c.Regs.Rsp = uc.MContext.Rsp c.Regs.Rip = uc.MContext.Rip c.Regs.Eflags = (c.Regs.Eflags & ^eflagsRestorable) | (uc.MContext.Eflags & eflagsRestorable) c.Regs.Cs = uint64(uc.MContext.Cs) | 3 // N.B. _UC_STRICT_RESTORE_SS not supported. c.Regs.Orig_rax = math.MaxUint64 // Restore floating point state. l := len(c.sigFPState) if l > 0 { c.x86FPState = c.sigFPState[l-1] // NOTE: State save requires that any slice // elements from '[len:cap]' to be zero value. c.sigFPState[l-1] = nil c.sigFPState = c.sigFPState[0 : l-1] } else { // This might happen if sigreturn(2) calls are unbalanced with // respect to signal handler entries. This is not expected so // don't bother to do anything fancy with the floating point // state. log.Infof("sigreturn unable to restore application fpstate") } return uc.Sigset, uc.Stack, nil }
SignalSetup
root@cloud:~# dlv attach 985757 could not attach to pid 985757: no such process root@cloud:~# dlv attach 980757 Type 'help' for list of commands. (dlv) b SignalSetup Breakpoint 1 set at 0x281974 for gvisor.dev/gvisor/pkg/sentry/arch.(*context64).SignalSetup() pkg/sentry/arch/signal_arm64.go:86 (dlv) c > gvisor.dev/gvisor/pkg/sentry/arch.(*context64).SignalSetup() pkg/sentry/arch/signal_arm64.go:86 (hits goroutine(278):1 total:1) (PC: 0x281974) Warning: debugging optimized function (dlv) bt 0 0x0000000000281974 in gvisor.dev/gvisor/pkg/sentry/arch.(*context64).SignalSetup at pkg/sentry/arch/signal_arm64.go:86 1 0x000000000051cfdc in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).deliverSignalToHandler at pkg/sentry/kernel/task_signals.go:284 2 0x000000000051c718 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).deliverSignal at pkg/sentry/kernel/task_signals.go:221 3 0x00000000005202e8 in gvisor.dev/gvisor/pkg/sentry/kernel.(*runInterrupt).execute at pkg/sentry/kernel/task_signals.go:1077 4 0x0000000000517d9c in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).run at pkg/sentry/kernel/task_run.go:97 5 0x0000000000077c84 in runtime.goexit at src/runtime/asm_arm64.s:1136 (dlv) clearall Breakpoint 1 cleared at 0x281974 for gvisor.dev/gvisor/pkg/sentry/arch.(*context64).SignalSetup() pkg/sentry/arch/signal_arm64.go:86 (dlv) n > gvisor.dev/gvisor/pkg/sentry/arch.(*context64).SignalSetup() pkg/sentry/arch/signal_arm64.go:87 (PC: 0x28198c) Warning: debugging optimized function (dlv) quit Would you like to kill the process? [Y/n] n root@cloud:~#
func (*runInterrupt) execute(t *Task) taskRunState { if t.ptraceSignalLocked(info) { // Dequeueing the signal action must wait until after the // signal-delivery-stop ends since the tracer can change or // suppress the signal. t.tg.signalHandlers.mu.Unlock() return (*runInterruptAfterSignalDeliveryStop)(nil) } act := t.tg.signalHandlers.dequeueAction(linux.Signal(info.Signo)) t.tg.signalHandlers.mu.Unlock() 1077 return t.deliverSignal(info, act) } t.unsetInterrupted() t.tg.signalHandlers.mu.Unlock() return (*runApp)(nil) }
SignalSetup
// SignalSetup implements Context.SignalSetup. func (c *context64) SignalSetup(st *Stack, act *SignalAct, info *SignalInfo, alt *SignalStack, sigset linux.SignalSet) error { sp := st.Bottom if !(alt.IsEnabled() && sp == alt.Top()) { sp -= 128 } // Construct the UContext64 now since we need its size. uc := &UContext64{ Flags: 0, Stack: *alt, MContext: SignalContext64{ Regs: c.Regs.Regs, Sp: c.Regs.Sp, Pc: c.Regs.Pc, Pstate: c.Regs.Pstate, }, Sigset: sigset, } ucSize := uc.SizeBytes() // frameSize = ucSize + sizeof(siginfo). // sizeof(siginfo) == 128. // R30 stores the restorer address. frameSize := ucSize + 128 frameBottom := (sp - usermem.Addr(frameSize)) & ^usermem.Addr(15) sp = frameBottom + usermem.Addr(frameSize) st.Bottom = sp // Prior to proceeding, figure out if the frame will exhaust the range // for the signal stack. This is not allowed, and should immediately // force signal delivery (reverting to the default handler). if act.IsOnStack() && alt.IsEnabled() && !alt.Contains(frameBottom) { return syscall.EFAULT } // Adjust the code. info.FixSignalCodeForUser() // Set up the stack frame. if _, err := info.CopyOut(st, StackBottomMagic); err != nil { return err } infoAddr := st.Bottom if _, err := uc.CopyOut(st, StackBottomMagic); err != nil { return err } ucAddr := st.Bottom // Set up registers. c.Regs.Sp = uint64(st.Bottom) c.Regs.Pc = act.Handler c.Regs.Regs[0] = uint64(info.Signo) c.Regs.Regs[1] = uint64(infoAddr) c.Regs.Regs[2] = uint64(ucAddr) c.Regs.Regs[30] = uint64(act.Restorer) // Save the thread's floating point state. c.sigFPState = append(c.sigFPState, c.aarch64FPState) // Signal handler gets a clean floating point state. c.aarch64FPState = newAarch64FPState() return nil }
newTask
(dlv) c > gvisor.dev/gvisor/pkg/sentry/kernel.(*TaskSet).newTask() pkg/sentry/kernel/task_start.go:131 (hits goroutine(264):1 total:1) (PC: 0x520fc0) Warning: debugging optimized function (dlv) bt 0 0x0000000000520fc0 in gvisor.dev/gvisor/pkg/sentry/kernel.(*TaskSet).newTask at pkg/sentry/kernel/task_start.go:131 1 0x0000000000520ca0 in gvisor.dev/gvisor/pkg/sentry/kernel.(*TaskSet).NewTask at pkg/sentry/kernel/task_start.go:106 2 0x00000000004f5000 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Kernel).CreateProcess at pkg/sentry/kernel/kernel.go:1047 3 0x00000000006c0f34 in gvisor.dev/gvisor/pkg/sentry/control.(*Proc).execAsync at pkg/sentry/control/proc.go:220 4 0x00000000006c0970 in gvisor.dev/gvisor/pkg/sentry/control.ExecAsync at pkg/sentry/control/proc.go:133 5 0x0000000000925470 in gvisor.dev/gvisor/runsc/boot.(*Loader).executeAsync at runsc/boot/loader.go:972 6 0x0000000000916a88 in gvisor.dev/gvisor/runsc/boot.(*containerManager).ExecuteAsync at runsc/boot/controller.go:321 7 0x0000000000075ec4 in runtime.call64 at src/runtime/asm_arm64.s:1 8 0x00000000000c0c80 in reflect.Value.call at GOROOT/src/reflect/value.go:475 9 0x00000000000c0444 in reflect.Value.Call at GOROOT/src/reflect/value.go:336 10 0x0000000000688c30 in gvisor.dev/gvisor/pkg/urpc.(*Server).handleOne at pkg/urpc/urpc.go:337 11 0x00000000006897d0 in gvisor.dev/gvisor/pkg/urpc.(*Server).handleRegistered at pkg/urpc/urpc.go:432 12 0x000000000068adbc in gvisor.dev/gvisor/pkg/urpc.(*Server).StartHandling.func1 at pkg/urpc/urpc.go:452 13 0x0000000000077c84 in runtime.goexit at src/runtime/asm_arm64.s:1136 (dlv) s > gvisor.dev/gvisor/pkg/sentry/kernel.(*TaskSet).newTask() pkg/sentry/kernel/task_start.go:132 (PC: 0x520fd0) Warning: debugging optimized function (dlv) p *runApp Command failed: could not find symbol value for runApp (dlv) p runApp Command failed: could not find symbol value for runApp (dlv)
t.deliverSignalToHandler
D0114 07:37:33.672935 899678 task_exit.go:221] [ 2] Transitioning from exit state TaskExitNone to TaskExitInitiated D0114 07:37:33.673074 899678 task_exit.go:221] [ 2] Transitioning from exit state TaskExitInitiated to TaskExitZombie D0114 07:37:33.673102 899678 task_signals.go:467] [ 1] Notified of signal 17 D0114 07:37:33.673140 899678 task_signals.go:176] [ 1] Not restarting syscall 260 after errno 512: interrupted by signal 17 D0114 07:37:33.673164 899678 task_signals.go:220] [ 1] Signal 17: delivering to handler
Not restarting syscall 260 after errno 512: interrupted by signal 17
/tmp/runsc/runsc.log.20210127-201134.461080.boot:1235:D0127 12:11:36.729733 1 task_signals.go:440] [ 2] Discarding ignored signal 28 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1258:D0127 12:11:37.731315 1 task_signals.go:467] [ 2] Notified of signal 14 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1260:D0127 12:11:37.731523 1 task_signals.go:220] [ 2] Signal 14: delivering to handler /tmp/runsc/runsc.log.20210127-201134.461080.boot:1280:D0127 12:11:38.747436 1 task_signals.go:467] [ 2] Notified of signal 14 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1282:D0127 12:11:38.747518 1 task_signals.go:220] [ 2] Signal 14: delivering to handler /tmp/runsc/runsc.log.20210127-201134.461080.boot:1302:D0127 12:11:38.903268 1 task_signals.go:467] [ 2] Notified of signal 2 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1305:D0127 12:11:38.903458 1 task_signals.go:220] [ 2] Signal 2: delivering to handler /tmp/runsc/runsc.log.20210114-153731.160970.boot:1002:D0114 07:37:33.673102 899678 task_signals.go:467] [ 1] Notified of signal 17 /tmp/runsc/runsc.log.20210114-153731.160970.boot:1003:D0114 07:37:33.673140 899678 task_signals.go:176] [ 1] Not restarting syscall 260 after errno 512: interrupted by signal 17 /tmp/runsc/runsc.log.20210114-153731.160970.boot:1004:D0114 07:37:33.673164 899678 task_signals.go:220] [ 1] Signal 17: delivering to handler
root@cloud:~/onlyGvisor# docker exec -it test ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: seq=0 ttl=42 time=11.731 ms 64 bytes from 8.8.8.8: seq=1 ttl=42 time=27.742 ms 64 bytes from 8.8.8.8: seq=2 ttl=42 time=11.389 ms 64 bytes from 8.8.8.8: seq=3 ttl=42 time=11.240 ms 64 bytes from 8.8.8.8: seq=4 ttl=42 time=11.168 ms 64 bytes from 8.8.8.8: seq=5 ttl=42 time=11.149 ms 64 bytes from 8.8.8.8: seq=6 ttl=42 time=11.174 ms
root@cloud:~# grep ' task_signals.go' /tmp/runsc/ -rn /tmp/runsc/runsc.log.20210127-201134.461080.boot:1235:D0127 12:11:36.729733 1 task_signals.go:440] [ 2] Discarding ignored signal 28 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1258:D0127 12:11:37.731315 1 task_signals.go:467] [ 2] Notified of signal 14 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1260:D0127 12:11:37.731523 1 task_signals.go:220] [ 2] Signal 14: delivering to handler /tmp/runsc/runsc.log.20210127-201134.461080.boot:1280:D0127 12:11:38.747436 1 task_signals.go:467] [ 2] Notified of signal 14 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1282:D0127 12:11:38.747518 1 task_signals.go:220] [ 2] Signal 14: delivering to handler /tmp/runsc/runsc.log.20210127-201134.461080.boot:1302:D0127 12:11:38.903268 1 task_signals.go:467] [ 2] Notified of signal 2 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1305:D0127 12:11:38.903458 1 task_signals.go:220] [ 2] Signal 2: delivering to handler /tmp/runsc/runsc.log.20210127-201134.461080.boot:1380:D0127 12:17:53.704141 1 task_signals.go:440] [ 3] Discarding ignored signal 28 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1433:D0127 12:17:54.714793 1 task_signals.go:467] [ 3] Notified of signal 14 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1435:D0127 12:17:54.714866 1 task_signals.go:220] [ 3] Signal 14: delivering to handler /tmp/runsc/runsc.log.20210127-201134.461080.boot:1453:D0127 12:17:55.715525 1 task_signals.go:467] [ 3] Notified of signal 14 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1455:D0127 12:17:55.715650 1 task_signals.go:220] [ 3] Signal 14: delivering to handler /tmp/runsc/runsc.log.20210127-201134.461080.boot:1473:D0127 12:17:56.716403 1 task_signals.go:467] [ 3] Notified of signal 14 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1475:D0127 12:17:56.716524 1 task_signals.go:220] [ 3] Signal 14: delivering to handler /tmp/runsc/runsc.log.20210127-201134.461080.boot:1493:D0127 12:17:57.731149 1 task_signals.go:467] [ 3] Notified of signal 14 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1495:D0127 12:17:57.731224 1 task_signals.go:220] [ 3] Signal 14: delivering to handler /tmp/runsc/runsc.log.20210127-201134.461080.boot:1513:D0127 12:17:58.731878 1 task_signals.go:467] [ 3] Notified of signal 14 /tmp/runsc/runsc.log.20210127-201134.461080.boot:1515:D0127 12:17:58.732139 1 task_signals.go:220] [ 3] Signal 14: delivering to handler /tmp/runsc/runsc.log.20210114-153731.160970.boot:1002:D0114 07:37:33.673102 899678 task_signals.go:467] [ 1] Notified of signal 17 /tmp/runsc/runsc.log.20210114-153731.160970.boot:1003:D0114 07:37:33.673140 899678 task_signals.go:176] [ 1] Not restarting syscall 260 after errno 512: interrupted by signal 17 /tmp/runsc/runsc.log.20210114-153731.160970.boot:1004:D0114 07:37:33.673164 899678 task_signals.go:220] [ 1] Signal 17: delivering to handler
root@cloud:~# kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX