网络程序要处理的三类事件:IO事件、信号事件、定时器事件。
1. 信号的概念:
1.1 谁会产生信号:
用户、系统、进程,都会产生信号,信号就是由用户/系统/进程 发给另一个进程的信息,用于通知进程某个状态的改变或者系统异常。
1.2 如何对待信号:
服务器程序必须处理一些信号(至少要忽略),因为如果不人为注册处理函数,系统会按照默认方式对一些信号进行处理,而很多信号的默认处理方式就是终止进程,对于服务器来说我们显然不想让它终止。
一旦有信号产生,用户进程对信号的处理方式有三种:
- 执行默认操作:
Linux对每种信号都规定了默认的操作,例如SIGTERM信号表示终止进程等; - 捕获信号:
可以为信号定义一个信号处理函数,当信号发生时,程序就会执行相应的处理函数; - 忽略信号:
如果不希望处理某些信号,就可以忽略该信号,不做任何处理。
有两个信号是应用进程无法捕获和忽略的:
SIGKILL(9) 和 SIGSTOP(19),它们用于在任何时候中断或结束某一进程。
2. 信号处理函数:
2.1 发送信号:
我们不能控制系统去给进程发送信号,但可以通过调用 kill
函数在一个进程中给另一个进程手动的发送信号:
//kill -- send signal to a process
#include <signal>
int kill(pid_t pid, int sig);
参数:
pid
表示要将信号发送给哪些进程:
当 pid > 0 时,只将信号发送给pid指定的进程;
当 pid = 0 时,(如果发送者进程有权限的话)将信号发送给发送者进程所在进程组内的所有进程;
当 pid < 0 时,(如果发送者进程有权限的话)将信号发送给系统内所有的进程。
2.2 sigaction() 函数:
函数原型:
//sigaction -- software signal facilities
#include <signal.h>
struct sigaction {
union __sigaction_u __sigaction_u; //指定信号处理函数
sigset_t sa_mask; //设置信号掩码,在sa_handler信号处理函数运行期间进程会屏蔽掉sa_mask指定的信号,防止信号处理函数被打断
int sa_flags; //设置程序接收到信号时的行为,例如 SA_RESTART,重新调用被该信号中止的系统调用
};
union __sigaction_u {
void (*__sa_handler)(int);
void (*__sa_sigaction)(int, siginfo_t *, void *);
};
#define sa_handler __sigaction_u.__sa_handler
#define sa_sigaction __sigaction_u.__sa_sigaction
int sigaction(int sig, const struct sigaction *restrict act,
struct sigaction *restrict oact)
------
//对于 sigset_t sa_mask :
typedef struct {
unsigned long int __val[SIGSET_NWORDS];
} _sigset_t;
int sigemptyset(sigset_t *set); //清空信号集
int sigfillset(sigset_t *set); //设置信号集中所有信号
int sigaddset(sigset_t *set, int signo); //将signo添加进set
int sigdelset(sigset_t *set, int signo); //将signo从set中移除
ing sigismember(sigset_t *set, int signo); //判断signo是否在set中
函数 sigaction() 可以理解为 “信号安装函数”,用来向系统中“安装”一个对应某信号的处理回调函数。
2.3 signal() 函数:
函数原型:
//signal -- simplified software signal facilities
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);
signal()函数这种方式比较简单粗暴,相比于sigaction()函数,signal()函数只是注册了 sa_handler处理函数,sa_mask和sa_flags均无法指定。
关于signal()函数的参数:
sig
:表示函数要处理的信号;
func
:对应信号处理的三种方式:
(1)如果是使用默认方式处理,func的值必须是 SIG_DFL
;
(2)如果是忽略信号,func的值必须是 SIG_IGN
;
(3)如果是捕获信号并注册处理函数,func的值是用户自定义的回调函数。
3. 实际应用:
signal(SIGCHLD, SIG_DFL); //使用该信号默认的处理方式
signal(SIGPIPE, SIG_IGN); //收到此信号后忽略该信号
signal(SIGINT, Stop); //捕获信号,并注册信号处理函数Stop
signal(SIGTERM, Stop);
signal(SIGUSR1, signal_handler_usr1);
signal(SIGUSR2, signal_handler_usr2);
signal(SIGHUP, signal_handler_hup);
------
void Stop(int sig_no) {
log("recieve signal: %d\n", sig_no);
switch(sig_no) {
case SIGINT:
case SIGTERM:
case SIGQUIT:
doQuitJob();
_exit(0);
break;
default:
cout << "unknown signal" << endl;
_exit(0);
}
}
static void signal_handler_user1(int sig_no) {
if(sig_no == SIGUSR1) {
log("recieve SIGUSR1");
g_up_msg_total_cnt = 0;
g_up_msg_miss_cnt = 0;
}
}
static void signal_handler_user2(int sig_no) {
if(sig_no == SIGUSR2) {
log("recieve SIGUSR2");
g_log_msg_toggle = !g_log_msg_toggle;
}
}
static void signal_hanlder_hup(int sig_no) {
if(sig_no == SIGHUP) {
log("recieve SIGHUP");
exit(0);
}
}
4. 网络编程中常见的几个信号:
Linux系统中提供的信号:(可在命令行中使用 kill -l
命令查看)
摘自 man signal 手册:
No Name Default Action Description
1 SIGHUP terminate process terminal line hangup
2 SIGINT terminate process interrupt program
3 SIGQUIT create core image quit program
4 SIGILL create core image illegal instruction
5 SIGTRAP create core image trace trap
6 SIGABRT create core image abort program (formerly SIGIOT)
7 SIGEMT create core image emulate instruction executed
8 SIGFPE create core image floating-point exception
9 SIGKILL terminate process kill program
10 SIGBUS create core image bus error
11 SIGSEGV create core image segmentation violation
12 SIGSYS create core image non-existent system call invoked
13 SIGPIPE terminate process write on a pipe with no reader
14 SIGALRM terminate process real-time timer expired
15 SIGTERM terminate process software termination signal
16 SIGURG discard signal urgent condition present on socket
17 SIGSTOP stop process stop (cannot be caught or ignored)
18 SIGTSTP stop process stop signal generated from keyboard
19 SIGCONT discard signal continue after stop
20 SIGCHLD discard signal child status has changed
21 SIGTTIN stop process background read attempted from control terminal
22 SIGTTOU stop process background write attempted to control terminal
23 SIGIO discard signal I/O is possible on a descriptor (see fcntl(2))
24 SIGXCPU terminate process cpu time limit exceeded (see setrlimit(2))
25 SIGXFSZ terminate process file size limit exceeded (see setrlimit(2))
26 SIGVTALRM terminate process virtual time alarm (see setitimer(2))
27 SIGPROF terminate process profiling timer alarm (see setitimer(2))
28 SIGWINCH discard signal Window size change
29 SIGINFO discard signal status request from keyboard
30 SIGUSR1 terminate process User defined signal 1
31 SIGUSR2 terminate process User defined signal 2
4.1 SIGINT(2)、SIGTERM(15)、SIGKILL(9) 的区别:
三者都是结束/终止进程运行。
SIGINT与 Ctrl+C 关联,SIGTERM和SIGKILL没有与任何控制字符关联;
SIGINT只能结束前台进程。
SIGTERM可以被阻塞、处理和忽略,SIGKILL不可以;
KILL命令默认的不带参数发送的信号就是 SIGTERM,让程序可以捕获并优雅的退出。但是由于SIGTERM可以被阻塞,所以当进程不能被结束时,需要使用SIGKILL强制进程退出。