一、信号
信号是软件范畴中一个类似于硬件中断的概念,但它不能像硬件中断那样直接打断程序的运行,目标进程对信号的处理只会发生在该进程被重新调度执行,也就是从内核态切换到用户态的时候。由于用户态和内核态的切换是很频繁的,因而信号通常能很快地得到目标进程的响应,看起来就跟中断的效果一样。
二、问题
1、假如在目标进程执行信号处理函数期间,处理函数还没有执行完,又收到了同样的信号,那此信号会被相应嘛?
1.1、编写代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void signalHandler(int sig)
{
int count = 5;
switch (sig) {
case SIGINT:
printf("signal int\n");
break;
case SIGTERM:
printf("signal term\n");
break;
default:
break;
}
while(count--) {
printf("count=%d\n", count);
sleep(1);
}
}
int main()
{
signal(SIGINT, signalHandler);
while (1);
return 0;
}
1.2、程序运行后,按ctrl+C发送SIGINT信号。
第一个^C发送SIGINT信号,信号处理函数开始运行。在第一次信号处理函数还没有结束的时候,第二个^C发送SIGINT信号,发信会继续运行信号处理函数。假如连续发送两次SIGINT信号(第三个和第四个^C)发现只会运行一次信号处理函数。
1.3、猜测
当执行信号处理函数的时候,sigmask取消信号,在信号处理函数还没有结束的时候第二次发送信号,此信号会sigpend,在第二次信号还没有开始运行再一次发送信号时,内核没有信号等待队列,直接丢弃。
1.4、寻找内核的实现
1.4.1、信号处理函数的运行都是通过kill命令触发执行。如kill -9 1234,向1234的进程发送信号9
分析kill讷河函数流程
SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
kill_something_info
group_send_sig_info
do_send_sig_info
send_signal
__send_signal
legacy_queue
static int __send_signal(int sig, struct kernel_siginfo *info, struct task_struct *t,
enum pid_type type, bool force)
static inline bool legacy_queue(struct sigpending *signals, int sig)
{
return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);
}
在legacy_queue函数中可以看出,假如当前signal没有执行完还处于pending状态,会goto ret,丢弃signal,与实验验证相呼应。
1.4.2、当发送signal后,信号处理函数什么阶段会执行。不同CPU环境入口会有差别,知道do_signal函数。以arm处理器为例。
在系统调用结束后返回时,会调用ret_fast_syscall
ret_fast_syscall
do_work_pending
do_signal
handle_signal(&ksig, regs);
如下图所示
因此,信号处理函数会在系统调用接口结束返回时执行到,看起来像是并行执行。
2、既然信号可以并行执行,如果信号处理函数A在执行过程中运行C函数,此时又响应了另外一个信号,这个信号处理函数也会运行C函数,这又会怎么样呢?
2.1、编写代码
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
int a = 0;
void handleSig5(int num, siginfo_t *info, void *no_used)
{
a = 5;
printf("\nbefore kill a = %d\n", a);
kill(getpid(), 6);
printf("after kill a = %d\n", a);
return;
}
void handleSig6(int num, siginfo_t *info, void *no_used)
{
printf("set a to 6\n");
a = 6;
return;
}
int main(void)
{
struct sigaction act;
act.sa_handler = handleSig5;
sigaction(5, &act, NULL);
act.sa_handler = handleSig6;
sigaction(6, &act, NULL);
printf("[%d]wait a signal....\n", getpid());
while(1);
return 0;
}
2.2、运行结果
2.3、发现全局变量a被另外一个信号处理函数修改。因此要求信号处理函数必须是可重入的(reentrant)。