??xml version="1.0" encoding="utf-8" standalone="yes"?>国产亚洲精久久久久久无码77777,国产精品成人99久久久久,婷婷久久五月天http://www.shnenglu.com/beautykingdom/category/9502.htmlzh-cnThu, 09 Sep 2010 16:05:45 GMTThu, 09 Sep 2010 16:05:45 GMT60有关异步d、通信 http://www.shnenglu.com/beautykingdom/archive/2010/09/06/126028.htmlchatlerchatlerMon, 06 Sep 2010 09:33:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/09/06/126028.htmlhttp://www.shnenglu.com/beautykingdom/comments/126028.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/09/06/126028.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/126028.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/126028.html ?/div>

一般来_单的异步QAsynchronousQ?调用是这样一U调用方式:发v者请求一个异步调用,通知执行者,然后处理其他工作Q在某一个同步点{待执行者的完成Q执行者执行调用的实际操作Q完成后?知发赯。可以看出,在异步调用中有两U角Ԍ发v者和执行者,它们都是能主动运行的对象Q我们称Z动对象,同时q有一个同步点Q主动对象在同步点协?同步。在本文中,我们讨论主要是通用计算机、多q程多线E的分时操作pȝ上的异步调用。在操作pȝ的角度上来看Q主动对象包括了q程、线E和g上的IC{,至于中断Q可以看作L在某个进E或者线E的上下文借用一下CPU。而同步操作可以通过操作pȝ得各U同步机Ӟ互斥锁,信号灯等{来完成?/div>

我们可以先看看异步调用在Windows(本文中一般不加指出的话,都是ҎNT/2000)d文g中的应用。Windows中的ReadFile和WriteFile都提供了异步的接口。以ReadFileZQ?/div>

BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);

如果最后一个参数lpOverlapped不ؓNULLQƈ且文件以FILE_FLAG_OVERLAPPED 标志打开Q那么这个调用就是异步的QReadFile会立刻返回,如果操作没有立刻完成Q返回FALSEq且GetLastError()q回 ERROR_IO_PENDINGQ,那么调用者可以在某个时刻通过WaitForSingleObject{函数来{待中的hEvent来等待操作完?Q可能已l完成)q行同步Q当操作完成以后Q可以调用GetOverlappedResult者获得操作的l果Q比如是否成功,d了多字节等{。这?的发赯就是应用程序,而执行者就是操作系l本w,至于执行者是怎么执行的,我们会在后面的篇q讨论。而两者的同步是通过一个Windows Event来完成?/div>

把这个异步调用的q程再抽象和扩展一些,我们可以把异步调用需要解决的问题归结Z个:一个是执行的动力,另一个是d对象的调度。简单来_前者是各个d对象Q线E、进E或者一些代码)是如何获得CPUQ后者是各个d对象如何协同工作Q?保证操作的流E是协调正确的。一般来_q程和线E都可以由操作系l直接调度而获得CPUQ而更l粒度的Q比如一些代码的调度Q往往需要一个更复杂的模 型(比如在操作系l内部的实现Q这时候线E的_度太粗了)。而主动对象的调度Q当参与者较的时候,可以通过基本的同步机制来完成Q在更复杂的情况下,?能通过一个schedule机制来做会更实际一些?/div>

动力和调?/div>

如前所qͼ异步调用主要需要解决两 个问题:执行的动力和执行的调度。最普遍的情况就是,一个主导流E的调用者进E(U程Q,一个或多个工作者进E(U程Q,通过操作pȝ提供的同步机制来?成异步调用。这个同步机制在扩展化的情Ş下,是一个或多个栅栏BarrierQ对应于每个同步的执行点。所有需要在q个执行点同步的d对象会等待相应的 BarrierQ直到所有对象都完成。在一些简化的情ŞQ比如说工作者ƈ不关心调用者的同步Q那么这个Barrier可以化成信号灯,在只有一个工作?的情况下Q可以简化成一个Windows事gEvent或者条件变?Condition Variable?/div>

现在来考虑复杂的情 形。假设我们用一些线E来协作完成一工作,各个U程的执行之间有先后序上的限制Q而操作系l就是这工作的调度者,负责在适当的时候调度适当的线E来 获得CPU。显Ӟq发执行中的一个线E对于另外一个线E来_本质上就是异步的Q假如它们之间有调用关系Q那也就是一个异步调用。而操作系l可以通过?本的同步机制使得合适的U程才被调度Q其他未完成的线E则处于{待状态。D例说Q我们有4个线EA,B,C,D来完成一工作,其中的顺序限制是 A>B;C>DQ?#8220;>”表示左边的线E完成必d于右边的U程执行Q?#8220;;”表示两个U程可以同时q行。同时假设B的一个操作需要调?C来完成,显而易见,q时候这个操作就是一个异步调用。我们可以在每个“>”的位|设定一个同步点Q然后通过一个信L来完成同步。线EBQC{待 W一个信LQ而D会等待第二个信号灯。这个例子的动力和调度都是通过操作pȝ的基本机ӞU程调度和同步机Ӟ来完成?/div>

把这个过E抽象一下,可以描述为:若干个主动对象(包括代码Q协调来完成一工作,通过一个调度器来调度,实际上,q个调度器可能只是一些调度规则。显 Ӟq程或者线E只要被调度p获得CPUQ所以我们主要考虑代码Q比如一个函敎ͼ怎么h能获得执行。用工作者线E来调用q个函数昄是直观和通用的一 个方案。事实上Q在用户I间(user space)或者用h?user mode)Q这个方法是很常用的。而在内核?kernel mode)Q则可以通过中断来获得CPUQ这个通过注册IDT?口和触发软中断就可以完成。硬件设备上的IC是另一个动力之源。而主动对象的调度Q最基本的也是前面说的各U同步机制。另一个常用的机制是回调函数Q需 要注意的是,回调函数一般会发生在跟调用者不一L上下文,比如说同一个进E的不同U程Q这个差别会带来一些限制。如果需要回调发生在调用者的q程Q线 E)上下文,则需要一些类似Unix下的signal或者Windows下的APC机制Q这一Ҏ们在后面会有所阐述。那么在回调函数里面一般作些什么事情呢Q最常用的, 跟同步机制结合在一P当然是释放一个互斥锁Q信L或者Windows EventQUnix的条件变量){等Q从而得等待同步的其他对象可以得到调度而重新执行,实际上,也可以看作是通知调度器(操作pȝQ某些主动对?Q等待同步的Q可以重新被调度了,从而调度器重新调度。但是对于另外一些调度器Q在q个q程中可能不需要同步对象的参与。在一些极端一些的例子里,调度?至不要求严格有序的?/div>

在实际应用中Q根据环境的限制Q异步调用的动力和调度的实现方式可以有很大差别。我们会在后面的例子里加以说明?操作pȝ中的异步QWindows的异步I/O?/div>

Windows NT/2000是一个抢占式的分时操作系l。Windows的调度单位是U程Q它?I/O架构是完全异步的Q也是说同步的I/O实际上都Z异步I/O来完成。一个用h的U程h一个I/O的时候会D一个运行状态从user mode到kernel mode的{变(操作pȝ把内核映到每个q程?G-4G的地址上,对于每个q程都是一LQ。这个过E是通过中断调用内核输出的一些System Service来完成,比如说ReadFile实际上会执行NtReadFileQZwReadFileQ,需要注意的是,q行上下文仍然是当前U程?NtReadFile的实现则ZWindows内核的异步I/O框架Q在I/O Manager的协助下完成。需要指出的是,I/O Manager只是pqAPI构成的一个抽象概念,q没有一个真正的I/O ManagerU程在运行?/div>

Windows的I/O驱动E序是层ơ堆U的。每个驱动程序会提供一致的接口以供初始化、清理和功能调用。驱动程序的调用ZI/Oh包(I/O Request Packet, IRPQ,而不是像普通的函数调用那样使用栈来传递参数。操作系l和PnP理器根据注册表?适当的时机初始化和清理相应的驱动E序。在一般的功能调用的时候,IRP里面会指定功能调用号码以及相应的上下文或者参敎ͼI/O stack locationQ。一个驱动程序可能调用别的驱动程序,q个q程可能是同步的Q线E上下文不改?Q也可能是异步的。NtReadFile的实玎ͼ大致 是向最上层的驱动程序发Z个或多个IRPQ然后等待相应事件的完成Q同步的情况Q,或者直接返回(带Overlapped的情况)Q这些都在发赯求的 U程执行?/div>

当驱动程序处理IRP的时候,它可能立d成,也可能在中断里才能完成,比如_往g讑֤发出一个请求(通常可以是写 I/O portQ,当设备完成操作的时候会触发一个中断,然后在中断处理函数里得到操作l果。Windows有两cM断,g讑֤的中断和软中断,分成若干个不 同的优先U(IRQLQ。Y中断主要有两U:DPC(Delayed Procedure Call)和APC(Asynchronous Procedure Call)Q都处于较低的优先。驱动程序可以ؓg中断注册ISR(Interrupt Service Routine)Q一般就是修改IDT某个条目的入口。同P操作pȝ也会为DPC和APC注册适当的中断处理例E(也是在IDT中)?/div>

值得指出的是QDPC是跟处理器相关的Q每个处理器会有一个DPC队列Q而APC是跟U程相关的,每个U程会有它的APC队列Q实际上包括一?Kernel APC队列和User APC队列Q它们的调度{略有所区别Q,可以惌QAPCq不严格意义上的中断,因ؓ中断可能发生在Q何一个线E的上下文中Q它被称Z断,主要是因?IRQL的提升(从PASSIVE到APCQ,APC的调度一般在U程切换{等情Ş下进行。当中断发生的时候,操作pȝ会调用中断处理例E,对于g讑֤ 的ISRQ一般处理是兌备中断,发出一个DPChQ然后返回。不在设备的中断处理中用太多的CPU旉Q主要考虑是否则可能丢失别 的中断。由于硬件设备中断的IRQL比DPC中断的高Q所以在ISR里面DPC会阻塞,直到ISRq回IRQL回到较低的水qI才会触发DPC中断Q在 DPC中断里执行从g讑֤d数据以及重新h、开中断{操作。ISR或者DPC可能在Q何被中断的线E上下文Qarbitrary thread contextQ执行,事实上线E的上下文是不可见的Q可以认为是pȝ借用一下时间片而已?/div>

ȝ来说QWindows的异步I/O?构中Q主要有两种动力Q一是发赯求的U程Q一部分内核代码会在q个U程上下文执行,二是ISR和DPCQ这部分内核代码会在中断里完成,可能使用M一 个线E的上下文。而调度常见用回调和事gQKEVENTQ,比如说在往下一层的驱动E序发出h的时候,可以指定一个完成例ECompletion RoutineQ当下层的驱动完成这个请求的时候会调用q个例程Q而往往在这个例E里Q就是简单的触发一下一个事件?另外可以Z提一下Linux。Linux 2.6也有cM的中断机Ӟ它有更多的Y中断优先U,即不同优先的softirqQ而类gDPCQLinux也提供了专门的Y中断Q对应DPC的就?tasklet。Linux没有一个像windowsq么一致的层次驱动E序架构Q所以它的异步I/OE微_糙一些,主要是通过以前的一些阻塞点Q现在直 接返?EIOCBRETRYQ而让调用者在合适的时机l箋重试。在q个Ҏ中,可以认ؓ整个操作׃个函数完成,每次操作有进展时Q都把这个函C头执 行一遍,当然已经完成的部分就不会再有实际的I/O。这L最大好处是原有的文件系l和驱动E序不用完全重写。而对于同步调用,只要d可以了Q这样对 pȝ的修改较。这时候,要提供POSIX aio的语义,可能需要提供一些用LE来完成重试的过E了Q回想Windows可以通过中断和DPC完成的)。而对于SolarisQ也是类似的处理Q如果设备支持异步I/OQ那通过中断可以完成Q否则就使用内部的LWP来模拟?/div>
应用E序Q一个异步的HTTP服务器的设计

假设我们要设计一个HTTP服务器,它的设计目标包括Q高q发性、精 Q部分支持HTTP/1.1Q、支持plug-inl构。在不少场合可能都有q个需求。M上来_HTTP服务器可以类比成一个基于多U程的操作系 l:OS调度每个工作U程在适当的时候获得执行,而工作线E提供服务(也就是处理HTTPhQ。在q个基础上,主要的考虑是调度_度的大,_度太大 的时候ƈ发性会降低Q而粒度太又可能因ؓd切换Q考虑OS的Context SwitchingQ而导致效率降低,所以这又是一个折Ll果。类gApacheQ以及其他的HTTP服务器)Q我们可以把一个HTTP处理q程分ؓ 若干个状态,Zq些状态可以构造出一个HTTP处理的状态机。这U情况下Q我们就可以把每个状态的处理作ؓ调度的粒度。一个调度过E就是:一个工作线E?从全局的Q务队列里取出一个HTTP_Contextl构Q根据当前的状态完成相应处理;然后Ҏ状态机讄下一个状态;再放回到全局的Q务队列里。这?子,若干个HTTP状态就可以通过q个调度{略构成一个完整HTTP处理q程。显而易见,一个状态对于下一个状态处理的调用都可以认为是异步的。一?HTTP状态机的设计如下图所C?/div>

    
?. HTTP状态机

工作U程的函数其实就是两个操作:从状态队列里取出一个HTTP_ContextQ调用HTTP_Context的service()函数Q周而复此?在这个架构上Q就很容易引入异步I/O和Plug-in的机制了。事实上我们也可以用基于事Ӟ例如select/pollQ的I/O{略来模拟异步I /OQ实C使用一个用LE就可以了?/div>

对于异步I/O和Plug-in的调用,我们也是采用cM于Linux 2.6里面aio的重试方案,而异步完成的时候采用回调函数。在某个状态上Q如果系l需要I/O操作Qrecv或者sendQ,则会h一个异步I /OQ操作系l提供的异步I/O或者由用户U程模拟的异步I/OQ,q时候相应的HTTP_Context不会重新回到状态队列里Q而在I/O完成的回?函数里面才会重新攑֛到状态队列,得到重新调度的机会。HTTP_Context得到重新调度的时候会查I/O状态(q个可以通过一些标志位来完成)Q?如果已经完成Q则处理然后讄下一状态,重新调度Q否则可以重新请求一个新的I/Oh。Plug-in也可以用类似的ҎQ比如说一个Plug-in 要跟外部的一个服务器通信Q这时候就可以在通信完成的时候才把HTTP_Context重新攑֛到状态队列。显ӞPlug-in跟HTTP状态是多对?的关p,一个Plug-in可以在若q个兛_的状态注册自w,同时q可以设|一些short-path来提高处理的效率?/div>

l论

ȝ来说Q异步调用的设计和应用归根结底就是对多个d对象的管理问题:如何提供执行的动力以及如何保证执行的序逻辑。主要考虑的问题是d对象的粒 度以及执行方式,同步或者回调来完成序的调度,或者用近似的调度而加一些鲁的错误处理机制来保证语义的正确。后者可以考虑在用基于事件的 socket的时候,readable事g的通知可以是冗余的Q或者说可以比实际中发生的readable事g更多Q这个时候用非d的socket, 有些read()Q或者recv()Q会直接q回EWOULDBLOCKQ系l只要考虑处理q种情况Q用non blocking socket而不是blocking socketQ,当例外的情况不多的时候是可以接受的。这时候可以说事g的报告就只是q似的?/div>
from:



chatler 2010-09-06 17:33 发表评论
]]>一个基于完成端口的TCP Server Framework,析IOCPhttp://www.shnenglu.com/beautykingdom/archive/2010/08/25/124731.htmlchatlerchatlerWed, 25 Aug 2010 12:42:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/08/25/124731.htmlhttp://www.shnenglu.com/beautykingdom/comments/124731.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/08/25/124731.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/124731.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/124731.html如果你不投递(POSTQOverlapped I/OQ那么I/O Completion Ports 只能Z提供一个Queue. 
    CreateIoCompletionPort的NumberOfConcurrentThreadsQ?/span>
1.只有当第二个参数ExistingCompletionPort为NULL时它才有效,它是个max threads limits.
2.大家有谁把它讄出cpu个数的|当然不只是cpu个数?倍,而是下面的MAX_THREADS 100甚至更大?/span>
对于q个值的讑֮Qmsdnq没有说非得设成cpu个数?倍,而且也没有把减少U程之间上下文交换这些媄响扯到这里来。I/O Completion Ports MSDN:"If your transaction required a lengthy computation, a larger concurrency value will allow more threads to run. Each completion packet may take longer to finish, but more completion packets will be processed at the same time. "?/span>
    对于struct OVERLAPPEDQ我们常会如下扩展,
typedef struct {
  WSAOVERLAPPED overlapped; //must be first member?   是的Q必LW一个。如果你不肯定,你可以试试?/span>
  SOCKET client_s;
  SOCKADDR_IN client_addr;
  WORD optCode;//1--read,2--send.  有h怼定义q个数据成员Q但也有Z用,争议在send/WSASend,此时的同步和异步是否有必要? 臛_我下面的server更本没用它?/span>
  char buf[MAX_BUF_SIZE];
  WSABUF wsaBuf;//inited ?  q个不要忘了Q?/span>
  DWORD numberOfBytesTransferred;
  DWORD flags;   

}QSSOverlapped;//for per connection
我下面的server框架的基本思想?
One connection VS one thread in worker thread pool ,worker thread performs completionWorkerRoutine.
A Acceptor thread 专门用来accept socket,兌至IOCP,qWSARecv:post Recv Completion Packet to IOCP.
在completionWorkerRoutine中有以下的职?
1.handle request,当忙时增加completionWorkerThread数量但不过maxThreads,post Recv Completion Packet to IOCP.
2.timeout时检查是否空闲和当前completionWorkerThread数量,当空闲时保持或减至minThreads数量.
3.Ҏ有Accepted-socket理生命周期,q里利用pȝ的keepalive probes,若想实现业务?心蟩探测"只需QSS_SIO_KEEPALIVE_VALS_TIMEOUT 改回pȝ默认?时.
下面l合源代?析一下IOCP:
socketserver.h
#ifndef __Q_SOCKET_SERVER__
#define __Q_SOCKET_SERVER__
#include <winsock2.h>
#include <mstcpip.h>
#define QSS_SIO_KEEPALIVE_VALS_TIMEOUT 30*60*1000
#define QSS_SIO_KEEPALIVE_VALS_INTERVAL 5*1000

#define MAX_THREADS 100
#define MAX_THREADS_MIN  10
#define MIN_WORKER_WAIT_TIMEOUT  20*1000
#define MAX_WORKER_WAIT_TIMEOUT  60*MIN_WORKER_WAIT_TIMEOUT

#define MAX_BUF_SIZE 1024

/*当Accepted socket和socket关闭或发生异常时回调CSocketLifecycleCallback*/
typedef void (*CSocketLifecycleCallback)(SOCKET cs,int lifecycle);//lifecycle:0:OnAccepted,-1:OnClose//注意OnClose此时的socket未必可用,可能已经被非正常关闭或其他异?

/*协议处理回调*/
typedef int (*InternalProtocolHandler)(LPWSAOVERLAPPED overlapped);//return -1:SOCKET_ERROR

typedef struct Q_SOCKET_SERVER SocketServer;
DWORD initializeSocketServer(SocketServer ** ssp,WORD passive,WORD port,CSocketLifecycleCallback cslifecb,InternalProtocolHandler protoHandler,WORD minThreads,WORD maxThreads,long workerWaitTimeout);
DWORD startSocketServer(SocketServer *ss);
DWORD shutdownSocketServer(SocketServer *ss);

#endif
 qsocketserver.c      U?qss,相应的OVERLAPPEDUqssOl.
#include "socketserver.h"
#include "stdio.h"
typedef struct {  
  WORD passive;//daemon
  WORD port;
  WORD minThreads;
  WORD maxThreads;
  volatile long lifecycleStatus;//0-created,1-starting, 2-running,3-stopping,4-exitKeyPosted,5-stopped 
  long  workerWaitTimeout;//wait timeout  
  CRITICAL_SECTION QSS_LOCK;
  volatile long workerCounter;
  volatile long currentBusyWorkers;
  volatile long CSocketsCounter;//Accepted-socket引用计数
  CSocketLifecycleCallback cslifecb;
  InternalProtocolHandler protoHandler;
  WORD wsaVersion;//=MAKEWORD(2,0);
  WSADATA wsData;
  SOCKET server_s;
  SOCKADDR_IN serv_addr;
  HANDLE iocpHandle;
}QSocketServer;

typedef struct {
  WSAOVERLAPPED overlapped;  
  SOCKET client_s;
  SOCKADDR_IN client_addr;
  WORD optCode;
  char buf[MAX_BUF_SIZE];
  WSABUF wsaBuf;
  DWORD numberOfBytesTransferred;
  DWORD flags;
}QSSOverlapped;

DWORD  acceptorRoutine(LPVOID);
DWORD  completionWorkerRoutine(LPVOID);

static void adjustQSSWorkerLimits(QSocketServer *qss){
  /*adjust size and timeout.*/
  /*if(qss->maxThreads <= 0) {
   qss->maxThreads = MAX_THREADS;
        } else if (qss->maxThreads < MAX_THREADS_MIN) {            
         qss->maxThreads = MAX_THREADS_MIN;
        }
        if(qss->minThreads >  qss->maxThreads) {
         qss->minThreads =  qss->maxThreads;
        }
        if(qss->minThreads <= 0) {
            if(1 == qss->maxThreads) {
             qss->minThreads = 1;
            } else {
             qss->minThreads = qss->maxThreads/2;
            }
        }
        
        if(qss->workerWaitTimeout<MIN_WORKER_WAIT_TIMEOUT) 
         qss->workerWaitTimeout=MIN_WORKER_WAIT_TIMEOUT;
        if(qss->workerWaitTimeout>MAX_WORKER_WAIT_TIMEOUT)
         qss->workerWaitTimeout=MAX_WORKER_WAIT_TIMEOUT;        */
}

typedef struct{
 QSocketServer * qss;
 HANDLE th;
}QSSWORKER_PARAM;

static WORD addQSSWorker(QSocketServer *qss,WORD addCounter){
 WORD res=0;
 if(qss->workerCounter<qss->minThreads||(qss->currentBusyWorkers==qss->workerCounter&&qss->workerCounter<qss->maxThreads)){
  DWORD threadId;
  QSSWORKER_PARAM * pParam=NULL;
  int i=0;  
  EnterCriticalSection(&qss->QSS_LOCK);
  if(qss->workerCounter+addCounter<=qss->maxThreads)
   for(;i<addCounter;i++)
   {
    pParam=malloc(sizeof(QSSWORKER_PARAM));
    if(pParam){
     pParam->th=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)completionWorkerRoutine,pParam,CREATE_SUSPENDED,&threadId);
     pParam->qss=qss;
     ResumeThread(pParam->th);
     qss->workerCounter++,res++; 
    }    
   }  
  LeaveCriticalSection(&qss->QSS_LOCK);
 }  
 return res;
}

static void SOlogger(const char * msg,SOCKET s,int clearup){
 perror(msg);
 if(s>0)
 closesocket(s);
 if(clearup)
 WSACleanup();
}

static int _InternalEchoProtocolHandler(LPWSAOVERLAPPED overlapped){
 QSSOverlapped *qssOl=(QSSOverlapped *)overlapped;
 
 printf("numOfT:%d,WSARecvd:%s,\n",qssOl->numberOfBytesTransferred,qssOl->buf);
 //Sleep(500); 
 return send(qssOl->client_s,qssOl->buf,qssOl->numberOfBytesTransferred,0);
}

DWORD initializeSocketServer(SocketServer ** ssp,WORD passive,WORD port,CSocketLifecycleCallback cslifecb,InternalProtocolHandler protoHandler,WORD minThreads,WORD maxThreads,long workerWaitTimeout){
 QSocketServer * qss=malloc(sizeof(QSocketServer));
 qss->passive=passive>0?1:0;
 qss->port=port;
 qss->minThreads=minThreads;
 qss->maxThreads=maxThreads;
 qss->workerWaitTimeout=workerWaitTimeout;
 qss->wsaVersion=MAKEWORD(2,0); 
 qss->lifecycleStatus=0;
 InitializeCriticalSection(&qss->QSS_LOCK);
 qss->workerCounter=0;
 qss->currentBusyWorkers=0;
 qss->CSocketsCounter=0;
 qss->cslifecb=cslifecb,qss->protoHandler=protoHandler;
 if(!qss->protoHandler)
  qss->protoHandler=_InternalEchoProtocolHandler; 
 adjustQSSWorkerLimits(qss);
 *ssp=(SocketServer *)qss;
 return 1;
}

DWORD startSocketServer(SocketServer *ss){ 
 QSocketServer * qss=(QSocketServer *)ss;
 if(qss==NULL||InterlockedCompareExchange(&qss->lifecycleStatus,1,0))
  return 0; 
 qss->serv_addr.sin_family=AF_INET;
 qss->serv_addr.sin_port=htons(qss->port);
 qss->serv_addr.sin_addr.s_addr=INADDR_ANY;//inet_addr("127.0.0.1");
 if(WSAStartup(qss->wsaVersion,&qss->wsData)){  
  /*q里q有个插曲就是这个WSAStartup被调用的时?它居然会启动一条额外的U程,当然E后q条U程会自动退出的.不知WSAClearup又会如何?......*/

  SOlogger("WSAStartup failed.\n",0,0);
  return 0;
 }
 qss->server_s=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
 if(qss->server_s==INVALID_SOCKET){  
  SOlogger("socket failed.\n",0,1);
  return 0;
 }
 if(bind(qss->server_s,(LPSOCKADDR)&qss->serv_addr,sizeof(SOCKADDR_IN))==SOCKET_ERROR){  
  SOlogger("bind failed.\n",qss->server_s,1);
  return 0;
 }
 if(listen(qss->server_s,SOMAXCONN)==SOCKET_ERROR)/*q里来谈?strong>backlog,很多Z知道设成何?我见到过1,5,50,100?有h说设定的大耗资?的确,q里设成SOMAXCONN不代表windows会真的用SOMAXCONN,而是" If set to SOMAXCONN, the underlying service provider responsible for socket s will set the backlog to a maximum reasonable value. "Q同时在现实环境中,不同操作pȝ支持TCP~冲队列有所不同Q所以还不如让操作系l来军_它的倹{像Apacheq种服务器:
#ifndef DEFAULT_LISTENBACKLOG
#define DEFAULT_LISTENBACKLOG 511
#endif
*/
    {        
  SOlogger("listen failed.\n",qss->server_s,1);
        return 0;
    }
 qss->iocpHandle=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,/*NumberOfConcurrentThreads-->*/qss->maxThreads);
 //initialize worker for completion routine.
 addQSSWorker(qss,qss->minThreads);  
 qss->lifecycleStatus=2;
 {
  QSSWORKER_PARAM * pParam=malloc(sizeof(QSSWORKER_PARAM));
  pParam->qss=qss;
  pParam->th=NULL;
  if(qss->passive){
   DWORD threadId;
   pParam->th=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)acceptorRoutine,pParam,0,&threadId); 
  }else
   return acceptorRoutine(pParam);
 }
 return 1;
}

DWORD shutdownSocketServer(SocketServer *ss){
 QSocketServer * qss=(QSocketServer *)ss;
 if(qss==NULL||InterlockedCompareExchange(&qss->lifecycleStatus,3,2)!=2)
  return 0; 
 closesocket(qss->server_s/*listen-socket*/);//..other accepted-sockets associated with the listen-socket will not be closed,except WSACleanup is called.. 
 if(qss->CSocketsCounter==0)
  qss->lifecycleStatus=4,PostQueuedCompletionStatus(qss->iocpHandle,0,-1,NULL);
 WSACleanup();  
 return 1;
}

DWORD  acceptorRoutine(LPVOID ss){
 QSSWORKER_PARAM * pParam=(QSSWORKER_PARAM *)ss;
 QSocketServer * qss=pParam->qss;
 HANDLE curThread=pParam->th;
 QSSOverlapped *qssOl=NULL;
 SOCKADDR_IN client_addr;
 int client_addr_leng=sizeof(SOCKADDR_IN);
 SOCKET cs; 
 free(pParam);
 while(1){  
  printf("accept starting.....\n");
  cs/*Accepted-socket*/=accept(qss->server_s,(LPSOCKADDR)&client_addr,&client_addr_leng);
  if(cs==INVALID_SOCKET)
        {
   printf("accept failed:%d\n",GetLastError());   
            break;
        }else{//SO_KEEPALIVE,SIO_KEEPALIVE_VALS q里是利用系l的"心蟩探测",keepalive probes.linux:setsockopt,SOL_TCP:TCP_KEEPIDLE,TCP_KEEPINTVL,TCP_KEEPCNT
            struct tcp_keepalive alive,aliveOut;
            int so_keepalive_opt=1;
            DWORD outDW;
            if(!setsockopt(cs,SOL_SOCKET,SO_KEEPALIVE,(char *)&so_keepalive_opt,sizeof(so_keepalive_opt))){
               alive.onoff=TRUE;
               alive.keepalivetime=QSS_SIO_KEEPALIVE_VALS_TIMEOUT;
               alive.keepaliveinterval=QSS_SIO_KEEPALIVE_VALS_INTERVAL;
               if(WSAIoctl(cs,SIO_KEEPALIVE_VALS,&alive,sizeof(alive),&aliveOut,sizeof(aliveOut),&outDW,NULL,NULL)==SOCKET_ERROR){
                    printf("WSAIoctl SIO_KEEPALIVE_VALS failed:%d\n",GetLastError());   
                    break;
                }

            }else{
                     printf("setsockopt SO_KEEPALIVE failed:%d\n",GetLastError());   
                     break;
            }  
  }
  
  CreateIoCompletionPort((HANDLE)cs,qss->iocpHandle,cs,0);
  if(qssOl==NULL){
   qssOl=malloc(sizeof(QSSOverlapped));   
  }
  qssOl->client_s=cs;
  qssOl->wsaBuf.len=MAX_BUF_SIZE,qssOl->wsaBuf.buf=qssOl->buf,qssOl->numberOfBytesTransferred=0,qssOl->flags=0;//initialize WSABuf.
  memset(&qssOl->overlapped,0,sizeof(WSAOVERLAPPED));  
  {
   DWORD lastErr=GetLastError();
   int ret=0;
   SetLastError(0);
   ret=WSARecv(cs,&qssOl->wsaBuf,1,&qssOl->numberOfBytesTransferred,&qssOl->flags,&qssOl->overlapped,NULL);
   if(ret==0||(ret==SOCKET_ERROR&&GetLastError()==WSA_IO_PENDING)){
    InterlockedIncrement(&qss->CSocketsCounter);//Accepted-socket计数递增.
    if(qss->cslifecb)
     qss->cslifecb(cs,0);
    qssOl=NULL;
   }    
   
   if(!GetLastError())
    SetLastError(lastErr);
  }
  
  printf("accept flags:%d ,cs:%d.\n",GetLastError(),cs);
 }//end while.

 if(qssOl)
  free(qssOl);
 if(qss)
  shutdownSocketServer((SocketServer *)qss);
 if(curThread)
  CloseHandle(curThread);

 return 1;
}

static int postRecvCompletionPacket(QSSOverlapped * qssOl,int SOErrOccurredCode){ 
 int SOErrOccurred=0; 
 DWORD lastErr=GetLastError();
 SetLastError(0);
 //SOCKET_ERROR:-1,WSA_IO_PENDING:997
 if(WSARecv(qssOl->client_s,&qssOl->wsaBuf,1,&qssOl->numberOfBytesTransferred,&qssOl->flags,&qssOl->overlapped,NULL)==SOCKET_ERROR
  &&GetLastError()!=WSA_IO_PENDING)//this case lastError maybe 64, 10054 
 {
  SOErrOccurred=SOErrOccurredCode;  
 }      
 if(!GetLastError())
  SetLastError(lastErr); 
 if(SOErrOccurred)
  printf("worker[%d] postRecvCompletionPacket SOErrOccurred=%d,preErr:%d,postedErr:%d\n",GetCurrentThreadId(),SOErrOccurred,lastErr,GetLastError());
 return SOErrOccurred;
}

DWORD  completionWorkerRoutine(LPVOID ss){
 QSSWORKER_PARAM * pParam=(QSSWORKER_PARAM *)ss;
 QSocketServer * qss=pParam->qss;
 HANDLE curThread=pParam->th;
 QSSOverlapped * qssOl=NULL;
 DWORD numberOfBytesTransferred=0;
 ULONG_PTR completionKey=0;
 int postRes=0,handleCode=0,exitCode=0,SOErrOccurred=0; 
 free(pParam);
 while(!exitCode){
  SetLastError(0);
  if(GetQueuedCompletionStatus(qss->iocpHandle,&numberOfBytesTransferred,&completionKey,(LPOVERLAPPED *)&qssOl,qss->workerWaitTimeout)){
   if(completionKey==-1&&qss->lifecycleStatus>=4)
   {
    printf("worker[%d] completionKey -1:%d \n",GetCurrentThreadId(),GetLastError());
    if(qss->workerCounter>1)
     PostQueuedCompletionStatus(qss->iocpHandle,0,-1,NULL);
    exitCode=1;
    break;
   }
   if(numberOfBytesTransferred>0){   
    
    InterlockedIncrement(&qss->currentBusyWorkers);
    addQSSWorker(qss,1);
    handleCode=qss->protoHandler((LPWSAOVERLAPPED)qssOl);    
    InterlockedDecrement(&qss->currentBusyWorkers);    
    
    if(handleCode>=0){
     SOErrOccurred=postRecvCompletionPacket(qssOl,1);
    }else
     SOErrOccurred=2;    
   }else{
    printf("worker[%d] numberOfBytesTransferred==0 ***** closesocket servS or cs *****,%d,%d ,ol is:%d\n",GetCurrentThreadId(),GetLastError(),completionKey,qssOl==NULL?0:1);
    SOErrOccurred=3;     
   }  
  }else{ //GetQueuedCompletionStatus rtn FALSE, lastError 64 ,995[timeout worker thread exit.] ,WAIT_TIMEOUT:258        
   if(qssOl){
    SOErrOccurred=postRecvCompletionPacket(qssOl,4);
   }else {    

    printf("worker[%d] GetQueuedCompletionStatus F:%d \n",GetCurrentThreadId(),GetLastError());
    if(GetLastError()!=WAIT_TIMEOUT){
     exitCode=2;     
    }else{//wait timeout     
     if(qss->lifecycleStatus!=4&&qss->currentBusyWorkers==0&&qss->workerCounter>qss->minThreads){
      EnterCriticalSection(&qss->QSS_LOCK);
      if(qss->lifecycleStatus!=4&&qss->currentBusyWorkers==0&&qss->workerCounter>qss->minThreads){
       qss->workerCounter--;//until qss->workerCounter decrease to qss->minThreads
       exitCode=3;      
      }
      LeaveCriticalSection(&qss->QSS_LOCK);
     }
    }    
   }    
  }//end GetQueuedCompletionStatus.

  if(SOErrOccurred){   
   if(qss->cslifecb)
    qss->cslifecb(qssOl->client_s,-1);
   /*if(qssOl)*/{
    closesocket(qssOl->client_s);
    free(qssOl);
   }
   if(InterlockedDecrement(&qss->CSocketsCounter)==0&&qss->lifecycleStatus>=3){    
    //for qss workerSize,PostQueuedCompletionStatus -1
    qss->lifecycleStatus=4,PostQueuedCompletionStatus(qss->iocpHandle,0,-1,NULL);        
    exitCode=4;
   }
  }
  qssOl=NULL,numberOfBytesTransferred=0,completionKey=0,SOErrOccurred=0;//for net while.
 }//end while.

 //last to do 
 if(exitCode!=3){ 
  int clearup=0;
  EnterCriticalSection(&qss->QSS_LOCK);
  if(!--qss->workerCounter&&qss->lifecycleStatus>=4){//clearup QSS
    clearup=1;
  }
  LeaveCriticalSection(&qss->QSS_LOCK);
  if(clearup){
   DeleteCriticalSection(&qss->QSS_LOCK);
   CloseHandle(qss->iocpHandle);
   free(qss); 
  }
 }
 CloseHandle(curThread);
 return 1;
}
------------------------------------------------------------------------------------------------------------------------
    对于IOCP的LastError的L别和处理是个隄,所以请注意我的completionWorkerRoutine的whilel构,
l构如下:
while(!exitCode){
    if(completionKey==-1){...break;}
    if(GetQueuedCompletionStatus){/*在这个if体中只要你投递的OVERLAPPED is not NULL,那么q里你得到的是?/strong>.*/
        if(numberOfBytesTransferred>0){
               /*在这里handle request,记得要l投递你的OVERLAPPED? */
        }else{
              /*q里可能客户端或服务端closesocket(the socket),但是OVERLAPPED is not NULL,只要你投递的不ؓNULL!*/
        }
    }else{/*在这里的if体中,虽然GetQueuedCompletionStatus return FALSE,但是不代表OVERLAPPED一定ؓNULL.特别是OVERLAPPED is not NULL的情况下,不要以ؓLastError发生?׃表当前的socket无用或发生致命的异常,比如发生lastError:995q种情况下此时的socket有可能是一切正常的可用?你不应该关闭?/strong>.*/
        if(OVERLAPPED is not NULL){
             /*q种情况?请不?7,21l箋投递吧!在投递后再检错?/strong>.*/
        }else{ 

        }
    }
  if(socket error occured){

  }
  prepare for next while.

    行文仓促,隑օ有错误或不之处,希望大家t跃指正评论,谢谢!

    q个模型在性能上还是有改进的空间哦Q?/strong>


from:

http://www.shnenglu.com/adapterofcoms/archive/2010/06/26/118781.aspx



chatler 2010-08-25 20:42 发表评论
]]>
IOCPhttp://www.shnenglu.com/beautykingdom/archive/2010/05/05/114496.htmlchatlerchatlerWed, 05 May 2010 07:51:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/05/05/114496.htmlhttp://www.shnenglu.com/beautykingdom/comments/114496.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/05/05/114496.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/114496.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/114496.htmlhttp://msdn.microsoft.com/en-us/library/aa365198%28v=VS.85%29.aspx

另外Q下面这个讲的也q是不错的?br>http://www.shnenglu.com/kevinlynx/archive/2008/06/23/54390.html

IOCP 与线E?/a>

author : Kevin Lynx

 

什么是完成包?

完成包,即IO Completion PacketQ是指异步IO操作完毕后OS提交l应用层的通知包。IOCPl护了一个IO操作l果队列Q里?br>保存着各种完成包。应用层调用 GQCS(也就是GetQueueCompletionStatus)函数获取q些完成包?

最大ƈ发线E数

在一个典型的 IOCPE序里,会有一些线E调用GQCS去获取IO操作l果。最大ƈ发线E数指定在同一时刻处理完成包的U程数目?br>该参数在调用 CreateIoCompletionPort时由NumberOfConcurrentThreads指定?

工作者线E?/strong>

工作者线E一般指? 是调用GQCS函数的线E。要注意的是Q工作者线E数和最大ƈ发线E数q不是同一回事(见下?。工作?br>U程由应用层昄创徏 (_beginthreadex 之类)。工作者线E通常是一个@环,会不断地GQCS到完成包Q然后处理完成包?

调度q程

工作者线E以是否d 分ؓ两种状态:q行状态和{待状态。当U程做一些阻塞操作时(U程同步Q甚至GQCSI的完成队列)Q线E?br>处于{待状态;否则Q线E处于运行状 态?

另一斚wQOS会始l保持某一时刻处于q行状态的U程数小于最大ƈ发线E数。每一? 调用GQCS函数的线EOS实际上都会进行记录,
当完成队列里有完成包ӞOS会首先检查当前处于运行状态的工作U程数是否小于最大ƈ发线E数Q? 如果于QOS会按照LIFO的顺
序让某个工作者线E从GQCSq回(此工作者线E{换ؓq行状?。如何决定这个LIFOQ这是简单地通过调用 GQCS函数的顺序决定的?

从这里可以看出,q里涉及到线E唤醒和睡眠的操作。如果两? U程被放|于同一个CPU上,׃有线E切换的开销。因此,Z?br>除这个开销Q最大ƈ发线E数被徏议ؓ讄成CPU数量?

从以上调度过E还可以看出Q如果某个处于运行状态的工作者线E在处理完成包时d?例如U程同步、其他IO操作)Q那么就?br>CPU 资源处于I闲状态。因此,我们也看到很多文档里Q工作者线E数?CPU?2+2)?

在一个等待线E{换到q行状态时Q有可能会出现短暂的旉q行U程数超q最大ƈ发线E数Q这个时候OS会迅速地让这个新转换
? U程dQ从而减这个数量?关于q个观点QMSDN上只_by not allowing any new active threadsQ却没说明not allowing
what)

? 度原?/strong>

q个知道了其实没什么意义,都是内核做的事,大致上都是操作线 Econtrol blockQ直接摘?lt;Inside IO Completion Ports>:

The list of threads hangs off the queue object. A thread's control block data structure has a pointer in it that
references the queue object of a queue that it is associated with; if the pointer is NULL then the thread is not
associated with a queue.

So how does NT keep track of threads that become inactive because they block on something other than the completion
port" The answer lies in the queue pointer in a thread's control block. The scheduler routines that are executed
in response to a thread blocking (KeWaitForSingleObject, KeDelayExecutionThread, etc.) check the thread's queue
pointer and if its not NULL they will call KiActivateWaiterQueue, a queue-related function. KiActivateWaiterQueue
decrements the count of active threads associated with the queue, and if the result is less than the maximum and
there is at least one completion packet in the queue then the thread at the front of the queue's thread list is
woken and given the oldest packet. Conversely, whenever a thread that is associated with a queue wakes up after
blocking the scheduler executes the function KiUnwaitThread, which increments the queue's active count.

? 考资?/strong>

<Inside I/O Completion Ports>:
http://technet.microsoft.com/en-us/sysinternals/bb963891.aspx
<I/O Completion Ports>:
http://msdn.microsoft.com/en-us/library/aa365198(VS.85).aspx
<INFO: Design Issues When Using IOCP in a Winsock Server>:
http://support.microsoft.com/kb/192800/en-us/





chatler 2010-05-05 15:51 发表评论
]]>
CALLBACK, WINAPI, AFXAPI和函数调用方?/title><link>http://www.shnenglu.com/beautykingdom/archive/2010/04/28/113791.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Tue, 27 Apr 2010 16:10:00 GMT</pubDate><guid>http://www.shnenglu.com/beautykingdom/archive/2010/04/28/113791.html</guid><wfw:comment>http://www.shnenglu.com/beautykingdom/comments/113791.html</wfw:comment><comments>http://www.shnenglu.com/beautykingdom/archive/2010/04/28/113791.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/beautykingdom/comments/commentRss/113791.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/beautykingdom/services/trackbacks/113791.html</trackback:ping><description><![CDATA[<p style="margin: 0cm 0cm 0pt;">(VC~译器下)</p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;"><strong>1. CALLBACK</strong><strong>Q?/strong><strong>WINAPI</strong><strong>?/strong><strong>AFXAPI</strong><strong>到底是什么?它们分别在什么地? 被定义的Q?/strong><strong></strong></p> <p style="margin: 0cm 0cm 0pt;">在头文gwindef.h中,CALLBACK, WINAPI, APIENTRY</p> <p style="margin: 0cm 0cm 0pt;">……</p> <p style="margin: 0cm 0cm 0pt;"><strong>#define CALLBACK  __stdcall</strong></p> <p style="margin: 0cm 0cm 0pt;"><strong>#define WINAPI         __stdcall</strong></p> <p style="margin: 0cm 0cm 0pt;">#define WINAPIV       __cdecl</p> <p style="margin: 0cm 0cm 0pt;"><strong>#define APIENTRY    WINAPI</strong></p> <p style="margin: 0cm 0cm 0pt;">……</p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">在头文gAFXVER_.H中,AFXAPI的定义如下:</p> <p style="margin: 0cm 0cm 0pt;">    ……</p> <p style="margin: 0cm 0cm 0pt;">   // AFXAPI is used on global public functions</p> <p style="margin: 0cm 0cm 0pt;">#ifndef AFXAPI</p> <p style="margin: 0cm 0cm 0pt;"><strong>        #define AFXAPI __stdcall</strong></p> <p style="margin: 0cm 0cm 0pt;">#endif</p> <p style="margin: 0cm 0cm 0pt;">    ……</p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;"><strong>2. __stdcall</strong><strong>?/strong><strong>__cdecl</strong><strong>有什么作用?他们的区别是什么?</strong><strong></strong></p> <p style="margin: 0cm 0cm 0pt;">a. __stdcall是新标准C/C++函数的调用方法。从底层上说Q用这U调用方法参数的q栈序? 标准C调用(__cdeclҎ)是一L,?/p> <p style="margin: 0cm 0cm 0pt;">  是从叛_?但是__stdcall采用自动清栈的方式,而__cdecl是手工清栈?/p> <p style="margin: 0cm 0cm 0pt;"></p> <p style="margin: 0cm 0cm 0pt;">b. windows规定,凡事由它来负责调用的函数必须定义为_stdcallcdQ比如回调函数、WinMain函数{?/p> <p style="margin: 0cm 0cm 0pt;"></p> <p style="margin: 0cm 0cm 0pt;">c. 如果没有显试声明的话Q函数的调用Ҏ默认是__cdecl?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;"><strong>3. </strong><strong>调用U定U类</strong><strong></strong></p> <p style="margin: 0cm 0cm 0pt;">   一共有5中函数调用约?calling convention)Q它军_一下内 容:</p> <p style="margin: 0cm 0cm 0pt;">   1) 函数参数的压栈顺?/p> <p style="margin: 0cm 0cm 0pt;">   2) p用者还是被调用者把参数弹出?/p> <p style="margin: 0cm 0cm 0pt;">   3) 产生函数修饰名的Ҏ</p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">__stdcall调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">函数的参数自叛_左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,</p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">__cdecl调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">是C和C++E序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码, 所以生的可执行文件大会比调用__stdcall函数的大。函数采用从叛_左的压栈方式。注意:对于可变参数的成员函敎ͼ始终使用__cdecl的{换方式?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">__fastcall调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更的参数Q剩下的参数仍旧自右向左压栈传送,被调用的函数? q回前清理传送参数的内存??/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">thiscall调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">仅仅应用?C++"? 员函数。this? 针存放于CX寄存 器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">naked call调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">采用上述4U调用约 定时Q如果必要的话,q入函数时编译器会生代码来保存ESIQEDIQEBXQEBP寄存器,退出函数时则生代码恢复这些寄存器的内宏Vnaked call不生这L代码。naked call不是cd修饰W,故必d_declspec共同使用?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">关键?__stdcall、__cdecl ?__fastcall 可以直接加在要输出的函数前,也可以在~译环境?Setting...\C/C++ \Co<wbr>de Generation w择。当加在 输出函数前的关键字与~译环境中的选择不同Ӟ直接加在输出函数前的关键字有效。它们对应的命o行参数分别ؓ/Gz?Gd ?/Gr。缺省状态ؓ/GdQ即__cdecl。缺省状态ؓ__cdecl?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;"><strong>4. </strong><strong>? 字修饰约?/strong><strong></strong></p> <p style="margin: 0cm 0cm 0pt;">"C" 或?"C++" 函数在内部(~译和链接)通过修饰名识别。修饰名是编译器在编? 函数定义或者原型时生成的字W串。有些情况下使用函数的修饰名是必要的Q如在模块定义文仉头指定输?C++"重蝲函数、构造函数、析构函敎ͼ又如在汇~代码里调用"C""?C++"函数{。修饰名由函数名、类名、调用约定、返回类型、参数等共同 军_。函数名修饰U定随编译种c?C或C++)和调用约定的不同而不同,下面分别说明?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">C~译时函数名修饰U定规则Q?/p> <p style="margin: 0cm 0cm 0pt;">__stdcall调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">    在输出函数名前加上一 个下划线前缀Q后面加上一?@"W号 和其参数的字节数Q格式ؓ _functionname@number?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">__cdecl调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">    仅在输出函数名前加上一个下划线前缀Q格式ؓ _functionname?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">__fastcall调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">    在输出函数名前加上一?@"W号Q后面也是一?@"W号和其参数的字节数Q格式ؓ@functionname@number?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">它们均不改变输出函数名中的字W大写?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">C++~译时函数名修饰U定规则Q?/p> <p style="margin: 0cm 0cm 0pt;">__stdcall调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">??"标识 函数名的开始,后跟函数名;函数名后面以"@@YG"标识参数表的开始,后跟参数表;</p> <p style="margin: 0cm 0cm 0pt;">参数表以代号表示Q?/p> <p style="margin: 0cm 0cm 0pt;">    X——voidQ?/p> <p style="margin: 0cm 0cm 0pt;">    D——charQ?/p> <p style="margin: 0cm 0cm 0pt;">    E——unsigned charQ?/p> <p style="margin: 0cm 0cm 0pt;">    F——shortQ?/p> <p style="margin: 0cm 0cm 0pt;">    H——intQ?/p> <p style="margin: 0cm 0cm 0pt;">    I——unsigned intQ?/p> <p style="margin: 0cm 0cm 0pt;">    J——longQ?/p> <p style="margin: 0cm 0cm 0pt;">    K——unsigned longQ?/p> <p style="margin: 0cm 0cm 0pt;">    M——floatQ?/p> <p style="margin: 0cm 0cm 0pt;">    N——doubleQ?/p> <p style="margin: 0cm 0cm 0pt;">    _N——boolQ?/p> <p style="margin: 0cm 0cm 0pt;">    ....</p> <p style="margin: 0cm 0cm 0pt;">    PA——表C指针,后面的代可明指针类型,如果相同cd的指针连l出玎ͼ ?0"代替Q一?0"代表一ơ重复;</p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">参数表的W一ؓ该函数的q回值类型,其后依次为参数的数据cd,指针标识在其所指数据类型前?/p> <p style="margin: 0cm 0cm 0pt;">参数表后?@Z"标识整个名字的结束,如果该函数无参数Q则?Z"标识l束。其格式?/p> <p style="margin: 0cm 0cm 0pt;">    "?functionname@@YG*****@Z" ? "?functionname@@YG*XZ"Q?/p> <p style="margin: 0cm 0cm 0pt;">    例如</p> <p style="margin: 0cm 0cm 0pt;">    int Test1(char *var1,unsigned long)    -----“?Test1@@YGHPADK@Z”</p> <p style="margin: 0cm 0cm 0pt;">    void Test2()              -----“?Test2@@YGXXZ”(W一个X表示q回cdQ第二个X表示参数 cd)</p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">__cdecl调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">    规则同上面的_stdcall调用U定Q只是参数表的开始标识由上面?@@YG"变ؓ"@@YA"。VC++对函数的省缺声明?__cedcl"Q将只能被C/C++调用?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">__fastcall调用U定Q?/p> <p style="margin: 0cm 0cm 0pt;">    规则同上面的_stdcall调用U定Q只是参数表的开始标识由上面?@@YG"变ؓ"@@YI"?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">对于C++的类成员函数Q其调用方式是thiscallQ,函数的名字修C非成员的C++函数E有不同Q首先就是在函数名字和参数表之间插入?#8220;@”字符引导的类名;其次是参数表的开始标识不同,公有QpublicQ成员函数的标识?#8220;@@QAE”,保护QprotectedQ成员函数的标识?#8220;@@IAE”,U有QprivateQ成员函数的标识?#8220;@@AAE”Q如果函数声明用了const关键字,则相应的标识应分别ؓ“@@QBE”Q?#8220;@@IBE”?#8220;@@ABE”。如果参数类型是cd例的引用Q则使用“AAV1”Q对于constcd的引用,则?#8220;ABV1”。下面就以类CTestZ说明C++成员函数的名字修饰规则:</p> <p style="margin: 0cm 0cm 0pt;">class CTest</p> <p style="margin: 0cm 0cm 0pt;">{</p> <p style="margin: 0cm 0cm 0pt;">......</p> <p style="margin: 0cm 0cm 0pt;">private:</p> <p style="margin: 0cm 0cm 0pt;">    void Function(int);</p> <p style="margin: 0cm 0cm 0pt;">protected:</p> <p style="margin: 0cm 0cm 0pt;">    void CopyInfo(const CTest &src);</p> <p style="margin: 0cm 0cm 0pt;">public:</p> <p style="margin: 0cm 0cm 0pt;">    long DrawText(HDC hdc, long pos, const TCHAR* text, RGBQUAD color, BYTE bUnder, bool bSet);</p> <p style="margin: 0cm 0cm 0pt;">    long InsightClass(DWORD dwClass) const;</p> <p style="margin: 0cm 0cm 0pt;">......</p> <p style="margin: 0cm 0cm 0pt;">};</p> <p style="margin: 0cm 0cm 0pt;">对于成员函数FunctionQ其函数修饰名ؓ“?Function@CTest@@AAEXH@Z”Q字W串“@@AAE”表示q是一个私有函数?#8220;X”表示q回cd为voidQ?#8220;H”表示参数cd为intcd?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">成员函数CopyInfo只有一个参敎ͼ是对cCTest的const引用参数Q其函数修饰名ؓ“?CopyInfo@CTest@@IAEXABV1@@Z”?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">DrawText是一个比较复杂的函数声明Q不仅有字符串参敎ͼq有l构体参数和HDC句柄参数Q需要指出的是HDC实际上是一个HDC__l构cd的指针,q个参数的表C就?#8220;PAUHDC__@@”Q其完整的函C饰名?#8220;?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z”?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">InsightClass是一个共有的const函数Q它的成员函数标识是“@@QBE”Q完整的修饰名就?#8220;?InsightClass@CTest@@QBEJK@Z”?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">举例Q?/p> <p style="margin: 0cm 0cm 0pt;">比如动态链接库a有以下导出函敎ͼ</p> <p style="margin: 0cm 0cm 0pt;">long MakeFun(long lFun);</p> <p style="margin: 0cm 0cm 0pt;">动态库生成的时候采用的函数调用U定是__stdcallQ所以编译生成的a.dll中函数MakeFun的调用约定是_stdcallQ也是函数调用时参C叛_左入栈,函数q回时自p原堆栈。现在某个程序模块b要引用a中的MakeFunQb和a一样用C++方式~译Q只是b模块的函数调用方式是__cdeclQ由于b包含了a提供的头文g中MakeFun函数声明Q所以MakeFun在b模块中被 其它调用MakeFun的函数认为是__cdecl调用方式Qb模块中的 q些函数在调用完MakeFun当然要帮着恢复堆栈啦,可是MakeFun已经在结束时自己恢复了堆栈,b模块中的函数q样多此一丑ְ引v了栈指针错误Q从而引发堆栈异常。宏观上的现象就是函数调用没有问题(因ؓ参数传? 序是一LQ,MakeFun也完成了自己的功能,只是函数q回后引发错误。解决的Ҏ也很单,只要保证两个模块的在~译时设|相同的函数调用U定p了?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">现在再假定两个模块在~译的时候都采用__stdcall调用U定Q但是a.dll使用C语言的语法编译的(C语言方式)Q所以a.dll? 载入库a.lib中MakeFun函数的名字修饰就?#8220;_MakeFun@4”。b包含了a提供的头文g中MakeFun函数声明Q但是由于b采用的是C++语言~译Q所以MakeFun在b模块中被按照C++的名字修饰规则命名ؓ“?MakeFun@@YGJJ@Z”Q编译过E相安无事,链接E序时c++的链接器到a.lib中去?#8220;?MakeFun@@YGJJ@Z”Q但是a.lib中只?#8220;_MakeFun@4”Q没?#8220;?MakeFun@@YGJJ@Z”Q于是链接器报告:</p> <p style="margin: 0cm 0cm 0pt;">error LNK2001: unresolved external symbol ?MakeFun@@YGJJ@Z</p> <p style="margin: 0cm 0cm 0pt;">解决的方法和单,是要让b模块知道q个函数是C语言~译的,extern "C"可以做到q一炏V一个采用C语言~译的库应该考虑C用这个库的程序可能是C++E序Q用C++~译器)Q所以在设计头文件时应该注意q一炏V通常应该q样声明 头文Ӟ</p> <p style="margin: 0cm 0cm 0pt;">#ifdef _cplusplus</p> <p style="margin: 0cm 0cm 0pt;">extern "C" {</p> <p style="margin: 0cm 0cm 0pt;">#endif</p> <p style="margin: 0cm 0cm 0pt;">long MakeFun(long lFun);</p> <p style="margin: 0cm 0cm 0pt;">#ifdef _cplusplus</p> <p style="margin: 0cm 0cm 0pt;">}</p> <p style="margin: 0cm 0cm 0pt;">#endif</p> <p style="margin: 0cm 0cm 0pt;">q样C++的编译器q道MakeFun的修饰名?#8220;_MakeFun@4”Q就不会有链接错误了?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;">许多Z明白Qؓ什么我使用的编译器都是VC的编译器q会产生“error LNK2001”错误Q其实,VC的编译器会根据源文g的扩展名选择~译方式Q如果文件的扩展名是“.C”Q编译器会采用C的语法编译,如果扩展名是“.cpp”Q编译器会用C++的语法编译程序,所以,最好的Ҏ是使用extern "C"?/p> <p style="margin: 0cm 0cm 0pt;"> </p> <p style="margin: 0cm 0cm 0pt;"><strong>5. </strong><strong>单看函数的名字修?/strong><strong></strong></p> 有两U方式可以检查你的程序中的函数的名字修饰Q用编译输出列表或使用Dumpbin工具。?FAcQ?FAs?FAcs命o行参数可以让~译器输出函数或变量名字列表。用dumpbin.exe /SYMBOLS命o也可以获得obj文g或lib文g中的函数或变量名字列表。此外,q可以?undname.exe 修饰名转换为未修饰形式? <br><br>from:<br><a >http://patmusing.blog.163.com/blog/static/13583496020103233446784/ </a><br><br><img src ="http://www.shnenglu.com/beautykingdom/aggbug/113791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/beautykingdom/" target="_blank">chatler</a> 2010-04-28 00:10 <a href="http://www.shnenglu.com/beautykingdom/archive/2010/04/28/113791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>An In-Depth Look into the Win32 Portable Executable File Formathttp://www.shnenglu.com/beautykingdom/archive/2010/03/25/110504.htmlchatlerchatlerThu, 25 Mar 2010 07:17:00 GMThttp://www.shnenglu.com/beautykingdom/archive/2010/03/25/110504.htmlhttp://www.shnenglu.com/beautykingdom/comments/110504.htmlhttp://www.shnenglu.com/beautykingdom/archive/2010/03/25/110504.html#Feedback0http://www.shnenglu.com/beautykingdom/comments/commentRss/110504.htmlhttp://www.shnenglu.com/beautykingdom/services/trackbacks/110504.html阅读全文

chatler 2010-03-25 15:17 发表评论
]]>
异步IO、APC、IO完成端口、线E池与高性能服务?/title><link>http://www.shnenglu.com/beautykingdom/archive/2010/01/04/104762.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Mon, 04 Jan 2010 07:21:00 GMT</pubDate><guid>http://www.shnenglu.com/beautykingdom/archive/2010/01/04/104762.html</guid><wfw:comment>http://www.shnenglu.com/beautykingdom/comments/104762.html</wfw:comment><comments>http://www.shnenglu.com/beautykingdom/archive/2010/01/04/104762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/beautykingdom/comments/commentRss/104762.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/beautykingdom/services/trackbacks/104762.html</trackback:ping><description><![CDATA[ <span style="border-collapse: collapse; color: rgb(2, 54, 141); font-family: song, Verdana; font-size: 19px; font-weight: bold; ">异步IO、APC、IO完成端口、线E池与高性能服务?/span> <div><font color="#02368D" face="song, Verdana" size="6"><span style="border-collapse: collapse; font-size: 19px;"><strong><div>异步IO、APC、IO完成端口、线E池与高性能服务器之一 异步IO</div><div><br></div><div>背景Q轮?PIO DMA 中断</div><div><br></div><div>    早期IO讑֤的速度与CPU相比Q还不是太悬D。CPU定时轮询一遍IO讑֤Q看看有无处理要求,有则加以处理Q完成后q回l箋工作。至今,软盘驱动器还保留着q种轮询工作方式?/div><div>    ?着CPU性能的迅速提高,q种效率低下的工作方式浪费了大量的CPU旉。因此,中断工作方式开始成为普遍采用的技术。这U技术得IO讑֤在需要得到服 务时Q能够生一个硬件中断,qCPU攑ּ目前的处理Q务,q入特定的中断服务过E,中断服务完成后,再l原先的处理。这样一来,IO讑֤和CPU?以同时进行处理,从而避免了CPU{待IO完成?/div><div>    早期数据的传输方式主要是PIOQ程控IOQ方式。通过对IO地址~程方式的方式来传输 数据。比如串行口QY件每ơ往串行口上写一个字节数据,串口讑֤完成传输d后,会产生一个中断,然后软g再次重复直到全部数据发送完成。性能更好的硬 件设备提供一个FIFOQ先q先出缓冲部ӞQ可以让软g一ơ传输更多的字节?/div><div>    昄Q用PIO方式对于高速IO讑֤来说Q还是占用了?多的CPU旉Q因为需要通过CPU~程控制传输Q。而DMAQ直接内存访问)方式能够极大地减CPU处理旉。CPU仅需告诉DMA控制器数据块的v 始地址和大,以后DMA控制器就可以自行在内存和讑֤之间传输数据Q其间CPU可以处理其他d。数据传输完毕后会产生一个中断?/div><div><br></div><div>同步文gIO和异步文件IO</div><div><br></div><div>下面摘抄于MSDN《synchronous file I/O and asynchronous file I/O》?/div><div>有两U类型的文gIO同步Q同步文件IO和异步文件IO。异步文件IO也就是重叠IO?/div><div>在同步文件IO中,U程启动一个IO操作然后q卌入等待状态,直到IO操作完成后才醒来l箋执行。而异步文件IO方式中,U程发送一个IOh到内核,然后l箋处理其他的事情,内核完成IOh后,会通知U程IO操作完成了?/div><div> </div><div>如果IOh需要大量时间执行的话,异步文gIO方式可以显著提高效率Q因为在U程{待的这D|间内QCPU会调度其他U程q行执行Q如果没 有其他线E需要执行的话,q段旉会费掉(可能会调度操作系l的雉U程Q。如果IOh操作很快Q用异步IO方式反而还低效Q还不如用同步IO?式?/div><div>    同步IO在同一时刻只允怸个IO操作Q也是说对于同一个文件句柄的IO操作是序列化的,即使用两个U程也不能同时对同一个文件句柄同时发写操作。重叠IO允许一个或多个U程同时发出IOh?/div><div>    异步IO在请求完成时Q通过文件句柄设为有信号状态来通知应用E序Q或者应用程序通过GetOverlappedResult察看IOh是否完成Q也可以通过一个事件对象来通知应用E序?/div><div><br></div><div>参考书?/div><div><br></div><div>1Q?   MSDN Library </div><div>2Q?   《Windows高~程指南?/div><div>3Q?   《Windows核心~程?/div><div>4Q?   《Windows 2000 讑֤驱动E序设计指南?/div><div><br></div><div>异步IO、APC、IO完成端口、线E池与高性能服务器之?APC</div><div><br></div><div>    Alertable IO(告警IO)提供了更有效的异步通知形式。ReadFileEx / WriteFileEx在发出IOh的同Ӟ提供一个回调函敎ͼAPCq程Q,当IOh完成后,一旦线E进入可告警状态,回调函数会执行?/div><div>    以下五个函数能够使线E进入告警状态:</div><div>    SleepEx</div><div>    WaitForSingleObjectEx</div><div>    WaitForMultipleObjectsEx</div><div>    SignalObjectAndWait</div><div>    MsgWaitForMultipleObjectsEx</div><div>    U?E进入告警状态时Q内核将会检查线E的APC队列Q如果队列中有APCQ将会按FIFO方式依次执行。如果队列ؓI,U程会挂v{待事g对象。以后的?个时刻,一旦APCq入队列Q线E将会被唤醒执行APCQ同时等待函数返回WAIT_IO_COMPLETION?/div><div>    QueueUserAPC可以用来Zؓ投递APCQ只要目标线E处于告警状态时QAPCp够得到执行?/div><div>    使用告警IO的主要缺Ҏ发出IOh的线E也必须是处理结果的U程Q如果一个线E退出时q有未完成的IOhQ那么应用程序将永远丢失IO完成通知。然而以后我们将会看到IO完成端口没有q个限制?/div><div>    下面的代码演CZQueueUserAPC的用法?/div><div><br></div><div>/************************************************************************/</div><div>/* APC Test.                                                            */</div><div>/************************************************************************/</div><div><br></div><div>DWORD WINAPI WorkThread(PVOID pParam)</div><div>{</div><div>    HANDLE Event = (HANDLE)pParam;</div><div><br></div><div>    for(;;)</div><div>    {</div><div>        DWORD dwRet = WaitForSingleObjectEx(Event, INFINITE, TRUE);</div><div>        if(dwRet == WAIT_OBJECT_0)</div><div>            break;</div><div>        else if(dwRet == WAIT_IO_COMPLETION)</div><div>            printf("WAIT_IO_COMPLETION\n");</div><div>    }</div><div><br></div><div>    return 0;</div><div>}</div><div><br></div><div>VOID CALLBACK APCProc(DWORD dwParam)</div><div>{</div><div>    printf("%s", (PVOID)dwParam);</div><div>}</div><div><br></div><div>void TestAPC(BOOL bFast)</div><div>{</div><div>    HANDLE QuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);</div><div><br></div><div>    HANDLE hThread = CreateThread(NULL,</div><div>        0,</div><div>        WorkThread,</div><div>        (PVOID)QuitEvent,</div><div>        0,</div><div>        NULL);</div><div><br></div><div>    Sleep(100); // Wait for WorkThread initialized.</div><div><br></div><div>    for(int i=5; i>0; i--)</div><div>    {</div><div>        QueueUserAPC(APCProc, hThread, (DWORD)(PVOID)"APC here\n");</div><div><br></div><div>        if(!bFast)</div><div>            Sleep(1000);</div><div>    }</div><div><br></div><div>    SetEvent(QuitEvent);</div><div>    WaitForSingleObject(hThread, INFINITE);</div><div>    CloseHandle(hThread);</div><div>}</div><div><br></div><div><br></div><div>参考书?/div><div><br></div><div>1Q?   MSDN Library </div><div>2Q?   《Windows高~程指南?/div><div>3Q?   《Windows核心~程?/div><div>4Q?   《Windows 2000 讑֤驱动E序设计指南?/div><div><br></div><div><br></div><div>异步IO、APC、IO完成端口、线E池与高性能服务器之?IO完成端口</div><div><br></div><div>IO完成端口</div><div><br></div><div>下面摘抄于MSDN《I/O Completion Ports》,smallfool译Q原文请参考CSDN文档中心文章《I/O Completion Ports》, http://dev.csdn.net/Develop/article/29%5C29240.shtm ?/div><div>I/O 完成端口是一U机Ӟ通过q个机制Q应用程序在启动时会首先创徏一个线E池Q然后该应用E序使用U程池处理异步I/Oh。这些线E被创徏的唯一目的是 用于处理I/Oh。对于处理大量ƈ发异步I/Oh的应用程序来_相比于在I/Oh发生时创建线E来_使用完成端口(s)它就可以做的更快且更?效率?/div><div>CreateIoCompletionPort函数会一个I/O完成端口与一个或多个文g句柄发生兌。当与一个完成端口相关的文g句柄 上启动的异步I/O操作完成Ӟ一个I/O完成包就会进入到该完成端口的队列中。对于多个文件句柄来_q种机制可以用来把多文g句柄的同步点攑֜单个?象中。(a下之意,如果我们需要对每个句柄文gq行同步Q一般而言我们需要多个对象(如:Event来同步)Q而我们用IO Complete Port 来实现异步操作,我们可以同多个文件相兌Q每当一个文件中的异步操作完成,׃把一个complete package攑ֈ队列中,q样我们可以用这个来完成所有文件句柄的同步Q?/div><div>调用GetQueuedCompletionStatus函数Q某 个线E就会等待一个完成包q入到完成端口的队列中,而不是直接等待异步I/Oh完成。线E(们)׃d于它们的q行在完成端口(按照后进先出队列序 的被释放Q。这意味着当一个完成包q入到完成端口的队列中时Q系l会释放最q被d在该完成端口的线E?/div><div>调用GetQueuedCompletionStatusQ线E就会将会与某个指定的完成端口徏立联p,一直gl其该线E的存在周期Q或被指定了不同的完成端口,或者释放了与完成端口的联系。一个线E只能与最多不过一个的完成端口发生联系?/div><div>?成端口最重要的特性就是ƈ发量。完成端口的q发量可以在创徏该完成端口时指定。该q发量限制了与该完成端口相关联的可运行线E的数目。当与该完成端口相关 联的可运行线E的L目达C该ƈ发量Q系l就会阻塞Q何与该完成端口相兌的后l线E的执行Q直C该完成端口相兌的可q行U程数目下降到小于该q发 量ؓ止。最有效的假x发生在有完成包在队列中等待,而没有等待被满Q因为此时完成端口达C其ƈ发量的极限。此Ӟ一个正在运行中的线E调?GetQueuedCompletionStatusӞ它就会立M队列中取走该完成包。这样就不存在着环境的切换,因ؓ该处于运行中的线E就会连l不 断地从队列中取走完成包,而其他的U程׃能运行了?/div><div>对于q发量最好的挑选值就是您计算ZCPU的数目。如果您的事务处理需要一个O长的计算旉Q一个比较大的ƈ发量可以允许更多U程来运行。虽然完成每个事务处理需要花Ҏ长的旉Q但更多的事务可以同时被处理。对于应用程序来_很容易通过试q发量来获得最好的效果?/div><div>PostQueuedCompletionStatus函数允许应用E序可以针对自定义的专用I/O完成包进行排队,而无需启动一个异步I/O操作。这点对于通知外部事g的工作者线E来说很有用?/div><div>在没有更多的引用针对某个完成端口Ӟ需要释放该完成端口。该完成端口句柄以及与该完成端口相关联的所有文件句柄都需要被释放。调用CloseHandle可以释放完成端口的句柄?/div><div><br></div><div>下面的代码利用IO完成端口做了一个简单的U程池?/div><div><br></div><div>/************************************************************************/</div><div>/* Test IOCompletePort.                                                 */</div><div>/************************************************************************/</div><div><br></div><div>DWORD WINAPI IOCPWorkThread(PVOID pParam)</div><div>{</div><div>    HANDLE CompletePort = (HANDLE)pParam;</div><div>    PVOID UserParam;</div><div>    WORK_ITEM_PROC UserProc;</div><div>    LPOVERLAPPED pOverlapped;</div><div>    </div><div>    for(;;)</div><div>    {</div><div>        BOOL bRet = GetQueuedCompletionStatus(</div><div>            CompletePort,</div><div>            (LPDWORD)&UserParam,</div><div>            (LPDWORD)&UserProc,</div><div>            &pOverlapped,</div><div>            INFINITE);</div><div><br></div><div>        _ASSERT(bRet);</div><div><br></div><div>        if(UserProc == NULL) // Quit signal.</div><div>            break;</div><div><br></div><div>        // execute user's proc.        </div><div>        UserProc(UserParam);        </div><div>    }</div><div><br></div><div>    return 0;</div><div>}</div><div><br></div><div>void TestIOCompletePort(BOOL bWaitMode, LONG ThreadNum)</div><div>{</div><div>    HANDLE CompletePort;</div><div>    OVERLAPPED Overlapped = {0, 0, 0, 0, NULL};</div><div><br></div><div>    CompletePort = CreateIoCompletionPort(</div><div>        INVALID_HANDLE_VALUE,</div><div>        NULL,</div><div>        NULL,</div><div>        0);</div><div>    </div><div>    // Create threads.</div><div>    for(int i=0; i<ThreadNum; i++)</div><div>    {</div><div>        HANDLE hThread = CreateThread(NULL,</div><div>            0,</div><div>            IOCPWorkThread,</div><div>            CompletePort,</div><div>            0,</div><div>            NULL);</div><div><br></div><div>        CloseHandle(hThread);</div><div>    }</div><div><br></div><div><br></div><div>    CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);</div><div>    BeginTime = GetTickCount();</div><div>    ItemCount = 20;</div><div><br></div><div>    for(i=0; i<20; i++)</div><div>    {</div><div>        PostQueuedCompletionStatus(</div><div>            CompletePort,</div><div>            (DWORD)bWaitMode,</div><div>            (DWORD)UserProc1,</div><div>            &Overlapped);</div><div>    }</div><div>    </div><div>    WaitForSingleObject(CompleteEvent, INFINITE);</div><div>    CloseHandle(CompleteEvent);</div><div><br></div><div><br></div><div>    // Destroy all threads.</div><div>    for(i=0; i<ThreadNum; i++)</div><div>    {</div><div>        PostQueuedCompletionStatus(</div><div>            CompletePort,</div><div>            NULL,</div><div>            NULL,</div><div>            &Overlapped);</div><div>    }</div><div><br></div><div>    Sleep(1000); // wait all thread exit.</div><div><br></div><div>    CloseHandle(CompletePort);</div><div>}</div><div><br></div><div><br></div><div>参考书?/div><div><br></div><div>1Q?   MSDN Library </div><div>2Q?   《Windows高~程指南?/div><div>3Q?   《Windows核心~程?/div><div>4Q?   《Windows 2000 讑֤驱动E序设计指南?/div><div><br></div><div><br></div><div>异步IO、APC、IO完成端口、线E池与高性能服务器之?U程?/div><div><br></div><div>U程?/div><div><br></div><div>下面摘抄于MSDN《Thread Pooling》?/div><div>?许多应用E序创徏的线E花费了大量旉在睡眠状态来{待事g的发生。还有一些线E进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息。线E池 可以让你更有效地使用U程Q它Z的应用程序提供一个由pȝ理的工作者线E池。至会有一个线E来监听攑ֈU程池的所有等待操作,当等待操作完成后Q线 E池中将会有一个工作者线E来执行相应的回调函数?/div><div>你也可以把没有等待操作的工作目攑ֈU程池中Q用QueueUserWorkItem函数来完成这个工作,把要执行的工作项目函数通过一个参C递给U程池。工作项目被攑ֈU程池中后,׃能再取消了?/div><div>Timer-queue timers和Registered wait operations也用线E池来实现。他们的回调函数也放在线E池中。你也可以用BindIOCompletionCallback函数来投递一个异 步IO操作Q在IO完成端口上,回调函数也是qE池U程来执行?/div><div>当第一ơ调用QueueUserWorkItem函数或?BindIOCompletionCallback函数的时候,U程池被自动创徏Q或者Timer-queue timers或者Registered wait operations攑օ回调函数的时候,U程池也可以被创建。线E池可以创徏的线E数量不限,仅受限于可用的内存,每一个线E用默认的初始堆栈大小Q?q行在默认的优先U上?/div><div>U程池中有两U类型的U程QIOU程和非IOU程。IOU程{待在可告警状态,工作目作ؓAPC攑ֈIOU程中。如果你的工作项目需要线E执行在可警告状态,你应该将它放到IOU程?/div><div>非IO工作者线E等待在IO完成端口上,使用非IOU程比IOU程效率更高Q也是_只要有可能的话,量使用非IOU程。IOU程和非IOU程在异步IO操作没有完成之前都不会退出。然而,不要在非IOU程中发出需要很长时间才能完成的异步IOh?/div><div>?用线E池的方法是Q工作项目函C及它会调用到的所有函数都必须是线E池安全的。安全的函数不应该假讄E是一ơ性线E的或者是怹U程。一般来 _应该避免使用U程本地存储和发出需要永久线E的异步IO调用Q比如说RegNotifyChangeKeyValue函数。如果需要在怹U程中执?q样的函数的话,可以lQueueUserWorkItem传递一个选项WT_EXECUTEINPERSISTENTTHREAD?/div><div>注意Q线E池不能兼容COM的单U程套间QSTAQ模型?/div><div><br></div><div>    Z更深入地讲解操作pȝ实现的线E池的优性,我们首先试着自己实现一个简单的U程池模型?/div><div><br></div><div>    代码如下Q?/div><div><br></div><div>/************************************************************************/</div><div>/* Test Our own thread pool.                                            */</div><div>/************************************************************************/</div><div><br></div><div>typedef struct _THREAD_POOL</div><div>{</div><div>    HANDLE QuitEvent;</div><div>    HANDLE WorkItemSemaphore;</div><div><br></div><div>    LONG WorkItemCount;</div><div>    LIST_ENTRY WorkItemHeader;</div><div>    CRITICAL_SECTION WorkItemLock;</div><div><br></div><div>    LONG ThreadNum;</div><div>    HANDLE *ThreadsArray;</div><div><br></div><div>}THREAD_POOL, *PTHREAD_POOL;</div><div><br></div><div>typedef VOID (*WORK_ITEM_PROC)(PVOID Param);</div><div><br></div><div>typedef struct _WORK_ITEM</div><div>{</div><div>    LIST_ENTRY List;</div><div><br></div><div>    WORK_ITEM_PROC UserProc;</div><div>    PVOID UserParam;</div><div>    </div><div>}WORK_ITEM, *PWORK_ITEM;</div><div><br></div><div><br></div><div>DWORD WINAPI WorkerThread(PVOID pParam)</div><div>{</div><div>    PTHREAD_POOL pThreadPool = (PTHREAD_POOL)pParam;</div><div>    HANDLE Events[2];</div><div>    </div><div>    Events[0] = pThreadPool->QuitEvent;</div><div>    Events[1] = pThreadPool->WorkItemSemaphore;</div><div><br></div><div>    for(;;)</div><div>    {</div><div>        DWORD dwRet = WaitForMultipleObjects(2, Events, FALSE, INFINITE);</div><div><br></div><div>        if(dwRet == WAIT_OBJECT_0)</div><div>            break;</div><div><br></div><div>        //</div><div>        // execute user's proc.</div><div>        //</div><div><br></div><div>        else if(dwRet == WAIT_OBJECT_0 +1)</div><div>        {</div><div>            PWORK_ITEM pWorkItem;</div><div>            PLIST_ENTRY pList;</div><div><br></div><div>            EnterCriticalSection(&pThreadPool->WorkItemLock);</div><div>            _ASSERT(!IsListEmpty(&pThreadPool->WorkItemHeader));</div><div>            pList = RemoveHeadList(&pThreadPool->WorkItemHeader);</div><div>            LeaveCriticalSection(&pThreadPool->WorkItemLock);</div><div><br></div><div>            pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);</div><div>            pWorkItem->UserProc(pWorkItem->UserParam);</div><div><br></div><div>            InterlockedDecrement(&pThreadPool->WorkItemCount);</div><div>            free(pWorkItem);</div><div>        }</div><div><br></div><div>        else</div><div>        {</div><div>            _ASSERT(0);</div><div>            break;</div><div>        }</div><div>    }</div><div><br></div><div>    return 0;</div><div>}</div><div><br></div><div>BOOL InitializeThreadPool(PTHREAD_POOL pThreadPool, LONG ThreadNum)</div><div>{</div><div>    pThreadPool->QuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);</div><div>    pThreadPool->WorkItemSemaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);</div><div>    pThreadPool->WorkItemCount = 0;</div><div>    InitializeListHead(&pThreadPool->WorkItemHeader);</div><div>    InitializeCriticalSection(&pThreadPool->WorkItemLock);</div><div>    pThreadPool->ThreadNum = ThreadNum;</div><div>    pThreadPool->ThreadsArray = (HANDLE*)malloc(sizeof(HANDLE) * ThreadNum);</div><div><br></div><div>    for(int i=0; i<ThreadNum; i++)</div><div>    {</div><div>        pThreadPool->ThreadsArray[i] = CreateThread(NULL, 0, WorkerThread, pThreadPool, 0, NULL);</div><div>    }</div><div><br></div><div>    return TRUE;</div><div>}</div><div><br></div><div>VOID DestroyThreadPool(PTHREAD_POOL pThreadPool)</div><div>{</div><div>    SetEvent(pThreadPool->QuitEvent);</div><div><br></div><div>    for(int i=0; i<pThreadPool->ThreadNum; i++)</div><div>    {</div><div>        WaitForSingleObject(pThreadPool->ThreadsArray[i], INFINITE);</div><div>        CloseHandle(pThreadPool->ThreadsArray[i]);</div><div>    }</div><div><br></div><div>    free(pThreadPool->ThreadsArray);</div><div><br></div><div>    CloseHandle(pThreadPool->QuitEvent);</div><div>    CloseHandle(pThreadPool->WorkItemSemaphore);</div><div>    DeleteCriticalSection(&pThreadPool->WorkItemLock);</div><div><br></div><div>    while(!IsListEmpty(&pThreadPool->WorkItemHeader))</div><div>    {</div><div>        PWORK_ITEM pWorkItem;</div><div>        PLIST_ENTRY pList;</div><div>        </div><div>        pList = RemoveHeadList(&pThreadPool->WorkItemHeader);</div><div>        pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);</div><div>        </div><div>        free(pWorkItem);</div><div>    }</div><div>}</div><div><br></div><div>BOOL PostWorkItem(PTHREAD_POOL pThreadPool, WORK_ITEM_PROC UserProc, PVOID UserParam)</div><div>{</div><div>    PWORK_ITEM pWorkItem = (PWORK_ITEM)malloc(sizeof(WORK_ITEM));</div><div>    if(pWorkItem == NULL)</div><div>        return FALSE;</div><div><br></div><div>    pWorkItem->UserProc = UserProc;</div><div>    pWorkItem->UserParam = UserParam;</div><div><br></div><div>    EnterCriticalSection(&pThreadPool->WorkItemLock);</div><div>    InsertTailList(&pThreadPool->WorkItemHeader, &pWorkItem->List);</div><div>    LeaveCriticalSection(&pThreadPool->WorkItemLock);</div><div><br></div><div>    InterlockedIncrement(&pThreadPool->WorkItemCount);</div><div><br></div><div>    ReleaseSemaphore(pThreadPool->WorkItemSemaphore, 1, NULL);</div><div><br></div><div>    return TRUE;</div><div>}</div><div><br></div><div>VOID UserProc1(PVOID dwParam)</div><div>{</div><div>    WorkItem(dwParam);</div><div>}</div><div><br></div><div>void TestSimpleThreadPool(BOOL bWaitMode, LONG ThreadNum)</div><div>{</div><div>    THREAD_POOL ThreadPool;    </div><div>    InitializeThreadPool(&ThreadPool, ThreadNum);</div><div>    </div><div>    CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);</div><div>    BeginTime = GetTickCount();</div><div>    ItemCount = 20;</div><div><br></div><div>    for(int i=0; i<20; i++)</div><div>    {</div><div>        PostWorkItem(&ThreadPool, UserProc1, (PVOID)bWaitMode);</div><div>    }</div><div>    </div><div>    WaitForSingleObject(CompleteEvent, INFINITE);</div><div>    CloseHandle(CompleteEvent);</div><div><br></div><div>    DestroyThreadPool(&ThreadPool);</div><div>}</div><div>    我们把工作项目放C个队列中Q用一个信号量通知U程池,U程池中L一个线E取出工作项目来执行Q执行完毕之后,U程q回U程池,l箋{待新的工作目?/div><div>    U程池中U程的数量是固定的,预先创徏好的Q永久的U程Q直到销毁线E池的时候,q些U程才会被销毁?/div><div>    U程池中U程获得工作目的机会是均等的,随机的,q没有特别的方式保证哪一个线E具有特D的优先获得工作目的机会?/div><div>    而且Q同一时刻可以q发q行的线E数目没有Q何限定。事实上Q在我们的执行计Q务的演示代码中,所有的U程都ƈ发执行?/div><div>    下面Q我们再来看一下,完成同样的Q务,pȝ提供的线E池是如何运作的?/div><div><br></div><div>/************************************************************************/</div><div>/* QueueWorkItem Test.                                                  */</div><div>/************************************************************************/</div><div><br></div><div>DWORD BeginTime;</div><div>LONG  ItemCount;</div><div>HANDLE CompleteEvent;</div><div><br></div><div>int compute()</div><div>{</div><div>    srand(BeginTime);</div><div><br></div><div>    for(int i=0; i<20 *1000 * 1000; i++)</div><div>        rand();</div><div><br></div><div>    return rand();</div><div>}</div><div><br></div><div>DWORD WINAPI WorkItem(LPVOID lpParameter)</div><div>{</div><div>    BOOL bWaitMode = (BOOL)lpParameter;</div><div><br></div><div>    if(bWaitMode)</div><div>        Sleep(1000);</div><div>    else</div><div>        compute();</div><div><br></div><div>    if(InterlockedDecrement(&ItemCount) == 0)</div><div>    {</div><div>        printf("Time total %d second.\n", GetTickCount() - BeginTime);</div><div>        SetEvent(CompleteEvent);</div><div>    }</div><div><br></div><div>    return 0;</div><div>}</div><div><br></div><div>void TestWorkItem(BOOL bWaitMode, DWORD Flag)</div><div>{</div><div>    CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);</div><div>    BeginTime = GetTickCount();</div><div>    ItemCount = 20;</div><div>    </div><div>    for(int i=0; i<20; i++)</div><div>    {</div><div>        QueueUserWorkItem(WorkItem, (PVOID)bWaitMode, Flag);</div><div>    }    </div><div><br></div><div>    WaitForSingleObject(CompleteEvent, INFINITE);</div><div>    CloseHandle(CompleteEvent);</div><div>}</div><div>    很简单,是吧Q我们仅需要关注于我们的回调函数即可。但是与我们的简单模拟来比,pȝ提供的线E池有着更多的优炏V?/div><div>    首先Q线E池中线E的数目是动态调整的Q其ơ,U程池利用IO完成端口的特性,它可以限制ƈ发运行的U程数目Q默认情况下Q将会限制ؓCPU的数目,q可以减线E切换。它挑选最q执行过的线E再ơ投入执行,从而避免了不必要的U程切换?/div><div>    pȝ提供的线E池背后的策略,我们下一节l再谈?/div><div><br></div><div>参考书?/div><div><br></div><div>1Q?   MSDN Library </div><div>2Q?   《Windows高~程指南?/div><div>3Q?   《Windows核心~程?/div><div>4Q?   《Windows 2000 讑֤驱动E序设计指南?/div><div><br></div><div><br></div><div>正文</div><div>异步IO、APC、IO完成端口、线E池与高性能服务器之?服务器的性能指标与实现高性能的途径</div><div><br></div><div>服务器的性能指标</div><div><br></div><div>    作ؓ一个网l服务器E序Q性能永远是第一位的指标。性能可以q样定义Q在l定的硬件条件和旉里,能够处理的Q务量。能够最大限度地利用g性能的服务器设计才是良好的设计?/div><div>    设计良好的服务器q应该考虑q_服务Q对于每一个客LQ服务器应该l予每个客户端^均的服务Q不能让某一个客L长时间得不到服务而发?#8220;饥饿”的状c?/div><div>    可׾~性,也就是说Q随着g能力的提高,服务器的性能能够随之呈线性增ѝ?/div><div><br></div><div>实现高性能的途径</div><div><br></div><div>    一 个实际的服务器的计算是很复杂的,往往是؜合了IO计算和CPU计算。IO计算指计Q务中以IOZ的计模型,比如文g服务器、邮件服务器{,混合?大量的网lIO和文件IOQCPU计算指计Q务中没有或很有IOQ比如加?解密Q编?解码Q数学计等{?/div><div>    在CPU计算中,单线 E和多线E模型效果是相当的。《Win32多线E的性能》中?#8220;在一个单处理器的计算ZQ基?CPU 的Q务的q发执行速度不可能比串行执行速度快,但是我们可以看到Q在 Windows NT 下线E创建和切换的额外开销非常;对于非常短的计算Qƈ发执行仅仅比串行执行?10%Q而随着计算长度的增加,q两个时间就非常接近了?#8221;</div><div>    可见Q对于纯_的CPU计算来说Q如果只有一个CPUQ多U程模型是不合适的。考虑一个执行密集的CPU计算的服务,如果有几十个q样的线Eƈ发执行,q于频繁CQ务切换导致了不必要的性能损失?/div><div>    ?~程实现上,单线E模型计模型对于服务器E序设计是很不方便的。因此,对于CPU计算采用U程池工作模型是比较恰当的?QueueUserWorkItem函数非常适合于将一个CPU计算攑օU程池。线E池实现会努力减少q种不必要的U程切换Q而且控制q发U程的数目ؓ CPU的数目?/div><div>    我们真正需要关心的是IO计算Q一般的|络服务器程序往往伴随着大量的IO计算。提高性能的途径在于要避免等待IO 的结束,造成CPUI闲Q要量利用g能力Q让一个或多个IO讑֤与CPUq发执行。前面介l的异步IOQAPCQIO完成端口都可以达到这个目的?/div><div>    ?于网l服务器来说Q如果客Lq发h数目比较的话,用简单的多线E模型就可以应付了。如果一个线E因为等待IO操作完成而被挂vQ操作系l将会调度另 外一个就l的U程投入q行Q从而Ş成ƈ发执行。经典的|络服务器逻辑大多采用多线E?多进E方式,在一个客L发v到服务器的连接时Q服务器会创徏一?U程Q让q个新的U程来处理后l事务。这U以一个专门的U程/q程来代表一个客L对象的编E方法非常直观,易于理解?/div><div>    对于大型|络服务 器程序来_q种方式存在着局限性。首先,创徏U程/q程和销毁线E?q程的代价非帔R昂,其是在服务器采用TCP“短连?#8221;方式或UDP方式通讯的情 况下Q例如,HTTP协议中,客户端发起一个连接后Q发送一个请求,服务器回应了q个h后,q接也就被关闭了。如果采用经典方式设计HTTP服务器,?么过于频J地创徏U程/销毁线E对性能造成的媄响是很恶劣的?/div><div>    其次Q即使一个协议中采取TCP“长连?#8221;Q客Lq上服务器后׃直保?此连接,l典的设计方式也是有弊病的。如果客Lq发h量很高,同一时刻有很多客L{待服务器响应的情况下,会有过多的U程q发执行Q频J的U程?换将用掉一部分计算能力。实际上Q如果ƈ发线E数目过多的话,往往会过早地耗尽物理内存Q绝大部分时间耗费在线E切换上Q因为线E切换的同时也将引v内存 调页。最l导致服务器性能急剧下降Q?/div><div>    对于一个需要应付同时有大量客户端ƈ发请求的|络服务器来_U程池是唯一的解x案。线E池不光能够避免频繁地创建线E和销毁线E,而且能够用数目很的U程可以处理大量客Lq发h?/div><div>    值得注意的是Q对于一个压力不大的|络服务器程序设计,我们q不推荐以上M技巧。在单的设计p够完成Q务的情况下,把事情弄得很复杂是很不明智,很愚蠢的行ؓ?/div><div>from:</div><div>http://blog.chinaunix.net/u2/67780/showart_2057137.html</div><div>原文地址 http://blog.chinaunix.net/u/14774/showart_88161.html</div></strong></span></font></div><img src ="http://www.shnenglu.com/beautykingdom/aggbug/104762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/beautykingdom/" target="_blank">chatler</a> 2010-01-04 15:21 <a href="http://www.shnenglu.com/beautykingdom/archive/2010/01/04/104762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> windows旉大全QzzQ?/title><link>http://www.shnenglu.com/beautykingdom/archive/2009/02/06/73148.html</link><dc:creator>chatler</dc:creator><author>chatler</author><pubDate>Fri, 06 Feb 2009 15:32:00 GMT</pubDate><guid>http://www.shnenglu.com/beautykingdom/archive/2009/02/06/73148.html</guid><wfw:comment>http://www.shnenglu.com/beautykingdom/comments/73148.html</wfw:comment><comments>http://www.shnenglu.com/beautykingdom/archive/2009/02/06/73148.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/beautykingdom/comments/commentRss/73148.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/beautykingdom/services/trackbacks/73148.html</trackback:ping><description><![CDATA[<div>介绍</div> <div><span>       </span>我们在衡量一个函数运行时_或者判断一个算法的旉效率Q或者在E序中我们需要一个定时器Q定时执行一个特定的操作Q比如在多媒体中Q比如在游戏中等Q都会用到时间函数。还比如我们通过记录函数或者算法开始和截至的时_然后利用两者之差得出函数或者算法的q行旉。编译器和操作系lؓ我们提供了很多时间函敎ͼq些旉函数的精度也是各不相同的Q所以,如果我们惛_到准的l果Q必M用合适的旉函数。现在我׃lwindows下的几种常用旉函数?/div> <div><strong>1</strong><strong>Q?/strong><strong>Sleep</strong><strong>函数</strong></div> <div style="TEXT-INDENT: 21pt"><strong>使用Q?/strong>sleep(1000)Q在Windows和Linux?000代表的含义ƈ不相同,Windows下的表示1000毫秒Q也是1U钟QLinux下表C?000U,Linux下用毫U别的函数可以使用usleep?/div> <div style="TEXT-INDENT: 21pt"><strong>原理Q?/strong>sleep函数是调用sleep函数的线E休眠,U程d攑ּ旉片。当l过指定的时间间隔后Q再启动U程Ql执行代码。Sleep函数q不能v到定时的作用Q主要作用是延时。在一些多U程中可能会看到sleep(0);其主要目的是让出旉片?/div> <div style="TEXT-INDENT: 21pt"><strong>_ֺQ?/strong>sleep函数的精度非怽Q当pȝ忙它精度也p低,有时候我们休?U,可能3U后才能l箋执行。它的精度取决于U程自n优先U、其他线E的优先U,以及U程的数量等因素?/div> <div><strong>2</strong><strong>Q?/strong><strong>MFC</strong><strong>下的</strong><strong>timer</strong><strong>事g</strong></div> <div><span>       </span><strong>使用Q?/strong>1.调用函数SetTimer()讄定时间隔Q如SetTimer(0,100,NULL)即ؓ讄100毫秒的时间间隔;2.在应用程序中增加定时响应函数OnTimer()Qƈ在该函数中添加响应的处理语句Q用来完成时间到时的操作?/div> <div><span>    </span><strong>原理Q?/strong>?span>sleep函数一栗不同的是timer是一个定时器Q可以指定回调函敎ͼ默认为OnTimer()函数?/span></div> <div><span>    </span><strong>_ֺQ?/strong>timer事g的精度范围在毫米U别Q系l越忙其_ֺ也就差?/div> <div><strong>3</strong><strong>Q?/strong><strong>C</strong><strong>语言下的</strong><strong>Time</strong></div> <div><span>       </span><strong>使用Q?/strong>time_t t;time(&t);Time函数是获取当前时间?/div> <div><span>    </span><strong>原理Q?/strong>time函数主要用于获取当前旉Q比如我们做一个电子时钟程序,可以用此函数Q获取系l当前的旉?/div> <div><span>    </span><strong>_ֺQ?/strong>U?/div> <div><strong>4</strong><strong>Q?span>COM对象中的</span></strong><strong>COleDateTime</strong><strong>Q?/strong><strong><span>COleDateTimeSpan</span><strong>c?/strong></strong></div> <div><strong><span>    </span></strong><strong>使用Q?/strong>COleDateTime start_time = COleDateTime::GetCurrentTime();</div> <div style="TEXT-INDENT: 21pt" align=left>COleDateTimeSpan end_time = COleDateTime::GetCurrentTime()-start_time;<br>While(end_time.GetTotalSeconds() < 2)<br>{<br>// 处理延时或定时期间能处理其他的消?br>DoSomething()<br>end_time = COleDateTime::GetCurrentTime-start_time;</div> <div align=left>}</div> <div style="TEXT-INDENT: 21pt" align=left><strong>原理Q?/strong>以上代表延时<span>2U,而这两秒内我们可以@环调用DoSomething()Q从而实现在</span><span>延时的时候我们也能够处理其他的函敎ͼ或者消息?/span><span>COleDateTime,COleDateTimeSpan</span><span>?/span><span>MFC</span><span>?/span><span>CTime</span><span>Q?/span><span>CTimeSpan</span><span>?/span><span>COM</span><span>中的应用Q所以,上面的方法对?/span><span>CTime</span><span>Q?/span><span>CTimeSpa</span><span>同样有效?/span></div> <div><strong><span>       </span></strong><strong>_ֺQ?/strong>U?/div> <div><strong>5</strong><strong>Q?/strong><strong>C</strong><strong>语言下的旉周期</strong><strong>clock()</strong></div> <div><span>       </span><strong>使用Q?/strong><span>   </span><span style="FONT-SIZE: 9pt">clock_t start = clock();<br>              Sleep(100);<br>              clock_t end = clock();<br>          <span style="COLOR: blue">double</span> d = (<span style="COLOR: blue">double</span>)(start - end) / CLOCKS_PER_SEC;</span></div> <div><span>       </span><strong>原理Q?/strong>clock()是获取计机启动后的旉间隔?</div> <div style="TEXT-INDENT: 21pt"><strong>_ֺQ?/strong>msU别Q对于短旉内的定时或者g时可以达到msU别Q对于时间比较长的定时或者gq精度还是不够。在windows下CLOCKS_PER_SEC?span>1000?/span></div> <div><strong>6</strong><strong>Q?/strong><strong>Windows</strong><strong>下的</strong><strong>GetTickCount()</strong></div> <div align=left><strong>使用Q?/strong> DWORD start = GetTickCount();<br><span>        Sleep(100);<br>        DWORD end = GetTickCount();</span></div> <div style="MARGIN-LEFT: 21pt"><strong>原理Q?/strong>GetTickCount()是获取系l启动后的时间间隔。通过q入函数开始定Ӟ到退出函数结束定Ӟ从而可以判断出函数的执行时_q种旉也ƈ非是函数或者算法的真实执行旉Q因为在函数和算法线E不可能一直占?span>CPUQ?/span><span>对于所有判断执行时间的函数都是一P</span>不过基本上已l很准确Q?span>可以通过查询q行定时?/span><span>GetTickCount()</span><span>?/span><span>Clock()</span><span>函数是向L</span><span>BIOS</span><span>?/span><span>real time clock</span><span>旉Q会有中断生,以及延迟问题?/span></div> <div style="MARGIN-LEFT: 21pt"><strong>_ֺQ?/strong>WindowsNT 3.5以及以后版本_ֺ?span>10msQ它的时间精度比clock函数的要高,GetTickCount()常用于多媒体中?/span></div> <div><strong>7</strong><strong>Q?/strong><strong>Windows</strong><strong>?span>timeGetTime</span></strong></div> <div style="TEXT-INDENT: 21pt"><strong><span>使用Q?/span><span>需要包?/span><span style="FONT-SIZE: 8.5pt; COLOR: black">Mmsystem.h</span><span style="FONT-SIZE: 8.5pt; COLOR: black">Q?/span><span style="FONT-SIZE: 8.5pt; COLOR: black">Windows.h</span><span style="FONT-SIZE: 8.5pt; COLOR: black">Q加入静态库</span><span style="FONT-SIZE: 8.5pt; COLOR: black">Winmm.lib.</span></strong></div> <div style="MARGIN-LEFT: 42pt; TEXT-INDENT: 21pt">timeBeginPeriod(1);<br><span style="FONT-SIZE: 9pt">DWORD start = timeGetTime();<br>              Sleep(100);<br>          DWORD end = timeGetTime();</span><br>timeEndPeriod(1);<strong><span></span></strong></div> <div style="TEXT-INDENT: 21pt"><strong><span>原理Q?/span><span>timeGetTime</span><span>也时常用于多媒体定时器中Q可以通过查询q行定时。通过查询q行定时Q本w也会媄响定时器的定时精度?/span></strong></div> <div style="TEXT-INDENT: 21pt"><strong><span>_ֺQ?/span><span>毫秒</span>Q与GetTickCount()相当。但是和GetTickCount相比QtimeGetTime可以通过timeBeginPeriodQ?span>timeEndPeriod讄定时器的最解析精? timeBeginPeriod,timeEndPeriod必须成对出现?/span></strong></div> <div><strong>8</strong><strong>Q?span>windows下的</span></strong><strong>timeSetEvent</strong></div> <div style="TEXT-INDENT: 21pt">使用Q还记的VC下的Timer吗?Timer是一个定时器Q而以上我们提到几U时间函数或者类型,实现定时功能只能通过轮训来实玎ͼ也就是必d外创Z个线E单独处理,q样会媄响定时精度,好在windows提供了内|的定时器timeSetEventQ函数原型ؓ</div> <div><span>MMRESULT timeSetEvent</span><span>Q?UINT uDelay, //</span><span>以毫U指定事件的周期</span><br><span>UINT uResolution, //</span><span>以毫U指定g时的_ֺQ数D定时器事g分L率越高。缺省gؓ1ms</span><br><span>LPTIMECALLBACK lpTimeProc, //</span><span>指向一个回调函?/span><br><span>WORD dwUser, //</span><span>存放用户提供的回调数?/span><br><span>UINT fuEvent </span><span>Q?/ </span>标志参数QTIME_ONESHOTQ执行一ơ;TIME_PERIODICQ周期性执?/div> <div><span>       </span>具体应用Ӟ可以通过调用timeSetEvent()函数Q将需要周期性执行的d定义?lpFunction回调函数?如:定时采样、控制等)Q从而完成所需处理的事件。需要注意的是:d处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后,应及时调用timeKillEvent()之释放?/div> <div style="TEXT-INDENT: 21pt"><strong>原理Q?/strong>可以理解Z回调函数的timeGetTime</div> <div style="TEXT-INDENT: 21pt"><strong>_ֺQ?/strong><span>毫秒</span>QtimeSetEvent可以通过timeBeginPeriodQ?span>timeEndPeriod讄定时器的最解析精? timeBeginPeriod,timeEndPeriod必须成对出现?/span><br><strong>9</strong><strong>Q高_ֺ时控函数</strong><strong>QueryPerformanceFrequency</strong><strong>Q?/strong><strong>QueryPerformanceCounter</strong></div> <div style="TEXT-INDENT: 21pt"><strong>使用Q?/strong><span style="FONT-SIZE: 9pt">LARGE_INTEGER m_nFreq;<br>          LARGE_INTEGER m_nBeginTime;<br>          LARGE_INTEGER nEndTime;<br>          QueryPerformanceFrequency(&m_nFreq); <span style="COLOR: green">// </span></span><span style="FONT-SIZE: 9pt; COLOR: green">获取旉周期</span><br><span style="FONT-SIZE: 9pt">          QueryPerformanceCounter(&m_nBeginTime); <span style="COLOR: green">// </span></span><span style="FONT-SIZE: 9pt; COLOR: green">获取旉计数</span><br><span style="FONT-SIZE: 9pt">          Sleep(100);<br>          QueryPerformanceCounter(&nEndTime);<br>     cout << (nEndTime.QuadPart-m_nBeginTime.QuadPart)*1000/m_nFreq.QuadPart << endl;</span></div> <div><strong>原理Q?/strong>CPU上也有一个计数器Q以机器?span>clock为单位,可以通过rdtscdQ而不用中断,因此其精度与pȝ旉相当?/span></div> <div><strong>_ֺQ?/strong>计算取硬件支持,_ֺ比较高,可以通过它判断其他时间函数的_ֺ范围?/div> <div><strong>10</strong><strong>结Q?/strong>以上提到常用?U时间函敎ͼ׃他们的用处不同,所以他们的_ֺ也不相同,所以如果简单的延时可以用sleep函数Q稍微准的延时可以使用clock函数QGetTickCount函数Q更高的实?timeGetTime函数Q简单的定时事g可以用TimerQ准地可以用timeSetEventQ或取一般系l时间可以通timeQ或?CTimeQ或者COleDateTimeQ获取准的旉可以用clockQ或者GetTickCount函数Q或者timeGetTime函数Q而获取准地pȝ旉要用硬件支持的QueryPerformanceFrequency函数QQueryPerformanceCounter函数?/div> <img src ="http://www.shnenglu.com/beautykingdom/aggbug/73148.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/beautykingdom/" target="_blank">chatler</a> 2009-02-06 23:32 <a href="http://www.shnenglu.com/beautykingdom/archive/2009/02/06/73148.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.dgltjsh.cn" target="_blank">þþƷAV鶹</a>| <a href="http://www.ireboot.cn" target="_blank">ձþþվ</a>| <a href="http://www.kanqiuwang.cn" target="_blank">۲ӰԺþ99</a>| <a href="http://www.chemzt.cn" target="_blank">þþþþþ</a>| <a href="http://www.gg4493.cn" target="_blank">þùŷպƷ</a>| <a href="http://www.aimingshi.cn" target="_blank">þþƷӰԺ</a>| <a href="http://www.16315.com.cn" target="_blank">þɫۺҹž</a>| <a href="http://www.ominimo.cn" target="_blank">þ㽶ۺɫһۺɫ88</a>| <a href="http://www.worldedu.org.cn" target="_blank">ɫۺϾþ88ɫۺ </a>| <a href="http://www.santai58.cn" target="_blank">þþþ޾Ʒ</a>| <a href="http://www.bachou.com.cn" target="_blank">պŷۺϾþӰԺDs</a>| <a href="http://www.jianzhuhr.net.cn" target="_blank">޾Ʒһþþ </a>| <a href="http://www.jinxing168.net.cn" target="_blank">˾þav</a>| <a href="http://www.kovnxs.cn" target="_blank">97þþƷƷ</a>| <a href="http://www.woaisheying.cn" target="_blank">Ҫþðѹۿ</a>| <a href="http://www.dpbz.net.cn" target="_blank">ƷþëƬ</a>| <a href="http://www.pluv.cn" target="_blank">ľƷþþþ޲</a>| <a href="http://www.haokan1.cn" target="_blank">þþƷһ</a>| <a href="http://www.jingxi.jx.cn" target="_blank">ƷۺϾþþþþ97</a>| <a href="http://www.donki.net.cn" target="_blank">þ޾Ʒվ</a>| <a href="http://www.xkeir8vz.cn" target="_blank">þþƷƵ</a>| <a href="http://www.gkcv.cn" target="_blank">ھƷþþþù</a>| <a href="http://www.paypaal.cn" target="_blank">þ99Ʒþþþþþþþ</a>| <a href="http://www.sbsinc.com.cn" target="_blank">þ99ֻƵƷ6</a>| <a href="http://www.tongdiaocj.cn" target="_blank">Ʒþþþþùţţapp</a>| <a href="http://www.k8uvo.cn" target="_blank">þþƷ</a>| <a href="http://www.ikxc.cn" target="_blank">þþƷav鶹С˵</a>| <a href="http://www.qcwxfw.cn" target="_blank">Ļ뾫ƷԴþ</a>| <a href="http://www.vauban.cn" target="_blank">޹þþþþþ</a>| <a href="http://www.7trade.cn" target="_blank">þþƷҹɫA</a>| <a href="http://www.djmb.net.cn" target="_blank">޹Ʒþþþվ</a>| <a href="http://www.6talent.cn" target="_blank">Ʒһþ㽶߿</a>| <a href="http://www.yxcyfa.cn" target="_blank">þþƷaĻ þþƷaĻؿ </a>| <a href="http://www.xh68.cn" target="_blank">ɫþþ99Ʒ91</a>| <a href="http://www.ycqdzgov.cn" target="_blank">97Ʒ91þþþþ</a>| <a href="http://www.suba400.cn" target="_blank">ۺϾþþþ</a>| <a href="http://www.cq928.cn" target="_blank">99þѹƷ</a>| <a href="http://www.epuda.cn" target="_blank">þþþһ</a>| <a href="http://www.b2721.cn" target="_blank">ɫۺɫþû</a>| <a href="http://www.zhinvest.cn" target="_blank">Ļһþվ</a>| <a href="http://www.888happy.cn" target="_blank">һþaþþƷۺҹҹ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>