在Ubuntu 14.04 64bit上编译并研究State Threads网络线程

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文章中提供的.
  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. }
  16. intmain(intargc,char**argv){
  17. if(argc<=1){
  18. printf("Testtheconcurrenceofstate-threads!\n"
  19. "Usage:%s<sthread_count>\n"
  20. "eg.%s10000\n",argv[0],argv[0]);
  21. return-1;
  22. }
  23. if(st_init()<0){
  24. printf("statethreadslibruntimeiniterror!");
  25. return-1;
  26. }
  27. inti,count=atoi(argv[1]);
  28. for(i=1;i<=count;i++){
  29. if(st_thread_create(do_calc,(void*)&i,0)==NULL){
  30. printf("createstatethread%dfailed\n",i);
  31. return-1;
  32. }
  33. }
  34. st_thread_exit(NULL);
  35. return0;
  36. }
编译
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 不可多得的心得荟萃


http://www.th7.cn/system/lin/201505/104951.shtml

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读2.3k次,点赞4次,收藏22次。最近安装了CARLA预编译版,总体过程感觉还是挺简单的,但是由于在网上找的参考资料并没有和自己遇到的情况完全一样的,所以把自己的安装过程记录下来,方便和自己一样的后来人进行安装,同时也自己作个记录留作备忘。其实预编译版的CARLA本身几乎不用什么命令行进行安装,主要步骤只有解压缩和拷贝文件,和极少的命令行安装操作。但是相关依赖以及其它的CARLA所使用的一些工具需要一起安装好,下面一起来看看安装步骤吧。_ubuntu22.04安装carla
文章浏览阅读6.3k次,点赞5次,收藏15次。在清华镜像中下载Ubuntu 20 安装包_ubuntu20下载
文章浏览阅读5k次。linux环境, python3.7.问题描述: 安装rally, pip3 install -r requirements.txt 时提示如下: 说明openssl 已安装。解决:1. pip3 install --upgrade pip , 结果输出如下 ..._error: command '/usr/bin/gcc' failed with exit code 1
文章浏览阅读1.1k次。自己刚踩坑装好的W11 WSL2 miniconda_wsl2 cuda
文章浏览阅读4.2k次,点赞4次,收藏17次。要安装这个 standalone的,会有一点区别(不识别 下面的 -localhost no)上面的命令会在~/.vnc/目录下生成一个 passwd文件。具体端口号,可以查看vncserver -list返回结果中的RFB PROT。作用是vncserver启动的时候运行这些命令,把桌面程序启动起来。如果希望用某个用户比如 demo登录,就用su切换到这个用户。桌面版默认就已安装,服务器版需要手动安装,并启动。关闭某个会话可以用下面的命令。远程地址输入ip:port。查看全部的vnc会话。_ubuntu vncserver
文章浏览阅读894次,点赞51次,收藏31次。在安卓使用vscode主要是连接一个远程的code-server服务,code-server服务安装在什么系统,调用的就是什么系统的环境,如这里使用ubuntu进行安装code-server,那使用的就是ubuntu环境,如需要go,python,java开发,需要在Ubuntu安装相关环境,创建的文件夹和项目也是在Ubuntu里面.这种方式其实在访问vscode的时候利于可以随时随地进行连接使用,且不受设备影响。
安装Ubuntu上超好用的终端Terminator。通过添加软件源、更新源、安装Terminator等步骤完成。
文章浏览阅读1.1k次,点赞83次,收藏74次。不要嫌弃Ubuntu的单调的终端界面啦,快来试试这些有意思的命令_ubuntu系统有趣的玩法
文章浏览阅读2.5k次。在ubuntu系统中,swap空间就是虚拟内存,所以考虑在磁盘空间富余的目录下设置虚拟内存swap文件,用来缓解内存不足的问题。通过上面的信息可以看到,/dev/vda2 目录下还剩余45G,那么就可以把swap文件生成在/dev/vda2目录下。在系统监控中发现,当数据库服务程序启动后,占用了大量内存空间,导致系统的剩余的内存往往只有几十MB。# bs 为块的大小,count 创建多少个块。# 执行命令后,删除创建的swap目录即可。#把生成的文件转换成 Swap 文件。2、创建一个swap文件。_ubuntu20修改swap大小
文章浏览阅读2.9k次,点赞2次,收藏10次。记录RV1126的SDK编译错误,以及解决处理_command exited with non-zero status 1
文章浏览阅读1.1w次,点赞22次,收藏101次。【记录】ubuntu20.04安装nvidia显卡驱动_ubuntu20.04安装nvidia显卡驱动
文章浏览阅读727次,点赞6次,收藏27次。在嵌入式Linux开发过程中,可能遇到uboot无法通过nfs服务从Ubuntu下载系统镜像(TTTTTT)的问题。如果你使用的是较新版本的Ubuntu,那么其**默认内核将不支持nfs2**!而**uboot仅支持到nfs2**,因此我们需要修改系统内核以及nfs配置文件,开启nfs2服务。**此问题非常棘手**,因为问题出现的时间太近,并且使用的人少。由于是2023年后才出现的问题,**chatgpt也无法解答**!本文参考网络上多篇博客资料,是解决此问题的最新办法。
ubuntu系统下安装软件的方法有多种,包括使用apt工具、deb软件包安装、源码编译安装以及通过软件中心安装。还有一种以 .run 后缀的软件包也可以在ubuntu系统下安装。具体的安装方法可以通过百度搜索来获取。
文章浏览阅读814次。本篇目的:Xubuntu如何安装pkg-configpkg-config是一个计算机软件包,用于帮助开发人员查找、定位和使用依赖库。它通常用于构建软件时,开发人员需要指定程序所依赖的外部库的位置和版本信息。使用pkg-config,开发人员可以很容易地查找、检索和导出这些依赖库的信息,从而简化了软件的构建过程。_ubuntu中怎么下载pkg-config
文章浏览阅读2k次。ubuntu创建共享文件夹_ubuntu20.04共享文件夹
文章浏览阅读2.9k次,点赞74次,收藏73次。对于有长期远程桌面需求的用户,建议将cpolar套餐升级到专业套餐,支持配置固定的公网TCP端口,且带宽也会相应的增大,支持更高效便捷的远程桌面连接Ubuntu系统。【cpolar内网穿透支持http/https/tcp协议,支持永久免费使用,不限制流量,无需公网IP,也不用进入路由器设置,操作简单。隧道创建成功后,点击左侧仪表盘的状态——在线隧道列表,查看xrdp隧道的所生成的公网TCP端口地址,复制下来。,使用cpolar内网穿透映射3389端口,生成公网TCP端口地址,实现在公网环境下,_ubuntu 局域网桌面
文章浏览阅读3.2k次。而在linux的ubuntu版本中,又多出两类用户:安装ubuntu系统的源用户xxx,其与root具有相同权限,仅在执行命令时,前面加sudo。在ubuntu中,用命令deluser username可以直接删除用户账号及家目录,而用centos7中的命令userdel -r username才能达到同样目的。在ubuntu中,没有moduser命令,centos7中的usermod修改用户信息的命令,同样可以用在ubuntu中。在系统中,创建新的用户或称为账号,一般需要多步操作。_ubuntu创建一个新用户
文章浏览阅读1.6w次,点赞4次,收藏23次。系统的许多日志文件都存储在 /var/log 目录中。你可以使用 ls /var/log 命令来列出可用的日志文件。/var/log/Xorg.0.log:包含 X 服务器的日志信息(图形界面)。打开DASH,搜索日志或者log,打开app,这个是/var/log的界面版。这将显示系统日志的末尾,并提供有关系统崩溃和错误的信息。/var/log/kern.log:包含内核日志信息。/var/log/dmesg:包含开机时的日志信息。/var/log/syslog:包含系统日志信息。_ubuntu查看系统日志
文章浏览阅读857次。首先将source.list复制为source.list.bak备份,然后将source.list内容改为需要的镜像源列表即可。Ubuntu采用apt作为软件安装工具,其镜像源列表记录在/etc/apt/source.list文件中。本节均为 Ubuntu 20.04 的镜像源列表。若为其他版本,将所有focal更改为其他版本代号即可。_apt 国内源