| 论坛注册| 加入收藏 | 设为首页| RSS
Google
您当前的位置:首页 > Linux频道 > Linux开发区 > 软件开发

使用epoll进行高性能网络编程

时间:2007-04-19 12:23:24  来源:Linux联盟收集整理  作者:
翻译:韩红军。hanhj@vrlab.buaa.edu.cn ; hongjun_han@163.comLx2Linux联盟
原文出自:https://www.captech.com.cnLx2Linux联盟
英文原文:http://www.xmailserver.org/linux-patches/nio-improve.htmlLx2Linux联盟
由于水平有限,错误在所难免,希望各位指正。Lx2Linux联盟
07-01-2001 – 初稿 - Davide Libenzi <davidel@xmailserver.org>Lx2Linux联盟
10-30-2002 – epoll补丁成为Linux内核一部分。请参考这个版本的,因为这个版本将会成为标准,并得到广泛支持Davide Libenzi <davidel@xmailserver.org>Lx2Linux联盟
绪论:Lx2Linux联盟
眼下的工作是分析不同的方法,这些方法都是用来实现从内核模式高效传递网络事件到用户模式。我们考察了五种方法:作为一种相对较好的老的方法poll ,标准/dev/poll接口,标准RT信号,RT signals with one-sig-per-fd patch,和使用很特别的通知方法的新式/dev/epoll。工作有如下四部分组成:Lx2Linux联盟
1) 新式 /dev/epoll 内核补丁Lx2Linux联盟
2) Provos-Lever修改的能够在内核 2.4.6 工作的/dev/poll 补丁Lx2Linux联盟
3) HTTP serverLx2Linux联盟
4) 一个能够产生“dead“连接的deadconn(tm)工具。Lx2Linux联盟
Httperf被采用作为度量工具,尽管不完美,但也提供了足够的网络负载选项。Lx2Linux联盟
新式 /dev/epoll 内核补丁:Lx2Linux联盟
这个补丁很简单,它在struct file的数据结构里面添加了通知回调链表项。如下代码段:Lx2Linux联盟
代码:Lx2Linux联盟
******* include/linux/fs.hLx2Linux联盟
struct file {Lx2Linux联盟
...Lx2Linux联盟
/* 文件回调列表 */Lx2Linux联盟
rwlock_t f_cblock;Lx2Linux联盟
struct list_head f_cblist;Lx2Linux联盟
};Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
代码:Lx2Linux联盟
Lx2Linux联盟
****include/linux/fcblist.hLx2Linux联盟
/* 文件回调通知事件 */Lx2Linux联盟
#define ION_IN 1Lx2Linux联盟
#define ION_OUT 2Lx2Linux联盟
#define ION_HUP 3Lx2Linux联盟
#define ION_ERR 4Lx2Linux联盟
Lx2Linux联盟
#define FCB_LOCAL_SIZE 4Lx2Linux联盟
Lx2Linux联盟
#define fcblist_read_lock(fp, fl) read_lock_irqsave(&(fp)->f_cblock, fl)Lx2Linux联盟
#define fcblist_read_unlock(fp, fl) read_unlock_irqrestore(&(fp)->f_cblock, fl)Lx2Linux联盟
#define fcblist_write_lock(fp, fl) write_lock_irqsave(&(fp)->f_cblock, fl)Lx2Linux联盟
#define fcblist_write_unlock(fp,fl) write_unlock_irqrestore(&(fp)->f_cblock, fl)Lx2Linux联盟
Lx2Linux联盟
struct fcb_struct {Lx2Linux联盟
struct list_head lnk;Lx2Linux联盟
void (*cbproc)(struct file *, void *, unsigned long *, long *);Lx2Linux联盟
void *data;Lx2Linux联盟
unsigned long local[FCB_LOCAL_SIZE];Lx2Linux联盟
};Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
extern long ion_band_table[];Lx2Linux联盟
extern long poll_band_table[];Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
static inline void file_notify_init(struct file *filep)Lx2Linux联盟
{Lx2Linux联盟
rwlock_init(&filep->f_cblock);Lx2Linux联盟
INIT_LIST_HEAD(&filep->f_cblist);Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
void file_notify_event(struct file *filep, long *event);Lx2Linux联盟
Lx2Linux联盟
int file_notify_addcb(struct file *filep,Lx2Linux联盟
void (*cbproc)(struct file *, void *, unsigned long *, long *), void *data);Lx2Linux联盟
Lx2Linux联盟
int file_notify_delcb(struct file *filep,Lx2Linux联盟
void (*cbproc)(struct file *, void *, unsigned long *, long *));Lx2Linux联盟
Lx2Linux联盟
void file_notify_cleanup(struct file *filep);Lx2Linux联盟
Lx2Linux联盟
这些回调方法链就是提供一种机制,向文件系统注册其“兴趣”的上层在兴趣事件发生时能够收到底层I/O的通知。初始化和清理代码已经加在了fs/file_table.c里,回调方法链处理代码在fs/fcblist.c里面,如下所示:Lx2Linux联盟
代码:Lx2Linux联盟
****** fs/file_table.cLx2Linux联盟
struct file * get_empty_filp(void)Lx2Linux联盟
{Lx2Linux联盟
...Lx2Linux联盟
file_notify_init(f);Lx2Linux联盟
...Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
int init_private_file(struct file *filp, struct dentry *dentry, int mode)Lx2Linux联盟
{Lx2Linux联盟
...Lx2Linux联盟
file_notify_init(filp);Lx2Linux联盟
...Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
void fput(struct file * file)Lx2Linux联盟
{Lx2Linux联盟
...Lx2Linux联盟
file_notify_cleanup(file);Lx2Linux联盟
...Lx2Linux联盟
}Lx2Linux联盟
****** fs/fcblist.cLx2Linux联盟
void file_notify_event(struct file *filep, long *event)Lx2Linux联盟
{Lx2Linux联盟
unsigned long flags;Lx2Linux联盟
struct list_head *lnk;Lx2Linux联盟
Lx2Linux联盟
fcblist_read_lock(filep, flags);Lx2Linux联盟
list_for_each(lnk, &filep->f_cblist) {Lx2Linux联盟
struct fcb_struct *fcbp = list_entry(lnk, struct fcb_struct, lnk);Lx2Linux联盟
Lx2Linux联盟
fcbp->cbproc(filep, fcbp->data, fcbp->local, event);Lx2Linux联盟
}Lx2Linux联盟
fcblist_read_unlock(filep, flags);Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
int file_notify_addcb(struct file *filep,Lx2Linux联盟
void (*cbproc)(struct file *, void *, unsigned long *, long *), void *data)Lx2Linux联盟
{Lx2Linux联盟
unsigned long flags;Lx2Linux联盟
struct fcb_struct *fcbp;Lx2Linux联盟
Lx2Linux联盟
if (!(fcbp = (struct fcb_struct *) kmalloc(sizeof(struct fcb_struct), GFP_KERNEL)))Lx2Linux联盟
return -ENOMEM;Lx2Linux联盟
memset(fcbp, 0, sizeof(struct fcb_struct));Lx2Linux联盟
fcbp->cbproc = cbproc;Lx2Linux联盟
fcbp->data = data;Lx2Linux联盟
fcblist_write_lock(filep, flags);Lx2Linux联盟
list_add_tail(&fcbp->lnk, &filep->f_cblist);Lx2Linux联盟
fcblist_write_unlock(filep, flags);Lx2Linux联盟
return 0;Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
int file_notify_delcb(struct file *filep,Lx2Linux联盟
void (*cbproc)(struct file *, void *, unsigned long *, long *))Lx2Linux联盟
{Lx2Linux联盟
unsigned long flags;Lx2Linux联盟
struct list_head *lnk;Lx2Linux联盟
Lx2Linux联盟
fcblist_write_lock(filep, flags);Lx2Linux联盟
list_for_each(lnk, &filep->f_cblist) {Lx2Linux联盟
struct fcb_struct *fcbp = list_entry(lnk, struct fcb_struct, lnk);Lx2Linux联盟
Lx2Linux联盟
if (fcbp->cbproc == cbproc) {Lx2Linux联盟
list_del(lnk);Lx2Linux联盟
fcblist_write_unlock(filep, flags);Lx2Linux联盟
kfree(fcbp);Lx2Linux联盟
return 0;Lx2Linux联盟
}Lx2Linux联盟
}Lx2Linux联盟
fcblist_write_unlock(filep, flags);Lx2Linux联盟
return -ENOENT;Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
void file_notify_cleanup(struct file *filep)Lx2Linux联盟
{Lx2Linux联盟
unsigned long flags;Lx2Linux联盟
struct list_head *lnk;Lx2Linux联盟
Lx2Linux联盟
fcblist_write_lock(filep, flags);Lx2Linux联盟
while ((lnk = list_first(&filep->f_cblist))) {Lx2Linux联盟
struct fcb_struct *fcbp = list_entry(lnk, struct fcb_struct, lnk);Lx2Linux联盟
Lx2Linux联盟
list_del(lnk);Lx2Linux联盟
fcblist_write_unlock(filep, flags);Lx2Linux联盟
kfree(fcbp);Lx2Linux联盟
fcblist_write_lock(filep, flags);Lx2Linux联盟
}Lx2Linux联盟
fcblist_write_unlock(filep, flags);Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
这些回调函数会收到一个long *参数,其第一个元素是ION_*事件中的一种,下一个可以存储额外的参数,这些额外参数的意义随这第一个元素即事件种类的不同而不同。Lx2Linux联盟
这个接口只是一个草案,我使用它只是验证传输方法是不是足够高效。目前通知只在socket文件出现,如下:Lx2Linux联盟
代码:Lx2Linux联盟
****** include/net/sock.hLx2Linux联盟
static inline void sk_wake_async(struct sock *sk, int how, int band)Lx2Linux联盟
{Lx2Linux联盟
if (sk->socket) {Lx2Linux联盟
if (sk->socket->file) {Lx2Linux联盟
long event[] = { ion_band_table[band - POLL_IN], poll_band_table[band - POLL_IN], -1 };Lx2Linux联盟
Lx2Linux联盟
file_notify_event(sk->socket->file, event);Lx2Linux联盟
}Lx2Linux联盟
if (sk->socket->fasync_list)Lx2Linux联盟
sock_wake_async(sk->socket, how, band);Lx2Linux联盟
}Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
文件fs/pipe.c和include/linux/pipe_fs_i.h也都被修改以扩展/dev/epoll的功能至管道(pipes pipe() )Lx2Linux联盟
两个新的文件driver/char/eventpoll.c和include/linux/eventpoll.h实现了/dev/epoll。Lx2Linux联盟
新的/dev/epoll接口和以前的有很大的不同,因为他只是通过设备文件描述符映射,而基于效率考虑放弃了拷贝数据到用户空间(copy-data- to-user-space),通过共享内存页来避免不必要的拷贝数据/dev/epoll可以高效运作,理由有1)更少的CPU周期,因为不必拷贝数据,2)在现代的缓存的存储体系架构下,有更少的内存占用(memory footprint)。Lx2Linux联盟
/dev/epoll实现利用新的文件回调通知机制来注册回调方法,这些回调方法将事件存储事件缓冲区里,初始化顺序如下:Lx2Linux联盟
代码:Lx2Linux联盟
if ((kdpfd = open("/dev/epoll", O_RDWR)) == -1) {Lx2Linux联盟
Lx2Linux联盟
}Lx2Linux联盟
if (ioctl(kdpfd, EP_ALLOC, maxfds))Lx2Linux联盟
{Lx2Linux联盟
Lx2Linux联盟
}Lx2Linux联盟
if ((map = (char *) mmap(NULL, EP_MAP_SIZE(maxfds), PROT_READ,Lx2Linux联盟
MAP_PRIVATE, kdpfd, 0)) == (char *) -1)Lx2Linux联盟
{Lx2Linux联盟
Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
maxfds 能够存储在轮询设备里面的最多的文件描述符数目,文件通过下面代码注册进兴趣集里面:Lx2Linux联盟
代码:Lx2Linux联盟
struct pollfd pfd;Lx2Linux联盟
pfd.fd = fd; Lx2Linux联盟
pfd.events = POLLIN | POLLOUT | POLLERR | POLLHUP;Lx2Linux联盟
pfd.revents = 0;Lx2Linux联盟
if (write(kdpfd, &pfd, sizeof(pfd)) != sizeof(pfd)) {Lx2Linux联盟
...Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
下面代码删除指定的文件描述符:Lx2Linux联盟
代码:Lx2Linux联盟
struct pollfd pfd;Lx2Linux联盟
pfd.fd = fd;Lx2Linux联盟
pfd.events = POLLREMOVE;Lx2Linux联盟
pfd.revents = 0;Lx2Linux联盟
if (write(kdpfd, &pfd, sizeof(pfd)) != sizeof(pfd)) {Lx2Linux联盟
...Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
核心分派(dispatch)代码类似下面:Lx2Linux联盟
代码:Lx2Linux联盟
struct pollfd *pfds;Lx2Linux联盟
struct evpoll evp;Lx2Linux联盟
Lx2Linux联盟
for (;;) {Lx2Linux联盟
evp.ep_timeout = STD_SCHED_TIMEOUT;Lx2Linux联盟
evp.ep_resoff = 0;Lx2Linux联盟
Lx2Linux联盟
nfds = ioctl(kdpfd, EP_POLL, &evp);Lx2Linux联盟
pfds = (struct pollfd *) (map + evp.ep_resoff);Lx2Linux联盟
for (ii = 0; ii < nfds; ii++, pfds++) {Lx2Linux联盟
...Lx2Linux联盟
}Lx2Linux联盟
}Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
驱动程序分配两个页面集来作为双缓冲机制,存储文件事件。可以从ep_resoff字段得知结果集在map里面的哪一块。在使用其中一个页面集的同时,内核可以使用另外一个存储进来的事件们。没有拷贝到用户空间的麻烦,来自同一个文件的事件将被放在一个槽(slot)里面,EP_POLL函数决不会线性查找兴趣集来执行file->f_ops->poll()。为了使用/dev/epoll接口,你必须使用mknod创建该文件,并且major =10 and minor=124。命令如下:Lx2Linux联盟
# mknod /dev/epoll c 10 124Lx2Linux联盟
下载补丁在下面的链接Lx2Linux联盟
epoll-lt-2.4.24-0.20.diffLx2Linux联盟
Lx2Linux联盟
Provos-Lever的/dev/poll补丁:Lx2Linux联盟
这个值得说的不多,修改virt_to_page()这个漏洞就可以使补丁正常工作,我修改了另外一个bug,程序为了调节哈希表大小,而试图使用 kmalloc()分配大块内存却不能满足,现在使用vmalloc()来为哈希表分配内存。我修改了在CITI网站上发现的2.4.3的补丁,这个补丁应该是Provos-Lever用来使(2.2.x)升级到2.4.x的那个。你可以在如下地址下载到:Lx2Linux联盟
http://www.xmailserver.org/linux-patches/olddp_last.diff.gzLx2Linux联盟
Lx2Linux联盟
实时信号一个文件描述符一个信号(one-sig-per-fd)补丁:Lx2Linux联盟
Vitaly Luban贡献的这个补丁实现了实时信号量,当实时信号量队列满了的时候,尽量避免SIGIO传输信息,你可以下载这个补丁如下网址:Lx2Linux联盟
http://www.luban.org/GPL/gpl.htmlLx2Linux联盟
Lx2Linux联盟
HTTP服务器:Lx2Linux联盟
http服务器很简单,它使用的是事件轮询和同步程序库(coroutines),服务器内部的coroutine库的实现来自如下网址:Lx2Linux联盟
http://www.goron.de/~froese/coro/Lx2Linux联盟
它很小,简单且高效。服务器使用的默认堆栈大小为8192,当试图有许多连接时,将造成内存浪费和虚存颠簸(vm trashing),其实4096的堆栈大小就足够了。另外一个问题是coro库使用mmap()方法来为堆栈分配空间,这样当accept() /close()比率较高时会损失性能,我改了这个库(只有一个文件coro.c)让其使用malloc()/free()而不是mmap() /munmap().每次的Http应答都一样,应答的报文大小可以通过命令行参数设置,另外还有两个命令行参数可以设置监听端口和fd集的大小。你可以在如下地址下载到服务器:Lx2Linux联盟
ephttpd-0.2.tar.gzLx2Linux联盟
老版本:Lx2Linux联盟
http://www.xmailserver.org/linux-patches/dphttpd_last.tar.gzLx2Linux联盟
Lx2Linux联盟
死连接工具:Lx2Linux联盟
如果说上面的服务器简单,这个就更简单了,它的目的就是给服务器制造死连接来模拟真实的负栽,这些连接都是一批的低速连接。你可以下载到:Lx2Linux联盟
http://www.xmailserver.org/linux-patches/deadconn_last.cLx2Linux联盟
Lx2Linux联盟
测试:Lx2Linux联盟
测试环境:PIII 600MHz, 128 Mb RAM, eepro100网卡连接到100Mbps快速以太网交换机上。RH 6.2,使用的内核是2.4.6, coroutine 库的版本是1.1.0-pre2Lx2Linux联盟
我使用双PIII 1GHz, 256 Mb RAM 和双eepro100作为httperf机器,双PIII 900 MHz, 256 Mb RAM 和双 eepro100作为deadconn(tm)机器,因为httperf当使用较高的num-conns时占用fds空间(改为8000了)很快消失,我采用了下面的命令行:Lx2Linux联盟
--think-timeout 5 --timeout 5 --num-calls 2500 --num-conns 100 --hog --rate 100Lx2Linux联盟
这个将分配100个连接,使服务器负栽不同的死连接。我改变的另一个参数是应答报文大小有128,512和1024。另外一个更加忠实于互联网会话就是使用一些突发连接发出HTTP请求,然后关闭,测试使用httperf,并有如下命令:Lx2Linux联盟
--think-timeout 5 --timeout 5 --num-calls 2 --num-conns 27000 --hog --rate 5000Lx2Linux联盟
每个数字都是求得三次的平均值,如下地址下载httperf:Lx2Linux联盟
http://www.hpl.hp.com/personal/David_Mosberger/httperf.htmlLx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
测试显示/dev/epoll约比RT signals one-sig实现快10-12%,/dev/epoll和RT signals都保持平坦在不同的死连接负栽下。Lx2Linux联盟
RT-one-sig实现比简单 实时信号实现要快一点, 但是本测试只有很少的 SIGIO.Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
512和1024 Content-Length 的应答报文测试表明 /dev/epoll, RT signals和RT one-sig 表现基本一致,图中重叠在一块了, 这是因为在本测试中以太网达到了饱和态( 100Mbps )。Lx2Linux联盟
Lx2Linux联盟
Lx2Linux联盟
这个测试表明/dev/epoll, RT signals 和RT one-sig实现在不同死连接情况下的每秒的Http应答报文一直平坦,并且/dev/epoll大约15% 快于RT one-sig ,而RT one-sig 大约10-15% 快于简单 RT signals.Lx2Linux联盟
Lx2Linux联盟
系统调用接口( aka sys_epoll ):Lx2Linux联盟
事件查询设备对于系统调用接口的需要促成了sys_epoll系统调用的实现,这个简单接口为开发人员实现了同样的可扩展性。新的系统调用引入了三个新的映射到用户空间的调用:Lx2Linux联盟
int epoll_create(int maxfds);Lx2Linux联盟
int epoll_ctl(int epfd, int op, int fd, unsigned int events);Lx2Linux联盟
int epoll_wait(int epfd, struct pollfd *events, int maxevents, int timeout);Lx2Linux联盟
这些函数在他们的手册页面里面描述:Lx2Linux联盟
epoll : PSTXTMANLx2Linux联盟
epoll_create : PS TXTMANLx2Linux联盟
epoll_ctl : PSTXT MANLx2Linux联盟
epoll_wait : PSTXT MANLx2Linux联盟
实现这些系统调用的补丁在这儿. 一个新的访问epoll ( 2.5.45 )的库在这个可得到: epoll-lib-0.11.tar.gzLx2Linux联盟
Lx2Linux联盟
一个简单基于管道的epoll性能测试:Lx2Linux联盟
pipetestLx2Linux联盟
Lx2Linux联盟
用户空间支持epoll的库 :Lx2Linux联盟
libeventLx2Linux联盟
ivykisLx2Linux联盟
在测试epoll过程中,做的一个 thttpd 的补丁:Lx2Linux联盟
thttpd.epoll.diffLx2Linux联盟
结论:Lx2Linux联盟
这些数据显示/dev/epoll ( and sys_epoll )提高了服务器的效率(从应答报文速率和CPU利用率角度看)。/dev/epoll的应答速率完全独立于死连接的数目,而标准poll()和旧的 /dev/poll似乎不可避免的受到影响。和标准poll()以及旧的/dev/poll相比/dev/epoll的方差也明显小的多,这不尽使我觉得 (1) 将有跟多的处理能力(2)在高负栽下有可以预见的应答速率。RT signals和RT one-sig 是现在死连接变化时也基本保持不变,并且one-sig大约比简单RT signals快10-12% 。RT singnals实现 (即使 one-sig 较少)似乎不能适用于大量突发请求模拟真实因特网的情况,在这种情况下有大量连接处于active状态。这是因为RT signals队列的限制,即使打了one-sig的补丁, 也很容易填满队列。Lx2Linux联盟
链接:Lx2Linux联盟
[1] The epoll scalability page at lse.Lx2Linux联盟
[2] David Weekly - /dev/epoll PageLx2Linux联盟
Lx2Linux联盟
References:Lx2Linux联盟
[1] W. Richard Stevens - "UNIX Network Programming, Volume I: Networking APIs: Sockets and XTI, 2nd edition"Lx2Linux联盟
Prentice Hall, 1998.Lx2Linux联盟
[2] W. Richard Stevens - "TCP/IP Illustrated, Volume 1: The Protocols"Lx2Linux联盟
Addison Wesley professional computing series, 1994.Lx2Linux联盟
[3] G. Banga and J. C. Mogul - "Scalable Kernel Performance for Internet Servers Under Realistic Load"Lx2Linux联盟
Proceedings of the USENIX Annual Technical Conference, June 1998.Lx2Linux联盟
[4] G. Banga. P. Druschel. J. C. Mogul - "Better Operating System Features for Faster Network Servers"Lx2Linux联盟
SIGMETRICS Workshop on Internet Server Performance, June 1998.Lx2Linux联盟
[5] G. Banga and P. Druschel - "Measuring the Capacity of a Web Server"Lx2Linux联盟
Proceedings of the USENIX Symposium on Internet Technologies and Systems, December 1997.Lx2Linux联盟
[6] Niels Provos and Charles Lever - "Scalable Network I/O in Linux"Lx2Linux联盟
http://www.citi.umich.edu/techreports/reports/citi-tr-00-4.pdfLx2Linux联盟
[7] Dan Kegel - "The C10K problem"Lx2Linux联盟
http://www.kegel.com/c10k.htmlLx2Linux联盟
[8] Richard Gooch - "IO Event Handling Under Linux"Lx2Linux联盟
http://www.atnf.csiro.au/~rgooch/linux/docs/io-events.htmlLx2Linux联盟
[9] Abhishek Chandra and David Mosberger - "Scalability of Linux Event-Dispatch Mechanisms"Lx2Linux联盟
http://www.hpl.hp.com/techreports/2000/HPL-2000-174.htmlLx2Linux联盟
[10] Niels Provos and Charles Lever - "Analyzing the Overload Behaviour of a Simple Web Server"Lx2Linux联盟
http://www.citi.umich.edu/techreports/reports/citi-tr-00-7.ps.gzLx2Linux联盟
[11] D. Mosberger and T. Jin - "httperf -- A Tool for Measuring Web Server Performance"Lx2Linux联盟
SIGMETRICS Workshop on Internet Server Performance, June 1998.
来顶一下
近回首页
返回首页
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
相关文章
栏目更新
栏目热门