Tor源码文件分析 — Main
Main文件是Tor系统的主要执行函数所处文件,内容偏多,但是较于其他底层处理函数所在的文件,也算较少。所以这里做简要的分析,其中删去很多不常见的,不重要的函数,大家可以自行在源文件内部查看。
1. 全局变量
全局变量在文件头部定义,每个全局变量都已经有比较详尽的英文分析和解释,这里再做简单的罗列。
1)系统(先前)已读与已写字节数以及用于流量控制的令牌桶全局变量;(先前与当前已读写字节数的区别与refill回调函数相关)
令牌桶用于控制整个系统的流量,经过初始化,减少,增加等操作(当读写相应的令牌数不够时,系统或连接会停止读或写):
// 初始化令牌桶令牌数
/** Initialize the global read bucket to options-\>BandwidthBurst. */
void
connection_bucket_init(void)
{
const or_options_t *options = get_options();
/* start it at max traffic */
global_read_bucket = (int)options->BandwidthBurst;
global_write_bucket = (int)options->BandwidthBurst;
if (options->RelayBandwidthRate) {
global_relayed_read_bucket = (int)options->RelayBandwidthBurst;
global_relayed_write_bucket = (int)options->RelayBandwidthBurst;
} else {
global_relayed_read_bucket = (int)options->BandwidthBurst;
global_relayed_write_bucket = (int)options->BandwidthBurst;
}
}
// 减少令牌桶令牌数
/** We just read num_read and wrote num_written bytes
* onto conn. Decrement buckets appropriately. */
static void
connection_buckets_decrement(connection_t *conn, time_t now,
size_t num_read, size_t num_written)
{
......
if (connection_counts_as_relayed_traffic(conn, now)) {
global_relayed_read_bucket -= (int)num_read;
global_relayed_write_bucket -= (int)num_written;
}
global_read_bucket -= (int)num_read;
global_write_bucket -= (int)num_written;
......
}
// 增加令牌桶令牌数
/** Time has passed; increment buckets appropriately. */
void
connection_bucket_refill(int milliseconds_elapsed, time_t now)
{
......
/* refill the global buckets */
connection_bucket_refill_helper(&global_read_bucket,
bandwidthrate, bandwidthburst,
milliseconds_elapsed,
"global_read_bucket");
connection_bucket_refill_helper(&global_write_bucket,
bandwidthrate, bandwidthburst,
milliseconds_elapsed,
"global_write_bucket");
connection_bucket_refill_helper(&global_relayed_read_bucket,
relayrate, relayburst,
milliseconds_elapsed,
"global_relayed_read_bucket");
connection_bucket_refill_helper(&global_relayed_write_bucket,
relayrate, relayburst,
milliseconds_elapsed,
"global_relayed_write_bucket");
......
}
先前已读写字节数全局变量的具体赋值处于函数second_elapsed_callback之中,其他地方没有引用:
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;
当前已读写字节数全局变量的具体赋值处于函数refill_callback之中,其他地方没有引用:
bytes_written = stats_prev_global_write_bucket - global_write_bucket;
bytes_read = stats_prev_global_read_bucket - global_read_bucket;
stats_n_bytes_read += bytes_read;
stats_n_bytes_written += bytes_written;
2)系统启动时间和运行时间;
系统启动时间初始化于Tor系统初始化阶段,未曾修改过:
tor_init(int argc, char *argv[])
{
......
time_of_process_start = time(NULL);
......
}
系统运行时间在函数second_elapsed_callback中随时间增加;在系统发生重要变故的时候会被重新设置为0,例如hibernation,Ip改变等情况。
3)启动下次DNS检查的时间;
只有在系统配置为服务器时候才使用,改变部分在函数run_scheduled_events之中,此处略去。
4)newnym相关变量;
newnym信号相关变量均于信号处理函数内部进行改变,入口大致为:
/** Do the work of acting on a signal received in sig */
void
process_signal(uintptr_t sig)
{
switch (sig)
{
....
case SIGNEWNYM: {
time_t now = time(NULL);
if (time_of_last_signewnym + MAX_SIGNEWNYM_RATE > now) {
signewnym_is_pending = 1;
log(LOG_NOTICE, LD_CONTROL,
"Rate limiting NEWNYM request: delaying by %d second(s)",
(int)(MAX_SIGNEWNYM_RATE+time_of_last_signewnym-now));
} else {
signewnym_impl(now);
}
break;
}
......
}
5)三大连接链表全局变量:全连接链表,可关闭的连接链表,活动的linked连接列表;(linked连接的含义,在Tor源码分析中会提到)
这三个连接链表因为引用非常多,不可能贴出其主要引用位置。大致的可以说,当系统出现相关连接时,会被加入到相关连接列表中:全部的,可关闭的,活动的linked。当系统删除相关连接时,这些对应的连接就会被从相关连接列表中删除。这个部分是Tor系统的核心控制部分,会在系统代码分析中经常遇到,所以此处就粗略描述。
6)Libevent循环指示变量;
call_loop_once变量用于指示Libevent循环是否阻塞。如果该值为1,则Libevent会不阻塞地执行被激活的事件处理函数,当没有需要执行的处理函数时,会返回。如果该值为0,则Libevent会阻塞地执行被激活的事件处理函数,即没有需要执行的处理函数时,阻塞不返回。在后者的情况下,Tor系统为了处理特定的事件而需要推出loop循环,则需要调用特定的tor_event_base_loopexit函数来让主循环暂时退出:
do_main_loop
{
......
/* All active linked conns should get their read events activated. */
SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn,
event_active(conn->read_event, EV_READ, 1));
called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0;
update_approx_time(time(NULL));
/* poll until we have an event, or the second ends, or until we have
* some active linked connections to trigger events for. */
loop_result = event_base_loop(tor_libevent_get_base(),
called_loop_once ? EVLOOP_ONCE : 0);
......
}
7)链路可用性指示变量。
以下为所有全局变量的罗列:
#ifndef USE_BUFFEREVENTS
int global_read_bucket; /**< Max number of bytes I can read this second. */
int global_write_bucket; /**< Max number of bytes I can write this second. */
/** Max number of relayed (bandwidth class 1) bytes I can read this second. */
int global_relayed_read_bucket;
/** Max number of relayed (bandwidth class 1) bytes I can write this second. */
int global_relayed_write_bucket;
/** What was the read bucket before the last second_elapsed_callback() call?
* (used to determine how many bytes we've read). */
static int stats_prev_global_read_bucket;
/** What was the write bucket before the last second_elapsed_callback() call?
* (used to determine how many bytes we've written). */
static int stats_prev_global_write_bucket;
#endif
/* DOCDOC stats_prev_n_read */
static uint64_t stats_prev_n_read = 0;
/* DOCDOC stats_prev_n_written */
static uint64_t stats_prev_n_written = 0;
/* XXX we might want to keep stats about global_relayed_*_bucket too. Or not.*/
/** How many bytes have we read since we started the process? */
static uint64_t stats_n_bytes_read = 0;
/** How many bytes have we written since we started the process? */
static uint64_t stats_n_bytes_written = 0;
/** What time did this process start up? */
time_t time_of_process_start = 0;
/** How many seconds have we been running? */
long stats_n_seconds_working = 0;
/** When do we next launch DNS wildcarding checks? */
static time_t time_to_check_for_correct_dns = 0;
/** How often will we honor SIGNEWNYM requests? */
#define MAX_SIGNEWNYM_RATE 10
/** When did we last process a SIGNEWNYM request? */
static time_t time_of_last_signewnym = 0;
/** Is there a signewnym request we're currently waiting to handle? */
static int signewnym_is_pending = 0;
/** How many times have we called newnym? */
static unsigned newnym_epoch = 0;
/** Smartlist of all open connections. */
static smartlist_t *connection_array = NULL;
/** List of connections that have been marked for close and need to be freed
* and removed from connection_array. */
static smartlist_t *closeable_connection_lst = NULL;
/** List of linked connections that are currently reading data into their
* inbuf from their partner's outbuf. */
static smartlist_t *active_linked_connection_lst = NULL;
/** Flag: Set to true iff we entered the current libevent main loop via
* loop_once. If so, there's no need to trigger a loopexit in order
* to handle linked connections. */
static int called_loop_once = 0;
/** We set this to 1 when we've opened a circuit, so we can print a log
* entry to inform the user that Tor is working. We set it to 0 when
* we think the fact that we once opened a circuit doesn't mean we can do so
* any longer (a big time jump happened, when we notice our directory is
* heinously out-of-date, etc.
*/
int can_complete_circuit=0;
2.功能函数(加*号的为极重要函数)
dumpmemusage
dumpstats
记录内存使用情况与系统当前状态,输出至log;
conn_read_callback*
conn_write_callback*
second_elapsed_callback* -> run_schedule_events -> run_connection_housekeeping
refill_callback
signal_callback -> process_signal
读写、秒定时器、令牌桶填充以及信号处理回调函数;
conn_close_if_marked
关闭连接池中已被标记为关闭的连接;
connection_should_read_from_linked_conn*
connection_start_reading_from_linked_conn*
connection_stop_reading_from_linked_conn*
判断linked连接的可读性,并对该连接进行读写准备操作;(所谓的读写准备操作,就与上文中所提到的活动的linked连接链表有直接关系。实际上就是对活动linked连接链表的增删操作。活动linked链表会在每次程序主循环时被检测,激活其中所有连接进行读写调度。
connection_add ->
connection_add_connecting -> connection_add_impl*
向连接池中添加新连接,并为该连接生成读写事件,但并不将事件加入调度池;(->代表调用,下同)
connection_unlink -> connection_remove -> connection_unregister_events
从右到左依次为将连接的读写事件从调度池中删除,并将连接从连接池中删除,最终将连接完全释放;
connection_watch_events* -> connection_start_reading,connection_start_writing,connection_stop_reading,connection_stop_writing
connection_is_reading
connection_is_writing
针对连接的读写事件加入调度池或调出调度池的操作和读写判断;普通连接的操作通过调度池完成,linked连接的操作通过活动linked连接链表完成。
try_locking
have_lockfile
release_locking
全局加锁文件加解锁控制;
tor_main -> tor_init*,do_main_loop*,do_list_fingerprint,do_hash_password
Tor程序主函数执行分支流程。