专栏内容:
postgresql内核源码分析
手写数据库toadb
并发编程
个人主页:我的主页
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
================================
信号是一种软中断的方式,让进程陷入中断处理调用中;
linux 下信号也是一种进程间通信的手段;进程间也可以互相发送信号,来传递状态,让对方获知,并处理一些事情。
linux下信号种类很多 ,可以通过kill 命令来查询
[senllang@localhost Dev]$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
这些信号分为两部分,
1-31 是POSIX定义的可靠信号,其中 SIGKILL,SIGSTOP 两个信号不能被应用程序捕获处理,不能被阻塞,也不能忽略。
34-64 是POSIX定义的real-time使用的信号,因为实时性,可能会丢失,主要有RTMAX, RTMIN两类;
大多数信号可以被应用程序捕获,这样就可以设置处理函数自定义处理行为。
信号的处理函数,一般有三种:
- 自定义,通过设置函数进行设置,当信号产生时调用此回调函数;
- SIG_DFL ,默认值,执行默认行为;
- SIG_IGN ,忽略,不执行任何动作;
自定义信号的处理函数, 设置处理函数有两个函数signal和sigaction ,它们定义如下:
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
这个函数相对简单,signum 就是要处理的信号值,handler就是自定义处理函数,类型必须是 sighandler_t;
让我们举个例子看一下。
#include
#include
#include
void MySigproc(int signumber)
{
printf("catch signal is %d, we process it now.\n",signumber);
}
int main()
{
signal(2,MySigproc);
signal(3,MySigproc);
signal(10,MySigproc);
signal(12,MySigproc);
do
{
/* code */
sleep(1000);
printf("signal break me dream.\n");
} while (1);
return 0;
}
获后我们来运行一下,看下效果
# 终端 1 运行
[senllang@localhost signal]$ ./a.out
# 终端 2 发送信号
[senllang@localhost Dev]$ ps -ef|grep a.out
senllang 2700150 2035891 0 14:41 pts/1 00:00:00 ./a.out
senllang 2700164 2119038 0 14:41 pts/4 00:00:00 grep --color=auto a.out
[senllang@localhost Dev]$ kill -2 2700150
[senllang@localhost Dev]$ kill -3 2700150
[senllang@localhost Dev]$ kill -10 2700150
[senllang@localhost Dev]$ kill -12 2700150
# 终端 1 捕获信号
catch signal is 2, we process it now.
signal break me dream.
catch signal is 3, we process it now.
signal break me dream.
catch signal is 10, we process it now.
signal break me dream.
catch signal is 12, we process it now.
signal break me dream.
最后按下Ctr+c已经不起作用了,最后用kill -9 强制结束
另外设置函数提供的选项比较丰富,通过传入sigaction结构体参数来设置
#include
int sigaction(int signum,
const struct sigaction *_Nullable restrict act,
struct sigaction *_Nullable restrict oldact);
struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
- 其中 sa_handler 和 sa_sigaction 设置其一就可以;
- sa_handler 可以设置三种处理方式,默认,忽略,自定义
- sa_restorer 不是给应用程序使用的,不用管;
- sa_mask 是设置那些信号位被阻塞,可以|多个信号;
- sa_flags 是定义一些行为;
信号阻塞,在设置信号阻塞掩码sigmask, 置1的信号在产生后,处于pending状态,即信号未决状态,
直到信号投递,也就是信号处理函数被调用。
sigprocmask 接口可以获取 或者 设置 信号mask
#include
int sigprocmask(int how, const sigset_t *_Nullable restrict set,
sigset_t *_Nullable restrict oldset);
参数说明
- how 可以为
- SIG_BLOCK, set 中的信号为要阻塞的信号,加到已有的阻塞信号中
- SIG_UNBLOCK, set 中的信号为要解除阻塞的信号,从已有的阻塞信号中去除
- SIG_SETMASK, 将现有的阻塞信号替换为 set 指定的信号
set , 要修改的信号集
oldset, 不为空,获取修改前的信号集
多线程时,要用 pthread_sigmask ,功能一样
下面举例,先将所有信号阻塞,然后再将SIGINT信号解除阻塞
void blocksig()
{
sigset_t set;
sigset_t oldset;
sigfillset(&set);//所有比特位全部置为1,则信号会全部被阻塞
sigprocmask(SIG_BLOCK,&set,&oldset);
sigemptyset(&set);//初始化信号量集
sigaddset(&set, SIGINT);//将SIGINT添加到信号量集中
sigprocmask(SIG_UNBLOCK, &set, &oldset);
}
信号可以是命令产生,如键盘按键,或kill 等;
也可以由程序控制,来给某个进程发送信号。
可以通过这些函数进行信号发送
#include
int raise(int sig);
#include
int kill(pid_t pid, int sig);
就是当前进程被阻塞,直到某个信号发生;
当信号处理函数返回时,当前阻塞的进程才继续运行
#include
int pause(void);
#include
int sigsuspend(const sigset_t *mask);
我们举个例子来看看
#include
#include
#include
void MySigproc(int signumber)
{
printf("catch signal is %d, we process it now.\n",signumber);
}
int main()
{
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGQUIT);
sigaddset(&set, SIGUSR1);
signal(SIGINT,MySigproc);
signal(SIGQUIT,MySigproc);
signal(SIGUSR1,MySigproc);
signal(SIGUSR2,MySigproc);
do
{
/* code */
sigsuspend(&set);
printf("signal break me dream.\n");
} while (1);
return 0;
}
通过两个终端进行演示
# 终端1 运行结果
[senllang@localhost signal]$ ./a.out
catch signal is 12, we process it now.
catch signal is 10, we process it now.
catch signal is 3, we process it now.
catch signal is 2, we process it now.
signal break me dream.
# 终端2 发送信号
[senllang@localhost Dev]$ ps -ef|grep a.out
senllang 2723802 2035891 0 19:28 pts/1 00:00:00 ./a.out
senllang 2723806 2119038 0 19:28 pts/4 00:00:00 grep --color=auto a.out
[senllang@localhost Dev]$ kill -2 2723802
[senllang@localhost Dev]$ kill -3 2723802
[senllang@localhost Dev]$ kill -3 2723802
[senllang@localhost Dev]$
[senllang@localhost Dev]$ kill -10 2723802
[senllang@localhost Dev]$ kill -12 2723802
我们看到,在终端2不断发送信号,直到发送了等待的非挂起信号12后,才开始解除信号阻塞,并处理消息,并解除进程阻塞
对于2,3,10是阻塞信号,并没有立即处理,而且阻塞信号只处理一次
当在关键代码段时,我们不希望被中断,不被干扰,在之前或之后再处理中断。
一般会在关键代码块前阻塞信号,然后执行完关键代码块后获取未决的信号进行处理;
非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!
作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!