Chinaunix首页 | 论坛 | 博客
  • 博客访问: 781755
  • 博文数量: 83
  • 博客积分: 7030
  • 博客等级: 少将
  • 技术积分: 1097
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-06 15:50
文章分类

全部博文(83)

文章存档

2011年(2)

2010年(9)

2009年(56)

2008年(16)

我的朋友

分类: 系统运维

2009-06-06 18:44:17

《The Design and Implementation of the FreeBSD Operating System》
Part IV: Interprocess Communication
Chapter 11. Interprocess Communication 
11.1. Interprocess-Communication Model
进程间通信和网络协议对内存管理的特殊要求有:
  1. 所需内存的大小变化无常。
  2. 数据长度可能会有经常性的动态改变。
  3. 数据常需化零为整或化整为零。
  4. 数据在收发时可能需要进行排队。
mbuf的定义是在mbuf.h中给出的:
_________________________________________________________________________________________
    79    /*
    80     * Header present at the beginning of every mbuf.
    81     */
    82    struct m_hdr {
    83        struct    mbuf *mh_next;        /* next buffer in chain */
    84        struct    mbuf *mh_nextpkt;    /* next chain in queue/record */
    85        caddr_t    mh_data;        /* location of data */
    86        int    mh_len;            /* amount of data in this mbuf */
    87        int    mh_flags;        /* flags; see below */
    88        short    mh_type;        /* type of data in this mbuf */
    89    };
    90   
    91    /*
    92     * Packet tag structure (see below for details).
    93     */
    94    struct m_tag {
    95        SLIST_ENTRY(m_tag)    m_tag_link;    /* List of packet tags */
    96        u_int16_t        m_tag_id;    /* Tag ID */
    97        u_int16_t        m_tag_len;    /* Length of data */
    98        u_int32_t        m_tag_cookie;    /* ABI/Module ID */
    99        void            (*m_tag_free)(struct m_tag *);
   100    };
   101   
   102    /*
   103     * Record/packet header in first mbuf of chain; valid only if M_PKTHDR is set.
   104     */
   105    struct pkthdr {
   106        struct    ifnet *rcvif;        /* rcv interface */
   107        int    len;            /* total packet length */
   108        /* variables for ip and tcp reassembly */
   109        void    *header;        /* pointer to packet header */
   110        /* variables for hardware checksum */
   111        int    csum_flags;        /* flags regarding checksum */
   112        int    csum_data;        /* data field used by csum routines */
   113        SLIST_HEAD(packet_tags, m_tag) tags; /* list of packet tags */
   114    };
   115   
   116    /*
   117     * Description of external storage mapped into mbuf; valid only if M_EXT is set.
   118     */
   119    struct m_ext {
   120        caddr_t    ext_buf;        /* start of buffer */
   121        void    (*ext_free)        /* free routine if not the usual */
   122                (void *, void *);
   123        void    *ext_args;        /* optional argument pointer */
   124        u_int    ext_size;        /* size of buffer, for ext_free */
   125        volatile u_int *ref_cnt;    /* pointer to ref count info */
   126        int    ext_type;        /* type of external storage */
   127    };
   128   
   129    /*
   130     * The core of the mbuf object along with some shortcut defines for
   131     * practical purposes.
   132     */
   133    struct mbuf {
   134        struct    m_hdr m_hdr;
   135        union {
   136            struct {
   137                struct    pkthdr MH_pkthdr;    /* M_PKTHDR set */
   138                union {
   139                    struct    m_ext MH_ext;    /* M_EXT set */
   140                    char    MH_databuf[MHLEN];
   141                } MH_dat;
   142            } MH;
   143            char    M_databuf[MLEN];        /* !M_PKTHDR, !M_EXT */
   144        } M_dat;
   145    };
   146    #define    m_next        m_hdr.mh_next
   147    #define    m_len        m_hdr.mh_len
   148    #define    m_data        m_hdr.mh_data
   149    #define    m_type        m_hdr.mh_type
   150    #define    m_flags        m_hdr.mh_flags
   151    #define    m_nextpkt    m_hdr.mh_nextpkt
   152    #define    m_act        m_nextpkt
   153    #define    m_pkthdr    M_dat.MH.MH_pkthdr
   154    #define    m_ext        M_dat.MH.MH_dat.MH_ext
   155    #define    m_pktdat    M_dat.MH.MH_dat.MH_databuf
   156    #define    m_dat        M_dat.M_databuf
