a8z8

 找回密码
 立即注册
a8z8 首页 计算机 linux and shell 查看内容

varnish 缓存原理

2013-4-13 23:53| 发布者: king| 查看: 131| 评论: 0

摘要: Varnish文件缓冲的实现 1、Varnish文件缓存的整体思路及优点 Varnish将所有的 HTTP object存于一个单独的大文件中,而该文件在工作进程初始时就将其整个 mmap到内存中。Varnish在该块内存中实现类似于一个简单的“文 ...

Varnish文件缓冲的实现

1、Varnish文件缓存的整体思路及优点
Varnish将所有的 HTTP object存于一个单独的大文件中,而该文件在工作进程初始时就
将其整个 mmap到内存中。Varnish在该块内存中实现类似于一个简单的“文件系统”,具
有分配、释放、修剪、合并等功能。


Varnish文件缓存的优点,就如其创始人
Poul-Henning Kamp所说,它是一个具有“现代
设计理念”的软件,其整体设计优点可以从[1]得知。我觉得它的优点主要有两点:1、它避
免了软件与系统对内存控制的冲突,引入了虚拟内存的概念,将内存与硬盘文件统一,软件
只需要注重对内存的操作即可;2、它将所有的
object存于一个文件中,避免类似
Squid为每

object存放一个小文件的设计,减少文件系统频繁的操作。当然这需要记录每个
object在
内存中的偏移量,并定期检查并释放内存,进而增加编程难度,但同时提高了系统的性能。


2、Varnish文件缓存的工作流程


Varnish与一般服务器软件类似,分为
master(management)进程和
child(worker,主要

cache的工作)进程。master进程读入命令,进行一些初始化,然后
fork并监控
child进程。
child进程分配若干线程进行工作,主要包括一些管理线程和很多
woker线程。

针对文件缓存部分,master读入存储配置(-s file[,path[,size[,granularity]]] ),调用合适
的存储类型,然后创建/读入相应大小的缓存大文件(根据其
man文档,为避免文件出现存
储分片[2]影响读写性能,作者建议用
dd(1)命令预先创建大文件)。接着,master初始化管
理该存储空间的结构体。这些变量都是全局变量,在
fork以后会被
child进程所继承(包括
文件描述符)。


child进程主线程初始化过程中,将前面打开的存储大文件整个
mmap到内存中(如果
超出系统的虚拟内存,mmap失败,进程会减少原来的配置
mmap大小,然后继续
mmap),此时创建并初始化空闲存储结构体,挂到存储管理结构体,以待分配。

接着,真正的工作开始,Varnish的某个负责接受新
HTTP连接的线程开始等待用户(具
体过程可参见[3]),如果有新的
HTTP连接过来,它总负责接收,然后叫醒某个等待中的线
程,并把具体的处理过程交给它。


Worker线程读入
HTTP请求的
URI,查找已有的
object,如果命中则直接返回并回复用
户。如果没有命中,则需要将所请求的内容,从后端服务器中取过来,存到缓存中,然后再
回复。

分配缓存的过程是这样的:它根据所读到
object的大小,创建相应大小的缓存文件。为
了读写方便,程序会把每个
object的大小变为最接近其大小的内存页面倍数。然后从现有的
空闲存储结构体中查找,找到最合适的大小的空闲存储块,分配给它。如果空闲块没有用完,
就把多余的内存另外组成一个空闲存储块,挂到管理结构体上。如果缓存已满,就根据
LRU[4]机制,把最旧的
object释放掉。

释放缓存的过程是这样的:有一个超时线程,检测缓存中所有
object的生存期,如果超
初设定的
TTL(Time To Live)没有被访问,就删除之,并且释放相应的结构体及存储内存。
注意释放时会检查该存储内存块前面或后面的空闲内存块,如果前面或后面的空闲内存和该
释放内存是连续的,就将它们合并成更大一块内存。

整个文件缓存的管理,没有考虑文件与内存的关系,实际上是将所有的
object都考虑是
在内存中,如果系统内存不足,系统会自动将其换到
swap空间,而不需要
varnish程序去控
制。


