linux 2.4内核之信号(一)
时间:2007-06-16 来源:yjfpb04
linux 2.4内核之信号(一)
这两天看信号,略有所感.记下以示.
限于规模.只是略讲以及自己困惑过的地方.估计难有人看得懂.反正不在乎,当自己练习描述能力
begin:
1.信号的许多基础问题我就不说了(当然是我认为的).这里主要讲下几个比较搞的概念
信号屏蔽: 主要区分好block和sa_mask就好,信号有两个地方屏蔽,第一是进程的task_struct中有一个
block,这是一直起作用的.还有就是设置信号处理程序时的sigaction结构的sa_mask.kill,stop信号不允许
被屏蔽.当然也不能设置处理函数. 在信号发送时,要将一个接收位图相关的位置1,并将信号挂入一个队列.
如果接收进程对应该信号没有被block,则唤醒接收进程
2. 信号接收位图:
当接收到信号后,要将对应的信号位图置1.被处理后再置回0,但由于由于在接收信号和处理信号之间还可能有
信号被接收,即存在时间窗口,信号可能被合并.所以引入了一个struct sigqueue.当发送信号时,就将
pending对应的位图置1.并加入sigqueue.这样才能保证记录信号的次数.但是对于以前老的机制(信号值小于
SIGRTMIN的),内核只将其抽取构造出相应结构,挂入sigqueue一次.所以信号值小于SIGRTMIN的信号,在处理
时,并不能记录在时间窗口中发生的次数,还是会合并掉
3. do_signal()
进程检测信号存在的时机.当(1)从系统调用返回,中断异常处理返回时,会检测信号task struct ->
sigpending.(2)当进程被唤醒时(必定是在系统调用中),如果发现有信号等待就提前从系统调用中返回.
这里主要讲一下是如何处理接收位图的.因为一开始这里没弄清楚(主要是忽略了dequeue_signal的代码,因为
情景分析里面没讲).具体实现就是在dequeue_signal()中,这个函数先参照block位图(看dequeue_signal
(t->blocked, &info);传了blocked参数),再根据进程的接收位图找到要处理的信号(即设置了位1)然后使其
脱队,再进行处理(实际上这个时候还没处理,还在系统空间为返回做准备,没切换到用户空间.在do_signal()
返回后才切换.).如果没有设置SA_NODEFER属性,则当前要处理的信号被自动屏蔽(是block).即通过
sigaddset(t->blocked,sig);实现,具体在内核和用户空间切换的代码还没看.反正切换到用户空间处理后再
通过一个系统调用回到系统空间.如果还有信号则继续处理.(不要问为什么不能一次性处理完,要在系统空间和
用户换来换去.记住处理程序在用户空间.)
4.最后注意sigorsets(t->blocked,t->blocked,&ka->sa.sa_mask); 从用户的信号屏蔽码刷新阻塞信
号集.找到了这个.相信可以区分一开始讲的进程的block还有sigaction结构的sa_mask(它其实也靠进程的
block位图实现屏蔽.) 还有一个接收位图了吧..虽然它们的类型都是sigset_t;
下面列出的是网上流传的一段注释(do_signal的).要理解整个信号机制的运转可以去参照.
int do_signal(struct pt_regs *regs, sigset_t *oldset)
{
siginfo_t info;
struct k_sigaction *ka;
/*
* We want the common case to go fast, which
* is why we may in certain cases get here from
* kernel mode. Just return without doing anything
* if so.
*/
; 只有返回到用户码才能执行信号
if ((regs->xcs & 3) != 3)
return 1;
if (!oldset)
oldset = ¤t->blocked;
for (;;) {
unsigned long signr;
spin_lock_irq(¤t->sigmask_lock);
; current->blocked是阻塞的信号集
signr = dequeue_signal(¤t->blocked, &info);
; 从信号队列中提取最小序数的信号
spin_unlock_irq(¤t->sigmask_lock);
if (!signr)
break; 如果dequeue_signal失败,退出
if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
/* Let the debugger run. */
current->exit_code = signr;
current->state = TASK_STOPPED;
notify_parent(current, SIGCHLD);
schedule();
/* We're back. Did the debugger cancel the sig? */
if (!(signr = current->exit_code))
continue;
current->exit_code = 0;
/* The debugger continued. Ignore SIGSTOP. */
if (signr == SIGSTOP)
continue;
/* Update the siginfo structure. Is this good? */
if (signr != info.si_signo) {
info.si_signo = signr;
info.si_errno = 0;
info.si_code = SI_USER;
info.si_pid = current->p_pptr->pid;
info.si_uid = current->p_pptr->uid;
}
/* If the (new) signal is now blocked, requeue it. */
if (sigismember(¤t->blocked, signr)) {
send_sig_info(signr, &info, current);
continue;
}
}
ka = ¤t->sig->action[signr-1];
if (ka->sa.sa_handler == SIG_IGN) {
if (signr != SIGCHLD)
continue; 如果忽略的不是SIGCHLD则继续循环寻找下一个信号
/* Check for SIGCHLD: it's special. */
while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
/* nothing */;
continue;
}
if (ka->sa.sa_handler == SIG_DFL) {
int exit_code = signr;
; 系统缺省的信号处理
/* Init gets no signals it doesn't want. */
if (current->pid == 1)
continue;
switch (signr) {
case SIGCONT: case SIGCHLD: case SIGWINCH:
continue;
case SIGTSTP: case SIGTTIN: case SIGTTOU:
if (is_orphaned_pgrp(current->pgrp))
continue;
/* FALLTHRU */
case SIGSTOP:
current->state = TASK_STOPPED;
current->exit_code = signr;
if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
notify_parent(current, SIGCHLD);
; 如果父进程SIGCHLD没有SA_NOCLDSTOP标志,就向父进程发送SIGCHLD通知信号
schedule();
continue;
case SIGQUIT: case SIGILL: case SIGTRAP:
case SIGABRT: case SIGFPE: case SIGSEGV:
case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ:
if (do_coredump(signr, regs))
exit_code |= 0x80;
; 当成功生成core文件时,返回码第8位置位
/* FALLTHRU */
default:
sigaddset(¤t->pending.signal, signr);
recalc_sigpending(current);
current->flags |= PF_SIGNALED;
; 将退出信号作为返回码
do_exit(exit_code);
/* NOTREACHED */
}
}
/* Reenable any watchpoints before delivering the
* signal to user space. The processor register will
* have been cleared if the watchpoint triggered
* inside the kernel.
*/
__asm__("movl %0,%?" : : "r" (current->thread.debugreg[7]));
; 在运行用户信号函数之前,刷新一下db7
/* Whee! Actually deliver the signal. */
handle_signal(signr, ka, &info, oldset, regs);
return 1; 从这里返回时就切换到用户信号函数
}
; 当信号队列中没有用户自已处理的信号时就会执行到这里
/* Did we come from a system call? */
if (regs->orig_eax >= 0) {
/* Restart the system call - no handlers present */
if (regs->eax == -ERESTARTNOHAND ||
regs->eax == -ERESTARTSYS ||
regs->eax == -ERESTARTNOINTR) {
; 如果系统调用因为信号而出错返回,恢复系统调用正常运行.
regs->eax = regs->orig_eax;
regs->eip -= 2;
}
}
return 0;
}
handle_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
{
/* Are we from a system call? */
if (regs->orig_eax >= 0) {
/* If so, check system call restarting.. */
switch (regs->eax) { 如果系统将要从系统调用返回,检查系统调用的返回码
case -ERESTARTNOHAND:
; 执行过信号函数之后不恢复原先系统调用状态
regs->eax = -EINTR;
break;
case -ERESTARTSYS:
; 执行过信号函数之后只有当信号具有SA_RESTART属性时才能恢复系统调用状态
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->eax = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
; 执行过信号函数之后无条件恢复系统调用状态
regs->eax = regs->orig_eax; 用原来的功能码取代返回码
regs->eip -= 2; 重新执行int 0x80指令
}
}
/* Set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO) 如果信号具有SA_SIGINFO属性,使用rt_sigframe
setup_rt_frame(sig, ka, info, oldset, regs);
else
setup_frame(sig, ka, oldset, regs);
if (ka->sa.sa_flags & SA_ONESHOT)
ka->sa.sa_handler = SIG_DFL;
; 如果信号具有SA_ONESHOT属性,每次运行后取消用户信号函数的定义
if (!(ka->sa.sa_flags & SA_NODEFER)) {
spin_lock_irq(¤t->sigmask_lock);
sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
; 从用户的信号屏蔽码刷新阻塞信号集
sigaddset(¤t->blocked,sig);
; 将要执行的信号加入阻塞信号集
recalc_sigpending(current);
; 刷新sigpending标志
spin_unlock_irq(¤t->sigmask_lock);
}
}
int
dequeue_signal(sigset_t *mask, siginfo_t *info)
{
int sig = 0;
#if DEBUG_SIG
printk("SIG dequeue (%s:%d): %d ", current->comm, current->pid,
signal_pending(current));
#endif
从当前进程的信号集中找出第一个有效的信号数
sig = next_signal(current, mask);
if (current->notifier) {
if (sigismember(current->notifier_mask, sig)) {
if (!(current->notifier)(current->notifier_data)) {
;在提取信号之前调用进程的notifier()函数,如果它返回0,就终止信号处理
current->sigpending = 0;
return 0;
}
}
}
if (sig) {
在当前进程的信号队列中提取sig信号的info结构
if (!collect_signal(sig, ¤t->pending, info))
sig = 0;
/* XXX: Once POSIX.1b timers are in, if si_code == SI_TIMER,
we need to xchg out the timer overrun values. */
}
; 刷新sigpeding标志
recalc_sigpending(current);
#if DEBUG_SIG
printk(" %d -> %d\n", signal_pending(current), sig);
#endif
return sig;
}
static int
next_signal(struct task_struct *tsk, sigset_t *mask)
{
unsigned long i, *s, *m, x;
int sig = 0;
s = tsk->pending.signal.sig; 信号集
m = mask->sig; 信号掩码
switch (_NSIG_WORDS) { _NSIG_WORDS 在i386中为 2
default:
for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m)
从信号集中排除阻塞的信号
if ((x = *s &~ *m) != 0) {
ffz寻找第一个为0的位,_NSIG_BPW为32
sig = ffz(~x) + i*_NSIG_BPW + 1;
找到信号集中的第一个信号
break;
}
break;
case 2: if ((x = s[0] &~ m[0]) != 0)
sig = 1;
else if ((x = s[1] &~ m[1]) != 0)
sig = _NSIG_BPW + 1;
else
break;
sig += ffz(~x);
break;
case 1: if ((x = *s &~ *m) != 0)
sig = ffz(~x) + 1;
break;
}
return sig;
}