linux signal传递(处理)

本文深入探讨了Linux内核中信号传递和处理的机制,包括异常/中断返回、系统调用、工作队列处理及用户自定义信号处理流程。重点解析了信号传递阶段、异常/中断返回处理、系统调用退出时的信号处理、以及核心堆栈与用户堆栈的变化。同时,介绍了信号默认处理方式、核心堆栈的恢复、信号队列的管理以及用户自定义信号处理函数的调用过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内核版本:2.6.32.60

linux信号传递阶段发生在异常/中断返回到用户态时;当内核由内核态返回到用户态时,如果有信号pending到当前进程,内核就将信号传递给进程并做信号处理

 

I.异常/中断返回
i.信号传递
由以下注释可以看出异常/中断或系统调用(特殊的中断)退出时,都会去检测pending的信号并做处理

/* arch/x86/kernel/entry_32.S */
   6 /*
   7  * entry.S contains the system-call and fault low-level handling routines.
   8  * This also contains the timer-interrupt handler, as well as all interrupts
   9  * and faults that can result in a task-switch.
  10  *
  11  * NOTE: This code handles signal-recognition, which happens every time
  12  * after a timer-interrupt and after each system call.
  42  */


ii.异常/中断返回

 336 /*
 337  * Return to user mode is not as complex as all this looks,
 338  * but we want the default path for a system call return to
 339  * go as quickly as possible which is why some of this is
 340  * less clear than it otherwise should be.
 341  */
 342 
 343         # userspace resumption stub bypassing syscall exit tracing
 344         ALIGN
 345         RING0_PTREGS_FRAME
 346 ret_from_exception:
 347         preempt_stop(CLBR_ANY)
 348 ret_from_intr:
 349         GET_THREAD_INFO(%ebp)
 350 check_userspace:
 351         movl PT_EFLAGS(%esp), %eax      # mix EFLAGS and CS
 352         movb PT_CS(%esp), %al
 353         andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
 354         cmpl $USER_RPL, %eax
 355         jb resume_kernel                # not returning to v8086 or userspace
 356 
 357 ENTRY(resume_userspace)
 358         LOCKDEP_SYS_EXIT
 359         DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
 360                                         # setting need_resched or sigpending
 361                                         # between sampling and the iret
 362         TRACE_IRQS_OFF
 363         movl TI_flags(%ebp), %ecx
 364         andl $_TIF_WORK_MASK, %ecx      # is there any work to be done on
 365                                         # int/exception return?
 366         jne work_pending
 367         jmp restore_all
 368 END(ret_from_exception)

由于异常/中断可以插入到中断(中断嵌套)、内核态、用户态的执行路径中,所以在异常/中断返回时要区别是返回到用户态还是内核态;
段寄存器的低两位bit0,bit1表示Requested Privilege Level (RPL),值范围0~3,即表示有4个级别,linux只用了0和3,分别表示内核态和用户态。
EFLAGS寄存器的第17位VM表示Virtual-8086 Mode
所以取出保存在内核堆栈中的被中断时的寄存器CS与EFLAGS,如果原执行流程是Virtual-8086 Mode或用户态,则做用户态恢复resume_userspace;否则做内核态恢复resume_kernel
返回用户态是,检测进程thread_info中的标识位,如果有pending的事件要做,调用work_pending去处理(主要就是处理信号);否则直接恢复寄存器、堆栈等后返回用户态,继续执行用户态指令

 