3、文件缓存的数据结构及其操作


3.1基本数据结构
尾队列

由于
Poul-Henning Kamp是
FreeBSD的内核维护者,Varnish代码中受
FreeBSD编程风格
的影响较大。程序中使用最多的是名叫尾队列(tail queue)的数据结构,是其四种基本数据
结构中的一种:单向列表(single-linked lists)、单向尾队列(single-linked tail queue)、列表
(lists)、尾队列(tail queues)。全部的数据结构定义可以在
varnish-dist/include/vqueue.h中找到,
附录
1中有尾队列全部操作的定义。参考文献[5]提到了这些数据结构,并简单提到了这样写
的原因,建议一看。

尾队列头部实际上是指向头部成员和尾部成员的结构体,基本定义如下:


#define VTAILQ_HEAD(name, type)

\
struct name {

\

struct type *vtqh_first; /* first element */ \

struct type **vtqh_last; /* address of last next element */ \

}

尾队列成员入口实际上是一个双向链表,但又有不同,基本定义如下:


#define VTAILQ_ENTRY(type)

\

struct {

\

struct type *vtqe_next; /* next element */

\

struct type **vtqe_prev; /* address of previous next element */\

}

vtqh_firstvtqh_lastvtqe_nextvtqe_prevvtqe_nextvtqe_prevvtqe_nextvtqe_prevvtqe_nextvtqe_prevelementelementelementelementNULL
图1 尾队列示意图
特别要注意的是,尾队列成员入口的
prev是双重指针,指向上一个成员的
next指针。这
样做主要为了通用,即使element成员是不同类型也可以组成链表。这与我们一般教科书上



prev操作不一样。
所以其
last和
prev的宏定义也颇为让人费解:


#define VTAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->vtqh_last))->vtqh_last))

#define VTAILQ_PREV(elm, headname, field)

\

(*(((struct headname *)((elm)->field.vtqe_prev))->vtqh_last))

注意尾队列的
head和
entry结构体的定义是一样的,所以在
element类型是一样的时候,
VTAILQ_LAST(head, headname)也等于*(struct type *)head->vtqh_last->vtqe_prev,相应的
VTAILQ_PREV(elm, headname, field)也等于*(struct type *)elm->field.vtqe_prev>
vtqe_prev。FreeBSD写成这样诡异的形式据说是为了可以方便得删除不同类型的
element。

二叉堆

二叉堆[6]由于其查找迅速(查找最值的复杂度为
O(log(n)))稳定,在大数据量下的最大
/最小值查找运用广泛,特别是游戏中的最短路径搜索中,作为基础存储结构。其插入和删
除的复杂度也为
log(n),而且二叉堆的数据可以仅仅存在一个一维的数组中。


varnish中,object的
TTL就存储在二叉堆中,以便迅速找到最近超时的结构体。其结
构体的定义在
varnish-dist/lib/libvarnish/binary_heap.c中:
struct binheap {

unsigned magic;
#define BINHEAP_MAGIC 0xf581581aU /* from /dev/random */

void *priv; /*NULL*/

binheap_cmp_t *cmp; /*比较函数*/

binheap_update_t *update; /*更新函数*/

void **array; /*指向保存二叉堆的一维数组*/

unsigned length; /*二叉堆的长度,最小为
16*/

unsigned next; /*下一个空闲的二叉堆节点,根的
index为
1*/

unsigned granularity; /*bh->granularity = getpagesize() / sizeof *bh->array*/
};

二叉堆的建立(binheap_new)、插入(binheap_insert)、删除(binheap_delelte)函数
都在该文件中实现,比较简单。


update和
cmp函数均在
varnish-dist/bin/varnishd/cache_expire.c中实现,进行数据的更新
和大小的比较。


3.2文件缓存的物理存储及管理结构体的数据结构
Varnish的物理存储的管理结构体名称是
struct smf_sc,其定义在
varnish-dist/bin/varnishd/
strorage_file.c中:


VTAILQ_HEAD(smfhead, smf); /*struct name, type*/

