信号在android源码/external/dhcpcd 源码项目中的应用解读分析

dhcp源码中也包含了信号的处理,现在单独拿出来看看代码的逻辑架构,分析下怎么样写一个合理的信号处理。、

先看signal.c里面的实现,如下:

#include <sys/types.h>
#include <sys/socket.h>

#include <errno.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#include "common.h"
#include "signals.h"

static int signal_pipe[2]; //定义pipe通信

static const int handle_sigs[] = { //信号数组的定义
	SIGALRM,
	SIGHUP,
	SIGINT,
	SIGPIPE,
	SIGTERM,
	SIGUSR1,
};

//将信号写入管道
static void
signal_handler(int sig)
{
	int serrno = errno;

	if (write(signal_pipe[1], &sig, sizeof(sig)) != sizeof(sig))
		syslog(LOG_ERR, "failed to write signal %d: %m", sig);
	/* Restore errno */
	errno = serrno;
}

//从管道读入信号
/* Read a signal from the signal pipe. Returns 0 if there is
 * no signal, -1 on error (and sets errno appropriately), and
 * your signal on success */
int
signal_read(void)
{
	int sig = -1;
	char buf[16];
	ssize_t bytes;

	memset(buf, 0, sizeof(buf));
	bytes = read(signal_pipe[0], buf, sizeof(buf));
	if (bytes >= 0 && (size_t)bytes >= sizeof(sig))
		memcpy(&sig, buf, sizeof(sig));
	return sig;
}

/* Call this before doing anything else. Sets up the socket pair
 * and installs the signal handler */
int
signal_init(void)
{  
               //定义pipe进程间通信方式,并设置为不阻塞以及防止任何脚本继承pipe
              if (pipe(signal_pipe) == -1)
		return -1;
	/* Don't block on read */
	if (set_nonblock(signal_pipe[0]) == -1)
		return -1;
	/* Stop any scripts from inheriting us */
	if (set_cloexec(signal_pipe[0]) == -1)
		return -1;
	if (set_cloexec(signal_pipe[1]) == -1)
		return -1;
                // 返回读pipe
                return signal_pipe[0];
}

static int
signal_handle(void (*func)(int))
{
	unsigned int i;
	struct sigaction sa;

	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = func;
	sigemptyset(&sa.sa_mask);
        //遍历上面信号数组,一旦由上述的信号产生,就会进入到上面的signal_handler里面,注意这里没有屏蔽信号,会中断信号的过程
	for (i = 0; i < sizeof(handle_sigs) / sizeof(handle_sigs[0]); i++)
if (sigaction(handle_sigs[i], &sa, NULL) == -1)return -1;return 0;}intsignal_setup(void){
//传入信号处理handler函数,见上面的定义return signal_handle(signal_handler);
}
//信号默认操作,恢复信号原始操作方式intsignal_reset(void){return signal_handle(SIG_DFL);}


接下来,继续看dhcp的主体函数在dhcpcd.c里面

如下:在main函数里面,如下,

	if ((signal_fd = signal_init()) == -1)
		exit(EXIT_FAILURE);
	if (signal_setup() == -1)
		exit(EXIT_FAILURE);
	add_event(signal_fd, handle_signal, NULL);

先初始化信号,然后设置信号的hanler函数,先看下面的代码流程,

* ARGSUSED */
static void
handle_signal(_unused void *arg)
{
	struct interface *ifp, *ifl;
	struct if_options *ifo;
	int sig = signal_read();
	int do_release, do_rebind, i;

	do_rebind = do_release = 0;
	switch (sig) {
	case SIGINT:
		syslog(LOG_INFO, "received SIGINT, stopping");
		break;
	case SIGTERM:
		syslog(LOG_INFO, "received SIGTERM, stopping");
		break;
	case SIGALRM:
#ifdef ANDROID
		syslog(LOG_INFO, "received SIGALRM, renewing");
		for (ifp = ifaces; ifp; ifp = ifp->next) {
			start_renew(ifp);
		}
#else
		syslog(LOG_INFO, "received SIGALRM, rebinding");
		for (i = 0; i < ifac; i++)
			free(ifav[i]);
		free(ifav);
		ifav = NULL;
		ifac = 0;
		for (i = 0; i < ifdc; i++)
			free(ifdv[i]);
		free(ifdv);
		ifdc = 0;
		ifdv = NULL;
		ifo = read_config(cffile, NULL, NULL, NULL);
		add_options(ifo, margc, margv);
		/* We need to preserve these two options. */
		if (options & DHCPCD_MASTER)
			ifo->options |= DHCPCD_MASTER;
		if (options & DHCPCD_DAEMONISED)
			ifo->options |= DHCPCD_DAEMONISED;
		options = ifo->options;
		free_options(ifo);
		reconf_reboot(1, ifc, ifv, 0);
#endif
		return;
	case SIGHUP:
		syslog(LOG_INFO, "received SIGHUP, releasing");
		do_release = 1;
		break;
	case SIGUSR1:
		syslog(LOG_INFO, "received SIGUSR, reconfiguring");
		for (ifp = ifaces; ifp; ifp = ifp->next)
			if (ifp->state->new)
				configure(ifp);
		return;
	case SIGPIPE:
		syslog(LOG_WARNING, "received SIGPIPE");
		return;
	default:
		syslog(LOG_ERR,
		    "received signal %d, but don't know what to do with it",
		    sig);
		return;
	}
              //后面与handler无关的先不用管
}

上面的函数很重要,看原型,在eloop.c里面,void
add_event(int fd, void (*callback)(void *), void *arg)

代码实现是:对fd进行相应的poll检测,如果由fd数据到达,就会进入到相应的回调函数里面

因此,当sig来的时候,步骤如下:

1)当sig发生的时候,进入到捕获函数里面,也就是:signal_handler

然后往里面写sig号,即:write(signal_pipe[1], &sig, sizeof(sig)) != sizeof(sig)

2)由于此时event机制死循环,检测到fd发生了变化,于是去读取相应的sig

然后调到了handle_signal

3)最后,通过signal_read 去读取相应的值,这样就实现了整套signal机制。

不过这里有个地方,和平时使用pipe不一样,没有fork进程,直接同一个进程通信,

恩,下次下一个demo看下!


源码第二个使用signal的地方是:configure.c里面,真正执行脚本的时候,

见下面:

static int
exec_script(char *const *argv, char *const *env)
{
	pid_t pid;
	sigset_t full;
	sigset_t old;

	/* OK, we need to block signals */
	sigfillset(&full);
	sigprocmask(SIG_SETMASK, &full, &old);
	signal_reset();//使用默认的操作

	switch (pid = vfork()) {
	case -1:
		syslog(LOG_ERR, "vfork: %m");
		break;
	case 0:
                              //复原了信号,然后执行脚本
                              sigprocmask(SIG_SETMASK, &old, NULL);
		execve(argv[0], argv, env);
		syslog(LOG_ERR, "%s: %m", argv[0]);
		_exit(127);
		/* NOTREACHED */
	}

	/* Restore our signals */
	signal_setup();//开始信号捕捉函数
	sigprocmask(SIG_SETMASK, &old, NULL);
	return pid;
}

这里就使用了signal屏蔽码的用法,需要学习下用法就行了。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值