iii.系统调用

 517 ENTRY(system_call)
 518         RING0_INT_FRAME                 # can't unwind into user space anyway
 519         pushl %eax                      # save orig_eax
 520         CFI_ADJUST_CFA_OFFSET 4
 521         SAVE_ALL
 522         GET_THREAD_INFO(%ebp)
 523                                         # system call tracing in operation / emulation
 524         testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
 525         jnz syscall_trace_entry
 526         cmpl $(nr_syscalls), %eax
 527         jae syscall_badsys
 528 syscall_call:
 529         call *sys_call_table(,%eax,4)
 530         movl %eax,PT_EAX(%esp)          # store the return value
 531 syscall_exit:
 532         LOCKDEP_SYS_EXIT
 533         DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
 534                                         # setting need_resched or sigpending
 535                                         # between sampling and the iret
 536         TRACE_IRQS_OFF
 537         movl TI_flags(%ebp), %ecx
 538         testl $_TIF_ALLWORK_MASK, %ecx  # current->work
 539         jne syscall_exit_work
 540 
 541 restore_all:

 684 syscall_exit_work:
 685         testl $_TIF_WORK_SYSCALL_EXIT, %ecx
 686         jz work_pending
 687         TRACE_IRQS_ON
 688         ENABLE_INTERRUPTS(CLBR_ANY)     # could let syscall_trace_leave() call
 689                                         # schedule() instead
 690         movl %esp, %eax
 691         call syscall_trace_leave
 692         jmp resume_userspace
 693 END(syscall_exit_work)

1.系统调用时,eax存放系统调用号,根据系统调用号去调用相应的系统服务例程
2.在系统调用退出时,会检测进程thread_info中的标识位,如果有pending的事件要做,调用work_pending去处理(主要就是处理信号);否则直接恢复寄存器、堆栈等后返回用户态,继续执行用户态指令
注:ENTRY(system_call)与ENTRY(interrupt)[common_interrupt]的区别,syscall会把eax即系统调用号压入堆栈中;后面会用内核堆栈中的该值(由syscall_get_nr取出)判断是否是系统调用由用户态切入内核态。

 

iv.work_pending
从上面可以看出不论是异常/中断或是系统调用返回到用户态都会调用work_pending,去做pending处理(主要就是处理信号)

101 #define resume_userspace_sig    resume_userspace
 623         # perform work that needs to be done immediately before resumption
 624         ALIGN
 625         RING0_PTREGS_FRAME              # can't unwind into user space anyway
 626 work_pending:
 627         testb $_TIF_NEED_RESCHED, %cl
 628         jz work_notifysig
 629 work_resched:
 630         call schedule
 631         LOCKDEP_SYS_EXIT
 632         DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
 633                                         # setting need_resched or sigpending
 634                                         # between sampling and the iret
 635         TRACE_IRQS_OFF
 636         movl TI_flags(%ebp), %ecx
 637         andl $_TIF_WORK_MASK, %ecx      # is there any work to be done other
 638                                         # than syscall tracing?
 639         jz restore_all
 640         testb $_TIF_NEED_RESCHED, %cl
 641         jnz work_resched
 642 
 643 work_notifysig:                         # deal with pending signals and
 644                                         # notify-resume requests
 645 #ifdef CONFIG_VM86
 646         testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)
 647         movl %esp, %eax
 648         jne work_notifysig_v86          # returning to kernel-space or
 649                                         # vm86-space
 650         xorl %edx, %edx
 651         call do_notify_resume
 652         jmp resume_userspace_sig
 653 
 654         ALIGN
 655 work_notifysig_v86:
 656         pushl %ecx                      # save ti_flags for do_notify_resume
 657         CFI_ADJUST_CFA_OFFSET 4
 658         call save_v86_state             # %eax contains pt_regs pointer
 659         popl %ecx
 660         CFI_ADJUST_CFA_OFFSET -4
 661         movl %eax, %esp
 662 #else
 663         movl %esp, %eax
 664 #endif
 665         xorl %edx, %edx
 666         call do_notify_resume
 667         jmp resume_userspace_sig
 668 END(work_pending)

1.如果需要放弃CPU占用(_TIF_NEED_RESCHED标识),则去调度其它进程执行
2.如果不需要重新调度,则调用do_notify_resume去处理pending的工作
注意:
  do_notify_resume返回时,会jmp resume_userspace_sig,即会再次去检查pending的工作是否做完,如果未做完会重复过程直到pending的工作做完为止。
  对于信号来说,处理完一个信号,如果还有信号需要处理,则会继续去处理,直到pending队列中的信号处理完为止;在后面可以看到,在从队列中取出信号时,如果队列中还有信号则会将_TIF_SIGPENDING置位,在resume_userspace中会去检查

 