struct smf_sc {

const char *filename; /*缓存大文件的名称*/

int fd; /*缓存大文件的文件描述符*/

unsigned pagesize; /*内存的页面大小,从系统调用
getpagesize()获得*/

uintmax_t filesize; /*缓存大文件的大小*/


struct smfhead order; /*以内存地址排序的
smf尾队列头部*/
struct smfhead free[NBUCKET]; /*空闲的
smf尾队列头部,NBUCKET表明的是空
闲页面数,大于
NBUCKET-1空闲页面数的
smf,
都挂在
free[NBUCKET-1]这个尾队列上*/
struct smfhead used; /*正在被使用的
smf*/
MTX mtx; /*#define MTX pthread_mutex_t*/
};
Varnish的物理存储的基本结构体名称是
struct smf,实际含义是缓存大文件中一小块连
续的内存,用以分配给相应大小的
HTTP对象,其定义也在
varnishdist/
bin/varnishd/strorage_file.c中:
struct smf { /*Storage Mmaped File*/
unsigned magic; /*魔法数,一般用来检验该结构体是否有效*/

#define SMF_MAGIC 0x0927a8a0

struct storage s; /*上层逻辑存储结构体*/

struct smf_sc *sc; /*smf管理结构体*/

int alloc; /*是否分配完成*/

off_t size; /*smf的大小*/

off_t offset; /*已经分配过的内存偏移*/

unsigned char *ptr; /*指向可分配内存的起始地址*/

VTAILQ_ENTRY(smf) order; /*按内存地址排序的
smf入口*/

VTAILQ_ENTRY(smf) status; /**/

struct smfhead *flist; /*指向smf_sc中
free [n]相应的空闲尾队列头部*/
};

结构体
smf_sc和
smf是
Varnish操作文件缓存的物理接口,主要操作函数有:
init、open、alloc、trim和
free,其函数名称均为
smf_*,函数原型都在
varnishdist/
bin/varnishd/strorage_file.c。这些函数都被通过函数指针被挂到逻辑操作结构体
stevedore
中(详见
3.3)。


init函数在
master进程初始化时被调用,用以打开相应大小的文件及初始化全局变量
smf_sc结构体,打开的具体行为为
open(fd, O_RDWR | O_CREAT | O_EXCL, 0600),init函数
的伪代码为:


{
从命令行读入缓存文件名称、大小
获取内存页面大小
分配并初始化
smf_sc结构体
初始化
smf_sc中的各尾队列成员
open缓存文件
计算命令行需要的文件大小,使用
ftruncate来进行调整文件
}


open函数在
child进程初始化时被调用,其函数主体是
smf_open_chunk(),mmap已经
打开的文件到内存,然后创建第一个
smf结构体。mmap的函数调用是这样的
mmap(NULL,
sz, PROT_READ|PROT_WRITE, MAP_NOCORE | MAP_NOSYNC | MAP_SHARED, sc->fd,
off)。我发现
MAP_NOCORE和
MAP_NOSYNC这两个标识在
Linux中是没有的,
MAP_SHARED的含义也是不一样的。在
FreeBSD中,MAP_NOCORE表明映射区域不包括
core文件,
MAP_NOSYNC表明虚拟内存中改动过的数据不会自动与文件系统中数据同步
(在
FreeBSD的
manual中说明,如果需要同步,可以使用
fsync(2)),而
MAP_SHARED
表明不同进程对虚拟内存改动是共享的;相应地,在
Linux中,
MAP_SHARED的含义是
FreeBSD中
MAP_NOSYNC和
MAP_SHARED标识的联合(在
Linux manual中说明,如果
需要同步,可以用
msync(2)和
munmap(2)实现)。smf_open_chunk函数的伪代码为:


{

把缓存文件
mmap到内存
IF mmap成功
建立一个新的
smf结构体,指向映射到内存的连续地址

ELSE
减小一次mmap的大小
再多次mmap


ENDIF
}

