Linux 2.6 核心閱讀筆記 訊號

來源:互聯網
上載者:User

標籤:style   blog   http   os   io   資料   for   2014   

2014年8月3日 訊號處理常式調用過程

當一個進程接收到一個訊號時,需要暫停進程執行轉去執行專門的訊號處理函數(如果定義了這個訊號的專門處理函數的話),然後再繼續執行進程代碼。

所有的訊號處理都是通過核心功能do_signal進行的,do_signal如果發現需要處理的訊號,並且這個訊號有專門的處理函數,就需要調用這個使用者態的函數,這是通過handle_signal來處理的。執行訊號處理函數是非常複雜的任務,因為在使用者態和核心態來回切換需要特別謹慎地處理棧裡的內容,在當前進程恢複“正常”執行前,首先需要執行使用者態的訊號處理函數。此外當核心打算恢複進程的正常執行時,核心態堆棧不再包含被中斷程式的硬體上下文,因為每當從核心態到使用者態轉換時,都會清空核心棧的內容。另外一個複雜性是因為訊號處理常式可以調用系統調用,在這種情況下,在執行了系統調用後,必須回到訊號處理常式而不是到被中斷程式的正常代碼。

核心採用的解決方案是把儲存在核心態中的硬體上下文拷貝到當前進程的使用者態堆棧中。當訊號處理常式結束時,自動調用sigreturn()系統調用把這個硬體上下文恢複到核心態堆棧中。

先來看個圖:


一個非阻塞的訊號發送給一個進程,當中斷或者異常發生時,切換到核心態,正要返回到使用者態之前,核心執行do_signal函數來處理訊號(如果有的話),通過調用handle_signal來處理使用者態的訊號處理函數的執行環境的建立(調用setup_frame),然後回到使用者態,直接到了訊號處理函數(由於對使用者態堆棧進行了修改),訊號處理函數處理完之後,又調用了sys_sigreturn系統調用(由於對使用者態堆棧進行了修改),然後對使用者堆棧進行恢複操作,結束後回到了使用者態的正常執行路徑。

接下來讓我們看下核心態是怎麼對使用者態核心的堆棧進行修改的,主要是setup_frame這個函數:

//sig為捕獲的訊號//ka包含使用者態的訊號處理函數//set為組設訊號的位元遮罩數組地址//regs為當前核心棧的寄存器值,修改這個對象可以使得核心跳到使用者態的訊號處理函數裡static int setup_frame(int sig, struct k_sigaction *ka,       sigset_t *set, struct pt_regs * regs){void __user *restorer;struct sigframe __user *frame;int err = 0;int usig;//得到一個使用者態的訊號處理棧結構,裡麵包含了訊號值,訊號處理函數執行完畢後的返回地址,訊號上下文等等資料frame = get_sigframe(ka, regs, sizeof(*frame));if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))goto give_sigsegv;usig = current_thread_info()->exec_domain&& current_thread_info()->exec_domain->signal_invmap&& sig < 32? current_thread_info()->exec_domain->signal_invmap[sig]: sig;//將訊號值存放到訊號處理棧裡err = __put_user(usig, &frame->sig);//if (err)goto give_sigsegv;//建立訊號處理上下文,存放到訊號處理棧裡err = setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);if (err)goto give_sigsegv;if (_NSIG_WORDS > 1) {err = __copy_to_user(&frame->extramask, &set->sig[1],      sizeof(frame->extramask));if (err)goto give_sigsegv;}//這裡就是訊號處理函數執行完畢後的返回地址restorer = &__kernel_sigreturn;if (ka->sa.sa_flags & SA_RESTORER)restorer = ka->sa.sa_restorer;/* Set up to return from userspace.  */err |= __put_user(restorer, &frame->pretcode); /* * This is popl %eax ; movl $,%eax ; int $0x80 * * WE DO NOT USE IT ANY MORE! It's only left here for historical * reasons and because gdb uses it as a signature to notice * signal handler stack frames. */err |= __put_user(0xb858, (short __user *)(frame->retcode+0));err |= __put_user(__NR_sigreturn, (int __user *)(frame->retcode+2));err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));if (err)goto give_sigsegv;/* Set up registers for signal handler *///將棧頂指標置為訊號處理棧regs->esp = (unsigned long) frame;//將epi指向訊號處理函數的第一條指令regs->eip = (unsigned long) ka->sa.sa_handler;//eax為訊號的值,也就是訊號處理函數中的參數regs->eax = (unsigned long) sig;regs->edx = (unsigned long) 0;regs->ecx = (unsigned long) 0;set_fs(USER_DS);regs->xds = __USER_DS;regs->xes = __USER_DS;regs->xss = __USER_DS;regs->xcs = __USER_CS;/* * Clear TF when entering the signal handler, but * notify any tracer that was single-stepping it. * The tracer may want to single-step inside the * handler too. */regs->eflags &= ~TF_MASK;if (test_thread_flag(TIF_SINGLESTEP))ptrace_notify(SIGTRAP);#if DEBUG_SIGprintk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",current->comm, current->pid, frame, regs->eip, frame->pretcode);#endifreturn 0;give_sigsegv:force_sigsegv(sig, current);return -EFAULT;}
由於對核心棧寄存器進行了修改(esp,eip),即當核心回到使用者態的時候,就到了訊號處理函數進行執行,當訊號處理函數執行完畢後,就回到了__kernel_sigreturn的彙編函數裡:

_ _kernel_sigreturn:popl %eax  //將訊號編號從棧中移除movl $_ _NR_sigreturn, %eax //將sys_sigreturn系統調用編號放入eaxint $0x80 //觸發系統調用中斷,進入sys_sigreturn系統調用
然後sys_sigreturn系統調用對核心棧和使用者棧進行了恢複操作:

asmlinkage int sys_sigreturn(unsigned long __unused){struct pt_regs *regs = (struct pt_regs *) &__unused;//注意這個frame在setup_fram函數裡已經存到esp裡了struct sigframe __user *frame = (struct sigframe __user *)(regs->esp - 8);sigset_t set;int eax;//檢查使用者態的地址是否合法if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))goto badframe;if (__get_user(set.sig[0], &frame->sc.oldmask)//將執行訊號處理函數之前所阻塞的訊號的位元組從frame->sc裡拷貝出來    || (_NSIG_WORDS > 1&& __copy_from_user(&set.sig[1], &frame->extramask,    sizeof(frame->extramask))))goto badframe;sigdelsetmask(&set, ~_BLOCKABLE);spin_lock_irq(¤t->sighand->siglock);current->blocked = set;//將當前進程的訊號阻塞掩碼恢複成執行訊號處理函數之前所阻塞的訊號的位元組recalc_sigpending();spin_unlock_irq(¤t->sighand->siglock);if (restore_sigcontext(regs, &frame->sc, &eax))//將存放在frame裡面的硬體上下文恢複到當前核心棧裡,並從使用者態刪除framegoto badframe;return eax;badframe:force_sig(SIGSEGV, current);return 0;}




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.