v.do_notify_resume

/* arch/x86/kernel/signal.c */
852 /*
853  * notification of userspace execution resumption
854  * - triggered by the TIF_WORK_MASK flags
855  */
856 void
857 do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
858 {
859 #ifdef CONFIG_X86_MCE
860         /* notify userspace of pending MCEs */
861         if (thread_info_flags & _TIF_MCE_NOTIFY)
862                 mce_notify_process();
863 #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
864 
865         /* deal with pending signal delivery */
866         if (thread_info_flags & _TIF_SIGPENDING)
867                 do_signal(regs);
868 
869         if (thread_info_flags & _TIF_NOTIFY_RESUME) {
870                 clear_thread_flag(TIF_NOTIFY_RESUME);
871                 tracehook_notify_resume(regs);
872                 if (current->replacement_session_keyring)
873                         key_replace_session_keyring();
874         }
875 
876 #ifdef CONFIG_X86_32
877         clear_thread_flag(TIF_IRET);
878 #endif /* CONFIG_X86_32 */
879 }

如果有信号需要传递,_TIF_SIGPENDING置位(在信号发送时置位,见signal_wake_up函数),则调用do_signal去传递信号(调用相应的handler处理信号)

 


II.信号传递(处理)

i.do_signal
/* arch/x86/kernel/signal.c */
773 /*
774  * Note that 'init' is a special process: it doesn't get signals it doesn't
775  * want to handle. Thus you cannot kill init even with a SIGKILL even by
776  * mistake.
777  */
778 static void do_signal(struct pt_regs *regs)
779 {
780         struct k_sigaction ka;
781         siginfo_t info;
782         int signr;
783         sigset_t *oldset;
784 
785         /*
786          * We want the common case to go fast, which is why we may in certain
787          * cases get here from kernel mode. Just return without doing anything
788          * if so.
789          * X86_32: vm86 regs switched out by assembly code before reaching
790          * here, so testing against kernel CS suffices.
791          */
792         if (!user_mode(regs))
793                 return;
794 
795         if (current_thread_info()->status & TS_RESTORE_SIGMASK)
796                 oldset = ¤t->saved_sigmask;
797         else
798                 oldset = ¤t->blocked;
799 
800         signr = get_signal_to_deliver(&info, &ka, regs, NULL);
801         if (signr > 0) {
802                 /*
803                  * Re-enable any watchpoints before delivering the
804                  * signal to user space. The processor register will
805                  * have been cleared if the watchpoint triggered
806                  * inside the kernel.
807                  */
808                 if (current->thread.debugreg7)
809                         set_debugreg(current->thread.debugreg7, 7);
810 
811                 /* Whee! Actually deliver the signal.  */
812                 if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
813                         /*
814                          * A signal was successfully delivered; the saved
815                          * sigmask will have been stored in the signal frame,
816                          * and will be restored by sigreturn, so we can simply
817                          * clear the TS_RESTORE_SIGMASK flag.
818                          */
819                         current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
820                 }
821                 return;
822         }
823 
824         /* Did we come from a system call? */
825         if (syscall_get_nr(current, regs) >= 0) {
826                 /* Restart the system call - no handlers present */
827                 switch (syscall_get_error(current, regs)) {
828                 case -ERESTARTNOHAND:
829                 case -ERESTARTSYS:
830                 case -ERESTARTNOINTR:
831                         regs->ax = regs->orig_ax;
832                         regs->ip -= 2;
833                         break;
834 
835                 case -ERESTART_RESTARTBLOCK:
836                         regs->ax = NR_restart_syscall;
837                         regs->ip -= 2;
838                         break;
839                 }
840         }
841 
842         /*
843          * If there's no signal to deliver, we just put the saved sigmask
844          * back.
845          */
846         if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
847                 current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
848                 sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
849         }
850 }