alloc函数是在创建
object首先需要调用的函数,它主要是用来分配相应的物理内存大小。
alloc分配的内存大小必定是页面大小的整数倍,由于系统对内存的操作单位是单个页面大小,
所以为了将每个
object的内存操作不会影响到其他object,Varnish中将每个
object用页面大
小来对齐(当然这会浪费一部分的内存),其函数的伪代码为:

{

根据原有的请求大小,找到最接近的整数倍内存页面大小
从smf_sc 中的free尾队列数组中,找到拥有相应页面数的队列
从上述尾队列中为storage分配分配一个smf
把smf从free队列中删除
从该smf中内存分配相应内存
IF 该smf还有内存剩余

把这个smf分裂出smf2,smf2为被使用掉的内存结构体
在order队列中,把smf2插入原有的smf成员之前
在used队列中,把smf2插入到尾部
在free队列中,把smf插入到尾部


ELSE

smf2=smf
ENDIF
将smf2分配给storage逻辑结构体
返回
storage


}

有时候,调用smf_alloc的时候并不知道到底需要多少,那就先实现分配一些内存(默
认是128K,也可以设置fetch_chunksize的值,分配大小为fetch_chunksize * 1024)。
当使用完内存后,还有一些内存剩余,这时候,就用到trim函数来裁剪内存,首先新建一



个smf2,把剩余的内存从原来已经分配出去的smf结构中拿过来,分配给smf2,smf2插入
到free的尾队列中等等。

free函数在有个名叫exp_timer的线程中不断被调用,它会定期查看所有object的
TTL(default_ttl,默认是120秒),如果有超时,就删除object,并释放相应的
smf。free函数的一个特点是,在删除时,会查看order序列上的内存块,如果相邻的内存
块是空闲的,就合并成更大的内存块。

上面这些操作是如何进行同步的呢?

因为Varnish的操作基本上都是在线程中实现,针对全局变量的一些非原子操作,默认
作者通过最简单的pthread_mutex_lock操作来进行互斥操作。


3.3文件缓存的逻辑分配、操作结构体
storage结构体是Varnish中存放每个object的地方,可以方便得使用其中分配的内存。
其结构体定义的位置为:varnish-dist/bin/varnishd/cache.h

struct storage {
unsigned magic;
#define STORAGE_MAGIC 0x1a4e51c0
VTAILQ_ENTRY(storage)list; /*访问入口 */

struct stevedore *stevedore; /*指向为其分配内存的stevedore*/
void*priv; /*指向其内存对应的smf*/
unsigned char *ptr; /*内存的指针*/
unsigned len; /*内存已使用的长度*/
unsigned space;/*分配的内存大小*/
int fd; /*smf->sc->fd*/
off_t where;/*smf->offset*/


};
stevedore是操纵storage的结构体,它在master进程读取命令行的时候就首先建立一

个stevedores的全局变量,然后根据相应的存储类型进行初始化,以便确定后续内存操作
函数的指针。其结构体定义的位置为:varnish-dist/bin/varnishd/stevedore.h
struct stevedore {

unsigned magic;

#define STEVEDORE_MAGIC 0x4baf43db
const char *name;
storage_init_f *init; /* 被master进程调用*/
storage_open_f *open; /* 被child进程调用
*/
storage_alloc_f *alloc;/*smf_alloc*/


storage_trim_f *trim; /*smf_trim*/
storage_free_f *free; /*smf_free*/
/* private fields */
void *priv; /*指向smf_sc*/
VTAILQ_ENTRY(stevedore) list;


};
上面对于
Varnish如何分配使用内存应该可以知道个大概了。


3.4 HTTP对象、hash存储等
Varnish的核心作用就是缓存
HTTP object,而
object的查找是通过
hash的方法来实现的,
其内存的组织形式也是挂在相应桶的
hash结构体下面(默认桶有
16383个)。


hash结构体的入口是全局变量
hcl_head(classic是默认的
hash方法,当然你也可以选择
simple_list),在子进程初始化时分配内存,hash结构体的头部和入口定义均在varnishdist/
bin/varnishd/hash_classic.c:


