对 zebra 的一点理解 thread+socket+read部分 (备忘)

一、主要从 daemon 的 thread角度 分析备忘。


注意: 具体函数功能详见附录,分析要结合zebra源代码(thread.c中)。


1、每个daemon(e.g igmp-snooping、ring)都定义一个 master 的 全局变量;

2、master 内 有六个 struct thread_list *read、write 等六个 链表;

3、最小的 执行单元为 thread , 必要的时候会 挂在 对应的 thread_list 上;

4、最终 最重要的是 各个daemon的 main函数最后的 while(thread_fetch(master, &thread))  thread_call(&thrad);


5、thread_fetch 功能是查找master 结构下的 几个 struct thread_list 链;

6、其中, 三个链是 比较 重要的,struct thread_list *eventtimerready;

7、thread_fetch  的查找顺序 也是上边这个 顺序, 所以其没有真正的 优先级 抢断问题(真正的线程pthread);

8、查找每个链的过程  就是 看看 该链上  有没有 准备好的 thread;

9、有的话从 该链上移除, 做适当的 type 等重新赋值, 然后加入到  unuse  链上; 《thread_run() 的执行过程》  

      注:移到 unuse 链上 ,而不是 直接free掉, 是为了以后 有新的thread要挂到  eventtimerready等链上的时                  候,可以直接 来 unuse 链上来取, 不用重新 malloc, 节省时间。

10、然后 返回查找到的 thread,然后main中的while下  调用thread_call(&thrad)  执行 该块 thread;


11、此外 还有 两个重要的  明示给 用户(程序员)的  thread_list * read、write,对应 socket 的 read、write事件;

12、当有 read、write类型的socket 要加入 到list 时, 调用 thread_add_read/write 把相应的 sfd及 func以thread的形         式加入到 对应的  read 或者 write  thread_list;

13、同时,重要的是: thread_add_read 里 只要是把 对应的  sfd 加入到 master->readfd/writefd(fd_set 类型);

14、而后 thread_fetch里调用 select 对 read、write、error 的fd_set进行 监听;

15、并调用两次thread_process_fd把  有事件的fd(FD_ISSET) 对应的 thread  从 thread_list * read、write 链中移除

16、并进一步加入ready链中;

17、在thread_fetch 最后, 再对ready链中 移除头元素,加入unuse 链,返回该thread块,并等待thread_call执行;


18、对于 thread_fetch中timer链的查找,遍历timer链中所有thread,查找超时的 thread,返回该thread:过程如下

19、当用 thread_add_timer 添加 timer 事件时,用参数func、time、master等, 构造一个 thread;

20、其中thread->u.sands 为 (gettimeofday + time),  是未来的 某一个时间值,遍历timer链上的thread时, 再                     gettimeofday一下, 然后与 thread->u.sands对比,确定该thread 是否  超时 可执行;


21、对于thread_fetch中 监听 read、write事件的 select 的timeout 时间问题: 函数 thread_time_wait()

22、(NO sort)遍历 timer链上 所有thread,取最小的  thread->u.sands  ,与现在的gettimeofday比较,若小于(表明最                       小的thread块已经超时)则timeout,应该给一个最小的值,免得timer事件 误差太大,timeout=10us;

                       若大于gettimeofday(sec>=0)(还未超时),则 timeout=该值与gettimeofday之差;再while1的时候下一个                         thread正好超时一点,不会误差太大;

                       若 timer链上无thread,则select 为 阻塞。(timeout=NULL);   (没有事件处理阻塞,进程挂起无影响)

   



二、daemon与vtysh间的通信(依赖daemon的thread机制)


注:①其间的通信主要是 vtysh从终端获取输入的CLI命令,然后解析,根据DEFSH或者其他发往指定daemon;

       ②vtysh进程会和每个daemon进行connect;

       ③connect分两种, 一种是vtysh进程main中 connect_default(rcs默认启动的daemon);

                                       二是 vtysh_send之前 会 connect 对应的 daemon

       


下边结合 daemon (e.g igmp-snooping) 与 vtysh 进程 通信来 举例:

1、 igmp-snooping 的 main 中,如下图所示吧:



2、实际上 但从 发送CLI命令字符 来看, 是daemon端 维护一个 select, vtysh 端 connect及 send;

3、可待续补充......



三、vtysh端的read、write的阻塞与  数据传输 不丢失保证

