zebra 的Thread机制 -- 003

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

一、线程机制概述

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

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

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

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

二、线程管理源码分析

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

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; /* 该线程队列元素数目 */

};


总的线程管理结构体,里面存有六种线程队列,三种文件描述符以及占用空间信息。

struct thread_master

{

//六种线程队列

struct thread_list read;

struct thread_list write;

struct thread_list timer;

struct thread_list event;

struct thread_list ready;

struct thread_list unuse;

//三种文件描述符

fd_set readfd;

fd_set writefd;

fd_set exceptfd;

//该thread_master所占空间大小

unsigned long alloc;

};

1.1 相关函数简介
下面给出了zebra关于线程管理的相关函数的简要功能介绍。

1.1.1 thread_master_create ()
为创建一个新的thread_master结构体动态开辟一块内存空间。

1.1.2 thread_list_add ()
在list双向链表尾部插入一个新的thread。

1.1.3 thread_list_add_before ()
在函数参数point所指向的thread前面插入一个新的thread。

1.1.4 thread_list_delete ()
删除参数中指定的thread。

1.1.5 thread_add_unuse ()
向指定thead_master中的unused链表尾部插入新thread。

1.1.6 thread_list_free ()
从内存中释放掉指定thread_master中的指定thread链表所占空间。

1.1.7 thread_master_free ()
彻底释放指定thread_master所占内存空间。

1.1.8 thread_trim_head ()
若指定thread链表中非空,删除该链表头指针所指thread,并将其返回,即从线程队列中取出一个线程。

1.1.9 thread_empty ()
判断指定thread链表是否为空。

1.1.10 thread_timer_remain_second ()
得到指定线程的剩余时间。

1.1.11 thread_get ()
若指定thread_master中的unuse链表非空,从该队列中取出一个thread,根据参数初始化并返回之。否则,给该thread_master多开辟一块空间给新的thread,根据参数初始化该thread并返回之。

1.1.12 thread_add_read ()
根据所给参数在指定thread_master中添加并初始化一个read类型的thread并返回之。

1.1.13 thread_add_write ()
根据所给参数在指定thread_master中添加并初始化一个write类型的thread并返回之。

1.1.14 thread_add_timer ()
根据所给参数在指定thread_master中添加并初始化一个timer类型的thread。若timer链表不要求排序,则直接返回新thread,若要求排序,则将新thread插入到队列的相应位置后再返回之。

1.1.15 thread_add_event ()
根据所给参数在指定thread_master中添加并初始化一个event类型的thread并返回之。

1.1.16 thread_cancel ()
删除指定thread,删除后将其类型置为THREAD_UNUSED,并将其插入到该thread_master的unuse链表中。

1.1.17 thread_cancel_event ()
将指定thread_master的event链表中与参数中arg相匹配的thread删除。

1.1.18 thread_timer_wait ()
找出指定thread_master的timer链表中最小的剩余时间并将其返回。

1.1.19 thread_run ()
将指定thread的值赋给thread类型的fetch,然后将其类型置为THREAD_UNUSED,并将其插入unuse链表,返回fetch。

1.1.20 thread_process_fd ()
将指定thread链表中的元素取出插入到该thread_master的ready链表中,返回该链表中插入元素的个数。

1.1.21 thread_fetch ()
若指定thread_master的event队列非空取出其头元素并用run函数处理。取出并用run函数处理timer队列中每一个之前创建的线程。若指定thread_master的ready队列非空取出其头元素并用run函数处理。拷贝该thread_master的文件描述符。将read和write链表插到ready链表中,再从ready链表取头元素用run函数处理。如此无限循环下去直到所有进程都处理完。

1.1.22 thread_consumed_time ()
得到该进程所耗费的时间。

1.1.23 thread_call ()
执行该thread中的功能函数,如果该thread持续时间超过CPU规定的独占时间,发出警告。

1.1.24 thread_execute ()
根据参数创建一个event类型的thread并用thread_call()函数对其进行处理

 

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