1.如果不是要返回用户态,直接返回
2.从信号pending队列中取出信号,如果信号可以由内核处理(如SIG_DFL,SIG_IGN),则内核直接处理掉,详细见下面的get_signal_to_deliver;如果信号要由用户态自定义的handler处理信号,则由handle_signal保存当前进程的运行环境,并准备信号处理handler的运行环境,当切回用户态后就去调用信号处理handler,处理完成后恢复进程的正常运行流程。
3.如果信号中断了系统调用,内核可以重启系统调用。信号是由内核处理还是用户进程提供的函数处理,重启系统调用的方式是不同的,因为内核处理不用切换到用户态中再处理信号。
4.内核处理的信号中断了系统调用,直接将原系统调用号regs->orig_ax或restart_syscall系统调用号赋值给ax,并将ip减2即可,因为int 0x80/sysenter均为两字节所以减2后ip指向了int/sysenter指令。此时ax是原系统调用号/restart_syscall(用于重启系统调用的系统调用),再次执行int/sysenter就能重启系统调用了
注:如果要处理多个pending信号,do_signal会进入多次,但是修改ip的值只会在第一次进入do_signal修改;因为系统调用的错误码放在regs->ax中,在修改ip时都会修改regs->ax为系统调用号,而该且是正数,而错误码都是负数,所以只会在第一次进入do_signal修改ip

 

ii.get_signal_to_deliver