struct hcl_entry {
unsigned magic;
#define HCL_ENTRY_MAGIC 0x0ba707bf
VTAILQ_ENTRY(hcl_entry) list;

struct hcl_hd *head; /*指向hash桶的头部*/
struct objhead *oh; /*objhead*/
unsigned refcnt; /*引用次数,应该也是该
hash桶上的成员数*/
unsigned digest; /*计算机出来的
hash值*/
unsigned hash; /*hash桶数,digest % hcl_nhash*/
};
struct hcl_hd {
unsigned magic;

#define HCL_HEAD_MAGIC 0x0f327016
VTAILQ_HEAD(, hcl_entry) head;
MTX mtx; /*线程锁,在访问hash队列时的互斥*/

};
static unsigned hcl_nhash = 16383;
static struct hcl_hd *hcl_head;


HTTP object在被创建并从后台服务器取到内容以后,是被挂到
hash中各桶的
objhead上
去的,object和
objhead的结构体定义在varnish-dist/bin/varnishd/cache.h:
struct object {


unsigned magic;
#define OBJECT_MAGIC 0x32851d42

unsigned refcnt; //被
hash结构体引用次数
unsigned xid;
struct objhead *objhead; /*存储该
objecct的
objhead*/
struct storage *objstore; /*存储的
st队列*/
struct objexp *objexp; /*存储该
object超时结构体*/
struct ws ws_o[1]; /*存储空间指向storage的内存
*/
unsigned char *vary;
struct ban *ban; /*用于禁止访问某些
object的结构体*/
unsigned pass;
unsigned response;
unsigned cacheable; /*已经cache?*/
unsigned busy; /*忙否?*/
unsigned len; /*所占内存长度*/
double age; /*存在时间*/
double entered; /*?*/
double ttl; /*生存时间*/
double grace; /*优雅时间,ttl = ttl-grace*/
double prefetch;
double last_modified; /*最后获取过来的时间*/
struct http http[1]; /*新的
http结构体*/

VTAILQ_ENTRY(object)
VTAILQ_HEAD(, storage)
VTAILQ_HEAD(, esi_bit)


double last_use;
/* Prefetch */
struct object *parent;
struct object *child;

int hits;
};

struct objhead {

unsigned magic;
#define OBJHEAD_MAGIC

list; /*入口地址*/
store; /*object所对应的
storage队列,可能不止一个*/
esibits;


 /*命中次数*/


0x1b96615d



void *hashpriv; /*指向某个
hash入口*/
pthread_mutex_t mtx;
VTAILQ_HEAD(,object) objects;
char *hash; /*指向产生hash的字符串*/
unsigned hashlen; /*hash长度,等于
sp->lhashptr*/
VTAILQ_HEAD(, sess) waitinglist; /* There are one or more busy objects, wait for them */


};
object对象被缓存到内存以后,一般有个
TTL(生存期)。有个名叫exp_timer的线程
会检查最老object的TTL,如果已经超时,就删除之。那么它是如何来迅速知道哪个
object是最老的呢?已经删除最老的那个object,又如何把次老的object提取出来呢?

Varnish用到到的主要是二叉堆的结构体(见3.1)。该结构体的定义在Varnish-dist/bin/
varnishd/cache_expire.c:
static struct binheap *exp_heap;


static MTX exp_mtx;
struct objexp {
unsigned magic;

#define OBJEXP_MAGIC 0x4d301302
struct object *obj;
double timer_when; /*超时时间点,注意Varnish是用一个
deouble的浮点

来保存时间的(秒数+微秒)*/
const char *timer_what; /*对上述超时器的描述*/
unsigned timer_idx; /*在二叉堆中的序号*/
VTAILQ_ENTRY(objexp) list; /*访问入口*/
int on_lru; /*是否处于
lru队列中*/


double lru_stamp;
};
static VTAILQ_HEAD(,objexp) lru = VTAILQ_HEAD_INITIALIZER(lru);

