零停重启程序工具Huptime研究

目录
目录1
1.官网1
2.功能1
3.环境要求2
4.实现原理2
5.SIGHUP信号处理 3
6.重启线程4
7.重启目标程序5
8.系统调用钩子辅助6
9.被勾住系统调用exit 6
10.被勾住系统调用listen 7
11.SymbolVersioning8
12.勾住bind等系统调用 10
13.系统调用过程13
14.测试代码13
14.1.Makefile13
14.2.s.c14
14.3.s.map14
14.4.x.cpp14
14.5.体验方法15
1.官网
https://github.com/amscanne/huptime
2.功能
零停重启目标程序,比如一个网络服务程序,不用丢失和中断任何消息实现重新启动,正在处理的消息也不会中断和丢失,重启的方法是给目标程序的进程发SIGHUP信号。
3.环境要求
由于使用了Google牛人TomHerbert为Linux内核打的补丁SO_REUSEPORT特性,因此要求Linux内核版本为3.9或以上,SO_REUSEPORT允许多个进程监听同一IP的同一端口。
4.实现原理
利用SIGHUP+SO_REUSEPORT+LD_PRELOAD,通过LD_PRELOAD将自己(huptime.so)注入到目标进程空间。
使用Python脚本huptime启动时会设置LD_PRELOAD,将huptime.so注入到目标程序的进程空间。
huptime.so启动时会执行setup函数,在setup中会创建一个线程impl_restart_thread用于重启目标程序的进程,另外还会安装信号SIGHUP的处理器sighandler用于接收零重启信号SIGHUP:
staticvoid__attribute__((constructor)) setup(void) { #definelikely(x)__builtin_expect(!!(x),1) if(likely(initialized))//只做一次 return; initialized=1; #defineGET_LIBC_FUNCTION(_name)\ libc._name=get_libc_function<_name##_t>(#_name,&_name) //初始化全局变量libc,让其指向GLIBC库的bind等 GET_LIBC_FUNCTION(bind);//libc.bind=dlsym(RTLD_NEXT,bind);//系统的bind GET_LIBC_FUNCTION(listen); GET_LIBC_FUNCTION(accept); GET_LIBC_FUNCTION(accept4); GET_LIBC_FUNCTION(close); GET_LIBC_FUNCTION(fork); GET_LIBC_FUNCTION(dup); GET_LIBC_FUNCTION(dup2); GET_LIBC_FUNCTION(dup3); GET_LIBC_FUNCTION(exit); GET_LIBC_FUNCTION(wait); GET_LIBC_FUNCTION(waitpid); GET_LIBC_FUNCTION(syscall); GET_LIBC_FUNCTION(epoll_create); GET_LIBC_FUNCTION(epoll_create1); #undefGET_LIBC_FUNCTION impl_init();//安装信号SIGHUP处理器、创建重启线程等 } template staticFUNC_T get_libc_function(constchar*name,FUNC_Tdef) { char*error; FUNC_Tresult; /*Clearlasterror(ifany).*/ dlerror(); /*Trytogetthesymbol.*/ result=(FUNC_T)dlsym(RTLD_NEXT,name); error=dlerror(); if(result==NULL||error!=NULL) { fprintf(stderr,"dlsym(RTLD_NEXT,\"%s\")failed:%s",name,error); result=def; } returnresult; } |
5.SIGHUP信号处理
//信号SIGHUP处理函数,作用是通过管道通知重启线程impl_restart_thread, //这里其实可以考虑使用eventfd替代pipe staticvoid*impl_restart_thread(void*); void sighandler(intsigno) { /*Notifytherestartthread. *Wehavetodothisinaseparatethread,because *wehavenoguaranteesaboutwhichthreadhasbeen *interruptedinordertoexecutethissignalhandler. *Becausethiscouldhavehappenedduringacritical *section(i.e.locksheld)wehavenochoicebutto *firetherestartasycnhronouslysothatittoocan *grablocksappropriately.*/ if(restart_pipe[1]==-1) { /*We'vealreadyrun.*/ return; } while(1) { chargo='R'; intrc=write(restart_pipe[1],&go,1);//通知重启线程 if(rc==0) { /*Wat?Tryagain.*/ continue; } elseif(rc==1) { /*Done.*/ libc.close(restart_pipe[1]); restart_pipe[1]=-1; break; } elseif(rc<0&&(errno==EAGAIN||errno==EINTR)) { /*Goagain.*/ continue; } else { /*Shit.*/ DEBUG("Restartpipefubared!?Sorry."); break; } } } |
6.重启线程
void* impl_restart_thread(void*arg) { /*Waitforoursignal.*/ while(1) { chargo=0; intrc=read(restart_pipe[0],&go,1);//等待SIGHUP信号 if(rc==1) { /*Go.*/ break; } elseif(rc==0) { /*Wat?Restart.*/ DEBUG("Restartpipeclosed?!"); break; } elseif(rc<0&&(errno==EAGAIN||errno==EINTR)) { /*Keeptrying.*/ continue; } else { /*Realerror.Let'srestart.*/ DEBUG("Restartpipefubared?!"); break; } } libc.close(restart_pipe[0]); restart_pipe[0]=-1; /*Seenoteaboveinsighandler().*/ impl_restart();//重启目标进程 returnarg; } |
7.重启目标程序
void impl_restart(void) { /*Indicatethatwearenowexiting.*/ L();//加锁 impl_exit_start(); impl_exit_check(); U();//解锁 } |
8.系统调用钩子辅助
funcs_timpl= { .bind=do_bind, .listen=do_listen, .accept=do_accept_retry, .accept4=do_accept4_retry, .close=do_close, .fork=do_fork, .dup=do_dup, .dup2=do_dup2, .dup3=do_dup3, .exit=do_exit, .wait=do_wait, .waitpid=do_waitpid, .syscall=(syscall_t)do_syscall, .epoll_create=do_epoll_create, .epoll_create1=do_epoll_create1, }; funcs_tlibc;//目标程序的进程调用的实际是huptime中的do_XXX系列 |
9.被勾住系统调用exit
staticvoid do_exit(intstatus) { if(revive_mode==TRUE)//如果是复活模式,也就是需要重启时 { DEBUG("Reviving..."); impl_exec();//调用execve重新启动目标程序 } libc.exit(status);//调用系统的exit } |
10.被勾住系统调用listen
staticint do_listen(intsockfd,intbacklog) { intrval=-1; fdinfo_t*info=NULL; if(sockfd<0) { errno=EINVAL; return-1; } DEBUG("do_listen(%d,...)...",sockfd); L(); info=fd_lookup(sockfd); if(info==NULL||info->type!=BOUND) { U(); 您可能想查找下面的文章:文章分类最近更新的内容
|