linux C项目webbench,tinyhttpd,thread_pool详解

分析一个开源项目,首先是明白是做什么的,会使用,然后才是源码的阅读,以及分析优化。

webbench的标准测试可以向我们展示服务器的两项内容:每秒钟相应请求数每秒钟传输数据量。webbench不但能具有便准静态页面的测试能力,还能对动态页面(ASP,PHP,JAVA,CGI)进 行测试的能力。还有就是他支持对含有SSL的安全网站例如电子商务网站进行静态或动态的性能测试。
Webbench最多可以模拟3万个并发连接去测试网站的负载能力。

Webbench通过在终端启动程序开始测试

webbench -c 10 -t 10 http://test.domain.com/phpinfo.php
webbench -c 并发数 -t 运行测试时间 URL

Webbench的主要逻辑是主进程创建子进程,子进程去连接服务器,通过管道将数据传给主进程,主进程统计输出到终端。

由于Webbench涉及到的功能较多,所以通过linux下的getopt_long 命令行解析的库函数,可通过 man 3 getopt_long 查看。

测试的时候,主进程先测试一下地址是否合法,然后创建管道以及子进程。

子进程连接服务器并将数据通过管道写入,父进程通过管道读数据。

子进程连接客户端的逻辑是设定一个定时器,比如1秒中,在1秒中内不断的去连接服务器,读完数据就断开。

 

tinyhttpd是一个用C语言写的简易Web服务器

拿到源码后,在linux下执行make命令,然后./httpd启动

工作流程

     (1) 服务器启动,在指定端口或随机选取端口绑定 httpd 服务。

     (2)收到一个 HTTP 请求时(其实就是 listen 的端口 accpet 的时候),派生一个线程运行 accept_request 函数。

     (3)取出 HTTP 请求中的 method (GET 或 POST) 和 url,。对于 GET 方法,如果有携带参数,则 query_string 指针指向 url 中 ? 后面的 GET 参数。

     (4) 格式化 url 到 path 数组,表示浏览器请求的服务器文件路径,在 tinyhttpd 中服务器文件是在 htdocs 文件夹下。当 url 以 / 结尾,或 url 是个目录,则默认在 path 中加上 index.html,表示访问主页。

     (5)如果文件路径合法,对于无参数的 GET 请求,直接输出服务器文件到浏览器,即用 HTTP 格式写到套接字上,跳到(10)。其他情况(带参数 GET,POST 方式,url 为可执行文件),则调用 excute_cgi 函数执行 cgi 脚本。

    (6)读取整个 HTTP 请求并丢弃,如果是 POST 则找出 Content-Length. 把 HTTP 200  状态码写到套接字。

    (7) 建立两个管道,cgi_input 和 cgi_output, 并 fork 一个进程。

    (8) 在子进程中,把 STDOUT 重定向到 cgi_outputt 的写入端,把 STDIN 重定向到 cgi_input 的读取端,关闭 cgi_input 的写入端 和 cgi_output 的读取端,设置 request_method 的环境变量,GET 的话设置 query_string 的环境变量,POST 的话设置 content_length 的环境变量,这些环境变量都是为了给 cgi 脚本调用,接着用 execl 运行 cgi 程序。

这样调用的cgi程序就能从cgi_input的读端,读到数据,并将数据写到cgi_outputt的写入端。

    (9) 在父进程中,关闭 cgi_input 的读取端 和 cgi_output 的写入端,如果 POST 的话,把 POST 数据写入 cgi_input,已被重定向到 STDIN,读取 cgi_output 的管道输出到客户端,该管道输入是 STDOUT。接着关闭所有管道,等待子进程结束。这一部分比较乱,见下图说明:

图 1    管道初始状态

图 2  管道最终状态 

    (10) 关闭与浏览器的连接,完成了一次 HTTP 请求与回应,因为 HTTP 是无连接的。

 

线程池的思想是生成者消费者模型,包括一个任务队列和一个线程数组,也可以增加一个调度线程。用来开启和关闭线程。

任务队列和线程数组既可以用链表实现,也可以用数组实现。

通常包括三个API函数,创建初始化线程池,像任务队列中添加任务,销毁线程池。

线程的工作函数设为静态函数,不对外开放。

/**
 * 创建线程池,有 thread_count 个线程,容纳 queue_size 个的任务队列,flags 参数没有使用
 */
threadpool_t *threadpool_create(int thread_count, int queue_size, int flags);
/**

* 添加任务到线程池, pool 为线程池指针,routine 为函数指针, arg 为函数参数, flags 未使用

*/

int threadpool_add(threadpool_t *pool, void (*routine)(void *),

void *arg, int flags);
/**
 * 销毁线程池,flags 可以用来指定关闭的方式
 */
int threadpool_destroy(threadpool_t *pool, int flags);

其中任务的定义形式是回调函数加参数的形式。

typedef struct {
    void (*function)(void *);
    void *argument;
} threadpool_task_t;

结构体的定义中包括一个互斥锁,一个条件变量,条件变量的作用是当有任务时唤醒线程。互斥锁的作用是使得线程有序并发访问任务队列。在添加任务队列,线程操作之前,都要加锁处理。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值