此外,有时内存不足时,就不得不将一些未超时的
object删除,删除的策略主要是通过
一个
LRU[4]队列来实现。它是一个存储
objexp的尾队列,每次某个
object被访问以后,该
object的超时结构体即被从上述的尾队列中移到末尾。所以,每次需要腾空内存时,该尾队
列的头部
object总是最久未被访问的对象。


3.5 session及一些工作结构体
每次Varnish接受一个
HTTP请求连接以后,就创建了一个
session对象,它负责保存有
关这个请求所有内容,它是
Varnish工作的核心结构体。此外ws(workspace)和seemem结
构体是为session预分配内存的结构体。其结构体定义在varnishdist/
bin/varnishd/cache.h:


struct sess {


unsigned magic;
#define SESS_MAGIC 0x2c2f9c5a

int fd; /*socket fd*/

int id; /*= fd*/

unsigned xid; /*状态机处理的序号*/

int restarts;

int esis;

struct worker*wrk; /*指向当前工作的线程*/

socklen_t sockaddrlen;/*sizeof(sm->sockaddr[0])*/

socklen_t mysockaddrlen;/*sizeof(sm->sockaddr[1])*/

struct sockaddr *sockaddr;/*(void*)(&sm->sockaddr[0])*/

struct sockaddr *mysockaddr;/*(void*)(&sm->sockaddr[1])*/

struct listen_sock *mylsock; /*指向本session的监听socket*/

/* formatted ascii client address */

char *addr;

char *port;

struct srcaddr *srcaddr;

/* HTTP request */

const char *doclose; /*redo with http_GetHdrField()*/

struct http *http; /*&sm->http[0]*/

struct http *http0;/*&sm->http[1]*/

struct ws ws[1]; /*sessmem内存后面的一段空间*/

char *ws_ses; /* WS above session data */

char *ws_req; /* WS above request data */

struct http_conn htc[1];

/* Timestamps, all on TIM_real() timescale */

double t_open;

double t_req; /*请求进入时间*/

double t_resp;/*回复时间*/

double t_end;

/* Acceptable grace period */

double grace;

enum step step; /*状态机字段*/

unsigned cur_method;


unsigned handling; /*处理结果:VCL_RET_LOOKUP*/
unsigned char sendbody;
unsigned char wantbody;
int err_code;
const char *err_reason;
VTAILQ_ENTRY(sess) list;
struct director *director; /* sp->vcl->director[0]*/
struct vbe_conn *vbe; /*sp->director->getfd(sp)*/
struct bereq
*bereq; /*指向针对后台服务器的请求*/
struct object *obj; /**/
struct objhead *objhead;
struct VCL_conf *vcl; /*处理的vcl配置*/
/* Various internal stuff */
struct sessmem*mem; /*指向其分配其空间的sessmem*/
struct workreq
workreq;
struct acct acct;
/* pointers to hash string components */
unsigned nhashptr; /* sp->vcl->nhashcount * 2*/
unsigned ihashptr; /* 需要hash的字符串长度
,制作hash时有用*/
unsigned lhashptr; /*hash length,
sp->lhashptr = 1; */
const char **hashptr; /*WS_Alloc(sp->http->ws, sizeof(const char *) *


(sp->nhashptr + 1))*/
};

struct ws {
unsigned magic;
#define WS_MAGIC 0x35fac554
const char *id; /* identity:“sess”,“
obj”*/
char *s; /* (S)tart of buffer */
char *f; /* (F)ree pointer */
char *r; /* (R)eserved length */
char *e; /* (E)nd of buffer */

int overflow; /* workspace overflowed */

};


/*使用两组seemem来分配session,这样是为了减少加锁的时间长度*/
struct sessmem {
unsigned magic;

#define SESSMEM_MAGIC 0x555859c5
struct sess sess; /*存储session的地方*/
struct http http[2];
unsigned workspace; /*params->sess_workspace*/
VTAILQ_ENTRY(sessmem)list;/*指向空闲出来的session*/
struct sockaddr_storage sockaddr[2];

};

处理session的都是一些worker线程,描述这些worker的结构体定义也在varnishdist/
bin/varnishd/cache.h:
struct workreq {
VTAILQ_ENTRY(workreq) list;
workfunc *func; /* wrk_do_cnt_sess()*/
void*priv; /*指向某个session*/
};