__________________________________________________________________/usr/src/sys/sys/mbuf.h
mbuf的典型内存布局如下图所示:
              +----------------------------------------------------+
              |                 mh_next                            |
              |                 mh_nextpkt                         |
              |                 mh_data                 (22 bytes) |
              |                 mh_len                             |
              |                 mh_flags                           |
              |                 mh_type                            |
              +-------------+--------------------------------------+
              | M_databuf[] |         rcvif                        |
              |             |         len                          |
              |             |         header            (24 bytes) |
              | (234 bytes) |         csum_flags                   |
              |             |         csum_data                    |
              |             |         tags                         |
              |             +--------------+-----------------------+
              |             | MH_databuf[] |  ext_buf              |
              |             |              |  ext_free             |
              |             |              |  ext_args  (24 bytes) |
              |             | (210 bytes)  |  ext_size             |
              |             |              |  ref_cnt              |
              |             |              |  ext_type             |
              |             |              +-----------------------+
              |             |              |                       |
                    ...           ...                 ...        
              |             |              |                       |
              +-------------+--------------+-----------------------+
              这是一个普通   这个mbuf是用来     这个mbuf使用了外部
              的用于存储数   存储某个packet     存储空间,因此本身
              据的mbuf       的多个mbuf中的     余下的内部存储空间
                             第一个             将废弃不用。(一个
                                                mbuf可以既存储
                                                packet头又使用外部
                                                空间)
mbuf的大小甴MSIZE宏给出(当前为256字节),每个mbuf都有一个长度为22字节的头部,因此每个mbuf最多可存储234字节的数据。
除mbuf之外,还可申请外部存储空间,称为cluster,其大小甴MCLBYTES宏给出(当前为2048字节)。数据要么存储在mbuf的内部数据区中,
要么存储在外部cluster中。在mbuf的标准头中,有一个数据指针指向实际数据的起始位置,另外还有一个字段用于记录数据的长度,
这两个东西结合在一起就可以方便地在数据区地首部和尾部进行数据的增删操作。上述两个宏值的定义如下:
_________________________________________________________________________________________
   134    /*
   135     * Constants related to network buffer management.
   136     * MCLBYTES must be no larger than PAGE_SIZE.
   137     */
   138    #ifndef    MSIZE
   139    #define MSIZE        256        /* size of an mbuf */
   140    #endif    /* MSIZE */
   141
   142    #ifndef    MCLSHIFT
   143    #define MCLSHIFT    11        /* convert bytes to mbuf clusters */
   144    #endif    /* MCLSHIFT */
   145   
   146    #define MCLBYTES    (1 << MCLSHIFT)    /* size of an mbuf cluster */
_________________________________________________________________/usr/src/sys/sys/param.h
多个mbuf可通过m_next指针串在一起,用以存储任意长度的数据。按照惯例,以这种方式串起来的mbuf链是被当作一个单一的对象来处理的,
比如一个完整的packet。m_nextpkt指针则用于将上述对象链接成一个队列。下面图示了甴两个packet组成的队列的情况:
        +-----------+        +-----------+        +-----------+
        | m_next    |------->| m_next    |------->| m_next    |-------> ...
        +-----------+        +-----------+        +-----------+
    ----| m_nextpkt |        |   ...     |        |   ...     |
    |   +-----------+        |           |        |           |
    |   |   ...     |        |           |        |           |
    |   +-----------+        +-----------+        +-----------+
    |
    |   +-----------+        +-----------+
    |-->| m_next    |------->| m_next    |-------> ...
        +-----------+        +-----------+
    --- | m_nextpkt |        |   ...     |
    |   +-----------+        |           |
    |   |   ...     |        |           |
    |   +-----------+        +-----------+
    |
    |--> ...
