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

2024-03-18 15:38

本文主要是介绍在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文章中提供的.
//gcc -I../obj -g huge_threads.c ../obj/libst.a -o huge_threads
//./huge_threads 10000
//./huge_threads 30000
//#include <stdio.h>
#include "st.h"#define SLEEP_INTERVAL 30  //in msvoid* do_calc(void* arg){long pidx = *(long*)arg;for(;;){printf("sthread [#%ld] usleep\n", pidx);st_usleep(SLEEP_INTERVAL * 1000);}return NULL;
}int main(int argc, char** argv){if(argc <= 1){printf("Test the concurrence of state-threads!\n""Usage: %s <sthread_count>\n""eg. %s 10000\n", argv[0], argv[0]);return -1;}if(st_init() < 0){printf("state threads lib runtime init error!");return -1;}int i, count = atoi(argv[1]);for(i = 1; i <= count; i++){if(st_thread_create(do_calc, (void*)&i, 0, 0) == NULL){printf("create state thread %d failed\n", i);return -1;}}st_thread_exit(NULL);return 0;
}
编译
gcc -I../obj -g huge_threads.c ../obj/libst.a  -o huge_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 handling threads,它的职责是首先保存连接双方的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地址

使用方法
./lookupdns www.baidu.com news.sohu.com new.sina.com.cn  www.ifeng.com v.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://blog.csdn.net/win_lin/article/details/8242653  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网络线程库源码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/822820

相关文章

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

Java中常见队列举例详解(非线程安全)

《Java中常见队列举例详解(非线程安全)》队列用于模拟队列这种数据结构,队列通常是指先进先出的容器,:本文主要介绍Java中常见队列(非线程安全)的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一.队列定义 二.常见接口 三.常见实现类3.1 ArrayDeque3.1.1 实现原理3.1.2

SpringBoot3中使用虚拟线程的完整步骤

《SpringBoot3中使用虚拟线程的完整步骤》在SpringBoot3中使用Java21+的虚拟线程(VirtualThreads)可以显著提升I/O密集型应用的并发能力,这篇文章为大家介绍了详细... 目录1. 环境准备2. 配置虚拟线程方式一:全局启用虚拟线程(Tomcat/Jetty)方式二:异步

Ubuntu设置程序开机自启动的操作步骤

《Ubuntu设置程序开机自启动的操作步骤》在部署程序到边缘端时,我们总希望可以通电即启动我们写好的程序,本篇博客用以记录如何在ubuntu开机执行某条命令或者某个可执行程序,需要的朋友可以参考下... 目录1、概述2、图形界面设置3、设置为Systemd服务1、概述测试环境:Ubuntu22.04 带图

如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socket read timed out的问题

《如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socketreadtimedout的问题》:本文主要介绍解决Druid线程... 目录异常信息触发场景找到版本发布更新的说明从版本更新信息可以看到该默认逻辑已经去除总结异常信息触发场景复

Linux网络配置之网桥和虚拟网络的配置指南

《Linux网络配置之网桥和虚拟网络的配置指南》这篇文章主要为大家详细介绍了Linux中配置网桥和虚拟网络的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、网桥的配置在linux系统中配置一个新的网桥主要涉及以下几个步骤:1.为yum仓库做准备,安装组件epel-re

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3

Ubuntu上手动安装Go环境并解决“可执行文件格式错误”问题

《Ubuntu上手动安装Go环境并解决“可执行文件格式错误”问题》:本文主要介绍Ubuntu上手动安装Go环境并解决“可执行文件格式错误”问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未... 目录一、前言二、系统架构检测三、卸载旧版 Go四、下载并安装正确版本五、配置环境变量六、验证安装七、常见

python如何下载网络文件到本地指定文件夹

《python如何下载网络文件到本地指定文件夹》这篇文章主要为大家详细介绍了python如何实现下载网络文件到本地指定文件夹,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下...  在python中下载文件到本地指定文件夹可以通过以下步骤实现,使用requests库处理HTTP请求,并结合o