struct worker {
unsigned magic;

#define WORKER_MAGIC 0x6391adcf
struct objhead*nobjhead;/*指向一个objhead*/
struct object *nobj; /*w->nobj = (void *)st->ptr*/
double lastused; /*最后的使用时间*/
pthread_cond_t cond; /*线程锁*/
VTAILQ_ENTRY(worker) list; /*入口*/
struct workreq
*wrq; /*指向当前工作的work请求*/
int *wfd;
unsigned werr; /* valid after WRK_Flush() */
struct iovec iov[MAX_IOVS];
int niov;
ssize_t liov;


struct VCL_conf *vcl;
struct srcaddr *srcaddr;
struct acct acct;
unsigned char *wlb, *wlp, *wle;

unsigned wlr;
};
struct wq {

unsigned magic;

#define WQ_MAGIC 0x606658fa
MTX
mtx;
struct workerhead idle; /*空闲的线程队列*/
VTAILQ_HEAD(, workreq) overflow; /*无法处理workreq的数目*/
unsigned nthr;
unsigned nqueue; /*排队数目*/
unsigned lqueue;
uintmax_t ndrop;
uintmax_t noverflow; /*无法处理workreq的数目*/

};
Varnish使用的是每个
HTTP请求对应一个线程的方式,所以采取的是线程队列的方式。



参考文献:


1、
http://varnish.projects.linpro.no/wiki/ArchitectNotes
该文献较为重要,作者阐明其软件的设计思路,建议一读,相应的中文翻译:


http://yaoweibin2008.blog.163.com/blog/static/11031392008101132330325/
2、http://en.wikipedia.org/wiki/File_system_fragmentation
3、http://varnish.projects.linpro.no/wiki/VarnishInternals
4、http://en.wikipedia.org/wiki/Cache_algorithms
5、http://freebsdchina.org/forum/viewtopic.php?t=37913
6、http://en.wikipedia.org/wiki/Binary_heap

附录:
1、FreeBSD中
tail queue的定义:


/*

* Tail queue declarations.
*/
#define VTAILQ_HEAD(name, type) \
struct name { \
struct type *vtqh_first; /* first element */ \
struct type **vtqh_last; /* addr of last next element */ \
}

#define VTAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).vtqh_first }

#define VTAILQ_ENTRY(type)
\

struct { \
struct type *vtqe_next; /* next element */ \
struct type **vtqe_prev; /* address of previous next element */\

}

/*

* Tail queue functions.
*/
#define VTAILQ_CONCAT(head1, head2, field) do { \
if (!VTAILQ_EMPTY(head2)) { \
*(head1)->vtqh_last = (head2)->vtqh_first; \
(head2)->vtqh_first->field.vtqe_prev = (head1)->vtqh_last;\



(head1)->vtqh_last = (head2)->vtqh_last; \
VTAILQ_INIT((head2)); \
}\
} while (0)

#define VTAILQ_EMPTY(head) ((head)->vtqh_first == NULL)

#define VTAILQ_FIRST(head) ((head)->vtqh_first)

#define VTAILQ_FOREACH(var, head, field) \

for ((var) = VTAILQ_FIRST((head)); \
(var); \
(var) = VTAILQ_NEXT((var), field))


#define VTAILQ_FOREACH_SAFE(var, head, field, tvar) \

for ((var) = VTAILQ_FIRST((head)); \
(var) && ((tvar) = VTAILQ_NEXT((var), field), 1); \
(var) = (tvar))


#define VTAILQ_FOREACH_REVERSE(var, head, headname, field) \

for ((var) = VTAILQ_LAST((head), headname); \
(var); \
(var) = VTAILQ_PREV((var), headname, field))


#define VTAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \

for ((var) = VTAILQ_LAST((head), headname); \
(var) && ((tvar) = VTAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))


#define VTAILQ_INIT(head) do { \
VTAILQ_FIRST((head)) = NULL; \
(head)->vtqh_last = &VTAILQ_FIRST((head)); \

} while (0)

