|
![]() | 作者: zhanjiajun [zhanjiajun]
![]() |
登录 |
这的安装类的太多了,其实安装不太难,因为目前的9.0/8.0的图形化安装都实现了SB式的安装了,我以前安双系统,搞得一塌糊涂,还不是格式化了两次硬盘才搞定.哪安了LINUX,如何学习,QQ,用lumqq,下一个,解压了就可以用了.听歌?用xmms就行了. 哪程序呢?装一linux不会就是装成类windows吧: 我写一个吧: #include <stdio.h> main() { printf("hello,world \n"); } 到shell下: :>gcc hello.c -o hello ./hello 我们就可以看到结果了. 好,菜鸟级数的我就只能做到这份上了,请各高手贴一些说级别的来吧(我需要pthread等的实例),有好心人吗? |
地主 发表时间: 05-07-18 13:35 |
![]() | 回复: SysHu0teR [syshunter] ![]() |
登录 |
代码: |
B1层 发表时间: 05-07-18 18:56 |
![]() | 回复: SysHu0teR [syshunter] ![]() |
登录 |
线程思想不管是在哪个系统下都是一样的。可能函数名和线程函数的申明有点不同。我还是喜欢*NIX的这种简洁。 |
B2层 发表时间: 05-07-18 19:03 |
![]() | 回复: zhanjiajun [zhanjiajun] ![]() |
登录 |
在windows下到是做过线程,不过,请问一下版主: 一 我可不可以这样理解,在linux中,线程类封装在了Pthread中,然后调用一些具体的方法,如创建一般是:create等,根据一些属性进行操作. 二 可不可以写一个类似hello world 的最简单的创建线程代码,在子线程中打印一个字符串. 三 我贴出一个线程池的实例: |
B3层 发表时间: 05-07-18 22:34 |
![]() | 回复: zhanjiajun [zhanjiajun] ![]() |
登录 |
Linux下通用线程池的创建与使用 本文给出了一个通用的线程池框架,该框架将与线程执行相关的任务进行了高层次的抽象,使之与具体的执行任务无关。另外该线程池具有动态伸缩性,它能根据执行任务的轻重自动调整线程池中线程的数量。文章的最后,我们给出一个简单示例程序,通过该示例程序,我们会发现,通过该线程池框架执行多线程任务是多么的简单。 为什么需要线程池 目前的大多数网络服务器,包括Web服务器、Email服务器以及数据库服务器等都具有一个共同点,就是单位时间内必须处理数目巨大的连接请求,但处理时间却相对较短。 传统多线程方案中我们采用的服务器模型则是一旦接受到请求之后,即创建一个新的线程,由该线程执行任务。任务执行完毕后,线程退出,这就是是“即时创建,即时销毁”的策略。尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器将处于不停的创建线程,销毁线程的状态。 我们将传统方案中的线程执行过程分为三个过程:T1、T2、T3。 T1:线程创建时间 T2:线程执行时间,包括线程的同步等时间 T3:线程销毁时间 那么我们可以看出,线程本身的开销所占的比例为(T1+T3) / (T1+T2+T3)。如果线程执行的时间很短的话,这比开销可能占到20%-50%左右。如果任务执行时间很频繁的话,这笔开销将是不可忽略的。 除此之外,线程池能够减少创建的线程个数。通常线程池所允许的并发线程是有上界的,如果同时需要并发的线程数超过上界,那么一部分线程将会等待。而传统方案中,如果同时请求数目为2000,那么最坏情况下,系统可能需要产生2000个线程。尽管这不是一个很大的数目,但是也有部分机器可能达不到这种要求。 因此线程池的出现正是着眼于减少线程池本身带来的开销。线程池采用预创建的技术,在应用程序启动之后,将立即创建一定数量的线程(N1),放入空闲队列中。这些线程都是处于阻塞(Suspended)状态,不消耗CPU,但占用较小的内存空间。当任务到来后,缓冲池选择一个空闲线程,把任务传入此线程中运行。当N1个线程都在处理任务后,缓冲池自动创建一定数量的新线程,用于处理更多的任务。在任务执行完毕后线程也不退出,而是继续保持在池中等待下一次的任务。当系统比较空闲时,大部分线程都一直处于暂停状态,线程池自动销毁一部分线程,回收系统资源。 基于这种预创建技术,线程池将线程创建和销毁本身所带来的开销分摊到了各个具体的任务上,执行次数越多,每个任务所分担到的线程本身开销则越小,不过我们另外可能需要考虑进去线程之间同步所带来的开销。 构建线程池框架 一般线程池都必须具备下面几个组成部分: 线程池管理器:用于创建并管理线程池 工作线程: 线程池中实际执行的线程 任务接口: 尽管线程池大多数情况下是用来支持网络服务器,但是我们将线程执行的任务抽象出来,形成任务接口,从而是的线程池与具体的任务无关。 任务队列:线程池的概念具体到实现则可能是队列,链表之类的数据结构,其中保存执行线程。 我们实现的通用线程池框架由五个重要部分组成CThreadManage,CThreadPool,CThread,CJob,CWorkerThread,除此之外框架中还包括线程同步使用的类CThreadMutex和CCondition。 CJob是所有的任务的基类,其提供一个接口Run,所有的任务类都必须从该类继承,同时实现Run方法。该方法中实现具体的任务逻辑。 CThread是Linux中线程的包装,其封装了Linux线程最经常使用的属性和方法,它也是一个抽象类,是所有线程类的基类,具有一个接口Run。 CWorkerThread是实际被调度和执行的线程类,其从CThread继承而来,实现了CThread中的Run方法。 CThreadPool是线程池类,其负责保存线程,释放线程以及调度线程。 CThreadManage是线程池与用户的直接接口,其屏蔽了内部的具体实现。 CThreadMutex用于线程之间的互斥。 CCondition则是条件变量的封装,用于线程之间的同步。 它们的类的继承关系如下图所示: 线程池的时序很简单,如下图所示。CThreadManage直接跟客户端打交道,其接受需要创建的线程初始个数,并接受客户端提交的任务。这儿的任务是具体的非抽象的任务。CThreadManage的内部实际上调用的都是CThreadPool的相关操作。CThreadPool创建具体的线程,并把客户端提交的任务分发给CWorkerThread,CWorkerThread实际执行具体的任务。 理解系统组件 下面我们分开来了解系统中的各个组件。 CThreadManage CThreadManage的功能非常简单,其提供最简单的方法,其类定义如下: class CThreadManage { private: CThreadPool* m_Pool; int m_NumOfThread; protected: public: void SetParallelNum(int num); CThreadManage(); CThreadManage(int num); virtual ~CThreadManage(); void Run(CJob* job,void* jobdata); void TerminateAll(void); }; 其中m_Pool指向实际的线程池;m_NumOfThread是初始创建时候允许创建的并发的线程个数。另外Run和TerminateAll方法也非常简单,只是简单的调用CThreadPool的一些相关方法而已。其具体的实现如下: CThreadManage::CThreadManage(){ m_NumOfThread = 10; m_Pool = new CThreadPool(m_NumOfThread); } CThreadManage::CThreadManage(int num){ m_NumOfThread = num; m_Pool = new CThreadPool(m_NumOfThread); } CThreadManage::~CThreadManage(){ if(NULL != m_Pool) delete m_Pool; } void CThreadManage::SetParallelNum(int num){ m_NumOfThread = num; } void CThreadManage::Run(CJob* job,void* jobdata){ m_Pool->Run(job,jobdata); } void CThreadManage::TerminateAll(void){ m_Pool->TerminateAll(); } CThread CThread 类实现了对Linux中线程操作的封装,它是所有线程的基类,也是一个抽象类,提供了一个抽象接口Run,所有的CThread都必须实现该Run方法。CThread的定义如下所示: class CThread { private: int m_ErrCode; Semaphore m_ThreadSemaphore; //the inner semaphore, which is used to realize unsigned long m_ThreadID; bool m_Detach; //The thread is detached bool m_CreateSuspended; //if suspend after creating char* m_ThreadName; ThreadState m_ThreadState; //the state of the thread protected: void SetErrcode(int errcode){m_ErrCode = errcode;} static void* ThreadFunction(void*); public: CThread(); CThread(bool createsuspended,bool detach); virtual ~CThread(); virtual void Run(void) = 0; void SetThreadState(ThreadState state){m_ThreadState = state;} bool Terminate(void); //Terminate the threa bool Start(void); //Start to execute the thread void Exit(void); bool Wakeup(void); ThreadState GetThreadState(void){return m_ThreadState;} int GetLastError(void){return m_ErrCode;} void SetThreadName(char* thrname){strcpy(m_ThreadName,thrname);} char* GetThreadName(void){return m_ThreadName;} int GetThreadID(void){return m_ThreadID;} bool SetPriority(int priority); int GetPriority(void); int GetConcurrency(void); void SetConcurrency(int num); bool Detach(void); bool Join(void); bool Yield(void); int Self(void); }; 线程的状态可以分为四种,空闲、忙碌、挂起、终止(包括正常退出和非正常退出)。由于目前Linux线程库不支持挂起操作,因此,我们的此处的挂起操作类似于暂停。如果线程创建后不想立即执行任务,那么我们可以将其“暂停”,如果需要运行,则唤醒。有一点必须注意的是,一旦线程开始执行任务,将不能被挂起,其将一直执行任务至完毕。 线程类的相关操作均十分简单。线程的执行入口是从Start()函数开始,其将调用函数ThreadFunction,ThreadFunction再调用实际的Run函数,执行实际的任务。 CThreadPool CThreadPool是线程的承载容器,一般可以将其实现为堆栈、单向队列或者双向队列。在我们的系统中我们使用STL Vector对线程进行保存。CThreadPool的实现代码如下: class CThreadPool { friend class CWorkerThread; private: unsigned int m_MaxNum; //the max thread num that can create at the same time unsigned int m_AvailLow; //The min num of idle thread that shoule kept unsigned int m_AvailHigh; //The max num of idle thread that kept at the same time unsigned int m_AvailNum; //the normal thread num of idle num; unsigned int m_InitNum; //Normal thread num; protected: CWorkerThread* GetIdleThread(void); void AppendToIdleList(CWorkerThread* jobthread); void MoveToBusyList(CWorkerThread* idlethread); void MoveToIdleList(CWorkerThread* busythread); void DeleteIdleThread(int num); void CreateIdleThread(int num); public: CThreadMutex m_BusyMutex; //when visit busy list,use m_BusyMutex to lock and unlock CThreadMutex m_IdleMutex; //when visit idle list,use m_IdleMutex to lock and unlock CThreadMutex m_JobMutex; //when visit job list,use m_JobMutex to lock and unlock CThreadMutex m_VarMutex; CCondition m_BusyCond; //m_BusyCond is used to sync busy thread list CCondition m_IdleCond; //m_IdleCond is used to sync idle thread list CCondition m_IdleJobCond; //m_JobCond is used to sync job list CCondition m_MaxNumCond; vector<CWorkerThread*> m_ThreadList; vector<CWorkerThread*> m_BusyList; //Thread List vector<CWorkerThread*> m_IdleList; //Idle List CThreadPool(); CThreadPool(int initnum); virtual ~CThreadPool(); void SetMaxNum(int maxnum){m_MaxNum = maxnum;} int GetMaxNum(void){return m_MaxNum;} void SetAvailLowNum(int minnum){m_AvailLow = minnum;} int GetAvailLowNum(void){return m_AvailLow;} void SetAvailHighNum(int highnum){m_AvailHigh = highnum;} int GetAvailHighNum(void){return m_AvailHigh;} int GetActualAvailNum(void){return m_AvailNum;} int GetAllNum(void){return m_ThreadList.size();} int GetBusyNum(void){return m_BusyList.size();} void SetInitNum(int initnum){m_InitNum = initnum;} int GetInitNum(void){return m_InitNum;} void TerminateAll(void); void Run(CJob* job,void* jobdata); }; CThreadPool::CThreadPool() { m_MaxNum = 50; m_AvailLow = 5; m_InitNum=m_AvailNum = 10 ; m_AvailHigh = 20; m_BusyList.clear(); m_IdleList.clear(); for(int i=0;i<m_InitNum;i++){ CWorkerThread* thr = new CWorkerThread(); thr->SetThreadPool(this); AppendToIdleList(thr); thr->Start(); } } CThreadPool::CThreadPool(int initnum) { assert(initnum>0 && initnum<=30); m_MaxNum = 30; m_AvailLow = initnum-10>0?initnum-10:3; m_InitNum=m_AvailNum = initnum ; m_AvailHigh = initnum+10; m_BusyList.clear(); m_IdleList.clear(); for(int i=0;i<m_InitNum;i++){ CWorkerThread* thr = new CWorkerThread(); AppendToIdleList(thr); thr->SetThreadPool(this); thr->Start(); //begin the thread,the thread wait for job } } CThreadPool::~CThreadPool() { TerminateAll(); } void CThreadPool::TerminateAll() { for(int i=0;i < m_ThreadList.size();i++) { CWorkerThread* thr = m_ThreadList[i]; thr->Join(); } return; } CWorkerThread* CThreadPool::GetIdleThread(void) { while(m_IdleList.size() ==0 ) m_IdleCond.Wait(); m_IdleMutex.Lock(); if(m_IdleList.size() > 0 ) { CWorkerThread* thr = (CWorkerThread*)m_IdleList.front(); printf("Get Idle thread %d\n",thr->GetThreadID()); m_IdleMutex.Unlock(); return thr; } m_IdleMutex.Unlock(); return NULL; } //add an idle thread to idle list void CThreadPool::AppendToIdleList(CWorkerThread* jobthread) { m_IdleMutex.Lock(); m_IdleList.push_back(jobthread); m_ThreadList.push_back(jobthread); m_IdleMutex.Unlock(); } //move and idle thread to busy thread void CThreadPool::MoveToBusyList(CWorkerThread* idlethread) { m_BusyMutex.Lock(); m_BusyList.push_back(idlethread); m_AvailNum--; m_BusyMutex.Unlock(); m_IdleMutex.Lock(); vector<CWorkerThread*>::iterator pos; pos = find(m_IdleList.begin(),m_IdleList.end(),idlethread); if(pos !=m_IdleList.end()) m_IdleList.erase(pos); m_IdleMutex.Unlock(); } void CThreadPool::MoveToIdleList(CWorkerThread* busythread) { m_IdleMutex.Lock(); m_IdleList.push_back(busythread); m_AvailNum++; m_IdleMutex.Unlock(); m_BusyMutex.Lock(); vector<CWorkerThread*>::iterator pos; pos = find(m_BusyList.begin(),m_BusyList.end(),busythread); if(pos!=m_BusyList.end()) m_BusyList.erase(pos); m_BusyMutex.Unlock(); m_IdleCond.Signal(); m_MaxNumCond.Signal(); } //create num idle thread and put them to idlelist void CThreadPool::CreateIdleThread(int num) { for(int i=0;i<num;i++){ CWorkerThread* thr = new CWorkerThread(); thr->SetThreadPool(this); AppendToIdleList(thr); m_VarMutex.Lock(); m_AvailNum++; m_VarMutex.Unlock(); thr->Start(); //begin the thread,the thread wait for job } } void CThreadPool::DeleteIdleThread(int num) { printf("Enter into CThreadPool::DeleteIdleThread\n"); m_IdleMutex.Lock(); printf("Delete Num is %d\n",num); for(int i=0;i<num;i++){ CWorkerThread* thr; if(m_IdleList.size() > 0 ){ thr = (CWorkerThread*)m_IdleList.front(); printf("Get Idle thread %d\n",thr->GetThreadID()); } vector<CWorkerThread*>::iterator pos; pos = find(m_IdleList.begin(),m_IdleList.end(),thr); if(pos!=m_IdleList.end()) m_IdleList.erase(pos); m_AvailNum--; printf("The idle thread available num:%d \n",m_AvailNum); printf("The idlelist num:%d \n",m_IdleList.size()); } m_IdleMutex.Unlock(); } void CThreadPool::Run(CJob* job,void* jobdata) { assert(job!=NULL); //if the busy thread num adds to m_MaxNum,so we should wait if(GetBusyNum() == m_MaxNum) m_MaxNumCond.Wait(); if(m_IdleList.size()<m_AvailLow) { if(GetAllNum()+m_InitNum-m_IdleList.size() < m_MaxNum ) CreateIdleThread(m_InitNum-m_IdleList.size()); else CreateIdleThread(m_MaxNum-GetAllNum()); } CWorkerThread* idlethr = GetIdleThread(); if(idlethr !=NULL) { idlethr->m_WorkMutex.Lock(); MoveToBusyList(idlethr); idlethr->SetThreadPool(this); job->SetWorkThread(idlethr); printf("Job is set to thread %d \n",idlethr->GetThreadID()); idlethr->SetJob(job,jobdata); } } 在CThreadPool中存在两个链表,一个是空闲链表,一个是忙碌链表。Idle链表中存放所有的空闲进程,当线程执行任务时候,其状态变为忙碌状态,同时从空闲链表中删除,并移至忙碌链表中。在CThreadPool的构造函数中,我们将执行下面的代码: for(int i=0;i<m_InitNum;i++) { CWorkerThread* thr = new CWorkerThread(); AppendToIdleList(thr); thr->SetThreadPool(this); thr->Start(); //begin the thread,the thread wait for job } 在该代码中,我们将创建m_InitNum个线程,创建之后即调用AppendToIdleList放入Idle链表中,由于目前没有任务分发给这些线程,因此线程执行Start后将自己挂起。 事实上,线程池中容纳的线程数目并不是一成不变的,其会根据执行负载进行自动伸缩。为此在CThreadPool中设定四个变量: m_InitNum:处世创建时线程池中的线程的个数。 m_MaxNum:当前线程池中所允许并发存在的线程的最大数目。 m_AvailLow:当前线程池中所允许存在的空闲线程的最小数目,如果空闲数目低于该值,表明负载可能过重,此时有必要增加空闲线程池的数目。实现中我们总是将线程调整为m_InitNum个。 m_AvailHigh:当前线程池中所允许的空闲的线程的最大数目,如果空闲数目高于该值,表明当前负载可能较轻,此时将删除多余的空闲线程,删除后调整数也为m_InitNum个。 m_AvailNum:目前线程池中实际存在的线程的个数,其值介于m_AvailHigh和m_AvailLow之间。如果线程的个数始终维持在m_AvailLow和m_AvailHigh之间,则线程既不需要创建,也不需要删除,保持平衡状态。因此如何设定m_AvailLow和m_AvailHigh的值,使得线程池最大可能的保持平衡态,是线程池设计必须考虑的问题。 线程池在接受到新的任务之后,线程池首先要检查是否有足够的空闲池可用。检查分为三个步骤: (1)检查当前处于忙碌状态的线程是否达到了设定的最大值m_MaxNum,如果达到了,表明目前没有空闲线程可用,而且也不能创建新的线程,因此必须等待直到有线程执行完毕返回到空闲队列中。 (2)如果当前的空闲线程数目小于我们设定的最小的空闲数目m_AvailLow,则我们必须创建新的线程,默认情况下,创建后的线程数目应该为m_InitNum,因此创建的线程数目应该为( 当前空闲线程数与m_InitNum);但是有一种特殊情况必须考虑,就是现有的线程总数加上创建后的线程数可能超过m_MaxNum,因此我们必须对线程的创建区别对待。 if(GetAllNum()+m_InitNum-m_IdleList.size() < m_MaxNum ) CreateIdleThread(m_InitNum-m_IdleList.size()); else CreateIdleThread(m_MaxNum-GetAllNum()); 如果创建后总数不超过m_MaxNum,则创建后的线程为m_InitNum;如果超过了,则只创建( m_MaxNum-当前线程总数 )个。 (3)调用GetIdleThread方法查找空闲线程。如果当前没有空闲线程,则挂起;否则将任务指派给该线程,同时将其移入忙碌队列。 当线程执行完毕后,其会调用MoveToIdleList方法移入空闲链表中,其中还调用m_IdleCond.Signal()方法,唤醒GetIdleThread()中可能阻塞的线程。 CWorkerThread CWorkerThread是CThread的派生类,是事实上的工作线程。在CThreadPool的构造函数中,我们创建了一定数量的CWorkerThread。一旦这些线程创建完毕,我们将调用Start()启动该线程。Start方法最终会调用Run方法。Run方法是个无限循环的过程。在没有接受到实际的任务的时候,m_Job为NULL,此时线程将调用Wait方法进行等待,从而处于挂起状态。一旦线程池将具体的任务分发给该线程,其将被唤醒,从而通知线程从挂起的地方继续执行。CWorkerThread的完整定义如下: class CWorkerThread:public CThread { private: CThreadPool* m_ThreadPool; CJob* m_Job; void* m_JobData; CThreadMutex m_VarMutex; bool m_IsEnd; protected: public: CCondition m_JobCond; CThreadMutex m_WorkMutex; CWorkerThread(); virtual ~CWorkerThread(); void Run(); void SetJob(CJob* job,void* jobdata); CJob* GetJob(void){return m_Job;} void SetThreadPool(CThreadPool* thrpool); CThreadPool* GetThreadPool(void){return m_ThreadPool;} }; CWorkerThread::CWorkerThread() { m_Job = NULL; m_JobData = NULL; m_ThreadPool = NULL; m_IsEnd = false; } CWorkerThread::~CWorkerThread() { if(NULL != m_Job) delete m_Job; if(m_ThreadPool != NULL) delete m_ThreadPool; } void CWorkerThread::Run() { SetThreadState(THREAD_RUNNING); for(;;) { while(m_Job == NULL) m_JobCond.Wait(); m_Job->Run(m_JobData); m_Job->SetWorkThread(NULL); m_Job = NULL; m_ThreadPool->MoveToIdleList(this); if(m_ThreadPool->m_IdleList.size() > m_ThreadPool->GetAvailHighNum()) { m_ThreadPool->DeleteIdleThread(m_ThreadPool->m_IdleList.size()-m_T hreadPool->GetInitNum()); } m_WorkMutex.Unlock(); } } void CWorkerThread::SetJob(CJob* job,void* jobdata) { m_VarMutex.Lock(); m_Job = job; m_JobData = jobdata; job->SetWorkThread(this); m_VarMutex.Unlock(); m_JobCond.Signal(); } void CWorkerThread::SetThreadPool(CThreadPool* thrpool) { m_VarMutex.Lock(); m_ThreadPool = thrpool; m_VarMutex.Unlock(); } 当线程执行任务之前首先必须判断空闲线程的数目是否低于m_AvailLow,如果低于,则必须创建足够的空闲线程,使其数目达到m_InitNum个,然后将调用MoveToBusyList()移出空闲队列,移入忙碌队列。当任务执行完毕后,其又调用MoveToIdleList()移出忙碌队列,移入空闲队列,等待新的任务。 除了Run方法之外,CWorkerThread中另外一个重要的方法就是SetJob,该方法将实际的任务赋值给线程。当没有任何执行任务即m_Job为NULL的时候,线程将调用m_JobCond.Wait进行等待。一旦Job被赋值给线程,其将调用m_JobCond.Signal方法唤醒该线程。由于m_JobCond属于线程内部的变量,每个线程都维持一个m_JobCond,只有得到任务的线程才被唤醒,没有得到任务的将继续等待。无论一个线程何时被唤醒,其都将从等待的地方继续执行m_Job->Run(m_JobData),这是线程执行实际任务的地方。 在线程执行给定Job期间,我们必须防止另外一个Job又赋给该线程,因此在赋值之前,通过m_VarMutex进行锁定, Job执行期间,其于的Job将不能关联到该线程;任务执行完毕,我们调用m_VarMutex.Unlock()进行解锁,此时,线程又可以接受新的执行任务。 在线程执行任务结束后返回空闲队列前,我们还需要判断当前空闲队列中的线程是否高于m_AvailHigh个。如果超过m_AvailHigh,则必须从其中删除(m_ThreadPool->m_IdleList.size()-m_ThreadPool->GetInitNum())个线程,使线程数目保持在m_InitNum个。 CJob CJob类相对简单,其封装了任务的基本的属性和方法,其中最重要的是Run方法,代码如下: class CJob { private: int m_JobNo; //The num was assigned to the job char* m_JobName; //The job name CThread *m_pWorkThread; //The thread associated with the job public: CJob( void ); virtual ~CJob(); int GetJobNo(void) const { return m_JobNo; } void SetJobNo(int jobno){ m_JobNo = jobno;} char* GetJobName(void) const { return m_JobName; } void SetJobName(char* jobname); CThread *GetWorkThread(void){ return m_pWorkThread; } void SetWorkThread ( CThread *pWorkThread ){ m_pWorkThread = pWorkThread; } virtual void Run ( void *ptr ) = 0; }; CJob::CJob(void) :m_pWorkThread(NULL) ,m_JobNo(0) ,m_JobName(NULL) { } CJob::~CJob(){ if(NULL != m_JobName) free(m_JobName); } void CJob::SetJobName(char* jobname) { if(NULL !=m_JobName) { free(m_JobName); m_JobName = NULL; } if(NULL !=jobname) { m_JobName = (char*)malloc(strlen(jobname)+1); strcpy(m_JobName,jobname); } } 线程池使用示例 至此我们给出了一个简单的与具体任务无关的线程池框架。使用该框架非常的简单,我们所需要的做的就是派生CJob类,将需要完成的任务实现在Run方法中。然后将该Job交由CThreadManage去执行。下面我们给出一个简单的示例程序 class CXJob:public CJob { public: CXJob(){i=0;} ~CXJob(){} void Run(void* jobdata) { printf("The Job comes from CXJOB\n"); sleep(2); } }; class CYJob:public CJob { public: CYJob(){i=0;} ~CYJob(){} void Run(void* jobdata) { printf("The Job comes from CYJob\n"); } }; main() { CThreadManage* manage = new CThreadManage(10); for(int i=0;i<40;i++) { CXJob* job = new CXJob(); manage->Run(job,NULL); } sleep(2); CYJob* job = new CYJob(); manage->Run(job,NULL); manage->TerminateAll(); } CXJob和CYJob都是从Job类继承而来,其都实现了Run接口。CXJob只是简单的打印一句”The Job comes from CXJob”,CYJob也只打印”The Job comes from CYJob”,然后均休眠2秒钟。在主程序中我们初始创建10个工作线程。然后分别执行40次CXJob和一次CYJob。 线程池使用后记 线程池适合场合 事实上,线程池并不是万能的。它有其特定的使用场合。线程池致力于减少线程本身的开销对应用所产生的影响,这是有前提的,前提就是线程本身开销与线程执行任务相比不可忽略。如果线程本身的开销相对于线程任务执行开销而言是可以忽略不计的,那么此时线程池所带来的好处是不明显的,比如对于FTP服务器以及Telnet服务器,通常传送文件的时间较长,开销较大,那么此时,我们采用线程池未必是理想的方法,我们可以选择“即时创建,即时销毁”的策略。 总之线程池通常适合下面的几个场合: (1) 单位时间内处理任务频繁而且任务处理时间短 (2) 对实时性要求较高。如果接受到任务后在创建线程,可能满足不了实时要求,因此必须采用线程池进行预创建。 (3) 必须经常面对高突发性事件,比如Web服务器,如果有足球转播,则服务器将产生巨大的冲击。此时如果采取传统方法,则必须不停的大量产生线程,销毁线程。此时采用动态线程池可以避免这种情况的发生。 结束语 本文给出了一个简单的通用的与任务无关的线程池的实现,通过该线程池能够极大的简化Linux下多线程的开发工作。该线程池的进一步完善开发工作还在进行中,希望能够得到你的建议和支持。 参考资料 http://www-900.ibm.com/developerWorks/cn/java/j-jtp0730/index.shtml ,Java理论与实践:线程池与工作队列 POSIX多线程程序设计,David R.Butenhof 译者:于磊 曾刚,中国电力出版社 C++面向对象多线程编程,CAMERON HUGHES等著 周良忠译,人民邮电出版社 Java Pro,结合线程和分析器池,Edy Yu 关于作者 张中庆,西安交通大学软件所,在读硕士,目前研究方向为分布式网络与移动中间件,对Linux极其爱好,可以通过flydish1234@sina.com.cn与我联系。 WINDOWS的钩子函数可以认为是WINDOWS的主要特性之一。利用它们,您可以捕捉您自己进程或其它进程发生的事件。通过“钩挂”,您可以给WINDOWS一个处理或过滤事件的回调函数,该函数也叫做“钩子函数”,当每次发生您感兴趣的事件时,WINDOWS都将调用该函数。一共有两种类型的钩子:局部的和远程的。 • 局部钩子仅钩挂您自己进程的事件。 • 远程的钩子还可以将钩挂其它进程发生的事件。远程的钩子又有两种: o 基于线程的 它将捕获其它进程中某一特定线程的事件。简言之,就是可以用来观察其它进程中的某一特定线程将发生的事件。 o 系统范围的 将捕捉系统中所有进程将发生的事件消息。 安装钩子函数将会影响系统的性能。监测“系统范围事件”的系统钩子特别明显。因为系统在处理所有的相关事件时都将调用您的钩子函数,这样您的系统将会明显的减慢。所以应谨慎使用,用完后立即卸载。还有,由于您可以预先截获其它进程的消息,所以一旦您的钩子函数出了问题的话必将影响其它的进程。记住:功能强大也意味着使用时要负责任。 在正确使用钩子函数前,我们先讲解钩子函数的工作原理。当您创建一个钩子时,WINDOWS会先在内存中创建一个数据结构,该数据结构包含了钩子的相关信息,然后把该结构体加到已经存在的钩子链表中去。新的钩子将加到老的前面。当一个事件发生时,如果您安装的是一个局部钩子,您进程中的钩子函数将被调用。如果是一个远程钩子,系统就必须把钩子函数插入到其它进程的地址空间,要做到这一点要求钩子函数必须在一个动态链接库中,所以如果您想要使用远程钩子,就必须把该钩子函数放到动态链接库中去。当然有两个例外:工作日志钩子和工作日志回放钩子。这两个钩子的钩子函数必须在安装钩子的线程中。原因是:这两个钩子是用来监控比较底层的硬件事件的,既然是记录和回放,所有的事件就当然都是有先后次序的。所以如果把回调函数放在DLL中,输入的事件被放在几个线程中记录,所以我们无法保证得到正确的次序。故解决的办法是:把钩子函数放到单个的线程中,譬如安装钩子的线程。 钩子一共有14种,以下是它们被调用的时机: • WH_CALLWNDPROC 当调用SendMessage时 • WH_CALLWNDPROCRET 当SendMessage的调用返回时 • WH_GETMESSAGE 当调用GetMessage 或 PeekMessage时 • WH_KEYBOARD 当调用GetMessage 或 PeekMessage 来从消息队列中查询WM_KEYUP 或 WM_KEYDOWN 消息时 • WH_MOUSE 当调用GetMessage 或 PeekMessage 来从消息队列中查询鼠标事件消息时 • WH_HARDWARE 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时 • WH_MSGFILTER 当对话框、菜单或滚动条要处理一个消息时。该钩子是局部的。它时为那些有自己的消息处理过程的控件对象设计的。 • WH_SYSMSGFILTER 和WH_MSGFILTER一样,只不过是系统范围的 • WH_JOURNALRECORD 当WINDOWS从硬件队列中获得消息时 • WH_JOURNALPLAYBACK 当一个事件从系统的硬件输入队列中被请求时 • WH_SHELL 当关于WINDOWS外壳事件发生时,譬如任务条需要重画它的按钮. • WH_CBT 当基于计算机的训练(CBT)事件发生时 • WH_FOREGROUNDIDLE 由WINDOWS自己使用,一般的应用程序很少使用 • WH_DEBUG 用来给钩子函数除错 现在我们知道了一些基本的理论,现在开始讲解如何安装/卸载一个钩子。 要安装一个钩子,您可以调用SetWindowHookEx函数。该函数的原型如下: SetWindowsHookEx proto HookType:DWORD, pHookProc:DWORD, hInstance:DWORD, ThreadID:DWORD • HookType 是我们上面列出的值之一,譬如: WH_MOUSE, WH_KEYBOARD • pHookProc 是钩子函数的地址。如果使用的是远程的钩子,就必须放在一个DLL中,否则放在本身代码中 • hInstance 钩子函数所在DLL的实例句柄。如果是一个局部的钩子,该值为NULL • ThreadID 是您安装该钩子函数后想监控的线程的ID号。该参数可以决定该钩子是局部的还是系统范围的。如果该值为NULL,那么该钩子将被解释成系统范围内的,那它就可以监控所有的进程及它们的线程。如果您指定了您自己进程中的某个线程ID 号,那该钩子是一个局部的钩子。如果该线程ID是另一个进程中某个线程的ID,那该钩子是一个全局的远程钩子。这里有两个特殊情况:WH_JOURNALRECORD 和 WH_JOURNALPLAYBACK总是代表局部的系统范围的钩子,之所以说是局部,是因为它们没有必要放到一个DLL中。WH_SYSMSGFILTER 总是一个系统范围内的远程钩子。其实它和WH_MSGFILTER钩子类似,如果把参数ThreadID设成0的话,它们就完全一样了。 如果该函数调用成功的话,将在eax中返回钩子的句柄,否则返回NULL。您必须保存该句柄,因为后面我们还要它来卸载钩子。 要卸载一个钩子时调用UnhookWidowHookEx函数,该函数仅有一个参数,就是欲卸载的钩子的句柄。如果调用成功的话,在eax中返回非0值,否则返回NULL。 现在您知道了如何安装和卸载一个钩子了,接下来我们将看看钩子函数。. 只要您安装的钩子的消息事件类型发生,WINDOWS就将调用钩子函数。譬如您安装的钩子是WH_MOUSE类型,那么只要有一个鼠标事件发生时,该钩子函数就会被调用。不管您安装的时那一类型钩子,钩子函数的原型都时是一样的: HookProc proto nCode:DWORD, wParam:DWORD, lParam:DWORD o nCode 指定是否需要处理该消息 o wParam 和 lParam 包含该消息的附加消息 HookProc 可以看作是一个函数名的占位符。只要函数的原型一致,您可以给该函数取任何名字。至于以上的几个参数及返回值的具体含义各种类型的钩子都不相同。譬如: WH_CALLWNDPROC • nCode 只能是HC_ACTION,它代表有一个消息发送给了一个窗口 • wParam 如果非0,代表正被发送的消息 • lParam 指向CWPSTRUCT型结构体变量的指针 • return value: 未使用,返回0 WH_MOUSE • nCode 为HC_ACTION 或 HC_NOREMOVE • wParam 包含鼠标的事件消息 • lParam 指向MOUSEHOOKSTRUCT型结构体变量的指针 • return value: 如果不处理返回0,否则返回非0值 所以您必须查询您的WIN32 API 指南来得到不同类型的钩子的参数的详细定义以及它们返回值的意义。这里还有一个问题需要注意:所有的钩子都串在一个链表上,最近加入的钩子放在链表的头部。当一个事件发生时,WINDOWS将按照从链表头到链表尾调用的顺序。所以您的钩子函数有责任把消息传到下一个链中的钩子函数。当然您可以不这样做,但是您最好明白这时这么做的原因。在大多数的情况下,最好把消息事件传递下去以便其它的钩子都有机会获得处理这一消息的机会。调用下一个钩子函数可以调用函数CallNextHookEx。该函数的原型如下: CallNextHookEx proto hHook:DWORD, nCode:DWORD, wParam:DWORD, lParam:DWORD • hHook 时是您自己的钩子函数的句柄。利用该句柄可以遍历钩子链。 • nCode, wParam and lParam 您只要把传入的参数简单传给CallNextHookEx即可。 请注意:对于远程钩子,钩子函数必须放到DLL中,它们将从DLL中映射到其它的进程空间中去。当WINDOWS映射DLL到其它的进程空间中去时,不会把数据段也进行映射。简言之,所有的进程仅共享DLL的代码,至于数据段,每一个进程都将有其单独的拷贝。这是一个很容易被忽视的问题。您可能想当然的以为,在DLL中保存的值可以在所有映射该DLL的进程之间共享。在通常情况下,由于每一个映射该DLL的进程都有自己的数据段,所以在大多数的情况下您的程序运行得都不错。但是钩子函数却不是如此。对于钩子函数来说,要求DLL的数据段对所有的进程也必须相同。这样您就必须把数据段设成共享的,这可以通过在链接开关中指定段的属性来实现。在MASM中您可以这么做: /SECTION:<section name>, S 已初期化的段名是.data,未初始化的段名是.bss。`加入您想要写一个包含钩子函数的DLL,而且想使它的未初始化的数据段在所有进程间共享,您必须这么做: link /section:.bss,S /DLL /SUBSYSTEM:WINDOWS .......... S 代表该段是共享段。 例子: 一共有两个模块:一个是GUI部分,另一个是安装和卸载钩子的DLL。 ;--------------------------------------------- 主程序的源代码部分-------------------------------------- .386 .model flat,stdcall option casemap:none include masm32includewindows.inc include masm32includeuser32.inc include masm32includekernel32.inc include mousehook.inc includelib mousehook.lib includelib masm32libuser32.lib includelib masm32libkernel32.lib wsprintfA proto C :DWORD,:DWORD,:VARARG wsprintf TEXTEQU <wsprintfA> .const IDD_MAINDLG equ 101 IDC_CLASSNAME equ 1000 IDC_HANDLE equ 1001 IDC_WNDPROC equ 1002 IDC_HOOK equ 1004 IDC_EXIT equ 1005 WM_MOUSEHOOK equ WM_USER+6 DlgFunc PROTO :DWORD,:DWORD,:DWORD,:DWORD .data HookFlag dd FALSE HookText db "&Hook",0 UnhookText db "&Unhook",0 template db "%lx",0 .data? hInstance dd ? hHook dd ? .code start: invoke GetModuleHandle,NULL mov hInstance,eax invoke DialogBoxParam,hInstance,IDD_MAINDLG,NULL,addr DlgFunc,NULL invoke ExitProcess,NULL DlgFunc proc hDlg:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD LOCAL hLib:DWORD LOCAL buffer[128]:byte LOCAL buffer1[128]:byte LOCAL rect:RECT .if uMsg==WM_CLOSE .if HookFlag==TRUE invoke UninstallHook .endif invoke EndDialog,hDlg,NULL .elseif uMsg==WM_INITDIALOG invoke GetWindowRect,hDlg,addr rect invoke SetWindowPos, hDlg, HWND_TOPMOST, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW .elseif uMsg==WM_MOUSEHOOK invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128 invoke wsprintf,addr buffer,addr template,wParam invoke lstrcmpi,addr buffer,addr buffer1 .if eax!=0 invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer .endif invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128 invoke GetClassName,wParam,addr buffer,128 invoke lstrcmpi,addr buffer,addr buffer1 .if eax!=0 invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer .endif invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128 invoke GetClassLong,wParam,GCL_WNDPROC invoke wsprintf,addr buffer,addr template,eax invoke lstrcmpi,addr buffer,addr buffer1 .if eax!=0 invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer .endif .elseif uMsg==WM_COMMAND .if lParam!=0 mov eax,wParam mov edx,eax shr edx,16 .if dx==BN_CLICKED .if ax==IDC_EXIT invoke SendMessage,hDlg,WM_CLOSE,0,0 .else .if HookFlag==FALSE invoke InstallHook,hDlg .if eax!=NULL mov HookFlag,TRUE invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText .endif .else invoke UninstallHook invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText mov HookFlag,FALSE invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL .endif .endif .endif .endif .else mov eax,FALSE ret .endif mov eax,TRUE ret DlgFunc endp end start ;----------------------------------------------------- DLL的源代码部分 -------------------------------------- .386 .model flat,stdcall option casemap:none include masm32includewindows.inc include masm32includekernel32.inc includelib masm32libkernel32.lib include masm32includeuser32.inc includelib masm32libuser32.lib .const WM_MOUSEHOOK equ WM_USER+6 .data hInstance dd 0 .data? hHook dd ? hWnd dd ? .code DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD .if reason==DLL_PROCESS_ATTACH push hInst pop hInstance .endif mov eax,TRUE ret DllEntry Endp MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD invoke CallNextHookEx,hHook,nCode,wParam,lParam mov edx,lParam assume edx:PTR MOUSEHOOKSTRUCT invoke WindowFromPoint,[edx].pt.x,[edx].pt.y invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0 assume edx:nothing xor eax,eax ret MouseProc endp InstallHook proc hwnd:DWORD push hwnd pop hWnd invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL mov hHook,eax ret InstallHook endp UninstallHook proc invoke UnhookWindowsHookEx,hHook ret UninstallHook endp End DllEntry ;---------------------------------------------- DLL的Makefile文件 ---------------------------------------------- NAME=mousehook $(NAME).dll: $(NAME).obj Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS /LIBPATH:c:masmlib $(NAME).obj $(NAME).obj: $(NAME).asm ml /c /coff /Cp $(NAME).asm 分析: 该应用程序的主窗口中包括三个编辑控件,它们将分别显示当前鼠标光标所在位置的窗口类名、窗口句柄和窗口过程的地址。还有两个按钮:“Hook”和“Eixt”。当您按下Hook时,应用程序将钩挂鼠标输入的事件消息,该按钮的文本将变成“Unhook”。当您把鼠标关标滑过一个窗口时,该窗口的有关消息将显示在主窗口中。当您按下“Unhook”时,应用程序将卸载钩子。 主窗口使用一个对话框来作为它的主窗口。它自定义了一个消息WM_MOUSEHOOK,用来在主窗口和DLL之间传递消息。当主窗口接收到该消息时,wParam中包含了光标所在位置的窗口的句柄。当然这是我们做的安排。我这么做只是为了方便。您可以使用您自己的方法在主应用程序和DLL之间进行通讯。 .if HookFlag==FALSE invoke InstallHook,hDlg .if eax!=NULL mov HookFlag,TRUE invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText .endif 该应用程序有一个全局变量,HookFlag,它用来监视钩子的状态。如果安装来钩子它就是TRUE,否则是FALSE。 当用户按下Hook按钮时,应用程序检查钩子是否已经安装。如果还没有的话,它将调用DLL中引出的函数InstallHook来安装它。注意我们把主对话框的句柄传递给了DLL,这样这个钩子DLL就可以把WM_MOUSEHOOK消息传递给正确的窗口了。当应用程序加载时,钩子DLL也同时加载。时机上当主程序一旦加载到内存中后,DLL就立即加载。DLL的入口点函数载主程序的第一条语句执行前就前执行了。所以当主程序执行时,DLL已经初始化好了。我们载入口点处放入如下代码: .if reason==DLL_PROCESS_ATTACH push hInst pop hInstance .endif 该段代码把DLL自己的实例句柄放到一个全局变量中保存。由于入口点函数是在所有函数调用前被执行的,所以hInstance总是有效的。我们把该变量放到.data中,使得每一个进程都有自己一个该变量的值。因为当鼠标光标停在一个窗口上时,钩子DLL被映射进进程的地址空间。加入在DLL缺省加载的地址处已经加载其它的DLL,那钩子DLL将要被映射到其他的地址。hInstance将被更新成其它的值。当用户按下Unhook再按下Hook时,SetWindowsHookEx将被再次调用。这一次,它将把新的地址作为实例句柄。而在例子中这是错误的,DLL装载的地址并没有变。这个钩子将变成一个局部的,您只能钩挂发生在您窗口中的鼠标事件,这是很难让人满意的 。 InstallHook proc hwnd:DWORD push hwnd pop hWnd invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL mov hHook,eax ret InstallHook endp InstallHook 函数非常简单。它把传递过来的窗口句柄保存在hWnd中以备后用。接着调用SetWindowsHookEx函数来安装一个鼠标钩子。该函数的返回值放在全局变量hHook中,将来在UnhookWindowsHookEx中还要使用。在调用SetWindowsHookEx后,鼠标钩子就开始工作了。无论什么时候发生了鼠标事件,MouseProc函数都将被调用: MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD invoke CallNextHookEx,hHook,nCode,wParam,lParam mov edx,lParam assume edx:PTR MOUSEHOOKSTRUCT invoke WindowFromPoint,[edx].pt.x,[edx].pt.y invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0 assume edx:nothing xor eax,eax ret MouseProc endp 钩子函数首先调用CallNextHookEx函数让其它的钩子处理该鼠标事件。然后,调用WindowFromPoint函数来得到给定屏幕坐标位置处的窗口句柄。注意:我们用lParam指向的MOUSEHOOKSTRUCT型结构体变量中的POINT成员变量作为当前的鼠标位置。在我们调用PostMessage函数把WM_MOUSEHOOK消息发送到主程序。您必须记住的一件事是:在钩子函数中不要使用SendMessage函数,它会引起死锁。MOUSEHOOKSTRUCT的定义如下: MOUSEHOOKSTRUCT STRUCT DWORD pt POINT <> hwnd DWORD ? wHitTestCode DWORD ? dwExtraInfo DWORD ? MOUSEHOOKSTRUCT ENDS • pt 是当前鼠标所在的屏幕位置。 • hwnd 是将接收鼠标消息的窗口的句柄。通常它是鼠标所在处的窗口,但是如果窗口调用了SetCapture,鼠标的输入将到向到这个窗口。因我们不用该成员变量而是用WindowFromPoint函数。 • wHitTestCode 指定hit-test值,该值给出了更多的鼠标位置值。它指定了鼠标在窗口的那个部位。该值的完全列表,请参考WIN32 API 指南中的WM_NCHITTEST消息。 • dwExtraInfo 该值包含了相关的信息。一般该值由mouse_event函数设定,可以调用GetMessageExtraInfo来获得。 当主窗口接收到WM_MOUSEHOOK 消息时,它用wParam参数中的窗口句柄来查询窗口的消息。 .elseif uMsg==WM_MOUSEHOOK invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128 invoke wsprintf,addr buffer,addr template,wParam invoke lstrcmpi,addr buffer,addr buffer1 .if eax!=0 invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer .endif invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128 invoke GetClassName,wParam,addr buffer,128 invoke lstrcmpi,addr buffer,addr buffer1 .if eax!=0 invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer .endif invoke GetDlgItemText,hDlg,IDC_WNDPROC,addr buffer1,128 invoke GetClassLong,wParam,GCL_WNDPROC invoke wsprintf,addr buffer,addr template,eax invoke lstrcmpi,addr buffer,addr buffer1 .if eax!=0 invoke SetDlgItemText,hDlg,IDC_WNDPROC,addr buffer .endif 为了避免重绘文本时的抖动,我们把已经在编辑空间中线时的文本和我们将要显示的对比。如果相同,就可以忽略掉。得到类名调用GetClassName,得到窗口过程调用GetClassLong并传入GCL_WNDPROC标志,然后把它们格式化成文本串并放到相关的编辑空间中去。 invoke UninstallHook invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText mov HookFlag,FALSE invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL invoke SetDlgItemText,hDlg,IDC_WNDPROC,NULL 当用户按下Unhook后,主程序调用DLL中的UninstallHook函数。该函数调用UnhookWindowsHookEx函数。然后,它把按钮的文本换回“Hook”,HookFlag的值设成FALSE再清除掉编辑控件中的文本。 链接器的开关选项如下: Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS 它指定.bss段作为一个共享段以便所有映射该DLL的进程共享未初始化的数据段。如果不用该开关,您DLL中的钩子就不能正常工作了。 作者Blog:http://blog.csdn.net/kgdiwss/ 相关文章 C#实现根据域名查询ip实例(原创) 删除WISPTIS.EXE进程 tcp/ip入门(认真读完后会对会对互连网有一个深刻的认识) 用VC编制DLL程序的方法小结 如何突破TCP-IP过滤防火墙进入内网 |
B4层 发表时间: 05-07-18 22:34 |
![]() | 回复: SysHu0teR [syshunter] ![]() |
登录 |
代码: gcc -lpthread pth.c -o pth 刚装上个debian,用着还满不错滴! ![]() |
B5层 发表时间: 05-07-19 19:49 |
![]() | 回复: SysHu0teR [syshunter] ![]() |
登录 |
忘了回答你第一个问题: 你说的c++方式我不大明白,我也没听说过LINUX封装好的线程操作类。看一些资料说LInux下的pthread就是通过调用clone来实现的(类似fork的东西); 创建线程: pthread(pthread_t *thread_id,pthread_attr_t *attr void * (* thread_function)(void *),void *arg); 成功返回0,第一个参数就是创建该线程的ID, 第二个参数是该线程的属性值,可以设置是否立刻执行thread_function,优先级等等,NULL表示缺省属性,具体太多我也懒得看。第三个参数就是要执行的线程函数的地址,第四个就是穿给该线程函数(thread_function)的参数。 线程退出: pthread_exit(void *res) 退出线程并可以传一个值给调用他的线程的pthread_join(),记住这个res; phtread_join(pthread_t thread_id,void **res); 这个函数就是等待thread_id指定的线程结束并从该线程的pthread_exit得到我上面说的res地址。 例子: int main() { ....... pthread_create(&id,NULL,fun,null); pthread_join(id,res); //在这里等待ID为id的线程结束 ........... //继续执行 } void *fun(void *arg) { ... pthread_exit(null); } 其他的操作还有很多很多…… |
B6层 发表时间: 05-07-19 20:42 |
![]() | 回复: zhanjiajun [zhanjiajun] ![]() |
登录 |
仁兄可是深圳的??? |
B7层 发表时间: 05-07-28 16:46 |
![]() | 回复: SysHu0teR [syshunter] ![]() |
登录 |
合肥的![]() |
B8层 发表时间: 05-08-01 16:21 |
![]() | 回复: romario [romario] ![]() |
登录 |
佩服的五体投地,有机会想向syshunter学习,我正学c |
B9层 发表时间: 05-10-04 14:08 |
![]() | 回复: XiYang [cimsxiyang] ![]() |
登录 |
其实线程是很easy的 他只是一个为“multi-threaded programming”提供的库而已。 如果你读过核心代码的话,你会发现他是通过clone()系统调用创建的. 与传统的“ multi-processing programming”的不同之处其实也就是所有的线程使用同一个memory space.正是因为如此,它的上下文切换的开销是非常小的。这往往也就意味着performance。 :).......... |
B10层 发表时间: 05-10-06 04:25 |
|
20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon
粤ICP备05087286号