注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

SIGCONT/SIGSTP及ERESTARTSYS的意义  

2015-04-11 20:59:00|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、问题的引入
想到的问题是SIGSTP的处理流程,在发送信号给进程A时,如果A正在内核态执行一个可中断等待动作,例如futexwait、select、ttyread等操作,此时一个SIGSTP将会把目标进程从等待中唤醒,这个SIGSTP通常给调试器使用,但是bash的job管理也是用了这个信号,当用户按下ctrl+Z时,前台进程就会收到一个SIGSTP信号。现在的问题是,进程被唤醒之后,它返回用户态岂不是导致很多意想不到的问题。
二、以select为例
sys_select-->.core_sys_select-->>do_select
if (retval || !*timeout || signal_pending(current))
break;
信号会将执行该操作的进程唤醒并退出休眠。另一个常见的终端读取操作,read_chan
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
三、STOP之后进程如何被唤醒
信号可以唤醒可中断休眠,但是无法唤醒STOPPED状态进程,所以在唤醒时和普通的唤醒流程不同。
__group_send_sig_info--->>handle_stop_signal
do {
unsigned int state;
rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending);
/*
* If there is a handler for SIGCONT, we must make
* sure that no thread returns to user mode before
* we post the signal, in case it was the only
* thread eligible to run the signal handler--then
* it must not do anything between resuming and
* running the handler.  With the TIF_SIGPENDING
* flag set, the thread will pause and acquire the
* siglock that we hold now and until we've queued
* the pending signal. 
*
* Wake up the stopped thread _after_ setting
* TIF_SIGPENDING
*/
state = TASK_STOPPED;
if (sig_user_defined(t, SIGCONT) && !sigismember(&t->blocked, SIGCONT)) {
set_tsk_thread_flag(t, TIF_SIGPENDING);
state |= TASK_INTERRUPTIBLE;
}
wake_up_state(t, state);

t = next_thread(t);
} while (t != p);
普通信号则在__group_complete_signal-->.signal_wake_up
mask = TASK_INTERRUPTIBLE;
if (resume)
mask |= TASK_STOPPED | TASK_TRACED;
if (!wake_up_state(t, mask))
kick_process(t);
四、恢复之后如何继续
当进程被信号唤醒之后,在linux-2.6.21\arch\i386\kernel\entry.S
syscall_call:
call *sys_call_table(,%eax,4)
movl %eax,PT_EAX(%esp) # store the return value,这里将系统调用的返回值保存在EAX寄存器中
syscall_exit:
……
xorl %edx, %edx
call do_notify_resume
jmp resume_userspace_sig
END(work_pending)

linux-2.6.21\arch\i386\kernel\signal.c
do_notify_resume-->>do_signal
signr = get_signal_to_deliver(&info, &ka, regs, NULL);//由于SIGSTOP被内核直接消耗掉,所以该函数不可能返回SIGSTOP。
……
/* Did we come from a system call? */
if (regs->orig_eax >= 0) {//这里如果是通过系统调用进入内核则该值为正数,其它中断下切换为负数(这一点在《深入理解linux内核中》有说明)
/* Restart the system call - no handlers present */
switch (regs->eax) {//这里的eax就是上面汇编中看到的系统调用的返回值。read_chan函数返回的-ERESTARTSYS将在这里被用到。
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
regs->eax = regs->orig_eax;
regs->eip -= 2;回退EIP指针,重新执行系统调用int 0x80,所以用户态感知不到这个过程。
break;

case -ERESTART_RESTARTBLOCK:
regs->eax = __NR_restart_syscall;
regs->eip -= 2;
break;
}
}
这里可能就意味着一个问题,对于终端读入的情况,如果说要求读入长度为20,读取10个之后被STOP/CONT,再次执行系统调用,这中间读到的10个字节就会被丢弃到,相当于从系统消失(未验证)。
  评论这张
 
阅读(192)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017