
8.系统调用钩子辅助 6
9.被勾住系统调用 exit 6
10.被勾住系统调用 listen 7
11.SymbolVersioning 8
12.勾住 bind 等系统调用 10
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;
#define GET_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_t libc ; //目标程序的进程调用的实际是huptime中的 do_XXX 系列
9.被勾住系统调用 exitstaticvoid
do_exit (intstatus)
{
if(revive_mode==TRUE)