#define VTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if ((VTAILQ_NEXT((elm), field) = VTAILQ_NEXT((listelm), field)) != NULL)\
VTAILQ_NEXT((elm), field)->field.vtqe_prev = \


 &VTAILQ_NEXT((elm), field); \
else { \


(head)->vtqh_last = &VTAILQ_NEXT((elm), field); \
}\
VTAILQ_NEXT((listelm), field) = (elm); \
(elm)->field.vtqe_prev = &VTAILQ_NEXT((listelm), field);\

} while (0)

#define VTAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.vtqe_prev = (listelm)->field.vtqe_prev; \
VTAILQ_NEXT((elm), field) = (listelm); \
*(listelm)->field.vtqe_prev = (elm); \
(listelm)->field.vtqe_prev = &VTAILQ_NEXT((elm), field);\

} while (0)

#define VTAILQ_INSERT_HEAD(head, elm, field) do { \
if ((VTAILQ_NEXT((elm), field) = VTAILQ_FIRST((head))) != NULL) \
VTAILQ_FIRST((head))->field.vtqe_prev = \
&VTAILQ_NEXT((elm), field); \
else \
(head)->vtqh_last = &VTAILQ_NEXT((elm), field); \
VTAILQ_FIRST((head)) = (elm); \
(elm)->field.vtqe_prev = &VTAILQ_FIRST((head)); \

} while (0)

#define VTAILQ_INSERT_TAIL(head, elm, field) do { \
VTAILQ_NEXT((elm), field) = NULL; \
(elm)->field.vtqe_prev = (head)->vtqh_last; \
*(head)->vtqh_last = (elm); \
(head)->vtqh_last = &VTAILQ_NEXT((elm), field); \

} while (0)

#define VTAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->vtqh_last))->vtqh_last))

#define VTAILQ_NEXT(elm, field) ((elm)->field.vtqe_next)


#define VTAILQ_PREV(elm, headname, field) \

(*(((struct headname *)((elm)->field.vtqe_prev))->vtqh_last))

#define VTAILQ_REMOVE(head, elm, field) do { \

if ((VTAILQ_NEXT((elm), field)) != NULL) \

VTAILQ_NEXT((elm), field)->field.vtqe_prev = \

 (elm)->field.vtqe_prev; \

else { \

(head)->vtqh_last = (elm)->field.vtqe_prev; \

}\

*(elm)->field.vtqe_prev = VTAILQ_NEXT((elm), field); \

} while (0)

2、Varnish源代码中一些特定的缩写字母含义:


BH:Binary Heap
CLI:Command-Line Interface,part of the published Varnish-API,see "cli.h"
CNT:CeNTer
EVB:Event Variable Base
EXP:EXPire
HCL:Hash CLassic
HTC:HTtp Connection
MCF:Main ConFigure
mgt:managment
PAN:PANic
PFD:PoolFD, Poll File Description
SES:SESsion
SMF:Storage Mmaped File
SMS:Storage Mutex Synth
STV:STeVedore
TMO:TiMe Out
VBE:Varnish BackEnd
VBM:Varnish Bit Map
VBP:Varnish Backend Polling
VCA:Varnish Cache Acceptor
VCC:Varnish Configure Compile?
VCL:Varnish Configure Language
VCT:Varnish Character Type
VDI:Varnish DIrector
VEV:Varnish Event Variable
VLU:Varnish Line Up
VPF:Varnish PID File
VSB:Varnish Storage Buffer
VSS:Varnish addreSS?
VSL:Varnish Share-memory Log
VTAILQ:Varnish Tail Queue,
WQ:Work Queue
WRK:WoRKer


WSL:Worker Share-emory Log




鲜花

握手

雷人

路过

鸡蛋

相关阅读


网络文摘 ( 京ICP备11047313号 )

GMT+8, 2013-7-11 07:01 , Processed in 0.160196 second(s), 24 queries .

Copyright © 2011 www.a8z8.com 网络文摘

回顶部