m_flags字段的取值是在mbuf.h中定义的,这些宏值被分为了两组,一组用于描述单个mbuf,一组用于描述整个packet:
_________________________________________________________________________________________
   158    /*
   159     * mbuf flags.
   160     */
   161    #define    M_EXT        0x0001    /* has associated external storage */
   162    #define    M_PKTHDR    0x0002    /* start of record */
   163    #define    M_EOR        0x0004    /* end of record */
   164    #define    M_RDONLY    0x0008    /* associated data is marked read-only */
   165    #define    M_PROTO1    0x0010    /* protocol-specific */
   166    #define    M_PROTO2    0x0020    /* protocol-specific */
   167    #define    M_PROTO3    0x0040    /* protocol-specific */
   168    #define    M_PROTO4    0x0080    /* protocol-specific */
   169    #define    M_PROTO5    0x0100    /* protocol-specific */
   170    #define    M_SKIP_FIREWALL    0x4000    /* skip firewall processing */
   171    #define    M_FREELIST    0x8000    /* mbuf is on the free list */
   172   
   173    /*
   174     * mbuf pkthdr flags (also stored in m_flags).
   175     */
   176    #define    M_BCAST        0x0200    /* send/received as link-level broadcast */
   177    #define    M_MCAST        0x0400    /* send/received as link-level multicast */
   178    #define    M_FRAG        0x0800    /* packet is a fragment of a larger packet */
   179    #define    M_FIRSTFRAG    0x1000    /* packet is first fragment */
   180    #define    M_LASTFRAG    0x2000    /* packet is last fragment */
   181    #define    M_VLANTAG    0x10000    /* packet has VLAN tag attached */
__________________________________________________________________/usr/src/sys/sys/mbuf.h
如果某个mbuf设置了M_PKTHDR标志,则表明:
  1. 这个mbuf是用来存储某个packet的一串mbuf中的第一个。
  2. 在这个mbuf中22字节的标准头之后紧接着的是24字节的用于描述packet的头部字段,因此这个mbuf本身最多只能再存储210字节的数据。
如果某个mbuf设置了M_EXT标志,则表明:
  1. 这个mbuf使用了外部存储空间(cluster)。
  2. 在这个mbuf中22字节的标准头之后的数据区中会有一个24字节的用于描述外部存储空间的头部字段。
  3. 这个mbuf本身剩余的数据区未被使用。
注意,一个mbuf可以同时设置上述两个标志,这时在标准头之后是packet头,然后紧接着就是外部存储头。
mbuf大小固定的理由:
  1. 可以最大限度地避免内存碎片的产生。
  2. 可在不重新分配和拷贝的前提下完成协议对数据区的操作。
  3. 如果mbuf不是固定长度,dtom()函数的开销将会非常大。
mbuf中的pakcet头中的tags用于携带一些关于packet的但又不能放在packet内部的信息。
存储管理算法:
  1. 每个CPU都有自己私有的mbuf和cluster容器。此外还有一个单独的通用的mbuf和cluster池,当某个CPU的链表为空时就会从其上分配,当某个CPU的链表已满时就会把内存释放给它。单处理器系统就相当于只有一个CPU的SMP系统,它只有一个用于CPU的链表和一个通用链表。
  2. 在系统引导的时候就会初始化每个CPU的mbuf和cluster链表,其中包括的mbuf和cluster的数目可以调节。通用链表则初始化为空。
  3. 系统中所有cluster的引用计数是用一个数组来维护的。用于mbuf和cluster的内存的大小取决于内核参数maxusers。
  4. mbuf的分配可以分为“可等候”和“不等候”两种。

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

==========嵌入式操作系统VxWorks中网络协议存储池原理及实现====================

VxWorks操作系统是美国WindRiver公司于1983年设计开发的一种嵌入式实时操作系统(RTOS)。它以良好的持续能力、高性能的内核以及卓越的实时性被广泛的应用在通信、军事、航空、航天等高精尖技术及实时性要求极高的领域中。VxWorks操作系统有着优越的网络性能,而缓冲区的数据拷贝是影响网络性能的主要因素。

    众所周知,缓冲区在网络协议栈中有两个作用:第一,提供载体,使分组或报文可以在各协议层中流动;第二,为各级缓冲区提供空间。缓冲区的设立使得TCP/IP协议栈支持异步I/O操作,异步操作对于协议栈的I/0性能是非常重要的。在网络输出的过程中每一层需要在数据的首部或者尾部添加数据头和数据尾来对数据进行封装使得接收端对应的层能够进行正确的接收,在输入的过程中每层都需要将本层的数据头和数据尾去掉而最终还原成发送端发送的数据。上述的封装/去封装和拷贝操作使得网络协议对内核的存储器管理能力提出了很多要求。这些要求包括能方便地操作可变长缓存,能在缓存头部和尾部添加数据(如低层封装来自高层的数据),能从缓存中移去数据(如当数据包向上经过协议栈时要去掉首部),并能尽量减少这些操作所作的数据复制。

