quagga线程机制概述

从别地转过来的,感觉挺有帮助,对自己理解系统进程线程的调用很好
原文地址: 点击打开链接

A) quagga线程机制概述

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

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

quagga源码中有关线程管理的各个函数放置于文件夹的thread.h和thread.c两个文件中。

B) 线程管理具体分析

i) 首先对重要数据结构进行介绍:

* thread

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

/* Thread itself. */

struct thread

{

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

    unsigned char add_type;     /* thread type */

    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类型 */

    struct cpu_thread_history *hist;    /* 统计thread对CPU的使用情况 */

    char *funcname;

};

thread_list

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

struct thread_list

{

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

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

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

};

thread_master

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

/* Master of the theads. */

struct thread_master

{

      //8种线程队列

    struct thread_list   read;

    struct thread_list   write;

    struct thread_list   timer;

    struct thread_list   event;

    struct thread_list   ready;

    struct thread_list   unuse;

struct thread_list   background;

//3种文件描述符

    nps_fd_set      readfd;

    nps_fd_set      writefd;

nps_fd_set      exceptfd;

//该thread_master所占空间大小

    unsigned long alloc;

    /* zebra work mutex lock */

    VOS_MUTEX *mutexlock;

};



iii) thread的生命周期

注意:所有的thread queue都是FIFO类型的queue。

步骤一:当quagga创建一个新的thread master的时候,它会调用

thread_add_read、thread_add_write或thread_add_timer,这取决于创建的thread的类型:

thread_add_read添加一个thread到read queue,主要负责从外部某个socket读入数据;

thread_add_write添加一个thread到read queue,主要负责从外部某个socket写入数据;

thread_add_timer添加一个thread到timer queue,主要用于timing一个事件(如定期更新路由表或是定期发送数据包等)。

步骤二:thread_new用于为一个新的thread分配内存空间。它首先会检查在unuse queue中是否存在任何类型为unuse的thread,如果有的话,那么它会将它当做这个新的 thread,否则就会调用内存分配函数为这个新的thread分配内存空间。最后,thread_new调用thread_all_list添加这个新的thread到它应该在的thread queue中。

步骤三:quagga不停的在event queue中获取就绪的thread然后执行它们,一旦该thread被执行了,则该thread的类型就被设定为unuse,同时它也就会被转移到unuse queue中。

步骤四:如果event queue为空,则quagga执行select函数来监视文件描述符readfds、writefds和exceptfds,一旦监视到某个描述符准备就绪,就将该描述符对应的thread加入到ready queue中。

步骤五:当进行select操作之后,在read queue中的所有readfds标志为已经就绪的thread,会被移动到event queue;而在write queue中的所有writefds标志已经就绪的thread,会被移动到event queue;但是其他没有为I/O操作准备就绪的thread则依然老老实实的呆在原来自己该呆的queue中。一旦原来在read queue或write queue中的thread被转移到event queue,那么该thread的readfds和writefds就会被清掉。

步骤六:只有timer thread使用它自己的timer,当timer到时间了之后,该timer thread就会被转移到event queue中。

步骤七:位于event queue头部的thread会被取出来然后被执行,如果event queue是空的,那么将会在步骤三到五之间无限循环。

猜你喜欢

转载自blog.csdn.net/timebomb/article/details/7275862