一.Think库
提供跨平台的C语言库,各类C、C++程序都可以用到其中的东西,已支持AIX、HP-UX、Solaris、FreeBSD、Linux、Mac OS X和Windows操作系统
本人辛苦了四年,颠覆多次,终成这个发布版,现在作为unix-center的开源项目,任何非册用户进入此链接都可以下载有兴趣的先顶一下,便于后面的伙计看到此贴。
第二版主要增加了进程通讯的一些东西,包括线程,线程锁,进程锁,信号量,共享内存及由信号量与共享内存实现的消息队列
下载地址: http://unix-center.org/projects/think/
以下为网站公布的部分接口文档:
发表人: enigma1983 Unix-Center
日期: 2009-08-22 21:15
概要: Think库之NET接口(socket接口)
项目: Think
Think NET是socket通讯的基本操作的封装,提供connect、listen、accept、send、recv、close几个基本接口,另外对select进行了封装,对包含socket名柄信息的双向循环链表进行侦听,同样也对UNIX上的poll实现了同样的封装,喜欢用poll的只需更换函数名即可,其它操作完全一样
struct __think_net {
int sockfd;
char ip[16];
unsigned short port;
int flags;
};
typedef struct __think_net THINK_NET;
1:建立连接
THINK_NET *think_netconnect(const char *ip,unsigned short port);
参数故名思意
2:建立侦听
THINK_NET *think_netlisten(const char *ip,unsigned short port);
3:THINK_NET *think_netaccept(THINK_NET *net);
net为侦听名柄
4:接收数据
int think_netrecv(THINK_NET *net,void *buf,unsigned int siz,int flags);
buf为数据存放地址
siz为大小
flags为标志,可以接受的值为THINK_NET_WAIT,即一直等到siz个字节全部接收完毕或有错误时才返回
5:发送数据
int think_netsend(THINK_NET *net,const void *buf,unsigned int len,int flags);
flags意思与think_netrecv中的相同,此处为len个字节全部发送完毕
6:关闭连接
int think_netclose(THINK_NET *net);
7:加载动态库(Windows平台必须要调用的)
int think_netstart(void);
int think_netstop(void);
netstart为使用网络接之间要调用的,netstop为不使用网络时候调用,也可以由程序退出时自动执行,即think_netstart是必须要执行的。
struct __think_netlist {
THINK_NET *net;
int flags;
struct __think_netlist *prior;
struct __think_netlist *next;
};
typedef struct __think_netlist THINK_NETLIST;
8:向链表中增加一个连接
int think_netlist_add(THINK_NETLIST **netlist,THINK_NET *net);
netlist为链表的地址,如定义一个变量THINK_NETLIST *netlist=NULL,调用时传&netlist即可
9:从链表删除一个连接
int think_netlist_del(THINK_NETLIST **netlist,THINK_NET *net);
10:在链表中查找一个连接
THINK_NETLIST *think_netlist_find(THINK_NETLIST *netlist,THINK_NET *net);
11:清理链表
int think_netlist_clean(THINK_NETLIST **netlist);
调用think_netlist_del时,只是将标志位置为已删除,并未真正从链中删除,因此需要调用这个接口进行清理
12:释放链表
int think_netlist_free(THINK_NETLIST **netlist);
将删除所有连接,并释放内存
13:使用select进行侦听
int think_netselect(THINK_NETLIST **netlist,int timeout);
timeout为等待时间
>0为阻塞式等待一定时间,直到超时
=0为非阻塞式,立即返回
<0为永久阻塞式,直到有事件发生
THINK_NETLIST结构中有一个成员flags,要在调用think_netselect之前,将需要侦听的事件设置好,如将flags设为THINK_NET_READ或THINK_NET_WRITE或THINK_NET_READ|THINK_NET_WRITE,即侦听读、写、读写
think_netselect返回后,通过检查该标志是否被置位成THINK_NET_READ、THINK_NET_WRITE,以得知连接目前为可读或可写
14:使用poll进行侦听
int think_netpoll(THINK_NETLIST **netlist,int timeout);
用法与think_netselect完全一样
15:发送消息
int think_netsendmsg(THINK_NET *net,const void *buf,unsigned int len);
将先送四字节网络字节序的长度,然后再发送具体长度的信息
16:int think_netrecvmsg(THINK_NET *net,void *buf,unsigned int siz);
先接收四字节网络字节序的长度,然后再接收具体长度的信息
发表人: enigma1983 Unix-Center
日期: 2009-08-22 21:54
概要: Think库之NETCENTER接口(socket通讯框架)
项目: Think
Think NETCENTER是借鉴ACE的设计思路,即使用select对所有连接进行侦听,如有连接上有事件发生,则调用相应的回调函数进行处理,这里采用缓冲的方式先将数据接收到缓冲区,然后调用相应的回调函数进行处理,当然如果接口是可写的,那么将查看写缓冲区是否有数据,如果有则从缓冲区读取数据进行发送。
1:建立网络中心
THINK_NETCENTER *think_netcenter_new();
主要是得到一个名柄,网络中心操作名柄
2:进行一次网络处理
int think_netcenter_idle(THINK_NETCENTER *netcenter,int timeout);
这个接口为网络中心的总控接口,它先去调用select进行事件侦听,然后进行数据接收并调用回调函数进行处或从缓冲区发送数据,直到所有接口处理完毕后才返回,应用层需要不断调用此接口完成网络数据的持续处理
timeout与think_netselect中的意思完全一样,这个参数就是原封不动的传给think_netselect的。
3:释放网络中心
int think_netcenter_free(THINK_NETCENTER *netcenter);
这个接口将释放所有表及内存
4:向网络中心注册连接
THINK_NETCENTER_NETLIST *think_netcenter_netadd(THINK_NETCENTER *netcenter,THINK_NET *net,THINK_NETCENTER_NETHANDLE nethandle);
net为所要注册的连接
nethandle为该连接的回调函数地址
5:从网络中心注销连接
int think_netcenter_netdel(THINK_NETCENTER *netcenter,THINK_NET *net);
6:从网络中心查找连接
THINK_NETCENTER_NETLIST *think_netcenter_netfind(THINK_NETCENTER *netcenter,THINK_NET *net);
7:清理网络中心的链表
int think_netcenter_netclean(THINK_NETCENTER *netcenter);
与think_netclean的意思一样,删除时只是置了标志,因此需要进行清理
8:从连接缓冲中读取数据
int think_netcenter_recv(THINK_NETCENTER_NET *net,void *buf,unsigned int siz);
该函数是在回调中调用的,当有数据可读时,通过这个接口可获取数据
9:向连接缓冲区中写入数据
int think_netcenter_send(THINK_NETCENTER_NET *net,const void *buf,unsigned int len);
该函数是在业务处理时调用的,当需要发送数据时,通过这个接口将数据发送到该连接的缓冲区中,由网络中心在idle时进行发送
10:从缓冲区中窃取数据
int think_netcenter_peek(THINK_NETCENTER_NET *net,void *buf,unsigned int siz);
当不知道缓冲区里有什么数据时,可用此接口进行偷窥,即将数据取出来,但缓冲区的数据仍原封不动的保留在那里,这个接口在判断缓冲区是否有完整的数据包时很有用
以下为使用GL语言通讯时使用的接口
11:判断缓冲区是否有完整的数据包
int think_netcenter_ismsgok(THINK_NETCENTER_NET *net);
完整的数据包即四字节网络字节序的长度+对应的数据内容都在缓冲里了
12:从缓冲区接收一个数据包
int think_netcenter_recvmsg(THINK_NETCENTER_NET *net,void *buf,unsigned int siz);
13:向缓冲区发送一个数据包
int think_netcenter_sendmsg(THINK_NETCENTER_NET *net,const void *buf,unsigned int len);
以下为网络中心内部使用的接口
14:从网络上接收数据至缓冲区中
int think_netcenter_recvto(THINK_NETCENTER_NET *net);
15:从缓冲区中读取数据发送到网络上
int think_netcenter_sendfrom(THINK_NETCENTER_NET *net);
16:对网络中心的连接进行侦听
int think_netcenter_select(THINK_NETCENTER *netcenter,int timeout);
即对每个连接置相应的侦听标志,然后调用think_netselect进行侦听
昨天一个同事一大早在群里推荐了一个google project上的开源内存分配器(http://code.google.com/p/google-perftools/),据说google的很多产品都用到了这个内存分配库,而且经他测试,我们的游戏客户端集成了这个最新内存分配器后,FPS足足提高了将近10帧左右,这可是个了不起的提升,要知道3D组的兄弟忙了几周也没见这么大的性能提升。
如果我们自己本身用的crt提供的内存分配器,这个提升也算不得什么。问题是我们内部系统是有一个小内存管理器的,一般来说小内存分配的算法都大同小异,现成的实现也很多,比如linux内核的slab、SGI STL的分配器、ogre自带的内存分配器,我们自己的内存分配器也和前面列举的实现差不多。让我们来看看这个项目有什么特别的吧。
一、使用方法
打开主页,由于公司网络禁止SVN从外部更新,所以只能下载了打包的源代码。解压后,看到有个doc目录,进去,打开使用文档,发现使用方法极为简单:To use TCMalloc, just link TCMalloc into your application via the "-ltcmalloc" linker flag.再看算法,也没什么特别的,还是和slab以及SGI STL分配器类似的算法。
unix环境居然只要链接这个tcmalloc库就可以了!,太方便了,不过我手头没有linux环境,文档上也没提到windows环境怎么使用,
打开源代码包,有个vs2003解决方案,打开,随便挑选一个测试项目,查看项目属性,发现仅仅有2点不同:
1、链接器命令行里多了
"..\..\release\libtcmalloc_minimal.lib",就是链接的时候依赖了这个内存优化库。
2、链接器->输入->强制符号引用 多了 __tcmalloc。
这样就可以正确的使用tcmalloc库了,测试了下,测试项目运行OK!
二、如何替换CRT的malloc
从前面的描述可知,项目强制引用了__tcmalloc, 搜索了测试代码,没发现用到_tcmalloc相关的函数和变量,这个选项应该是为了防止dll被优化掉(因为代码里没有什么地方用到这个dll的符号)。初看起来,链接这个库后,不会影响任何现有代码:我们没有引用这个Lib库的头文件,也没有使用过这个dll的导出函数。那么这个dll是怎么优化应用程序性能的呢?
实际调试,果然发现问题了,看看如下代码
void* pData = malloc(100);
00401085 6A 64 push 64h
00401087 FF 15 A4 20 40 00 call dword ptr [__imp__malloc (4020A4h)]
跟踪 call malloc这句,step进去,发现是
78134D09 E9 D2 37 ED 97 jmp `anonymous namespace'::LibcInfoWithPatchFunctions<8>::Perftools_malloc (100084E0h)
果然,从这里开始,就跳转到libtcmalloc提供的Perftools_malloc了。
原来是通过API挂钩来实现无缝替换系统自带的malloc等crt函数的,而且还是通过大家公认的不推荐的改写函数入口指令来实现的,一般只有在游戏外挂和金山词霸之类的软件才会用到这样的挂钩技术,
而且金山词霸经常需要更新补丁解决不同系统兼容问题。
三、性能差别原因
如前面所述,tcmalloc确实用了很hacker的办法来实现无缝的替换系统自带的内存分配函数(本人在使用这类技术通常是用来干坏事的。。。),但是这也不足以解释为什么它的效率比我们自己的好那么多。回到tcmalloc 的手册,tcmalloc除了使用常规的小内存管理外,对多线程环境做了特殊处理,这和我原来见到的内存分配器大有不同,一般的内存分配器作者都会偷懒,把多线程问题扔给使用者,大多是加
个bool型的模板参数来表示是否是多线程环境,还美其名曰:可定制,末了还得吹嘘下模板的优越性。
tcmalloc是怎么做的呢? 答案是每线程一个ThreadCache,大部分操作系统都会支持thread local storage 就是传说中的TLS,这样就可以实现每线程一个分配器了,
这样,不同线程分配都是在各自的threadCache里分配的。我们的项目的分配器由于是多线程环境的,所以不管三七二十一,全都加锁了,性能自然就低了。
仅仅是如此,还是不足以将tcmalloc和ptmalloc2分个高下,后者也是每个线程都有threadCache的。
关于这个问题,doc里有一段说明,原文贴出来:
ptmalloc2 also reduces lock contention by using per-thread arenas but there is a big problem with ptmalloc2's use of per-thread arenas. In ptmalloc2 memory can never move from one arena to another. This can lead to huge amounts of wasted space.
大意是这样的:ptmalloc2 也是通过tls来降低线程锁,但是ptmalloc2各个线程的内存是独立的,也就是说,第一个线程申请的内存,释放的时候还是必须放到第一个线程池中(不可移动),这样可能导致大量内存浪费。
四、代码细节
1、无缝替换malloc等crt和系统分配函数。
前面提到tcmalloc会无缝的替换掉原有dll中的malloc,这就意味着使用tcmalloc的项目必须是 MD(多线程dll)或者MDd(多线程dll调试)。tcmalloc的dll定义了一个static TCMallocGuard module_enter_exit_hook;
的静态变量,这个变量会在dll加载的时候先于DllMain运行,在这个类的构造函数,会运行PatchWindowsFunctions来挂钩所有dll的 malloc、free、new等分配函数,这样就达到了替换功能,除此之外,
为了保证系统兼容性,挂钩API的时候还实现了智能分析指令,否则写入第一条Jmp指令的时候可能会破环后续指令的完整性。
2、LibcInfoWithPatchFunctions 和ThreadCache。
LibcInfoWithPatchFunctions模板类包含tcmalloc实现的优化后的malloc等一系列函数。LibcInfoWithPatchFunctions的模板参数在我看来没什么用处,tcmalloc默认可以挂钩最多10个带有malloc导出函数的库(我想肯定是够用了)。ThreadCache在每个线程都会有一个TLS对象:
__thread ThreadCache* ThreadCache::threadlocal_heap_。
3、可能的问题
设想下这样一个情景:假如有一个dll 在tcmalloc之前加载,并且在分配了内存(使用crt提供的malloc),那么在加载tcmalloc后,tcmalloc会替换所有的free函数,然后,在某个时刻,
在前面的那个dll代码中释放该内存,这岂不是很危险。实际测试发现没有任何问题,关键在这里:
span = Static::pageheap()->GetDescriptor(p);
if (!span) {
// span can be NULL because the pointer passed in is invalid
// (not something returned by malloc or friends), or because the
// pointer was allocated with some other allocator besides
// tcmalloc. The latter can happen if tcmalloc is linked in via
// a dynamic library, but is not listed last on the link line.
// In that case, libraries after it on the link line will
// allocate with libc malloc, but free with tcmalloc's free.
(*invalid_free_fn)(ptr); // Decide how to handle the bad free request
return;
}
tcmalloc会通过span识别这个内存是否自己分配的,如果不是,tcmalloc会调用该dll原始对应函数(这个很重要)释放。这样就解决了这个棘手的问题。
五、其他
其实tcmalloc使用的每个技术点我从前都用过,但是我从来没想过用API挂钩来实现这样一个有趣的内存优化库(即使想过,也是一闪而过就否定了)。从tcmalloc得到灵感,结合常用的外挂技术,可以很轻松的开发一个独立工具:这个工具可以挂载到指定进程进行内存优化,在我看来,这可能可以作为一个外挂辅助工具来优化那些
内存优化做的很差导致帧速很低的国产游戏。
小内存分配器主要作用是“减小内存碎片化趋势,减小薄记内存比例,提高小内存利用率”,从性能上说,系统内存分配器已针对小内存分配进行优化,单纯使用自定义的小内存分配器,对性能帮助不会很大。内置分配器意义还是体现在,实现无锁分配,避免API调用切换开销。
CRT自身new-delete会用到500个时钟周期,而一个CS会消耗50个时钟周期,一个mutex会用到2000个时钟周期,以上是无竞争的情况。所以,如果用mutex做互斥,那还不如用系统的分配器;如果用CS,也不见会好多少,因为CS会随锁竞争加剧大幅增加时间,甚至会超过mutex。
所以结论是,对于单线程,内置分配器有一定的价值;对于多线程,带锁内置分配器基本上可以无视了(至少对于winxp以后是这样,win2k好像要打补丁)呵呵,从你说的情况来看,很有可能你们原来的分配器用mutex帮倒忙了。
tcmalloc中的唯一亮点应该是,如何做到跨线程归还内存,又能保持高性能,猜想可能使用了某种二级分配策略,内存块可以属于任何线程的内存池,归还到那个线程内存池,就由这个内存池管理。由于各个线程的分配和释放多半不平衡,有线程池会撑满,有的会不足。估计撑满的就会归还到公共内存池。第一级分配无锁,如果内存池不足了,就进入第二级带锁批量分配,而且第二级分配会先从公共内存池获取,如果还不够,这才使用系统内存分配,这该算是第三级分配了。
最后,tcmalloc也是可以用于MT版本的哦,详见(要*才能看见)http://groups.google.com/group/google-perftools/browse_thread/thread/41cd3710af85e57b
三、跨平台的USB设备访问C语言库,libusb
libusb的是一个C库,它提供通用的访问USB设备。它支持Linux,Mac OS X,Windows,Windows CE,Android,和OpenBSD/ NetBSD。
版本说明:此版本标志着将libusbx项目合并成libusb。
四、可直接商用的跨平台c,c++动态线程池,任务池stpool库
代码片段(10)[全屏查看所有代码]
1. [文件] libstpool-3.2.rar ~ 4MB 下载(87)
2. [文件] libstpool.2.6.6库+文档+demo.rar ~ 2MB 下载(126)
3. [代码]stpool 线程池,任务池
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 | 1. hp = stpool_create(5, 2, 0, 10); 2. 往任务池中添加执行任务. struct sttask_t *ptsk = stpool_new_task( "mytask" , mytask_run, mytask_complete, mytask_arg); stpool_add_task(hp, ptsk); 或者直接可往线程池中添加一个回调或执行路径 stpool_add_routine(hp, callbak, callback_complete, callback_arg, NULL); 任务添加后,将会被尽快执行. 同时可使用 stpool_task_setschattr(ptsk, &attr); 设置优先级任务,优先级越高的任务越优先调度 3. 等待任务被完成.(stpool 提供对任务的控制,当任务成功加入线程池后, 可以使用stpool_task_wait来等待任务完成,当用户的task_complete被 调用完毕后, stpool_task_wait才返回) stpool_task_wait(hp, ptask, ms); 4. 暂停线程池(线程池被暂停后,除正在被调度的任务外,线程池将不继续执行任务, 但仍然可以往线程池中添加任务,只是这些任务都处于排队状态) stpool_suspend(hp, 0). 5. 恢复线程池的运行. stpool_resume(hp); 6. 禁止用户继续向线程池投递任务(用户调用@tpool_add_task时会返回POOL_ERR_THROTTLE错误码) tpool_throttle_enable(hp, 1) 7. 等待何时可以投递任务 stpool_throttle_wait(hp, ms); 8. 线程池服务线程数量控制 .) 重新设置线程池最大数量为2,预留线程为0 stpool_adjust_abs(hp, 2, 0); .)在原来的基础上,将线程池最大服务线程数量+1, 最小数量-1 (这并不代表stpool马上会创建线程,而只是在任务繁重的时候内部精心调度开启) stpool_adjust(hp, 1, -1) 9. 获取任务池的状态 .)获取stpool内服务线程数目,任务执行情况 struct stpool_stat_t stat; stpool_getstat(hp, &stat); .)获取任务的状态 long stat = stpool_gettskstat(hp, &mytask); .) 访问线程池中的所有任务状态 stpool_mark_task(hp, mark_walk, arg) 10.移除所有在等待的任务 stpool_remove_pending_task(hp, NULL); 11.提供引用计数,线程可被其它模块使用,确保线程池对象的生命周期. 第三方模块使用线程池. stpool_addref(hp) //保证线程池对象不会被销毁 stpool_adjust(hp, 2, 0); //添加本模块的需求(增大最大服务线程数+2) //投递本模块的任务 stpool_release(hp) //释放线程池 12. 销毁线程池.(当引用计数为0时候,线程池对象会被自动释放, @stpool_create成功后其用户引用计数为1) stpool_release(hp) |
4. [文件] demo-c++.cpp ~ 3KB 下载(12)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 | /* #include using namespace #include #ifdef #ifdef #ifdef #pragma #pragma #pragma #else #pragma #pragma #pragma #endif #else #ifdef #pragma #pragma #pragma #else #pragma #pragma #pragma #endif #endif #endif /* * libmsglog.lib <-------------libstpool.lib <--------------------libstpoolc++.lib */ class myTask: public CTask { public : /* We can allocate a block manually for the proxy object. * and we can retreive its address by @getProxy() */ myTask(): CTask( /*new char[getProxySize()]*/ NULL, "mytask" ) {} ~myTask() { /* NOTE: We are responsible for releasing the proxy object if * the parameter @cproxy passed to CTask is NULL */ if (isProxyCreatedBySystem()) freeProxy(getProxy()); else delete [] reinterpret_cast < char *>(getProxy()); } private : virtual int
{ cout << taskName() << ": onTask.\n" ; return 0; } virtual void
long sm, int errCode) { if (CTask::sm_DONE & sm) cout << taskName() << " has been done with code:" << dec << errCode << " stat:0x" << hex << stat() << " sm:0x" << sm << endl; else cerr << taskName() << " has not been done. reason:" << dec << errCode << " stat:0x" << hex << stat() << " sm:0x" << sm << endl; static int
/* We reschedule the task again. * NOTE: * task->wait() will not return until the task * does not exist in both the pending pool and the * scheduling queue. */ if (++ slTimes < 5) queue(); /* The task will be marked with @sm_ONCE_AGAIN if user calls * @queue to reschedule it while it is being scheduled. and * @sm_ONCE_AGAIN will be removed by the pool after it having * been delived into the pool. */ cout << dec << slTimes << " sm:0x" << hex << this ->sm() << endl << endl; } }; int main() { /* Create a pool instance with 1 servering thread */ CTaskPool *pool = CTaskPool::createInstance(1, 0, false ); /* Test running the task */ myTask *task = new myTask; /* Set the task's parent before our's calling @queue */ task->setParent(pool); /* Deliver the task into the pool */ task->queue(); /* Wait for the task's being done */ task->wait(); cout << "\ntask has been done !" << endl; /* Free the task object */ delete task; /* Shut down the pool */ pool->release(); cin.get(); return 0; } |
5. [图片] linux_win_demo-c++.png
6. [文件] demo-sche.c ~ 3KB 下载(8)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 | /* #include #include #ifdef #ifdef #ifdef #pragma #pragma #else #pragma #pragma #endif #else #ifdef #pragma #pragma #else #pragma #pragma #endif #endif #else #include #include #endif /* * libmsglog.lib <-------------libstpool.lib */ static void
int *val) { *val += 100; *val *= 0.371; } int task_run( struct sttask_t *ptsk) { size_t i, j, sed = 20; for (i=0; i<sed; i++) for (j=0; j<sed; j++) do_work(( int *)ptsk->task_arg); /* Do not call @printf in the test since it will waste our * so much time on competing the IO. */ return 0; } void task_complete( struct sttask_t *ptsk, long vmflags, int code) { } int main() { time_t now; int i, c, times, j=0; int sum, *arg; HPOOL hp; /* Creat a task pool */ hp = stpool_create(50, /* max servering threads */ 0, /* 0 servering threads that reserved for waiting for tasks */ 1, /* suspend the pool */ 0); /* default number of priority queue */ printf ( "%s\n" , stpool_status_print(hp, NULL, 0)); /* Add tasks */ times = 90000; arg = ( int *) malloc (times * sizeof ( int )); for (i=0; i<times; i++) { /* It may take a long time to load a large amount of tasks * if the program is linked with the debug library */ if (i % 4000 == 0 || (i + 1) ==times) { printf ( "\rLoading ... %.2f%% " , ( float )i * 100/ times); fflush (stdout); } arg[i] = i; stpool_add_routine(hp, "sche" , task_run, task_complete, ( void *)&arg[i], NULL); } printf ( "\nAfter having executed @stpool_add_routine for %d times:\n" "--------------------------------------------------------\n%s\n" , times, stpool_status_print(hp, NULL, 0)); printf ( "Press any key to resume the pool.\n" ); getchar (); /* Wake up the pool to schedule tasks */ stpool_resume(hp); stpool_task_wait(hp, NULL, -1); /* Get the sum */ for (i=0, sum=0; i<times; i++) sum += arg[i]; free (arg); now = time (NULL); printf ( "--OK. finished. <arg: %d> %s\n%s\n" , sum, ctime (&now), stpool_status_print(hp, NULL, 0)); #if /* You can use debug library to watch the status of the pool */ while ( 'q' != getchar ()) { for (i=0; i<40; i++) stpool_add_routine(hp, "debug" , task_run, NULL, &sum, NULL); } /* Clear the stdio cache */ while ((c= getchar ()) && c != '\n' && c != EOF) ; #endif getchar (); /* Release the pool */ printf ( "Shut down the pool now.\n" ); stpool_release(hp); getchar (); return 0; } |
7. [图片] win32_linux_demo_sche1.jpg
8. [图片] win32_linux_demo_sche2.jpg
9. [代码]demo-sche结果分析
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 | 线程和任务峰值统计(现仅统计win32/linux) win32(xp) linux(ubuntu 10.04) ---------------------------------------------------- threads_peak: 9 threads_peak: 6 tasks_peak: 90000 tasks_peak: 90000 ------------------------------------------------------ 完成90000个任务,stpool最高峰线程数目为9(win32),6(linux),根据任务 执行情况智能调度任务,90000个任务都在1s内完成. (ubuntu为xp的vmware虚拟机, 运行设置为2核2线程) root@ubuntu_xrh:~/localhost/task/stpool# processor vendor_id cpu model model stepping cpu cache fdiv_bug hlt_bug f00f_bug coma_bug fpu fpu_exception cpuid wp flags bogomips clflush cache_alignment address virtual power processor vendor_id cpu model model stepping cpu cache fdiv_bug hlt_bug f00f_bug coma_bug fpu fpu_exception cpuid wp flags bogomips clflush cache_alignment address virtual power |
10. [文件] demo.c ~ 7KB 下载(9)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 | /* #include #include #ifdef #include #define #ifdef #ifdef #pragma #else #pragma #endif #else #ifdef #pragma #else #pragma #endif #endif #else #include #define #endif static int int task_run( struct sttask_t *ptsk) { /* TO DO */ printf ( "@task_run ...:%d\n" , *( int *)ptsk->task_arg); ++ *( int *)ptsk->task_arg; //msleep(3000); return 0; } void task_complete( struct sttask_t *ptsk, long vmflags , int task_code) { /* NOTE: * If vmflags has been marked with STTASK_VMARK_DONE, it indicates that * the @task_run has been excuted by the pool. */ if (!(STTASK_VMARK_DONE & vmflags)) { printf ( "@task_run is not executed: 0x%lx-code:%d\n" , vmflags, task_code /* STPOOL_XX */ ); return ; } if (g_test_reschedule) { struct schattr_t attr; /* We sleep for a while to slow down the test */ msleep(1500); /* We adjust the task's priority */ stpool_task_getschattr(ptsk, &attr); if (!attr.permanent) { attr.permanent = 1; attr.sche_pri = 80; stpool_task_setschattr(ptsk, &attr); } /* Reschedule the task */ stpool_add_task(ptsk->hp_last_attached, ptsk); } } static int int task_run2( struct sttask_t *ptsk) { static int printf ( "@task_run2: %d\n" , ++i); return 0; } void task_complete2( struct sttask_t *ptsk, long vmflags, int task_code) { if (!(STTASK_VMARK_DONE & vmflags)) { printf ( "@task_run2 is not executed: 0x%lx-code:%d\n" , vmflags, task_code); return ; } if (g_test_reschedule) { msleep(1500); /* Reschedule the task */ stpool_add_task(ptsk->hp_last_attached, ptsk); } } long mark_walk( struct stpool_tskstat_t *stat, void *arg) { /* If you want to stop walking the task, you should return -1 */ //return -1; /* If you just want to walk the tasks, you should return 0 */ //return 0; /* Return the marks */ return STTASK_VMARK_REMOVE_BYPOOL /* Remove the task */ ; } int main() { int i, error; HPOOL hp; struct schattr_t attr = { 0, 1, STP_SCHE_TOP }; struct sttask_t *ptsk; /* NO buffer */ setbuf (stdout, 0); /* Create a pool */ hp = stpool_create(20, /*limited threads number*/ 0, /*number of threads reserved to waiting for tasks*/ 0, /*do not suspend the pool */ 1 /*priority queue num */ ); /* Set the sleep time for the threads (10s + random() % 60s)*/ stpool_set_activetimeo(hp, 10, 20); /* Print the status of the pool */ printf ( "@tpool_create(20, 0, 0, 10)\n%s\n" , stpool_status_print(hp, NULL, 0)); /************************************************************/ /********************Test @stpool_adjust(_abs)****************/ /************************************************************/ printf ( "\nPress any key to test the @tpool_adjust(abs) ....\n" ); getchar (); stpool_adjust_abs(hp, 300, 1); printf ( "@tpool_adjust_abs(pool, 300, 1)\n%s\n" , stpool_status_print(hp, NULL, 0)); /* We call @stpool_adjust to recover the pool env */ stpool_adjust(hp, -280, -1); stpool_adjust_wait(hp); printf ( "@tpool_adjust(pool, -280, -1)\n%s\n" , stpool_status_print(hp, NULL, 0)); /*******************************************************************/ /********************Test the throttle******************************/ /*******************************************************************/ printf ( "\nPress any key to test the throttle ....\n" ); getchar (); /* Turn the throttle on */ stpool_throttle_enable(hp, 1); ptsk = stpool_task_new( "test" , task_run, task_complete, ( void *)&counter); error = stpool_add_task(hp, ptsk); if (error) printf ( "***@stpool_add_task error:%d\n" , error); /* Turn the throttle off */ stpool_throttle_enable(hp, 0); /*******************************************************************/ /******************Test the priority********************************/ /*******************************************************************/ printf ( "\nPress any key to test the priority ....\n" ); getchar (); stpool_suspend(hp, 0); /* Add a task with zero priority, and the task will be pushed into the * lowest priority queue. */ stpool_add_routine(hp, "test" , task_run, task_complete, ( void *)&counter, NULL); /* @task_run2 will be scheduled prior to the @task_run since the @task_run2 has * a higher priority. */ stpool_add_routine(hp, "routine" , task_run2, task_complete2, NULL, &attr); /* Wake up the pool to schedule the tasks */ stpool_resume(hp); /* Wait for all tasks' being done completely */ stpool_task_wait(hp, NULL, -1); /******************************************************************/ /****************Test rescheduling task****************************/ /******************************************************************/ printf ( "\nPress any key to test rescheduling task. <then press key to stop testing.>\n" ); getchar (); g_test_reschedule = 1; stpool_add_task(hp, ptsk); stpool_add_routine(hp, "routine" , task_run2, task_complete2, NULL, NULL); getchar (); g_test_reschedule = 0; stpool_task_wait(hp, NULL, -1); /******************************************************************/ /***************Test running amount of tasks***********************/ /******************************************************************/ printf ( "\nPress any key to add tasks ... <then can press any key to remove them.>\n" ); getchar (); /* We can suspend the pool firstly, and then resume the pool after delivering our * tasks into the pool, It'll be more effecient to do it like that if there are * a large amount of tasks that will be added into the pool. */ /* NOTE: We can add the same task into the pool one more times */ for (i=0; i<4000; i++) { stpool_add_routine(hp, "test" , task_run, task_complete, ( void *)&counter, NULL); stpool_add_routine(hp, "routine" , task_run2, task_complete2, NULL, NULL); } /****************************************************************/ /*************Test stoping all tasks fastly**********************/ /****************************************************************/ printf ( "\nPress any key to test stoping all tasks fastly.\n" ); getchar (); /* Remove all pending tasks by calling @stpool_mark_task_ex, * We can also call @stpool_remove_pending_task to reach our * goal, But we call @stpool_mark_task_ex here for showing * how to use @stpool_mark_task to do the customed works. */ stpool_mark_task_ex(hp, mark_walk, NULL); stpool_throttle_enable(hp, 1); /* Wait for all tasks' being done */ stpool_task_wait(hp, NULL, -1); printf ( "---------------------------tasks have been finished.\n" ); /* Free the task object */ stpool_task_delete(ptsk); /* Release the pool */ printf ( "%s\n" , stpool_status_print(hp, NULL, 0)); stpool_release(hp); printf ( "Press any key to exit ...\n" ); getchar (); return 0; } |
六、一款高效、灵活、跨平台的内存池
项目地址:https://code.google.com/p/elr-memery-pool/
简介
这是一款高效、灵活、跨平台的内存池实现。使用MIT Licence发布,完全不排斥商业使用。它已经在许多生产环境中使用了。在该实现中内存被划分为节点(node)和切片(slice)。node一大块内存,slice是node上的小片内存,从内存池中申请的每一个内存都属于一个slice。每一个内存池实例里德slice都是一样大小的,所以这个内存池更像对象池。但是仍然可以基于该内存池实现一款更加灵活的可以从中申请不同尺寸的内存的内存池。
node链接成一个链表,可以使用的slice也链接成一个链表。当从内存池中申请内存时,首先检查是否有空闲的slice,如果有取出一个;如果没有,就检查最近申请的node里是否还有从未使用过的切片。如果最近申请的node里有从未使用过的slice,那么取出一个;如果没有将这个node添加到node链表的头部再申请一个新node并从中取出一个slice返回。释放内存时,仅仅需要将slice插入到空闲的slice链表头部。
这个内存池被组织为树状结构。当创建一个内存池时,可以为其指定父内存池,在调用elr_mpl_create时使用父内存池的指针作为第一个参数即可。当一个内存池被销毁时,它的子内存池也会被销毁。所以当一个内存池和它的子内存池不再使用时不必将所有的内存池一一销毁,仅仅为父内存池调用销毁接口即可。如果在创建内存池时不指定父内存池,那么一个全局的内存池就是它的父内存池。它是在第一次调用初始化内存池函数(elr_mpl_init)时被创建的。所有的内存池结构所占据的内存空间都来自于这个全局内存池。在最后一次调用终止化(elr_mpl_finalize)内存池时这个全局内存池被销毁。同时可以看出所有的内存池实例都是这个全局内存池的直接或者间接的子内存池。那么当elr_mpl_finalize被调用后所有的内存池实例也将被销毁。这将内存泄露的可能性降到了最低。
这个内存池也支持多线程。如果需要在多线程环境下使用它,就需要实现elr_mtx.h中定义的六个接口并且在编译时定义宏ELR_USE_THREAD。幸运的是实现它们非常简单,并且已经提供了一个windows平台下的实现。在提供windows平台下的实现时也考虑到了linux的兼容性。所以原子计数器类型和计数器类型(counter(interger) type and counter value type )被分别定义了。在windows平台下并没有对原子计数器类型单独定义,而是提供一个被volatile修饰的LONG类型。在LONG和volatile LONG之间赋值是被允许的。在linux平台下原子计数器类型被定义为这样:typedef struct { volatile int counter; } atomic_t; 。在int和atomic_t之间赋值是违法语法的。
这个内存池在生产环境中被证明是非常有效的。即使如此这个内存池也有很大的改良空间。在多线程环境下使用时,每一个内存池实例都拥有一个互斥体,很多情形下这不是必须的。所以这个内存池至少有两个地方可以改进。第一,减少对互斥体的消耗,这对嵌入式系统来说是非常有意义的。第二,扩展它使得它能够像appache的内存池一样可以从内存池实例中申请多种规则尺寸的内存块。
使用示例
#include <stdio.h>#include <stdlib.h>#include "elr_mpl.h"int main(){ elr_mpl_t mypool = ELR_MPL_INITIALIZER; elr_mpl_t mysubpool = ELR_MPL_INITIALIZER; void* mem = NULL; int len = 0; elr_mpl_init(); mypool = elr_mpl_create(NULL,256); printf("%s/n","create a memory pool: mypool."); mysubpool = elr_mpl_create(&mypool,128); printf("%s/n","create a sub memory pool of mypool, name is mysubpool."); mem = elr_mpl_alloc(&mysubpool); printf("%s/n","alloc a memory block form mysubpool."); len = elr_mpl_size(mem); printf("the memory block size is %d./n",len); elr_mpl_free(mem); printf("give back the memory block to mysubpool./n",len); mem = elr_mpl_alloc(&mypool); printf("%s/n","alloc a memory block form mypool."); len = elr_mpl_size(mem); printf("the memory block size is %d./n",len); elr_mpl_free(mem); printf("give back the memory block to mypool./n",len); elr_mpl_destroy(&mypool); printf("destroy mypool./n",len); printf("when mypool has destoryed, it`s sub pool, mysubpool, did %s destoryed./n", elr_mpl_avail(&mysubpool) == 0?"also":"not"); elr_mpl_finalize(); getchar(); return 0;}
七、跨平台的通用网络通信库:acl 框架库简介
一、概述
acl 工程是一个跨平台的通用网络通信库,同时提供更多的其它有价值功能。通过该库,用户可以非常容易地编写支持多种模式的服务器程序、WEB 应用程序以及数据库应用程序。此外,该库还提供了 XML/JSON/MIME 编码及解码功能,这些编码解码库均支持流式解析模式,从而使之更适应不同的网络通讯方式。
本工程主要包含 5 个库及大量示例。5 个库的说明如下:
1) lib_acl: 该库是最基础的库,其它 4 个库均依赖于该库; 该库以 C 语言实现。
2) lib_protocol: 该库主要实现了 http 协议及 icmp/ping 协议; 该库以 C 语言实现。
3) lib_acl_cpp: 该库用 C++ 语言封装了 lib_acl/lib_protocol 两个库,同时增加了一些其它有价值的功能应用。
4) lib_dict: 该库主要实现了 KEY-VALUE 的字典式存储库,该库另外还依赖于 BDB, CDB 以及 tokyocabinet 库。
5) lib_tls: 该库封装了 openssl 库,使 lib_acl 的通信模式可以支持 ssl。
二、平台支持及编译
整个工程目前支持 Linux(AS4,5,6, CS4,5,6), Windows, MacOS, (原本也支持 FreeBSD, Solaris, 现在如果谁有这些环境,可以轻松移植到这些平台上)。
1) Linux/MacOS: 直接在终端命令行方式下分别进入 lib_acl/lib_protocol/lib_acl_cpp/lib_dict/lib_tls 目录下,运行 make 命令即可。
2) Windows: 可以用 VC2003/VC2010/vc2012 进行编译。(如果您需要用 VC6/VC2005/VC2008 编译,可以参考 VC2003 的编译条件)。
当在 WIN32 环境下使用动态库时有几点需要注意:
a) 使用 lib_acl 的动态库时,需要在用户的工程预定义: ACL_DLL;
b) 使用 lib_protocol 动态库中的 HTTP 库或 ICMP 库时,需要在工程中预定义 HTTP_DLL 、SMTP_DLL或 ICMP_DLL;
c) 使用 lib_acl_cpp 的动态库时,需要在工程中预定义 ACL_CPP_DLL,如果您使用用 VC2003 编译环境则还需要预定义 VC2003;
d) 使用 lib_dict 的动态库时,需要在工程中预定义 DICT_DLL;
e) 使用 lib_tls 的动态库时,需要在工程中预定义 TLS_DLL。
三、本工程目录结构说明
1) lib_acl
1.1 init:主要用于初始化 acl 基础库
1.2 stdlib:是一些比较基础的功能函数库,在 stdlib/ 根目录下主要包括一些有关日志记录、网络/文件流处理、VSTRING缓冲操作等功能函数;在 stdlib/ 下还有二级目录,如下:
1.2.1 common:该目录主要为一些常用的数据结构及算法的功能函数库,象哈希表、链表、队列、动态数组、堆栈、缓存、平衡二叉树、模式匹配树等;
1.2.2 memory:该目录主要包含与内存操作相关的函数库,象内存基础分配与校验、内存池管理、内存切片管理等;
1.2.3 filedir:该目录主要包含与目录遍历、目录创建等相关的库;
1.2.4 configure:该目录主要包含配置文件的分析库;
1.2.5 iostuff:该目录主要包含一些常用的IO操作的函数库,象读/写超时、设置IO句柄的阻塞模式等;
1.2.6 string:该目录主要包含一些常用的字符串操作的库,提供了比标准C更灵活高效的字符串操作功能;
1.2.7 debug:主要用于协助调试内存的泄露等功能;
1.2.8 sys:主要是与不同操作系统平台相关的API的封装函数库;
1.3 net:是与网络操作相关的函数库,包含网络监听、网络连接、DNS查询、套接口参数设置等功能;
1.3.1 connect:主要是与网络连接相关的函数库,包含网络连接、域套接口连接等;
1.3.2 listen:主要是与网络监听相关的函数库,包含网络监听、域套接口监听等;
1.3.3 dns:主要是与DNS域名查询相关的函数库,包含对 gethostbyname 等接口的封装、按RFC1035标准直接发送UDP包方式进行查询等功能;
1.4 event:主要封装了 select/poll/epoll/iocp/win message/kqueue/devpoll 等系统API接口,使处理网络事件更加灵活、高效、简单,另外还包含定时器接口,acl 中的很多网络应用都会用到这些接口,象 aio、master 等模块;
1.5 aio:主要包含网络异步操作的功能函数,该套函数库在处理高并发时有非常高的效率,而且提供了比基础API更为高级的调用方式,比使用象 libevent 之类的函数库更为简单,而且是线程安全的;
1.6 msg:主要包含了基于线程的消息事件及基于网络的消息事件功能;
1.7 thread:主要是封装了各个OS平台下的基础线程API,使对外接口保持一致性,消除了平台的差异性,同时还提供了半驻留线程池的函数库,以及对于线程局部变量的扩展;
1.8 db:主要是一些与数据库有关的功能库,定义了一个通用的数据库连接池的框架(并且实现了mysql的连接池实例);一个简单的内存数据库(由哈希表、链表、平衡二叉树组合而成);ZDB数据存储引擎,这是一个高效的基于数字键的存储引擎;
1.9 proctl:win32 平台下父子进程控制功能库;
1.10 code:常见编码函数库,包括 base64编解码、URL编解码以及一些汉字字符集编码等;
1.11 unit_test:包含有关进行 C 语言单元测试的功能库;
1.12 xml :是一个流式的 xml 解析器及构造器,可以支持阻塞及阻塞式网络通信;
1.13 json :是一个流式的 json 解析器及构造器,可以支持阻塞及阻塞式网络通信;
1.14 master:是在 UNIX 环境下支持多种服务器模式的服务器框架,目前主要支持多进程模式、多进程多线程模式、多进程非阻塞模式、UDP通信模式以及多进程触发器模式;
2) lib_protocol
2.1 http:HTTP 协议相关的库,支持 HTTP/1.1,通讯方式支持同步/异步方式
2.2 icmp:icmp/ping 协议库,支持同步/异步通信方式
2.3 smtp:支持 SMTP 客户端通信库
3) lib_acl_cpp
3.1 stdlib:主要包含字符串处理类(string),xml/json 解析库,zlib 压缩库(依赖于 zlib 库), 日志记录类, 字符集转码(在UNIX环境下需要 iconv 库), 线程类/线程池类, 互斥类(支持线程锁、文件锁);
3.2 mime:支持完整的与邮件编码相关的库(邮件的 rfc2045-rfc2047/rfc822/base64/uucode 编码及解码库);
3.3 master:封装了 C 版 lib_acl 库中的服务器框架,支持进程池模式、线程池模式、非阻塞模式、UDP 通信模式以及触发器模式;
3.4 stream:支持网络流/文件流,支持阻塞/非阻塞两种通信方式,在非阻塞模式下支持 select/poll/epoll/iocp/win32 message/kqueue/devpoll;支持 ssl 加密传输(阻塞及非阻塞方式,需要 polarssl库);
3.5 ipc:在非阻塞通信方式,提供了阻塞模块与非阻塞模块整合的方式;
3.6 http:比较完整的 HTTP 通信库及协议解析库,支持客户端及服务端模式,支持 ssl/gzip 传输方式; 支持类似于 Java HttpServlet 方式的大部分接口,方便编写 CGI 及服务器程序;
3.7 db:封装了 MYSQL/SQLITE 库,支持数据库连接池;
3.8 hsocket:实现了完整的 handler-socket 客户端通信库;
3.9 beanstalk:支持消息队列服务器 beanstalkd 的客户端通信库;
3.10 connpool:通用的 TCP 连接池框架;
3.11 memcache:支持 memcached 通信协议的客户端库(支持连接池);
3.12 queue:磁盘文件队列管理器;
3.13 session:会话管理器,目前支持使用 memcache 客户端库存储会话数据。
4) samples:该目录下的程序主要是基于 lib_acl 及 lib_protocol 库的示例
5) lib_acl_cpp/samples:该目录下的程序主要是基于 lib_acl_cpp 库的示例
下载:http://sourceforge.net/projects/acl/
svn:svn checkout svn://svn.code.sf.net/p/acl/code/trunk acl-code
github:https://github.com/zhengshuxin/acl