State Threads是一个广受关注的高性能网络线程库,winlin在SRS中做了比较充分的应用,我很遗憾直到现在才精心研究它。下面是我的研究实录,以作备忘。

一、源码编译

下面是在Ubuntu 14.04 64bit上面的实操记录:
从官网 http://sourceforge.net/projects/state-threads/下载源码包,最新版是1.9,如果不能下载,就从github上下载fork版本
https://github.com/toffaletti/state-threads

下载完st-1.9.tar.gz,然后解压
tar zxvf st-1.9.tar.gz
cd st-1.9
make linux-debug
然后会得到obj目录。里面有生成的中间文件*.o,头文件st.h,libst.so,libst.a和example中的几个例子:lookupdns,proxy,server等
下面我们在example中创建一个huge_threads.c并编译。这个例子来自winlin文章中提供的.
[cpp] view plain copy
  1. //gcc-I../obj-ghuge_threads.c../obj/libst.a-ohuge_threads
  2. //./huge_threads10000
  3. //./huge_threads30000
  4. //
  5. #include<stdio.h>
  6. #include"st.h"
  7. #definesLEEP_INTERVAL30//inms
  8. void*do_calc(void*arg){
  9. longpidx=*(long*)arg;
  10. for(;;){
  11. printf("sthread[#%ld]usleep\n",pidx);
  12. st_usleep(SLEEP_INTERVAL*1000);
  13. }
  14. returnNULL;
  15. intmain(intargc,char**argv){
  16. if(argc<=1){
  17. printf("Testtheconcurrenceofstate-threads!\n"
  18. "Usage:%s<sthread_count>\n"
  19. "eg.%s10000\n",argv[0],argv[0]);
  20. return-1;
  21. if(st_init()<0){
  22. printf("statethreadslibruntimeiniterror!");
  23. return-1;
  24. }
  25. inti,count=atoi(argv[1]);
  26. for(i=1;i<=count;i++){
  27. if(st_thread_create(do_calc,(void*)&i,0)==NULL){
  28. printf("createstatethread%dFailed\n",i);
  29. st_thread_exit(NULL);
  30. return0;
  31. }
编译
gcc-I../obj-ghuge_threads.c../obj/libst.a-ohuge_threads
运行
./huge_threads 10000
运行过程中,利用top查看程序性能,按ctrl+C中断

注意st.h是动态生成的,这种方法值得学习

二、doc目录研究
在st-1.9源码中doc目录有几个文档,可以参考
st.html ST库概论,winlin翻译的那篇文章
timeout_heap.txt 超时heap实现
notes.html 给出了编程注意点,包括移植,信号,进程内同步,进程间同步,非网络IO,超时处理,特别谈到进程内同步非常简单,不需要同步资源;非网络IO中谈到drawback和设计时需要避免的方法
reference.html 一个API接口文档介绍,需要认真阅读和熟悉,但是需要编码实战来加深理解

st_set_eventsys() 设置事件通知机制event notification mechanism,在st_init()之前调用,建议优先选用ST_EVENTSYS_ALT,它会使用epoll,然后才是ST_EVENT_DEFAULT
st_get_eventsys_name() 得到State thread库当前使用的时间通知机制名称,可能返回值是select,poll,kequeue,或epoll
st _set_switch_in_cb(st_switch_cb_t cb) 设置thread被resume时的回调函数,默认NULL
st_set_switch_out_cb(st_switch_cb_t cb) 设置thread被stop时的回调函数,默认NULL

st_randomize_stacks() 打开或关闭stack base address randomization,打开会提高性能,避免所有线程的stack是page aligned,而是随机生成的
st_key_create() 为进程内的所有线程创建一个非负整数的key,以便去set和get thread-specific data,所有线程的key是一样的但是存放的私有数据不同
st_thread_setspecific() 设置每个线程自己的私有数据,不同的线程可以对这个相同的key bindig不同的值

set_timecache_set() 将time caching打开或是关闭, ST库能够缓存time()库函数报告的值,如果是realtime time应用,建议不要开启该功能
set_time() 返回1970**以来的秒数

set_netfd_free() free file descriiptor但是不closing所在的os file descriptor
st_readv() 从指定文件描述符读数据到multiple buffers中

最重要的是末尾的Program Structure,给出了 一个网络应用程序中使用ST库的基本步骤
1.假如愿意,使用下面的pre-init函数配置ST库,设置时间,事件通知机制
2.调用st_init()来初始化ST库
3.假如愿意,调用post-init函数来配置ST库,设置timecache,随机化线程栈,进程resume和stop的回调函数
4.生成不同process之间共享的资源,创建并绑定socket,打开socket,生成共享内存段,IPC channel和同步原语primitives
5.创建多进程,fork(),父进程退出或是watchdog
6.在子进程中创建thread pool来处理user connection,线程池中的每个线程可以accept client connection,connect到其他服务器,执行各种network I/O等等
每个process的每个socket的空闲线程spare thread的最大个数默认为8, server一启动就创建线程池,它是最大空闲线程个数,线程池可以增长到最大线程个数,也可以限制总线程个数,而非针对每个listening socket
注意:只有ST库的I/O函数可以用于network I/O,其他的I/O calls都可能阻塞调用进程

三、example目录
下面研究example目录下面的3个例子
首先阅读里面的README,它简单介绍了这三个例子的基本情况和用法
server 包含server.c和error.c
lookupdns 包含lookupdns.c和res.c
proxy 包含proxy.c

1.server.c程序分析:
General server example,accept a client connection and just outputs a short html page
先定义进程数组,个数是 vp_count,是当前cpu的core数,这样能充分利用cpu多核能力,再定义listening socket数组 srv_socket[],个数是 sk_count,再定义其上每个srv_socket的wait for thread和busing thread个数。每个进程一个线程池在运转,都在独立地运行
在每个process中启动线程数时,先创建access log flushing thread,它的职责是定期(配置为30s)写日志,然后是建立connection handlingthreads,它的职责是首先保存连接双方的ip和port,不管对方发的啥,闷头就向对方发送一个"It worked!"的html页面, 然后关闭该tcp连接,参见线程函数handle_connections,它调用handle_session()
在创建socket并绑定侦听时,ST的socket fd是使用st_netfd_open_socket(sock)得到的,就是在该sock上面得到的。
在C/S交互过程中,使用到线程私有数据方法来保存listening socket index和client的sockaddr

整个程序的架构值得我们借鉴。


程序使用方法
./server -l ./ 指定访问日志目录,必须指定,下面都是可选参数
-b 127.0.0.1:8000 binding and listening sockets对数,否则使用0.0.0.0:8000代替
-p 8 生成进程个数,默认按照cpu内核个数定
-t 2:10 每个listening socket上的thread limits
-u taoyx 指定运行该程序的user名,据此找出其group名和user名
-q 1000 待处理连接队列的长度
-a 启用访问日志记录
-i 以互动模式运行吗?否则以daemon守护进程运行。互动模式不记录日志信息,建议命令行调试时使用
-S Serialize all accept() calls,依据平台来确定
-h print usage info

为了简单,我直接使用默认命令打开
./server -l ./

下面使用curl作为客户端来测试
curl -vx 127.0.0.1:8000 -o a.html "http://news.sohu.com"

服务器返回的固定page内容如下


通过发送信号来研究进程的行为
使用kill -l来列出所有的信号编码表,我们这里只需要使用到SIGHUP(1),SIGUSR1(10),SIGTERM(15)

kill -10 4044 dump info打印当前各进程的侦听端口,线程池线程情况,请求信息

kill -1 4044 重启进程,重读配置文件
kill -15 4044 终止进程,让程序退出
这两种方式在命令行没有结果显示,只能从日志文件error中看出响应细节


2.res.c分析
_res从哪儿来的?
dns_getaddr() ==>query_domain()==> res_mkquery() ==> dn_skipname(),dn_expand(),
||==>parse_answer()

lookupdns.c分析
异步host name解析,一个host name对应一个ST线程,所有的线程都是并发的,查询成功后打印出host name和对应的ip地址

使用方法
./lookupdnswww.baidu.comnews.sohu.comnew.sina.com.cn www.ifeng.comv.youku.com


源码分析
main() ==> do_resolve() ==>dns_getaddr() ==> 引用res.c中的函数

3.proxy.c分析
该程序充当一个普通的gateway,它在本地地址监听, 一接收到client connection,就连接到指定的远端地址,获取响应,不加修改地转发给client

使用方法
-l 127.0.0.1:8086 本地侦听的ip和port
-r 1.2.3.4: 80 远程连接的ip和port,如果不是点分十进制
-p 8 并行进程个数,如果不指定,默认使用cpu的内核数
-S serialize accept
-a 使用alternate备用的事件通知机制,比如epoll,它会调用ST_EVENTSYS_ALT设置st_set_eventsys(ST_EVENTSYS_ALT)
-t mask 测试或调试模式,掩码为1,4,8,16,这里选择1,打印所有信息
-X 只用一个进程,不使用守护进程方式

使用下面的命令启动
./proxy -l 127.0.0.1:8086 -r news.sohu.com:80 -t 1

我们看到会分别起4个进程独立工作,这样停止这些线程比较麻烦,只能手动kill掉各个进程
kil -9 13458
kil -9 13459
kil -9 13460
kil -9 13461
现在使用curl做客户端测试
curl -vx 127.0.0.1:8086 -o sohu.html "http://news.sohu.com"

得到的网页可以在chrome中正常打开

可见代理功能是完备的。另外我们可以设置mask的不同值来研究st_write_resid等接口函数的用法。

深入调研工作
1.dns查找是如何实现的?



参考链接

[1].http://www.jb51.cc/article/p-fqkxzqpu-so.html winlin的译文,不错,原文在源码doc目录中

[2].http://www.obroot.com/state-threads-for-internet-applications/?utm_source=tuicool 另一篇中文翻译,不错

[3].https://github.com/winlinvip/state-threads winlin移植精简版本

[4].http://sourceforge.net/projects/state-threads/ state-threads官网源码

[5].http://coolshell.cn/articles/12012.html 不可多得的心得荟萃

在Ubuntu 14.04 64bit上编译并研究State Threads网络线程库源码的更多相关文章

  1. HTML实现代码雨源码及效果示例

    这篇文章主要介绍了HTML实现代码雨源码及效果示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. iOS:核心图像和多线程应用程序

    我试图以最有效的方式运行一些核心图像过滤器.试图避免内存警告和崩溃,这是我在渲染大图像时得到的.我正在看Apple的核心图像编程指南.关于多线程,它说:“每个线程必须创建自己的CIFilter对象.否则,你的应用程序可能会出现意外行为.”这是什么意思?我实际上是试图在后台线程上运行我的过滤器,所以我可以在主线程上运行HUD(见下文).这在coreImage的上下文中是否有意义?

  3. ios – 多个NSPersistentStoreCoordinator实例可以连接到同一个底层SQLite持久性存储吗?

    我读过的关于在多个线程上使用CoreData的所有内容都讨论了使用共享单个NSPersistentStoreCoordinator的多个NSManagedobjectContext实例.这是理解的,我已经使它在一个应用程序中工作,该应用程序在主线程上使用CoreData来支持UI,并且具有可能需要一段时间才能运行的后台获取操作.问题是NSPersistentStoreCoordinator会对基础

  4. ios – XCode断点应该只挂起当前线程

    我需要调试多线程错误.因此,为了获得生成崩溃的条件,我需要在代码中的特定点停止一个线程,并等待另一个线程到达第二个断点.我现在遇到的问题是,如果一个线程遇到断点,则所有其他线程都被挂起.有没有办法只停止一个线程,让其他线程运行,直到它们到达第二个断点?)其他更有趣的选择:当你点击第一个断点时,你可以进入控制台并写入这应该在该断点处暂停当前上下文中的线程一小时.然后在Xcode中恢复执行.

  5. ios – 在后台线程中写入Realm后,主线程看不到更新的数据

    >清除数据库.>进行API调用以获取新数据.>将从API检索到的数据写入后台线程中的数据库中.>从主线程上的数据库中读取数据并渲染UI.在步骤4中,数据应该是最新数据,但我们没有看到任何数据.解决方法具有runloops的线程上的Realm实例,例如主线程,updatetothelatestversionofthedataintheRealmfile,因为通知被发布到其线程的runloop.在后台

  6. ios – NSURLConnectionLoader线程中的奇怪崩溃

    我们开始看到我们的应用启动时发生的崩溃.我无法重现它,它只发生在少数用户身上.例外情况是:异常类型:EXC_BAD_ACCESS代码:KERN_INVALID_ADDRESS位于0x3250974659崩溃发生在名为com.apple.NSURLConnectionLoader的线程中在调用时–[NSBlockOperationmain]这是该线程的堆栈跟踪:非常感谢任何帮助,以了解可能导致这种崩

  7. ios – 合并子上下文时的NSObjectInaccessbileExceptions

    我尝试手动重现,但失败了.是否有其他可能发生这种情况的情况,是否有处理此类问题的提示?解决方法在创建子上下文时,您可以尝试使用以下行:

  8. ios – 从后台线程调用UIKit时发出警告

    你如何处理项目中的这个问题?

  9. ios – 在SpriteKit中,touchesBegan在与SKScene更新方法相同的线程中运行吗?

    在这里的Apple文档AdvancedSceneProcessing中,它描述了更新方法以及场景的呈现方式,但没有提到何时处理输入.目前尚不清楚它是否与渲染循环位于同一个线程中,或者它是否与它并发.如果我有一个对象,我从SKScene更新方法和touchesBegan方法(在这种情况下是SKSpriteNode)更新,我是否要担心同步对我的对象的两次访问?解决方法所以几天后没有回答我设置了一些实验

  10. ios – 在后台获取中加载UIWebView

    )那么,有一种方法可以在后台加载UIWebView吗?解决方法如果要从用户界面更新元素,则必须在应用程序的主队列(或线程)中访问它们.我建议您在后台继续获取所需的数据,但是当需要更新UIWebView时,请在主线程中进行.你可以这样做:或者您可以创建一个方法来更新UIWebView上的数据,并使用以下方法从后台线程调用它:这将确保您从正确的线程访问UIWebView.希望这可以帮助.

随机推荐

  1. crontab发送一个月份的电子邮件

    ubuntu14.04邮件服务器:Postfixroot收到来自crontab的十几封电子邮件.这些邮件包含PHP警告.>我已经解决了这些警告的原因.>我已修复每个cronjobs不发送电子邮件(输出发送到>/dev/null2>&1)>我删除了之前的所有电子邮件/var/mail/root/var/spool/mail/root但我仍然每小时收到十几封电子邮件.这些电子邮件来自cronjobs,

  2. 模拟两个ubuntu服务器计算机之间的慢速连接

    我想模拟以下场景:假设我有4台ubuntu服务器机器A,B,C和D.我想在机器A和机器C之间减少20%的网络带宽,在A和B之间减少10%.使用网络模拟/限制工具来做到这一点?

  3. ubuntu-12.04 – 如何在ubuntu 12.04中卸载从源安装的redis?

    我从源代码在Ubuntu12.04上安装了redis-server.但在某些时候它无法完全安装,最后一次makeinstallcmd失败.然后我刚刚通过apt包安装.现在我很困惑哪个安装正在运行哪个conf文件?实际上我想卸载/删除通过源安装的所有内容,只是想安装一个包.转到源代码树并尝试以下命令:如果这不起作用,您可以列出软件自行安装所需的步骤:

  4. ubuntu – “apt-get source”无法找到包但“apt-get install”和“apt-get cache”可以找到它

    我正在尝试下载软件包的源代码,但是当我运行时它无法找到.但是当我运行apt-cache搜索squid3时,它会找到它.它也适用于apt-getinstallsquid3.我使用的是Ubuntu11.04服务器,这是我的/etc/apt/sources.list我已经多次更新了.我尝试了很多不同的debs,并没有发现任何其他地方的错误.这里的问题是你的二进制包(deb)与你的源包(deb-src)不

  5. ubuntu – 有没有办法检测nginx何时完成正常关闭?

    &&touchrestarted),因为即使Nginx没有完成其关闭,touch命令也会立即执行.有没有好办法呢?这样的事情怎么样?因此,pgrep将查找任何Nginx进程,而while循环将让它坐在那里直到它们全部消失.你可以改变一些有用的东西,比如睡1;/etc/init.d/Nginx停止,以便它会休眠一秒钟,然后尝试使用init.d脚本停止Nginx.你也可以在某处放置一个计数器,这样你就可以在需要太长时间时发出轰击信号.

  6. ubuntu – 如何将所有外发电子邮件从postfix重定向到单个地址进行测试

    我正在为基于Web的应用程序设置测试服务器,该应用程序发送一些电子邮件通知.有时候测试是使用真实的客户数据进行的,因此我需要保证服务器在我们测试时无法向真实客户发送电子邮件.我想要的是配置postfix,以便它接收任何外发电子邮件并将其重定向到一个电子邮件地址,而不是传递到真正的目的地.我正在运行ubuntu服务器9.10.先感谢您设置本地用户以接收所有被困邮件:你需要在main.cf中添加:然后

  7. ubuntu – vagrant无法连接到虚拟框

    当我使用基本的Vagrantfile,只配置了两条线:我看到我的虚拟框打开,但是我的流氓日志多次显示此行直到超时:然后,超时后的一段时间,虚拟框框终于要求我登录,但是太久了!所以我用流氓/流氓记录.然后在我的物理机器上,如果我“流氓ssh”.没有事情发生,直到:怎么了?

  8. ubuntu – Nginx – 转发HTTP AUTH – 用户?

    我和Nginx和Jenkins有些麻烦.我尝试使用Nginx作为Jenkins实例的反向代理,使用HTTP基本身份验证.它到目前为止工作,但我不知道如何传递带有AUTH用户名的标头?}尝试将此指令添加到您的位置块

  9. Debian / Ubuntu – 删除后如何恢复/ var / cache / apt结构?

    我在ubuntu服务器上的空间不足,所以我做了这个命令以节省空间但是现在在尝试使用apt时,我会收到以下错误:等等显然我删除了一些目录结构.有没有办法做apt-getrebuild-var-tree或类似的?

  10. 检查ubuntu上安装的rubygems版本?

    如何查看我的ubuntu盒子上安装的rubygems版本?只是一个想法,列出已安装的软件包和grep为ruby或宝石或其他:)dpkg–get-selections

返回
顶部