windows 线程

一、线程

    线程由2部分组成:(1)线程内核对象,OS用它来关系线程,存放线程统计信息;(2)线程栈,维护线程执行时所需要的所有函数参数和局部变量。

二、线程相关的数据结构

(1)ETHREAD

typedef struct _ETHREAD {
    KTHREAD Tcb;     // 本线程的KTHREAD结构
    LARGE_INTEGER CreateTime;
    ... ...
    CLIENT_ID Cid;   //本线程的cid
    ULONG_PTR TopLevelIrp;  // either NULL, an Irp or a flag defined in FsRtl.h
    PEPROCESS ThreadsProcess;  //挂入EPROCESS中的线程队列
    LIST_ENTRY ThreadListEntry;
} ETHREAD, *PETHREAD;
 </span>
(2)KTHREAD

typedef struct _KTHREAD {   
    DISPATCHER_HEADER Header;  //使线程成为“可等待”对象
    ... ...
    PVOID InitialStack;
    PVOID StackLimit;  
    PVOID KernelStack;    //指向本线程的系统空间堆栈   
    union {
        KAPC_STATE ApcState;  //本线程当前的APC状态
        ... ...
    };
    PVOID Teb;   //指向用户空间的线程环境块TEB
    PKTRAP_FRAME TrapFrame; //指向系统空间堆栈上的自陷框架
    PVOID CallbackStack;
    PVOID ServiceTable;
    PKPROCESS Process;  //指向所属进程的KPROCESS结构
    KAFFINITY Affinity;   
    PKAPC_STATE ApcStatePointer[2]; 
    PVOID Win32Thread;   //指向本线程的W32THREAD结构
    PVOID StackBase;    
    LIST_ENTRY ThreadListEntry;   //挂入KRPOCESS中的线程队列
} KTHREAD, *PKTHREAD, *PRKTHREAD;</span>

(3)TEB

typedef struct _TEB {
    NT_TIB NtTib;  // 线程信息块,包含了基本信息,且被结构中有一个指针指向TEB;还有一个ExceptionList
    PVOID  EnvironmentPointer;
    CLIENT_ID ClientId;
    PVOID ActiveRpcHandle;
    PVOID ThreadLocalStoragePointer;
    PPEB ProcessEnvironmentBlock;     //指向所属进程PEB
    ULONG LastErrorValue;
    ... ...    
    PVOID Win32ThreadInfo;          // PtiCurrent    
    PVOID TlsSlots[TLS_MINIMUM_AVAILABLE];
    LIST_ENTRY TlsLinks;    
} TEB;</span>

三、线程的创建

(1)CreateThread 该方法基本已经不再使用

HANDLECreateThread(
 LPSECURITY_ATTRIBUTES    lpThreadAttributes,
 SIZE_T   dwStackSize,
 LPTHREAD_START_ROUTINE    lpStartAddress,
 LPVOID   lpParameter,
 DWORD    dwCreationFlags,
 LPDWORD    lpThreadId
);</span>

lpThreadAttributes安全属性;

dwStackSize 线程栈大小;

lpStartAddr 线程函数地址;

lpParam 函数参数;

dwCreateFlags 控制线程创建时的状态,如Create_suspended

lpThreadID 输出线程id

(2) _beginthreadex 提供多线程安全

_MCRTIMP uintptr_t __cdecl _beginthreadex(  
    void *security,  
    unsigned stacksize,  
    unsigned (__CLR_OR_STD_CALL * initialcode) (void *),  
    void * argument,  
    unsigned createflag,  
    unsigned *thrdaddr  
)</span>

(a)每个线程都有自己专用的_tiddata内存块,他们是从c/c++运行时库的堆上分配的;

(b)传给_beginthreadex的函数地址和参数都保存在_tiddata中;

(c)_beginthreadex  会在内部调用CreateThread,因为操作系统只知道用这种方式来创建一个线程;

(d)CreateThread函数被调用时,传给他的函数是_threadstartex。另外,参数是_tiddata的地址;

(e)如果一切顺利,会返回线程的句柄,就像CreateThread那样。任何失败,都会返回0。

  _threadstartex

(a)首先执行RtlUserThreadStart,然后再跳转到   _threadstartex;

(b) _threadstartex的唯一参数就是_tiddata的地址;

(c) tlsSetvalue是一个系统函数,他将一个值与线程关联起来;

(d) 调用_callthreadstartex中,有一个SEH帧,他将预期要执行的函数包含起来,处理运行时库的很多事情,比如:运行时错误,和运行时库相关的signal函数;

(e) 执行预期的函数;

(f) 线程函数的返回值被认为是线程的退出码;调用了_endthreadex, 在这个函数里,会销毁tiddata

四、线程的退出

和进程的退出类似,一共有4种方式:

(1)线程函数返回

    线程创建的C++对象都通过析构函数被正确销毁;

    操作系统正确释放线程栈使用的内存;

    操作系统把线程的退出代码设为线程函数的返回值;

    系统递减对内核对象的使用计数;

(2)线程通过调用exitthread来杀死自己

    终止线程运行;

    释放操作系统资源;

   不会释放C/C++资源,推荐使用_endthreadex;

(3)同一个进程或另一个进程中的线程调用terminatethread