一、主要从 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 *event、timer、ready;

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

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

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

      注:移到 unuse 链上 ,而不是 直接free掉, 是为了以后 有新的thread要挂到  event、timer、ready等链上的时                  候,可以直接 来 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。

 

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

1.thread的四种创建方法

一个新的thread可以通过如下三种方式被创建,主要是看你需要创建的thread的类型:

1,  thread_add_read:添加一个thread到read queue,该thread负责通过socket接受和读取从client端来的数据。 

2,  thread_add_write:添加一个thread到write queue,该thread负责通过socket向client端填充和写数据。

3,  thread_add_timer function calls:添加一个thread到timer queue,该thread负责定时一个event,例如update和redistribute一个route table.

4,  thread_add_event:添加一个event thread到event queue。

上面这三个函数的处理过程都差不多:

1,  创建thread。首先在unuse queue查找,如果有unuse thread,就使用它,否则重新分配空间。

2,  根据参数,对thread进行赋值。

3,  将该thread加入到相应的queue中。

2. thread的调用

1,bgp daemon不断地从event queue中取出thread并且执行它。一旦该thread被执行了,将该thread的type设置为unuse。并且将该thread添加到unuse queue中。

2,如果event queue为空时,bgp daemon 通过select函数监控读、写、异常三个描述符集。一旦有某个描述符准备就绪,则将该描述符所对应的thread加入ready queue.

而对于timer queue中的thread,只有当select函数超时后才会进入ready queue.

3.zebrad端的thread

zebrad启动后会,在read queue中会出现两个thread,一个是等待来自local client端bgpd的连接,另一个是等待来自vty client端的连接。

第1个thread

zebra_init ( )-> zebra_serv_un ( )中创建一个thread,加入read queue。该thread的处理函数为zebra_accept,监听内部client的socket。

 zebra_client_create (client_sock);创建一个新的zebra client

  /* Register myself. */

  zebra_event (ZEBRA_SERV, accept_sock, NULL);继续监听server socket

  puts("<-zebra_accept");

  return 0;

vty_accept,加入read queue。作为vty server监听internet的vty socket.。 

vty_create(vty_sock, &su);

根据vty_sock和ip地址信息su,创建一个新的vty。 

  puts("<-vty_accept");

  return 0;

vty_flush

  vty_event (VTY_READ, vty_sock, vty);

根据new client vty的vty_sock创建新的VTY_READ thread,加入read queue,该thread的处理函数为vty_read

  return vty;

sockunion_bind(accept_sock, &su, port, NULL);

将accept_socket文件描述符与一个特定的逻辑网络连系起来。服务器端使用su->sin.sin_addr.s_addr = htonl (INADDR_ANY);表示接受任何一个主机网络接口上的连接请求。

  if (ret < 0)

    {

      close (accept_sock);   /* Avoid sd leak. */

      return;

    } 

  /* Listen socket under queue 3. */

  ret = listen (accept_sock, 3);

将accept_sock套接口设置成被动监听状态,用于接受连接,只能在服务器端使用。

  if (ret < 0)

    {

      zlog (NULL, LOG_WARNING, "can't listen socket");

      close (accept_sock);   /* Avoid sd leak. */

      return;

    } 

  /* Add vty server event. */

  vty_event(VTY_SERV, accept_sock, NULL);

sockunion_bind操作

/* Bind socket to specified address. */

int sockunion_bind (int sock, union sockunion *su, unsigned short port,

              union sockunion *su_addr)

{

  int size = 0;

  int ret; 

  if (su->sa.sa_family == AF_INET)

    {

      size = sizeof (struct sockaddr_in);

      su->sin.sin_port = htons (port);

#ifdef HAVE_SIN_LEN

      su->sin.sin_len = size;

#endif /* HAVE_SIN_LEN */

      if (su_addr == NULL)

       su->sin.sin_addr.s_addr = htonl (INADDR_ANY);

服务器一般将sin_addr.s_addr 字段设置为INADDR_ANY表示套接字应接收任何一个主机网络接口上的连接请求。

客户端将sin_addr.s_addr字段设置为服务器主机的IP地址。

    }

#ifdef HAVE_IPV6

