RIP源码分析
Int main (int argc, char **argv)
{
char *p;
int daemon_mode = 0;
char *progname;
struct thread thread;
/* Set umask before anything for security */
umask (0027);
/* Get program name. */
progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
/* First of all we need logging init. */
// 在这里设置 log
zlog_default = openzlog (progname, ZLOG_NOLOG, ZLOG_RIP,
LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
/* Command line option parse. */
while (1)
{
int opt;
// 解析参数
opt = getopt_long (argc, argv, "df:hA:P:rv", longopts, 0);
if (opt == EOF)
break;
switch (opt)
{
case 0:
break;
case 'd':
daemon_mode = 1;
break;
case 'f':
config_file = optarg;
break;
case 'A':
vty_addr = optarg;
break;
case 'i':
pid_file = optarg;
break;
case 'P':
vty_port = atoi (optarg);
break;
case 'r':
retain_mode = 1;
break;
case 'v':
print_version (progname);
exit (0);
break;
case 'h':
usage (progname, 0);
break;
default:
usage (progname, 1);
break;
}
}
/* Prepare master thread. */
master = thread_master_create ();
/* Library initialization. */
signal_init ();
cmd_init (1);
vty_init ();
memory_init ();
keychain_init ();
/* RIP related initialization. */
rip_init ();
rip_if_init ();
rip_zclient_init ();
rip_peer_init ();
/* Sort all installed commands. */
sort_node ();
/* Get configuration file. */
vty_read_config (config_file, config_current, config_default);
/* Change to the daemon program. */
if (daemon_mode) // 进入后台运行,成为守护进程
daemon (0, 0);
/* Pid file create. */
pid_output (pid_file);
/* Create VTY's socket */
vty_serv_sock (vty_addr, vty_port, RIP_VTYSH_PATH);
/* Execute each thread. */
while (thread_fetch (master, &thread)) // 真正执行线程在这里
thread_call (&thread);
/* Not reached. */
exit (0);
}
先看看 thread_call (&thread); 这一行,进入此函数
void
thread_call (struct thread *thread)
{
unsigned long thread_time;
RUSAGE_T ru;
GETRUSAGE (&thread->ru);
(*thread->func) (thread); // 此处调用线程链表的钩子函数,具体钩子函数是什么,待会看
GETRUSAGE (&ru);
thread_time = thread_consumed_time (&ru, &thread->ru);
#ifdef THREAD_CONSUMED_TIME_CHECK
if (thread_time > 200000L)
{
/*
* We have a CPU Hog on our hands.
* Whinge about it now, so we're aware this is yet another task
* to fix.
*/
zlog_err ("CPU HOG task %lx ran for %ldms",
/* FIXME: report the name of the function somehow */
(unsigned long) thread->func,
thread_time / 1000L);
}
#endif /* THREAD_CONSUMED_TIME_CHECK */
}
在看看 thread_fetch ,贴出代码
struct thread *
thread_fetch (struct thread_master *m, struct thread *fetch)
{
int num;
int ready;
struct thread *thread;
fd_set readfd;
fd_set writefd;
fd_set exceptfd;
struct timeval timer_now;
struct timeval timer_val;
struct timeval *timer_wait;
struct timeval timer_nowait;
timer_nowait.tv_sec = 0;
timer_nowait.tv_usec = 0;
while (1)
{
/* Normal event is the highest priority. */ event 事件优先级最高,其实就是触发更新,所谓触发更新,就是路由表一改变,马上调用线程的钩子函数,多播出去
if ((thread = thread_trim_head (&m->event)) != NULL)
return thread_run (m, thread, fetch);
/* Execute timer. */
gettimeofday (&timer_now, NULL);
// 在这里看是否超时,也就是一个路由表项在 180S 内没有更新,则将对应的线程从活动链表取出,放入 master - >unuse 链表。说白了就是把此线程挂起,不再执行
for (thread = m->timer.head; thread; thread = thread->next)
if (timeval_cmp (timer_now, thread->u.sands) >= 0)
{
thread_list_delete (&m->timer, thread);
return thread_run (m, thread, fetch);
}
// 如果接收到新的 RIP 数据包,则读入 , 采用 select 机制
/* If there are any ready threads, process top of them. */
if ((thread = thread_trim_head (&m->ready)) != NULL)
return thread_run (m, thread, fetch);
/* Structure copy. */
readfd = m->readfd;
writefd = m->writefd;
exceptfd = m->exceptfd;
/* Calculate select wait timer. */
timer_wait = thread_timer_wait (m, &timer_val);
num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
if (num == 0)
continue;
if (num < 0)
{
if (errno == EINTR)
continue;
zlog_warn ("select() error: %s", strerror (errno));
return NULL;
}
/* Normal priority read thead. */
ready = thread_process_fd (m, &m->read, &readfd, &m->readfd);
/* Write thead. */
ready = thread_process_fd (m, &m->write, &writefd, &m->writefd);
if ((thread = thread_trim_head (&m->ready)) != NULL)
return thread_run (m, thread, fetch);
}
}
通过以上分析,发现就是 RIP 经过初始化后,然后进入一个 while 死循环,在这个死循环中根据不同的优先级去执行不同的线程钩子函数。而这些钩子函数在什么地方注册的呢,进入 ripd.c 的 void rip_event (enum rip_event event, int sock) 函数。
void
rip_event (enum rip_event event, int sock)
{
int jitter = 0;
switch (event)
{
//read 事件,通过 thread_add_read 注册的钩子函数为 rip_read.
case RIP_READ:
rip->t_read = thread_add_read (master, rip_read, NULL, sock);
break;
//update 事件,通过 thread_add_read 注册的钩子函数为 rip_update.
case RIP_UPDATE_EVENT:
if (rip->t_update)
{
thread_cancel (rip->t_update);
rip->t_update = NULL;
}
jitter = rip_update_jitter (rip->update_time);
rip->t_update =
thread_add_timer (master, rip_update, NULL,
sock ? 2 : rip->update_time + jitter);
break;
// 触发更新,通过 thread_add_read 注册的钩子函数为 rip_triggered_update.
case RIP_TRIGGERED_UPDATE:
printf("come in RIP_TRIGGERED_UPDATE\n");//jimmy
if (rip->t_triggered_interval)
rip->trigger = 1;
else if (! rip->t_triggered_update)
{
printf("add event rip_triggered_update\n");//jimmy
rip->t_triggered_update =
thread_add_event (master, rip_triggered_update, NULL, 0);
}
break;
default:
break;
}
}