打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
linux内核中的信号机制
linux内核中的信号机制--信号机制的管理结构
Kernel version:2.6.14
CPU architecture:ARM920T
Author:ce123(http://blog.csdn.net/ce123)
信号只是一个数字,数字为0-31表示不同的信号,如下表所示。
信号名
默认动作
说明
1
SIGHUP
进程终止
终端断开连接
2
SIGINT
进程终止
用户在键盘上按下CTRL+C
3
SIGQUIT
进程意外结束(Dump)
用户在键盘上按下CTRL+\
4
SIGILL
进程意外结束(Dump)
遇到非法指令
5
SIGTRAP
进程意外结束(Dump)
遇到断电,用于调试
6
SIGABRT/SIGIOT
进程意外结束(Dump)
7
SIGBUS
进程意外结束(Dump)
总线错误
8
SIGFPE
进程意外结束(Dump)
浮点异常
9
SIGKILL
进程终止
其他进程发送SIGKILL将导致目标进程终止
10
SIGUSR1
进程终止
应用程序可自定义使用
11
SIGSEGV
进程意外结束(Dump)
非法的内存访问
12
SIGUSR2
进程终止
应用程序可自定义使用
13
SIGPIPE
进程终止
管道读取端已经关闭,写入端进程会收到该信号
14
SIGALRM
进程终止
定时器到时
15
SIGTERM
进程终止
发送该信号使目标进程终止
16
SIGSTKFLT
进程终止
堆线错误
17
SIGCHLD
忽略
子进程退出时会向父进程发送该信号
18
SIGCONT
忽略
进程继续执行
19
SIGSTOP
进程暂停
发送该信号会使目标进程进入TASK_STOPPED状态
20
SIGTSTP
进程暂停
在终端上按下CTRL+Z
21
SIGTTIN
进程暂停
后台进程从控制终端读取数据
22
SIGTTOU
进程暂停
后台进程从控制终端读取数据
23
SIGURG
忽略
socket收到设置紧急指针标志的网络数据包
24
SIGXCPU
进程意外结束(Dump)
进程使用CPU已经超过限制
25
SIGXFSZ
进程意外结束(Dump)
进程使用CPU已经超过限制
26
SIGVTALRM
进程终止
进程虚拟定时器到期
27
SIGPROF
进程终止
进程Profile定时器到期
28
SIGMNCH
忽略
进程终端窗口大小改变
29
SIGIO
进程暂停
用于异步IO
29
SIGPOLL
进程暂停
用于异步IO
30
SIGPWR
进程暂停
电源失效
31
SIGUNUSED
进程暂停
保留未使用
注意在上标中的默认动作是指,在没有任何程序为相应的信号设置信号处理函数的情况下,内核接收到该信号的默认处理方式,但在实际中,有可能不是这样的。另外,在这里,进程终止一般是指进程通过do_exit()退出,进程意外结束(Dump)则表示进程遇到了一个异常。默认情况下,内核会根据进程当时的内存情况,在进程的当前目录中生成一个Core Dump文件,以后用户可以通过这个文件分析进程异常的原因。这个工作主要通过do_coredump()来完成。
由于早期只有31个信号,内核仅仅使用一个32位的变量signal来表示进程接收到的信号,因此如果要向一个进程发送一个信号,就把signal的第n位设置为1,这非常类似中断请求寄存器SRCPND寄存器,同时,还有一个blocked的变量,用来屏蔽信号,这类似中断屏蔽寄存器INTMSK。这样做的好处是可以“很快”判断出一个进程收到了哪些信号,如果采用链表或者数组,则需要扫描整个队列,但这也带来了新的问题,如果向一个进程发送了SIGINT信号,在这个信号处理之前,再次发送SIGINT,当这个进程开始处理信号时,它只知道收到了SIGINT信号,而无法判断出有几个SIGINT需要处理。此后加入了信号队列,把收到的信号保存在这个队列中,就可以很好的解决这个问题了。但是为了兼容的目的,仍能保留了旧的信号处理方式,因此1-32还是按原有的方式进行处理,而33-64则使用新的机制,为了区别对待,编号为33-64的信号又称为实时信号。需要注意的是:这里的“实时”和实时操作系统中的“实时”没有任何联系,实时信号在处理速度上并不会比普通信号快,它们之间的区别就是:普通信号会对多次的同一个信号进行“合并”处理,而实时信号会一一处理。因此我们这里仅讨论普通信号。
信号机制的相关管理结构位于task_struct结构中,其主要结构如下图所示。
实时信号引入了信号队列,为了处理上的方便,普通信号也使用了信号队列,仅仅从数字上无法区分实时信号和普通信号。由于在linux中,进程对象和线程对象都是task_struct,因此需要区别对待线程的信号和进程的信号。在上图中,Private Signal Queue是线程(在linux中称为轻权进程)信号队列,而Shared Signal Queue是进程(在linux中被称为进程组)信号队列。对于进程信号,则由进程组中的每一个线程共享。例如,在上图中,pending和shared_pending分别是Private Signal Queue和Shared Signal Queue其类型都是sigpending(/include/linux/signal.h),定义如下:
[plain]?
struct sigpending {
struct list_head list;
sigset_t signal;
};
list用于连接信号队列,signal是一个位图,每一位表示一个对应的信号,用于指示信号队列中有哪些信号等待处理,其类型为sigset_t(include/asm-arm/signal.h),其定义如下:
[plain]?
#define _NSIG       64
#define _NSIG_BPW   32
#define _NSIG_WORDS (_NSIG / _NSIG_BPW)
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t;
sigset_t是一个数组,总共有64位,对应64个信号位图(32个普通信号和32个实时信号)。在以后的信号发送的分析中,我们会看到,对于普通信号,只需要把sigset_t中对应的位置1就可以了,而对于实时信号,还需要把相关信息添加到list的信号队列,信号队列类型为sigqueue(include/linux/signal.h),定义如下:
[plain]?
/*
* Real Time signals may be queued.
*/
struct sigqueue {
struct list_head list;
spinlock_t *lock;
int flags;
siginfo_t info;
struct user_struct *user;
};
sigqueue中的list是队列链表指针,info为这个信号的相关信息,其定义如下(include/asm-generic/siginfo.h):
[plain]?
typedef struct siginfo {
int si_signo;
int si_errno;
int si_code;
union {
int _pad[SI_PAD_SIZE];
/* kill() */
struct {
pid_t _pid;     /* sender's pid */
__ARCH_SI_UID_T _uid;   /* sender's uid */
} _kill;
/* POSIX.1b timers */
struct {
timer_t _tid;       /* timer id */
int _overrun;       /* overrun count */
char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];
sigval_t _sigval;   /* same as below */
int _sys_private;       /* not to be passed to user */
} _timer;
/* POSIX.1b signals */
struct {
pid_t _pid;     /* sender's pid */
__ARCH_SI_UID_T _uid;   /* sender's uid */
sigval_t _sigval;
} _rt;
/* SIGCHLD */
struct {
pid_t _pid;     /* which child */
__ARCH_SI_UID_T _uid;   /* sender's uid */
int _status;        /* exit code */
clock_t _utime;
clock_t _stime;
} _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
struct {
void __user *_addr; /* faulting insn/memory ref. */
#ifdef __ARCH_SI_TRAPNO
int _trapno;    /* TRAP # which caused the signal */
#endif
} _sigfault;
/* SIGPOLL */
struct {
__ARCH_SI_BAND_T _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
} _sifields;
} siginfo_t;
上图中的sighand保存信号的处理函数指针,其作用类似于中断向量表,类型为sighand_struct(include/linux/sched.h),定义为:
[plain]?
struct sighand_struct {
atomic_t        count;
struct k_sigaction  action[_NSIG];
spinlock_t      siglock;
};
_NSIG定义在asm-arm/signal.h中,为64,数组action,对应64个信号处理函数的相关信息,烈性为k_sigaction,在arm平台上,k_sigaction(include/asm-arm/signal.h)是死噶长提哦你的一个包装,定义如下:
[plain]?
struct k_sigaction {
struct sigaction sa;
};
sigantion(include/asm-arm/signal.h)的定义如下:
[plain]?
struct sigaction {
__sighandler_t sa_handler;
unsigned long sa_flags;
__sigrestore_t sa_restorer;
sigset_t sa_mask;       /* mask last for extensibility */
};
sa_handler就是信号处理函数指针。另外在task_struct结构中还有一个blocked可以用来屏蔽信号。明白了上面的主要数据结构的作用之后,很容易想到信号的处理主要有以下几方面。
设置信号回调函数:内核吧函数的相关信息保存到对应的sigantion结构中。
信号的发送:通过相关系统调用吧一个指定的信号发送到目标进程,如果该信号没有被屏蔽,就把信号的相关信息添加到信号队列中,如果有必要就唤醒目标进程。
信号响应:进程被唤醒后,根据信号队列中的信息,调用信号回调函数。
我们再来看一下信号回调函数,sa_handler的类型是__sihandler_t(include/asm-generic/singal.h),其定义为:
[plain]?
typedef void __signalfn_t(int);
typedef __signalfn_t __user *__sighandler_t;
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
libevent源码深度剖析八
Linux signal 那些事儿(2)
深入理解Linux信号机制
linux signal 处理
深刻理解 Linux 进程间七大通信(IPC)
Linux内核中task
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服