(4)包含线程的进程终止运行,被Terminate

    类似于为每个线程调用terminatethread,意味着正确的清理操作不会被执行:C++对象不会被析构,数据不会被写回到磁盘上等。

五、线程切换

(1)用户态看线程切换:

    线程有以下几种状态: Initialized,    Ready,    Running,    Standby,    Terminated,    Waiting,    Transition。每个线程都一个时间片段,时间片段消耗完后,会发生时钟中断,然后选择ready状态且优先级高的线程执行。

    windows 是任务抢占式操作系统,线程之间不是平均分配时间的,而是优先运行优先级高的线程,高优先级的线程可以中断优先级的线程;

    线程切换的几种方式:

    (a)当前线程主动让出剩余的运行时间:该调度是当前线程主动让出剩余运行时间,如执行了SleepWait一个对象等等;

    (b)有新的线程变得可调度:比如一个被挂起的线程被中断处理程序激活,变为可调度,要进行一次调度,中断处理程序将该线程的优先级和当前线程比较,如果优先级较高,该线程将成为抢夺者,否则该线程按照其优先级挂入相应的就绪队列。

    (c)就绪线程的优先级提高:如当前线程将一个就绪线程的级别提高,将引发一次调度,如果该线程的优先级比当前cpu上的线程的优先级高,该线程就成为剥夺者。

    (d)调度时钟中断:每一次时钟中断,都会将当前线程的定量减少一定值,如果发现当前线程的时间片已经用完,就会进行一次调度,如果和当前线程优先级对应的就绪队列非空,该排在该就绪队列最面的线程将会得到调度,成为即将运行的线程。

(2)内核态:

    每个任务、即进程或线程、都有一个独立的“任务状态段”TSS,里面包含了几乎所有寄存器的映像,而通过TSS的切换来实现任务的切换,而且只要一条指令就能完成这样的切换。这条指令把几乎所有寄存器(除一些“系统寄存器”如GDTR等以外)的当前内容都一下子保存到当前任务的TSS中;然后TR寄存器指向目标任务的TSS,再把TSS中的寄存器值放入相应的寄存器中,任务切换就完成了。

    Windows内核中为此定义了一套以“处理器控制区(Processor Control Region)KPCR为枢纽的数据结构,使每个CPU都有个KPCR结构,用来保存与线程切换有关的全局信息。

typedef struct _KPCR {
  KPCR_TIB  <strong>Tib</strong>;                  
  struct _KPCR  *Self;              
  struct _KPRCB  *<strong>Prcb</strong>;          
  KIRQL  Irql;                  
  ULONG  IRR;                   
  ULONG  IrrActive;          
  ULONG  IDR;                    
  PVOID  KdVersionBlock;   
  PUSHORT  IDT;                 
  PUSHORT  <strong>GDT</strong>;               
  struct _KTSS  *<strong>TSS</strong>;             
  USHORT  MajorVersion;        
  USHORT  MinorVersion;          
  KAFFINITY  SetMember;         
  ULONG  StallScaleFactor;        
  UCHAR  DebugActive;             
  UCHAR  ProcessorNumber;        
  UCHAR  Reserved;              
  UCHAR  L2CacheAssociativity;      
  ULONG  VdmAlert;              
  ULONG  KernelReserved[14];       
  ULONG  L2CacheSize;            
  ULONG  HalReserved[16];          
  ULONG  InterruptMode;         
  UCHAR  KernelReserved2[0x48];    
  KPRCB  <strong>PrcbData</strong>;               
} KPCR, *PKPCR;</span>

typedef struct _KPCR_TIB {
  PVOID  <strong>ExceptionList</strong>;            
  PVOID  StackBase;               
  PVOID  StackLimit;               
  PVOID  SubSystemTib;            
  _ANONYMOUS_UNION union {
      PVOID  FiberData;           
      DWORD  Version;          
  } DUMMYUNIONNAME;
  PVOID  ArbitraryUserPointer;     
  struct _NT_TIB *Self;             
} KPCR_TIB, *PKPCR_TIB;         
</span>
/* ProcessoR Control Block */
typedef struct _KPRCB {
       USHORT MinorVersion;
       USHORT MajorVersion;
       struct _KTHREAD *<strong>CurrentThread</strong>;
       struct _KTHREAD *<strong>NextThread</strong>;
       struct _KTHREAD *<strong>IdleThread</strong>;
       . . . . . .
       UCHAR CpuType;
       UCHAR CpuID;
       USHORT CpuStep;
       KPROCESSOR_STATE ProcessorState;
       . . . . . .
       PVOID LockQueue[33];    // Used for Queued Spinlocks
       struct _KTHREAD *NpxThread;
       ULONG InterruptCount;
       ULONG KernelTime;
       ULONG UserTime;
       . . . . . .
       struct _KEVENT *DpcEvent;
       UCHAR ThreadDpcEnable;
       BOOLEAN QuantumEnd;
       . . . . . .
       LONG MmPageReadCount;
       LONG MmPageReadIoCount;
       LONG MmCacheReadCount;
       LONG MmCacheIoCount;
       LONG MmDirtyPagesWriteCount;
       . . . . . .
       FX_SAVE_AREA NpxSaveArea;
       PROCESSOR_POWER_STATE PowerState;
} KPRCB, *PKPRCB;</span>

线程切换可以参考资料:http://www.longene.org/techdoc/0984381001224576913.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值