1  使用netBufLib管理存储池的基本原理

    网络协议存储池使用mBlk结构、clBlk结构、簇缓冲区和netBufLib提供的函数进行组织和管理。mBlk和clBlk结构为簇缓冲区(cluster)中数据的缓冲共享和缓冲链接提供必要的信息。netBufLib例程使用mBlk和clBlk来管理cluster和引用cluster中的数据,这些结构体中的信息用于管理cluster中的数据并且允许他们通过引用的形式来实现数据共享,从而达到数据“零拷贝”的目的。

1.1  结构体mBlk和clBlk及其数据结构

    mBlk是访问存储在内存池中数据的最基本对象,由于mBlk仅仅只是通过clBlk来引用数据,这使得网络层在交换数据时就可以避免数据复制。只需把一个mBlk连到相应mBlk链上就可以存储和交换任意多的数据。一个mBlk结构体包括两个成员变量mNext和mNextPkt,由它们来组成纵横两个链表:mNext来组成横的链表,这个链表中的所有结点构成一个包(packet);mNextPkt来组成纵的链表,这个链表中的每个结点就是一个包(packet),所有的结点链在一起构成一个包队列,如图1所示。

图1 包含两个数据包的mBlk链
    结构体mBlk和clBlk的数据结构如下所示:
struct mBlk
{
 M_BLK_HDR    mBlkHdr;              /* header */
 M_PKT_HDR    mBlkPktHdr;         /* pkthdr */
 CL_BLK *         pClBlk;      /* pointer to cluster blk */
} M_BLK;
struct clBlk
{
CL_BLK_LIST  clNode;/* union of next clBlk */
 UINT       clSize;/* cluster size */
 int    clRefCnt;/*countof the cluster */
 struct netPool *  pNetPool;  /* pointer to the netPool */
} CL_BLK;
/* header at beginning of each mBlk */
struct mHdr
{
 struct mBlk *     mNext;/* next buffer in chain */
  struct mBlk * mNextPkt;/* next chain in queue/record */
char *mData;                /* location of data */
int  mLen;/* amount of data in this mBlk */
UCHAR   mType;/* type of data in this mBlk */
UCHAR   mFlags;               /* flags; see below */
} M_BLK_HDR;
/* record/packet header in first mBlk of chain; valid if M_PKTHDR set */
struct          pktHdr
{
struct ifnet *         rcvif;/* rcv interface */
int      len;             /* total packet length */
} M_PKT_HDR;

1.2  网络协议存储池的初始化

    VxWorks在网络初始化时给网络协议分配存储池并调用netPoolInit()函数对其初始化,由于一个网络协议通常需要不同大小的簇,因此它的存储池也必须包含很多簇池(每一个簇池对应一个大小的簇)。如图2所示。另外,每个簇的大小必须为2的方幂,最大可为64KB(65536),存储池的常用簇的大小为64,128,256,512,1024比特,簇的大小是否有效取决于CL_DESC表中的相关内容,CL_DESC表是由netPoolInit()函数调用设定的。

图2   网络协议存储池初始化后的结构

1.3  存储池的链接及释放

存储池在初始化后,由netPool结构组织几个下一级子池:一个mBlk池、一个clBlk池和一个cluster池。mBlk池就是由很多mBlk组成的一条mBlk链;clBlk池就是由很多clBlk组成的一条clBlk链。cluster池由很多的更下一级cluster子池构成,每一个cluster子池就是一个cluster链。每一个cluster链中的所有cluster的大小相同,不同链中的cluster大小不同。但要实现不同进程访问同一簇而不需要作数据的拷贝,还需要把mBlk结构,clBlk结构和簇结构链接在一起。创建这三级结构一般要遵循这样五步:

    a.调用系统函数netClusterGet()预定一块簇缓冲区;
    b.调用系统函数netClBlkGet()预定一个clBlk结构;
    c.调用系统函数netMblkGet()预定一个mBlk结构;
    d.调用系统函数netClBlkJoin()把簇添加到clBlk结构中;
    e.调用系统函数netMblkClJoin()把clBlk结构添加到mBlk结构中。
