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

来源:互联网 发布:linux 删除tomcat日志 编辑:程序博客网 时间:2024/06/15 03:40

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 voidsignal_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 */intsignal_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 */intsignal_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 intsignal_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 voidhandle_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 ANDROIDsyslog(LOG_INFO, "received SIGALRM, renewing");for (ifp = ifaces; ifp; ifp = ifp->next) {start_renew(ifp);}#elsesyslog(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);#endifreturn;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 intexec_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屏蔽码的用法,需要学习下用法就行了。







原创粉丝点击