1、待 补充.....




四、daemon端接收其他报文的socket

1、igmp-snooping 要  申请一个 g_snoop_pkt_sock=socket(AF_INET,SOCK_PACKET,htons(0800));

2、该socket 用来 接收 igmp 报文;

3、根据网上的资料,对于socket类型的文件,不显式用fcntl (sockfd,F_SETFL,O_NONBLOCK) 设定时,默认阻塞;

4、所以 recv(g_snoop_pkt_sock, ,)  为 阻塞的;   但该阻塞 永远不会发生!!!  原因如下:

5、在 igmp-snooping enable中用 thread_add_read 把 g_snoop_pkt_sockadd到read 链里, 即,受select监控。

6、只有在 有 igmp 报文 到达 该g_snoop_pkt_sock  时,select检测到,并执行thread块,即igmp_snooping_read,

      进而才会执行到  阻塞的   recv(g_snoop_pkt_sock, ,),但此时为已经 有报文过来,所以该recv不会阻塞;

7、结论:对于该 接收其他AF_INET报文的 socket,在该架构下, 阻塞与否 几乎无差异!!


8、ring 中 接收ring报文 的   几乎 和上述相同,g_ring_sock = socket(AF_INET, SOCK_PACKET, htons(0x7010));

9、下边的 步骤 同上述 2---7, 另, ring 的 socket 用的 read。 




以下附录两篇 关于 zebra 的 文章, 以防收藏被删:


附录一: 原文链接:http://blog.csdn.net/xuyanbo2008/article/details/7439733


==========================================================

一、线程机制概述

zebra这个软件包整体结构大致可分为两大块:协议模块和守护进程模块。协议模块实现各协议的功能,各协议以子模块的形式加载到zebra中;守护进程模块的功能主要是管理各协议的信令传输、表项操作、系统操作调用等事务,为各协议提供底层信息以及相关的硬件处理等功能支持。Zebra与各协议的交互采用的是C-S模式,在每个协议子模块中均有一个Client端与守护进程模块中的Server端交互,它们所使用的socketzebra内部使用的socket,不与外部交互。


  zebra中的线程是分队列调度的,每个队列以一个链表的方式实现。线程队列可以分成五个列:eventtimerreadyreadwrite。队列的优先级由高到低排列。但是,readwrite队列并不参与到优先级的排列中,实际操作时,如果readwrite队列中的线程就绪,就加入ready队列中,等待调度。调度时,首先进行event队列中线程的调度,其次是timerready


实际上,zebra中的线程是“假线程”,它并没有实现线程中的优先级抢占问题。在zebra的线程管理中,有个虚拟的时间轴,必须等前一时间的线程处理完,才看下一时间的线程是否触发。由于电脑处理速度较快且处理每个线程的时间间隔较小,所以处理其多线程来可以打到类似“并行处理”的效果。


zebra源码中有关线程管理的各个函数放置于zebra-0.95a\lib文件夹的thread.hthread.c两个文件中。



二、线程管理源码分析

这是线程队列中每一个单个线程的代码描述,线程队列被描述成双向链表的形式,thread结构体是双向链表的元素。共有六种线程:readwritetimereventreadyunused。因此,线程队列也有六种。

struct thread

{

unsigned char type;        /* thread类型,共有六种 */

struct thread *next;        /* 指向下一thread的指针,双向链表 */

struct thread *prev;        /*指向前一thread的指针*/

struct thread_master *master;      /* 指向该thread所属thread_master结构体的指针 */

int (*func) (struct thread *); /* event类型thread的函数指针 */

void *arg;               /* event类型thread的参数 */

union {

    int val;                /* event类型thread的第二个参数*/

    int fd;                  /* read/write类型thread相应的文件描述符 */

    struct timeval sands;   /* thread的剩余时间,timeval类型,此结构体定义在time.h中,有两个元素,秒和微秒 */

} u;

RUSAGE_T ru;                    /* 详细用法信息,RUSAGE这个宏在该thread有用法描述时定义为rusage类型,描述其详细进程资源信息,没有用法描述时定义为timeval类型 */

};


一个thread_list结构体描述一个thread双向链表,也即一个进程队列。

struct thread_list

{

struct thread *head;/* 该线程队列头指针 */

struct thread *tail; /* 该线程队列尾指针 */

int count; /* 该线程队列元素数目 */

};


  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值