在之前的客户端源码分析中,我们讲述了整个客户端的事件集和相关调度规则。每一类事件的激活都有相应的条件,要么是socket可读写,要么是收到信号,要么是定时事件到达,还有手动的事件激活。总而言之,系统中添加的所有事件经过Libevent的调度,使得整个系统有条不紊的运行起来。同时,每个事件均有其对应的事件处理函数,在系统运行起来之后,一旦事件被激活,就会调用相应的回调函数进行处理。
本文我们着重介绍秒回调事件的事件处理函数second_elapsed_callback。该函数所做的工作非常之多,但是总的说来,是对系统的正常运行的维护和保障。之所以要最先介绍这个函数,是因为新安装的Tor系统在正常启动之后,没有任何事件会先于该秒回调事件被激活,也就是说,系统最先执行的回调函数是秒回调函数。在这个函数中,运行Tor系统的许多必要信息被获取或维护,保障了系统能够在正常地启动,最终为用户提供服务。下面,我们采取代码中加注释的方法,介绍函数的主要过程。英文注释已经描述地很清楚的部分就不再多说了。
1. second_elapsed_callback()
/** Libevent callback: invoked once every second. */
static void
second_elapsed_callback(periodic_timer_t *timer, void *arg) // 本函数的两个参数没有任何用处
{
static time_t current_second = 0; // 静态的当前时间,每次进入该函数时,该值就是上次执行该函数时的时间;
time_t now; // 真正的当前时间;
size_t bytes_written;
size_t bytes_read;
int seconds_elapsed;
const or_options_t *options = get_options();
(void)timer;
(void)arg;
n_libevent_errors = 0;
/* log_notice(LD_GENERAL, "Tick."); */
now = time(NULL);
update_approx_time(now);
/* the second has rolled over. check more stuff. */
seconds_elapsed = current_second ? (int)(now - current_second) : 0; // 之前进入本函数与本次进入本函数的时间差seconds_elapsed
#ifdef USE_BUFFEREVENTS
...... // 我们暂时不讨论使用BUFFEREVENTS的情况;
#else
// stats_n_bytes_read, stats_prev_n_read, stats_n_bytes_written, stats_prev_n_written
// 这四个变量是全局变量,分别用来存储系统当前读写的字节数;
// 开始时,四个全局变量值相等;
// 当有数据读写的时候,相应增加stats_n_bytes_read,stats_n_bytes_written两个全局变量记录当前读取的字节数;
// 当本回调函数被调用,利用他们前后数据差来计算两次本回调执行之间所读写的数据量:
bytes_read = (size_t)(stats_n_bytes_read - stats_prev_n_read);
bytes_written = (size_t)(stats_n_bytes_written - stats_prev_n_written);
// 最后时,更新相应前置读写数据为当前系统读写数据,以为下次计算两次回调函数之间读写数据量做准备:
stats_prev_n_read = stats_n_bytes_read;
stats_prev_n_written = stats_n_bytes_written;
// 总而言之,此处这么做的最终目的,就是为了每次均可以有效算出两次回调函数执行之间(过去的一秒内)读写数据的总字节数;
#endif
// 下面两个函数与控制连接相关,暂不讨论
control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
control_event_stream_bandwidth_used();
// 该判断成立的条件为:
// 1.Tor程序运行身份为服务器;
// 2.网络没有被禁用;
// 3.前后两次回调函数的执行不是无时间间隔的,也就是的确有时间流逝;
// 4.系统曾成功开启过链路,也就是说估计系统当前应该处理连接正常状态;
// 5.系统运行时间大概增加了20分钟;
if (server_mode(options) &&
!net_is_disabled() &&
seconds_elapsed > 0 &&
can_complete_circuit &&
stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT !=
(stats_n_seconds_working+seconds_elapsed) / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
/* every 20 minutes, check and complain if necessary */
const routerinfo_t *me = router_get_my_routerinfo(); // 获取自身路由信息结构体;
if (me && !check_whether_orport_reachable()) { // 检测自身OR端口是否从外界可达,即OR服务是否有效可用;如果不可用,则产生抱怨= =:
log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
"its ORPort is reachable. Please check your firewalls, ports, "
"address, /etc/hosts file, etc.",
me->address, me->or_port);
control_event_server_status(LOG_WARN,
"REACHABILITY_FAILED ORADDRESS=%s:%d",
me->address, me->or_port);
}
if (me && !check_whether_dirport_reachable()) { // 检测自身DIR端口是否从外界可达,即DIR服务是否有效可用;如果不可用,则产生抱怨:
log_warn(LD_CONFIG,
"Your server (%s:%d) has not managed to confirm that its "
"DirPort is reachable. Please check your firewalls, ports, "
"address, /etc/hosts file, etc.",
me->address, me->dir_port);
control_event_server_status(LOG_WARN,
"REACHABILITY_FAILED DIRADDRESS=%s:%d",
me->address, me->dir_port);
}
}
/** If more than this many seconds have elapsed, probably the clock
* jumped: doesn't count. */
#define NUM_JUMPED_SECONDS_BEFORE_WARN 100
if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN ||
seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) { // 此处进行时间异常的处理,也就是当逝去的时间出现不可能的情况之时,进行如下操作:
circuit_note_clock_jumped(seconds_elapsed);
/* XXX if the time jumps *back* many months, do our events in
* run_scheduled_events() recover? I don't think they do. -RD */
} else if (seconds_elapsed > 0) // 无时间异常时,增加系统运行的时间:
stats_n_seconds_working += seconds_elapsed;
run_scheduled_events(now); // !!!!!主要的维护函数!!!!!
current_second = now; /* remember which second it is, for next time */ // 记住本次进入函数的时间,为下一次计算时间差做准备。
}
2. run_scheduled_events()
这个函数非常之长,此处我们按功能,分段分析。
下面的主要静态变量的分析说明一定有错误的地方,笔者没有太仔细地去看每个部分的详细说明的情况下就写出了下面的简要介绍,会在后面每个部分的介绍里,详细的准确的说明每个部分的功能和执行大致流程。
/** Perform regular maintenance tasks. This function gets run once per
* second by second_elapsed_callback().
*/
static void
run_scheduled_events(time_t now)
{
// 以下所有变量全为静态变量,每一次变量的修改都会影响下一次函数执行时候变量的值;他们大多都表示下一次需要执行某操作的时间:
// 每1分钟之内进行的维护:
static time_t time_to_check_listeners = 0; // 每60秒,检查监听端口是否正常监听;
static time_t time_to_check_descriptor = 0; // 每60秒,检查自身描述符是否被修改并需要重新上载;
static time_t time_to_shrink_memory = 0; // 每60秒,尝试缩小系统使用的内存空间;
static time_t time_to_try_getting_descriptors = 0; // 每10秒或60秒,根据需要尝试下载所有路由描述符与额外信息;
static time_t time_to_check_port_forwarding = 0; // 每5秒,服务器进行的端口映射检查;
static time_t time_to_launch_reachability_tests = 0; // 每10秒,权威目录服务器发起的到Tor网络的连接测试;
static time_t time_to_next_heartbeat