/* kernel/signal.c */
1773 int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
1774                           struct pt_regs *regs, void *cookie)
1775 {
1776         struct sighand_struct *sighand = current->sighand;
1777         struct signal_struct *signal = current->signal;
1778         int signr;
1779 
1780 relock:
1781         /*
1782          * We'll jump back here after any time we were stopped in TASK_STOPPED.
1783          * While in TASK_STOPPED, we were considered "frozen enough".
1784          * Now that we woke up, it's crucial if we're supposed to be
1785          * frozen that we freeze now before running anything substantial.
1786          */
1787         try_to_freeze();
1788 
1789         spin_lock_irq(&sighand->siglock);
1790         /*
1791          * Every stopped thread goes here after wakeup. Check to see if
1792          * we should notify the parent, prepare_signal(SIGCONT) encodes
1793          * the CLD_ si_code into SIGNAL_CLD_MASK bits.
1794          */
1795         if (unlikely(signal->flags & SIGNAL_CLD_MASK)) {
1796                 int why = (signal->flags & SIGNAL_STOP_CONTINUED)
1797                                 ? CLD_CONTINUED : CLD_STOPPED;
1798                 signal->flags &= ~SIGNAL_CLD_MASK;
1799 
1800                 why = tracehook_notify_jctl(why, CLD_CONTINUED);
1801                 spin_unlock_irq(&sighand->siglock);
1802 
1803                 if (why) {
1804                         read_lock(&tasklist_lock);
1805                         do_notify_parent_cldstop(current->group_leader, why);
1806                         read_unlock(&tasklist_lock);
1807                 }
1808                 goto relock;
1809         }
1810 
1811         for (;;) {
1812                 struct k_sigaction *ka;
1813 
1814                 if (unlikely(signal->group_stop_count > 0) &&
1815                     do_signal_stop(0))
1816                         goto relock;
1817 
1818                 /*
1819                  * Tracing can induce an artifical signal and choose sigaction.
1820                  * The return value in @signr determines the default action,
1821                  * but @info->si_signo is the signal number we will report.
1822                  */
1823                 signr = tracehook_get_signal(current, regs, info, return_ka);
1824                 if (unlikely(signr < 0))
1825                         goto relock;
1826                 if (unlikely(signr != 0))
1827                         ka = return_ka;
1828                 else {
1829                         signr = dequeue_signal(current, ¤t->blocked,
1830                                                info);
1831 
1832                         if (!signr)
1833                                 break; /* will return 0 */
1834 
1835                         if (signr != SIGKILL) {
1836                                 signr = ptrace_signal(signr, info,
1837                                                       regs, cookie);
1838                                 if (!signr)
1839                                         continue;
1840                         }
1841 
1842                         ka = &sighand->action[signr-1];
1843                 }
1844 
1845                 if (ka->sa.sa_handler == SIG_IGN) /* Do nothing.  */
1846                         continue;
1847                 if (ka->sa.sa_handler != SIG_DFL) {
1848                         /* Run the handler.  */
1849                         *return_ka = *ka;
1850 
1851                         if (ka->sa.sa_flags & SA_ONESHOT)
1852                                 ka->sa.sa_handler = SIG_DFL;
1853 
1854                         break; /* will return non-zero "signr" value */
1855                 }
1856 
1857                 /*
1858                  * Now we are doing the default action for this signal.
1859                  */
1860                 if (sig_kernel_ignore(signr)) /* Default is nothing. */
1861                         continue;
1862 
1863                 /*
1864                  * Global init gets no signals it doesn't want.
1865                  * Container-init gets no signals it doesn't want from same
1866                  * container.
1867                  *
1868                  * Note that if global/container-init sees a sig_kernel_only()
1869                  * signal here, the signal must have been generated internally
1870                  * or must have come from an ancestor namespace. In either
1871                  * case, the signal cannot be dropped.
1872                  */
1873                 if (unlikely(signal->flags & SIGNAL_UNKILLABLE) &&
1874                                 !sig_kernel_only(signr))
1875                         continue;
1876 
1877                 if (sig_kernel_stop(signr)) {
1878                         /*
1879                          * The default action is to stop all threads in
1880                          * the thread group.  The job control signals
1881                          * do nothing in an orphaned pgrp, but SIGSTOP
1882                          * always works.  Note that siglock needs to be
1883                          * dropped during the call to is_orphaned_pgrp()
1884                          * because of lock ordering with tasklist_lock.
1885                          * This allows an intervening SIGCONT to be posted.
1886                          * We need to check for that and bail out if necessary.
1887                          */
1888                         if (signr != SIGSTOP) {
1889                                 spin_unlock_irq(&sighand->siglock);
1890 
1891                                 /* signals can be posted during this window */
1892 
1893                                 if (is_current_pgrp_orphaned())
1894                                         goto relock;
1895 
1896                                 spin_lock_irq(&sighand->siglock);
1897                         }
1898 
1899                         if (likely(do_signal_stop(info->si_signo))) {
1900                                 /* It released the siglock.  */
1901                                 goto relock;
1902                         }
1903 
1904                         /*
1905                          * We didn't actually stop, due to a race
1906                          * with SIGCONT or something like that.
1907                          */
1908                         continue;
1909                 }
1910 
1911                 spin_unlock_irq(&sighand->siglock);
1912 
1913                 /*
1914                  * Anything else is fatal, maybe with a core dump.
1915                  */
1916                 current->flags |= PF_SIGNALED;
1917 
1918                 if (sig_kernel_coredump(signr)) {
1919                         if (print_fatal_signals)
1920                                 print_fatal_signal(regs, info->si_signo);
1921                         /*
1922                          * If it was able to dump core, this kills all
1923                          * other threads in the group and synchronizes with
1924                          * their demise.  If we lost the race with another
1925                          * thread getting here, it set group_exit_code
1926                          * first and our do_group_exit call below will use
1927                          * that value and ignore the one we pass it.
1928                          */
1929                         do_coredump(info->si_signo, info->si_signo, regs);
1930                 }
1931 
1932                 /*
1933                  * Death signals, no core dump.
1934                  */
1935                 do_group_exit(info->si_signo);
1936                 /* NOTREACHED */
1937         }
1938         spin_unlock_irq(&sighand->siglock);
1939         return signr;
1940 }

