网络模块整体框架
一般的网络框架处理流程如下:
-
使用IO多路复用(linux下的select, poll, epoll)分离网络IO。
-
对分离出来的网络IO进行操作,分为socket句柄可读、可写和出错三种情况。
-
定时器事件,即检测一个定时器事件列表,如果有定时器到期,则执行该定时器事件。
net-snmp也类似,代码整体逻辑如下:
while (netsnmp_running)
{
//更新配置文件
update_config();
//IO multiplexing
count = netsnmp_large_fd_set_select(numfds, &readfds, &writefds, &exceptfds, tvp);
if(count)
{
snmp_read2(&readfds);
}
/*
* 如果更新配置之后,要先保存配置,防止之后崩溃
*/
snmp_store_if_needed();
/*
* 处理定时器事件
*/
run_alarms();
netsnmp_check_outstanding_agent_requests();
}
处理定时器事件的代码如下:
void
run_alarms(void)
{
struct snmp_alarm *a;
unsigned int clientreg;
struct timeval t_now;
/*
* Loop through everything we have repeatedly looking for the next thing to
* call until all events are finally in the future again.
*/
while ((a = sa_find_next()) != NULL) {
netsnmp_get_monotonic_clock(&t_now);
if (timercmp(&a->t_nextM, &t_now, >))
return;
clientreg = a->clientreg;
a->flags |= SA_FIRED;
DEBUGMSGTL(("snmp_alarm", "run alarm %d\n", clientreg));
(*(a->thecallback)) (clientreg, a->clientarg);
DEBUGMSGTL(("snmp_alarm", "alarm %d completed\n", clientreg));
a = sa_find_specific(clientreg);
if (a) {
a->t_lastM = t_now;
timerclear(&a->t_nextM);
a->flags &= ~SA_FIRED;
sa_update_entry(a);
} else {
DEBUGMSGTL(("snmp_alarm", "alarm %d deleted itself\n",
clientreg));
}
}
}
即遍历一个升序定时器列表,将定时器对象与当前时间(t_now)做比较,如果当前时间已经大于或等于定时器设置的时间,则表明定时器时间已经到了,执行定时器对应的回调函数。
再来看看对snmp_read2的处理:
void
snmp_read2(netsnmp_large_fd_set * fdset)
{
struct session_list *slp;
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
for (slp = Sessions; slp; slp = slp->next) {
snmp_sess_read2((void *) slp, fdset);
}
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
}
int
snmp_sess_read2(void *sessp, netsnmp_large_fd_set * fdset)
{
struct session_list *psl;
netsnmp_session *pss;
int rc;
rc = _sess_read(sessp, fdset);
psl = (struct session_list *) sessp;
pss = psl->session;
if (rc && pss->s_snmp_errno) {
SET_SNMP_ERROR(pss->s_snmp_errno);
}
return rc;
}
int
_sess_read(void *sessp, netsnmp_large_fd_set * fdset)
{
if (transport->flags & NETSNMP_TRANSPORT_FLAG_LISTEN)
{
int data_sock = transport->f_accept(transport);
return 0;
}
length = netsnmp_transport_recv(transport, rxbuf, rxbuf_len, &opaque,
&olength);
_sess_process_packet(sessp, sp, isp, transport,
ocopy, ocopy?olength:0, pptr,
pdulen)
}
_sess_read()函数根据状态标识transport->flags确定一个socket是侦听的socket还是普通与客户端连接的socket,如果是侦听sokcet则接收客户端的连接;如果是与客户端连接的socket,则先检测socket上有多少字节可读,如果没有字节可读或者检测字节数时出错,则关闭socket,反之调用处理函数。
snmpd程序使用一个session_list链表来管理所有的socket。
struct session_list *Sessions = NULL; /* MT_LIB_SESSION */
所以在删除或者新增socket时,实际上就是从这个session_list中删除或者向这个session_list中增加对象。多线程操作,需要一个锁来进行保护:
netsnmp_session *
snmp_open(netsnmp_session *session)
{
struct session_list *slp;
slp = (struct session_list *) snmp_sess_open(session);
if (!slp) {
return NULL;
}
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
slp->next = Sessions;
Sessions = slp;
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
return (slp->session);
}
int
snmp_close(netsnmp_session * session)
{
struct session_list *slp = NULL, *oslp = NULL;
{ /*MTCRITICAL_RESOURCE */
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
if (Sessions && Sessions->session == session) { /* If first entry */
slp = Sessions;
S
网友评论