这样,就构成了最后的缓冲区。
    在缓冲区中的数据使用完毕后要及时的释放内存,这一过程只需要调用系统函数netMblkC1ChainFree()释放存有数据的mBlk链表。例如当数据向上层传送时,在本层中可以释放已经不再使用的mBlk链表,由于在clBlk中记录着指向本模块的mBlk的个数,虽然释放了mBlk链表,但是这并不表示将cluster中的数据释放掉了,上层复制的链表仍然控制着这些数据,直到clBlk中的mBlk计数为0时才真正的将数据占用的簇释放掉,将数据占用的内存空间释放、归还给系统将来使用。

2  协议存储池与数据的封装处理

    VxWorks操作系统之所以采用mBlk—clBlk—cluster这样的网络数据存储结构,目的就是减少数据拷贝的次数,提高网络数据的传输速率。

图3 存储带有1460个字节数据的mBlk
    在网络输出的过程中当从上层向下层传递数据时,下层协议需要对数据进行封装使得接收端对应的层能够进行正确的接收。下面通过实例分析网络数据的封装过程。例如要在如图3所示的mBlk链中添加IP和UDP的首部。
    在mBlk链表中封装数据的方法是分配另外一个mBlk,把它放在链首,并将分组首部复制到这个mBlk。IP首部和UDP首部被放置在新mBlk的最后,这个新mBlk就成了整个链表的首部。如果需要,它允许任何其它低层协议(例如添加以太网首部)在IP首部前添加自己的首部,不需要再复制IP和UDP首部。在第一个mBlk中的mBlkHdr.mData指针指向首部的起始位置,mBlkHdr.mLen的值是28。在分组首部和IP首部之间有72字节的未用空间留给以后的首部,通过适当地修改mBlkHdr.mData指针和mBlkHdr.mLen添加在IP首部的前面。注意,分组首部已经移到新mBlk中了,分组首部必须放在mBlk链表的第一个mBlk中。在移动分组首部的同时,在第一个mBlk设置M_PKTHDR标志。在第二个mBlk中分组首部占用的空间现在未用。最后,改变在此分组首部中的长度成员mBlkPktHdr.len,成员mBlkPktHdr.len的值是这个分组的mBlk链表中所有数据的总长度:即所有通过mBlkHdr.mNext指针链接的mbuf的mBlkHdr.mLen值的和。本例中由于增加了28个字节变成了1488。如图4所示。


 

图4 添加完IP和UDP首部的mBlk


 

    这样,当报文在协议栈中流动时,不会拷贝报文链,而只需把指向mBlk的指针通过参数传递。当报文需要进人缓冲区时,也是通过链表的指针操作将报文插入或添加到队列中。

3  结论

    网络协议存储池的职责有两个:为协议栈提供合适的缓冲区,如果太大会浪费系统资源,太小会影响协议栈的吞吐量;提供合适的数据结构装载网络报文,既可以使协议栈方便地处理报文,又可以减少缓冲区拷贝的次数。减少拷贝次数不仅降低了CPU的负荷,还可以降低存储器的消耗。本文剖析了嵌入式操作系统VxWorks中网络协议存储池的原理,实现了数据能够动态增删、但在逻辑上又呈现连续性的数据结构。能够满足在各协议层之间传递数据而不需要进行内存拷贝。

[1] 翟东海,李力.mbuf的实现原理剖析及其在网络编程中的应用[J].机工程与应,2004(8):104-106.
[2] [美]DouglasE.Comer著.张娟等译.用TCP/IP进行网际互联第二卷:设计、实现与内核(第三版)[M].北京:出版社,2001.05.
[3] [美]GaryR.Wright W.RichardStevens著.陆雪莹,蒋慧等译.TCP/IP详解卷2:实现[M].北京:机械工业出版社.2000.07:10—50.
[4] Wind River System Inc.VxWorks Network
阅读(4683) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~