信号(signal)使用的疑问

一、信号

信号是软件范畴中一个类似于硬件中断的概念,但它不能像硬件中断那样直接打断程序的运行,目标进程对信号的处理只会发生在该进程被重新调度执行,也就是从内核态切换到用户态的时候。由于用户态和内核态的切换是很频繁的,因而信号通常能很快地得到目标进程的响应,看起来就跟中断的效果一样。

二、问题

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)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值