1.获取sighand_struct/signal_struct保护锁
2.从进程私有/共享信号pending队列中取出信号
  A.如果信号是可被忽略的SIG_IGN(显示忽略),走步骤2继续取信号
  B.如果信号由用户进程处理,则返回信号编号及信号处理sighand,交由handle_signal处理
  C.以下就是信号的默认处理
    a.如果信号的默认行为是忽略,则走步骤2继续取信号
    b.如果是暂停/继续类信号,则由do_signal_stop去暂停/继续进程执行
    c.如果信号的默认行为是产生coredump,则去coredump当前进程
    d.如果信号的默认行为是结束当前进程,则调用do_group_exit去结束当前进程
3.释放sighand_struct/signal_struct保护锁

 


III.用户自定义信号处理
i.handle_signal

/* arch/x86/kernel/signal.c */
690 static int
691 handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
692               sigset_t *oldset, struct pt_regs *regs)
693 {
694         int ret;
695 
696         /* Are we from a system call? */
697         if (syscall_get_nr(current, regs) >= 0) {
698                 /* If so, check system call restarting.. */
699                 switch (syscall_get_error(current, regs)) {
700                 case -ERESTART_RESTARTBLOCK:
701                 case -ERESTARTNOHAND:
702                         regs->ax = -EINTR;
703                         break;
704 
705                 case -ERESTARTSYS:
706                         if (!(ka->sa.sa_flags & SA_RESTART)) {
707                                 regs->ax = -EINTR;
708                                 break;
709                         }
710                 /* fallthrough */
711                 case -ERESTARTNOINTR:
712                         regs->ax = regs->orig_ax;
713                         regs->ip -= 2;
714                         break;
715                 }
716         }
717 
718         /*
719          * If TF is set due to a debugger (TIF_FORCED_TF), clear the TF
720          * flag so that register information in the sigcontext is correct.
721          */
722         if (unlikely(regs->flags & X86_EFLAGS_TF) &&
723             likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
724                 regs->flags &= ~X86_EFLAGS_TF;
725 
726         ret = setup_rt_frame(sig, ka, info, oldset, regs);
727 
728         if (ret)
729                 return ret;
730 
731 #ifdef CONFIG_X86_64
732         /*
733          * This has nothing to do with segment registers,
734          * despite the name.  This magic affects uaccess.h
735          * macros' behavior.  Reset it to the normal setting.
736          */
737         set_fs(USER_DS);
738 #endif
739 
740         /*
741          * Clear the direction flag as per the ABI for function entry.
742          */
743         regs->flags &= ~X86_EFLAGS_DF;
744 
745         /*
746          * Clear TF when entering the signal handler, but
747          * notify any tracer that was single-stepping it.
748          * The tracer may want to single-step inside the
749          * handler too.
750          */
751         regs->flags &= ~X86_EFLAGS_TF;
752 
753         spin_lock_irq(¤t->sighand->siglock);
754         sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask);
755         if (!(ka->sa.sa_flags & SA_NODEFER))
756                 sigaddset(¤t->blocked, sig);
757         recalc_sigpending();
758         spin_unlock_irq(¤t->sighand->siglock);
759 
760         tracehook_signal_handler(sig, info, ka, regs,
761                                  test_thread_flag(TIF_SINGLESTEP));
762 
763         return 0;
764 }

1.设置被中断的系统调用重启环境;ip只会修改一次见II.i
2.设置调用用户信号处理函数环境的堆栈帧
3.获取sighand_struct/signal_struct保护锁
4.recalc_sigpending重新计算是否需要继续处理后续信号,即设置/清空TIF_SIGPENDING标识
5.释放sighand_struct/signal_struct保护锁

 

ii.信号传递前后内核堆栈及用户堆栈变化情况



IV.信号默认处理
i.do_signal_stop