  else if (su->sa.sa_family == AF_INET6)

    {

      size = sizeof (struct sockaddr_in6);

      su->sin6.sin6_port = htons (port);

#ifdef SIN6_LEN

      su->sin6.sin6_len = size;

#endif /* SIN6_LEN */

      if (su_addr == NULL)

       {

#if defined(LINUX_IPV6) || defined(NRL)

         bzero (&su->sin6.sin6_addr, sizeof (struct in6_addr));

#else

         su->sin6.sin6_addr = in6addr_any;

#endif /* LINUX_IPV6 */

       }

    }

#endif /* HAVE_IPV6 */ 

  ret = bind (sock, (struct sockaddr *)su, size);

  if (ret < 0)

    zlog (NULL, LOG_WARNING, "can't bind socket : %s", strerror (errno)); 

  return ret;

vty_event操作 

/* struct thread_master *master; */

static void vty_event (enum event event, int sock, struct vty *vty)

{

  struct thread *vty_serv_thread;

  switch (event)

    {

    case VTY_SERV:

      vty_serv_thread = thread_add_read (master, vty_accept, vty, sock);

      vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);

      break;

    case VTY_READ:

      vty->t_read = thread_add_read (master, vty_read, vty, sock);

      /* Time out treatment. */

      if (vty->v_timeout)

       {

         if (vty->t_timeout)

           thread_cancel (vty->t_timeout);

         vty->t_timeout =

           thread_add_timer (master, vty_timeout, vty, vty->v_timeout);

       }

      break;

    case VTY_WRITE:

      if (! vty->t_write)

       vty->t_write = thread_add_write (master, vty_flush, vty, sock);

      break;

    case VTY_TIMEOUT_RESET:

      if (vty->t_timeout)

       {

         thread_cancel (vty->t_timeout);

         vty->t_timeout = NULL;

       }

      if (vty->v_timeout)

       {

         vty->t_timeout =

           thread_add_timer (master, vty_timeout, vty, vty->v_timeout);

       }

      break;

    }

第2 个thread

bgp_serv_sock( )-> bgp_serv_sock_family( )中创建一个thread,不是通过event的方式添加的。该thread的处理函数为bgp_accept。作为bgp_server,接受来自 internet上的bgp连接。

bgp_serv_sock_family操作

port = 179  family = AF_INET

/* Make bgpd's server socket. */

void bgp_serv_sock_family (unsigned short port, int family)

{

  int ret;

  int bgp_sock;

  union sockunion su;

  bzero (&su, sizeof (union sockunion));

  /* Specify address family. */

  su.sa.sa_family = family;

  bgp_sock = sockunion_stream_socket (&su);  产生一个BGP socket

  sockopt_reuseaddr (bgp_sock);

  sockopt_reuseport (bgp_sock);

  ret = sockunion_bind (bgp_sock, &su, port, NULL);

 

  ret = listen (bgp_sock, 3);

  if (ret < 0)

    {

      zlog (NULL, LOG_INFO, "Can't listen bgp server socket : %s",

           strerror (errno));

      return;

    } 

  thread_add_read (master, bgp_accept, NULL, bgp_sock); 添加一个thread 到readlist中。

VTY server和BGP server 在使用accept操作的方法如下:

他们均是通过创建一个THREAD_READ 类型的thread,加到Master的readlist的队列后面,thread 的处理函数会执行accept操作。

VTY accept:

   vty_serv_thread = thread_add_read (master, vty_accept, vty, sock);

BGP server accept:

  thread_add_read (master, bgp_accept, NULL, bgp_sock); 

bgpd和zebrad间通信

bgp和zebra是通过zebra message进行通信,格式如下:

报文头3字节 (前两字节length,后1字节为command type)

报文体长度不定。

 

/* Zebra message types. */

#define ZEBRA_INTERFACE_ADD                1

#define ZEBRA_INTERFACE_DELETE             2

#define ZEBRA_INTERFACE_ADDRESS_ADD        3

#define ZEBRA_INTERFACE_ADDRESS_DELETE     4

#define ZEBRA_INTERFACE_UP                 5

#define ZEBRA_INTERFACE_DOWN               6

#define ZEBRA_IPV4_ROUTE_ADD               7

#define ZEBRA_IPV4_ROUTE_DELETE            8

#define ZEBRA_IPV6_ROUTE_ADD               9

#define ZEBRA_IPV6_ROUTE_DELETE           10

#define ZEBRA_REDISTRIBUTE_ADD            11

#define ZEBRA_REDISTRIBUTE_DELETE         12

#define ZEBRA_REDISTRIBUTE_DEFAULT_ADD    13

#define ZEBRA_REDISTRIBUTE_DEFAULT_DELETE 14

#define ZEBRA_IPV4_NEXTHOP_LOOKUP         15

#define ZEBRA_IPV6_NEXTHOP_LOOKUP         16

 

bgpd和zebrad之间的api接口

bgp端接受到message后,会执行相应的bgp action:

 

bgp action func                                         message type

int (*interface_add) (int, struct zclient *, zebra_size_t);        ZEBRA_INTERFACE_ADD

int (*interface_delete) (int, struct zclient *, zebra_size_t);                       ZEBRA_INTERFACE_DELETE

int (*interface_up) (int, struct zclient *, zebra_size_t);                           ZEBRA_INTERFACE_UP

int (*interface_down) (int, struct zclient *, zebra_size_t);                       ZEBRA_INTERFACE_DOWN

int (*interface_address_add) (int, struct zclient *, zebra_size_t);          ZEBRA_INTERFACE_ADDRESS_ADD

int (*interface_address_delete) (int, struct zclient *, zebra_size_t);  ZEBRA_INTERFACE_ADDRESS_DELETE

int (*ipv4_route_add) (int, struct zclient *, zebra_size_t);                   ZEBRA_IPV4_ROUTE_ADD

int (*ipv4_route_delete) (int, struct zclient *, zebra_size_t);                  ZEBRA_IPV4_ROUTE_DELETE

int (*ipv6_route_add) (int, struct zclient *, zebra_size_t);                    ZEBRA_IPV6_ROUTE_ADD

int (*ipv6_route_delete) (int, struct zclient *, zebra_size_t);                 ZEBRA_IPV6_ROUTE_DELETE

 

zebra 端接受到message后,会执行相应的zebra action:

 

zebra action func                                                              message type

void zread_interface_add (struct zserv *client, u_short length)                         ZEBRA_INTERFACE_ADD

void zread_interface_delete (struct zserv *client, u_short length)                     ZEBRA_INTERFACE_DELETE

void zread_ipv4_add (struct zserv *client, u_short length)                                    ZEBRA_IPV4_ROUTE_ADD

void zread_ipv4_delete (struct zserv *client, u_short length)                                   ZEBRA_IPV4_ROUTE_DELETE

void zread_ipv6_add (struct zserv *client, u_short length)                              ZEBRA_IPV6_ROUTE_ADD

void zread_ipv6_delete (struct zserv *client, u_short length)                           ZEBRA_IPV6_ROUTE_DELETE

void zebra_redistribute_add (int command, struct zserv *client, int length)              ZEBRA_REDISTRIBUTE_ADD

void zebra_redistribute_delete (int command, struct zserv *client, int length)           ZEBRA_REDISTRIBUTE_DELETE

voidzebra_redistribute_default_add (int command,

struct zserv *client, int length)                                  ZEBRA_REDISTRIBUTE_DEFAULT_ADD

void zebra_redistribute_default_delete (int command,

struct zserv *client, int length)                               ZEBRA_REDISTRIBUTE_DEFAULT_DELETE

void zread_ipv4_nexthop_lookup (struct zserv *client, u_short length)     ZEBRA_IPV4_NEXTHOP_LOOKUP

void zread_ipv6_nexthop_lookup (struct zserv *client, u_short length)        ZEBRA_IPV6_NEXTHOP_LOOKUP

 

bgp action:将local_client_socket中数据,写入bgp数据库。

zebra action:将zebra数据库中的信息写入local_server_subsocket,让local client端进行读取。

bgp peer间通信

bgp_accept操作

/* Accept bgp connection. */

int bgp_accept (struct thread *thread)

{

  int bgp_sock;

  int accept_sock;

  union sockunion su;

  struct peer *peer;

  struct peer *peer1;

  char buf[SU_ADDRSTRLEN];

 

  /* Regiser accept thread. */

  accept_sock = THREAD_FD (thread);

    printf("->bgp_accept [%d]\n",accept_sock);

  thread_add_read (master, bgp_accept, NULL, accept_sock);

 

  /* Accept client connection. */

  bgp_sock = sockunion_accept (accept_sock, &su);

  if (bgp_sock < 0)

    {

      zlog_err ("[Error] BGP socket accept failed (%s)", strerror (errno));

         printf("[Error] BGP socket accept failed (%s)", strerror (errno));

         puts("<-bgp_accept 2");

      return -1;

    }

 

  if (BGP_DEBUG (events, EVENTS))

    zlog_info ("[Event] BGP connection from host %s", inet_sutop (&su, buf));

  printf("[Event] BGP connection from host %s", inet_sutop (&su, buf));

  /* Check remote IP address */

  peer1 = peer_lookup_by_su (&su);

  if (! peer1 || peer1->status == Idle)

    {

      if (BGP_DEBUG (events, EVENTS))

       {

         if (! peer1)

           zlog_info ("[Event] BGP connection IP address %s is not configured",

                     inet_sutop (&su, buf));

         else

           zlog_info ("[Event] BGP connection IP address %s is Idle state",

                     inet_sutop (&su, buf));

       }

      close (bgp_sock);

       puts("<-bgp_accept 2");

      return -1;

    }

 

  /* Make dummy peer until read Open packet. */

  if (BGP_DEBUG (events, EVENTS))

    zlog_info ("[Event] Make dummy peer structure until read Open packet");

 printf("[Event] Make dummy peer structure until read Open packet\n");

  {

    char buf[SU_ADDRSTRLEN + 1];

 

    peer = peer_create_accept ();

    SET_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER);

    peer->su = su;

    peer->fd = bgp_sock;

    peer->status = Active;

 

    /* Make peer's address string. */

    sockunion2str (&su, buf, SU_ADDRSTRLEN);

    peer->host = strdup (buf);

  }

 

  BGP_EVENT_ADD (peer, TCP_connection_open); 创建一个event thread执行bgp_event函数

  puts("<-bgp_accept 0");

  return 0;

}

 

bgp_event操作

/* Execute event process. */

int bgp_event (struct thread *thread)

{

  int ret;

  int event;

  int next;

  struct peer *peer;

 

  peer = THREAD_ARG (thread); // get peer

  event = THREAD_VAL (thread); // get FSM event  eg.TCP_connection_open

  puts("->bgp_event");

  /* Logging this event. */

  next = FSM [peer->status -1][event - 1].next_state;  next为下一个状态

 

  if (BGP_DEBUG (fsm, FSM))

    plog_info (peer->log, "%s [FSM] %s (%s->%s)", peer->host,

              bgp_event_str[event],

              LOOKUP (bgp_status_msg, peer->status),

              LOOKUP (bgp_status_msg, next));

  printf("%s [FSM] %s (%s->%s)", peer->host,

              bgp_event_str[event],

              LOOKUP (bgp_status_msg, peer->status),

              LOOKUP (bgp_status_msg, next));

 

  /* Call function. */

  ret = (*(FSM [peer->status - 1][event - 1].func))(peer); 执行本状态处理函数

 

  /* When function do not want proceed next job return -1. */

  if (ret < 0)

      {

      puts("<-bgp_event 1");

    return ret;

      }

  /* If status is changed. */

  if (next != peer->status)               判断状态是否需要转变

    fsm_change_status (peer, next);

 

  /* Make sure timer is set. */

  bgp_timer_set (peer);

  puts("<-bgp_event 0");

  return 0;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值