void sigfun(int signum)
{
printf("signal number = %d\n",signum);
(void)
signal(SIGINT,SIG_DFT); //set to default handler
}
int mian()
{
(void)
signal(SIGINT,sigfun); //set sigfun for signal SIGINT's handler
while(1) //wait the signal
{
printf("Signal
test.\n"); sleep(1); //sleep 1 second
}
return
0;
}
#gcc sigtest.c -o sigtest
#./sigtest
#Signal test.
#Signal test.
#signal number =
2 //第一次按Ctrl +
c,主程序捕获该信号,并调用sigfun处理
#signal test.
# //第二次按Ctrl
+ c,程序正常退出
下面我们就要开始看kernel的代码了,首先了解task_struct结构中signal相关的部分
struct
task_struct{
//...
/*signal handlers*/
//typedef
struct {volatile unsigned long lock;}spinlock_t;
spinlock_t sigmask_lock; /*protects signal and
blocked*/
struct signal_struct * sig; //用来保存对于不同信号采取的处理方式,所注册的新的信号处理函数就保存在这里
sigset_t blocked;
struct sigpending
pending; //进程的未决信号集,也就是进程接收到的未来得及处理和屏蔽掉的信号都保存在这里,用链表实现
//...
};
struct
signal_struct{
atomic_t count; //typedef struct
{volatile int couter;} atomic_t;
struct k_sigaction action[_NSIG]; //#define _NSIG
128
spinlock_t siglock;
};
struct
k_signation{
struct sigaction sa;
#ifdef
CONFIG_BINFMT_IRIX
void (*sa_restorer)(void);
#endif
};
struct
sigaction{
unsigned int sa_flags;
//typedef void
(*__sighandler)(int);
信号处理函数,如上例的函数sigfun
__sighandler sa_handler;
//typedef
sturct {unsigned long
sig[_NSIG_WORDS];} sigset_t;
//_NSIG_WORDS = 4
sigset_t sa_mask;
};
struct sigpending{
struct sigqueue * head,**tail;
sigset_t signal;
};
struct sigqueue {
struct sigqueue *next;
siginfo_t info;
};
OK,信号主要涉及的struct差不多都在这了,看程序吧。用户使用signal函数其实是个系统调用,反应到kernel里就是sys_signal,看程序:
unsigned long sys_signal(int
sig,__sighandler handler)
{
struct k_sigaction new_sa,old_sa;
int ret;
new_sa.sa.sa_handler =
handler; //保存用户传入的信号处理函数指针
new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
//调用do_sigaction
ret = do_sigaction(sig,&new_sa,&old_sa);
return ret? ret:(unsigned long)old_sa.sa.handler;
}
int do_sigaction(int sig,const struct
k_sigcation * act, struct k_sigaction * oact)
{ struct k_sigaction * k;
// ...
//读取当前进程中老的信号信息
k=&
current->sig->action[sig-1]; //task_struct
current;
if(oact) //将该信号信息保存起来
*oact = *k;
if(act){
*k
= *act; //再将新的信号信息保存到进程的信号列表中
//设置该信号需要屏蔽的信号
//#define sigmask(sig)
(1UL<
//获得SIGKILL和SIGSTOP的屏蔽码sigmask(SIGKILL) |
sigmask(SIGSTOP)
sigdelsetmask(&k->sa.sa_mask,sigmask(SIGKILL)|sigmask(SIGSTOP));
//到这里已经看得很清楚了,用户自己注册的信号处理方式已经存入到当前进程的信号集里,然后就是继续进程调度和等候信号事件发生了。。
//...
return 0;
}
}
static inline void
sigdelsetmask(sigset_t *set, unsigned long
mask)
{
//设置不可屏蔽的信号,比如SIGKILL为9,则~sigmask(SIGKILL)为0xfffeffff
//set->sig[0]为32位unsighed
long类型,刚好可以表示对32个信号是否屏蔽.
//然后通过&=操作,刚好可以将SIGKILL对应的第8个bit设为0.表示SIGKILL不被屏蔽
//对于大于32的信号的屏蔽,则可以用set->sig[1]来实现(struct
sigset_t定义了一个包含四个unsighed
long类型的数组,最大可以表示对128个信号的屏蔽情况).
//看似简单,实则巧妙!#$%
set->sig[0] &= ~mask;
}