1661 /*
1662  * This performs the stopping for SIGSTOP and other stop signals.
1663  * We have to stop all threads in the thread group.
1664  * Returns nonzero if we've actually stopped and released the siglock.
1665  * Returns zero if we didn't stop and still hold the siglock.
1666  */
1667 static int do_signal_stop(int signr)
1668 {
1669         struct signal_struct *sig = current->signal;
1670         int notify;
1671 
1672         if (!sig->group_stop_count) {
1673                 struct task_struct *t;
1674 
1675                 if (!likely(sig->flags & SIGNAL_STOP_DEQUEUED) ||
1676                     unlikely(signal_group_exit(sig)))
1677                         return 0;
1678                 /*
1679                  * There is no group stop already in progress.
1680                  * We must initiate one now.
1681                  */
1682                 sig->group_exit_code = signr;
1683 
1684                 sig->group_stop_count = 1;
1685                 for (t = next_thread(current); t != current; t = next_thread(t))
1686                         /*
1687                          * Setting state to TASK_STOPPED for a group
1688                          * stop is always done with the siglock held,
1689                          * so this check has no races.
1690                          */
1691                         if (!(t->flags & PF_EXITING) &&
1692                             !task_is_stopped_or_traced(t)) {
1693                                 sig->group_stop_count++;
1694                                 signal_wake_up(t, 0);
1695                         }
1696         }
1697         /*
1698          * If there are no other threads in the group, or if there is
1699          * a group stop in progress and we are the last to stop, report
1700          * to the parent.  When ptraced, every thread reports itself.
1701          */
1702         notify = sig->group_stop_count == 1 ? CLD_STOPPED : 0;
1703         notify = tracehook_notify_jctl(notify, CLD_STOPPED);
1704         /*
1705          * tracehook_notify_jctl() can drop and reacquire siglock, so
1706          * we keep ->group_stop_count != 0 before the call. If SIGCONT
1707          * or SIGKILL comes in between ->group_stop_count == 0.
1708          */
1709         if (sig->group_stop_count) {
1710                 if (!--sig->group_stop_count)
1711                         sig->flags = SIGNAL_STOP_STOPPED;
1712                 current->exit_code = sig->group_exit_code;
1713                 __set_current_state(TASK_STOPPED);
1714         }
1715         spin_unlock_irq(¤t->sighand->siglock);
1716 
1717         if (notify) {
1718                 read_lock(&tasklist_lock);
1719                 do_notify_parent_cldstop(current, notify);
1720                 read_unlock(&tasklist_lock);
1721         }
1722 
1723         /* Now we don't run again until woken by SIGCONT or SIGKILL */
1724         do {
1725                 schedule();
1726         } while (try_to_freeze());
1727 
1728         tracehook_finish_jctl();
1729         current->exit_code = 0;
1730 
1731         return 1;
1732 }

1.唤醒其它线程
2.如果线程暂停/继续,通过SIGCHLD信号通知主控线程,子线程状态已经改变
3.暂停该(轻量级)进程,直到收到SIGCONT/SIGKILL信号

 

ii.do_coredump

/* fs/exec.c */
1812 void do_coredump(long signr, int exit_code, struct pt_regs *regs)
1813 {
1985 }

产生coredump文件,coredump过程以后再分析

 

iii.do_group_exit

/* kernel/exit.c */
1058 /*
1059  * Take down every thread in the group.  This is called by fatal signals
1060  * as well as by sys_exit_group (below).
1061  */
1062 NORET_TYPE void
1063 do_group_exit(int exit_code)
1064 {
1087 }
1088 
1089 /*
1090  * this kills every thread in the thread group. Note that any externally
1091  * wait4()-ing process will get the correct exit code - even if this
1092  * thread is not the thread group leader.
1093  */
1094 SYSCALL_DEFINE1(exit_group, int, error_code)
1095 {
1096         do_group_exit((error_code & 0xff) << 8);
1097         /* NOTREACHED */
1098         return 0;
1099 }

与linux系统调用group_exit一致,即结束线程组:向其它线程发送SIGKILL信号结束线程组内的其它线程,调用do_exit退出本(轻量级)进程(与系统调用exit一致)。
exit实现以后再分析





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值