windows核心编程线程同步机制_第1页
windows核心编程线程同步机制_第2页
windows核心编程线程同步机制_第3页
windows核心编程线程同步机制_第4页
windows核心编程线程同步机制_第5页
已阅读5页,还剩2页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、windows核心编程-线程同步机制线程同步机制常用的线程同步机制有很多种,主要分为用户模式和内核对象两类;其中用户模式包括:原子操作、关键代码段内核对象包括:时间内核对象()n、等待定时器内核对象(i)、i信标内核对象()o互斥内核对象(Mute、x、用户模式:、1原子操作原子操作,就是该操作绝不会在执行完毕前被任何其他任务或事件打断,也就说,它是最小的执行单位,不可能有比它更小的执行单位,因此这里的原子实际是使用了物理学里的物质微粒的概念。原子操作需要硬件的支持,因此是架构相关的,其和原子类型的定义都定义在内核源码中,因为语言并不能实现这样的操作。这个技术主要用于实现资源计数。原子类型定义

2、类似下面结构:sdoOnoionoi核心就是通过o修饰变量,告诉编译器不要随该变量数据做优化处理,对它的访问每次都是对内存的访问,而不是对寄存器的访问。自减的原子操作运算后的结果参数必须是位对齐的变量(即读、写、加法、减法、自增、自减、比较等,常用封装如下:/M)thod:atomicR)ad/Acss/R)turns:ii11oi以原子的左式读取变量no修饰符确保读取的是当前的值,而不是缓存在寄存器的旧值sioiodinodoinss运算后的结果参数必须是位对齐的变量(即oi类型的),否则可能会有意想不到的问题参数所指变量将自增o自增的原子操作ionsioiIncv(odr)tuIrnnt)

3、rlock)dIncr)m)nt(valuls)modnm)nt64(vandifoidssioi类型的),否则可能会有意想不到的问题参数所指变量将自减istatic_CUR_VALUE_MYWORDatomicDec(_INOUT_MYWORDvolatile*value)#ifdef_X86_returnInterlockedDecrement(value);#elsereturnInterlockedDecrement64(value);#endif/*/Method:atomicSet/Access:public/Returns:void/Qualifier:参数必须是32位对齐的变量

4、(即long类型的),否则可能会有意想不到的问题/Parameter:_INOUT_LONGvolatile*target/参数所指变量将与第二参数相同/Parameter:_IN_LONGvalue/Description:long类型数值赋值以value值取代targe値/*staticvoidatomicSet(_INOUT_MYWORDvolatile*target,_IN_MYWORDvalue)#ifdef_X86_InterlockedExchange(target,value);#elseInterlockedExchange64(target,value);#endif/*/

5、Method:atomicAdd/Access:public/Returns:void/Qualifier:参数必须是32位对齐的变量(即long类型的),否则可能会有意想不到的问题/Parameter:_INOUT_LONGvolatile*target/参数所指变量将与第二参数相同/Parameter:_IN_LONGvalue/Description:long类型数值赋值/*staticvoidatomicAdd(_INOUT_MYWORDvolatile*target,_IN_MYWORDvalue)#ifdef_X86_InterlockedExchangeAdd(target,va

6、lue);#elseInterlockedExchangeAdd64(target,value);#endif原子操作的缺陷在于,只能对onglong数据进行,其他的复杂数据结构就无能为力。1.2、临界区(关键代码段)1.2.1关键代码段关键代码段CRITICAL_SECTION的结构具体定义在WinBase.h文件中,其结构重命名为RTL_CRITICAL_SECTION;使用它有两个要求:需要该资源的所有线程都必须知道负责保护资源的CRITICAL_SECTION结构的地址CRITICAL_SECTION结构必须在任何线程试图访问被保护资源之前初始化初始化函数InitializeCriti

7、calSection:WINBASEAPIVOIDWINAPIInitializeCriticalSection(_Out_LPCRITICAL_SECTIONlpCriticalSection);销毁函数DeleteCriticalSectionWINBASEAPIVOIDWINAPIDeleteCriticalSection(_Inout_LPCRITICAL_SECTIONlpCriticalSection);在所有共享资源之前调用EnterCriticalSection函数WINBASEAPIVOIDWINAPIEnterCriticalSection(_Inout_LPCRITICA

8、L_SECTIONlpCriticalSection);其功能如下:如果当前没有线程访问该资源,EnterCriticalSection函数便更新CRITICAL_SECTION中的相关变量,标识调用线程已取得共享资源访问权限,立即返回使调用线程继续执行如果CRITICAL_SECTION成员变量指明调用线程已经被赋予访问权限,那么EnterCriticalSection函数便更新赋予权限计数并返回-如果CRITICAL_SECTION成员变量指明共享资源已经被其他线程占用,那么EnterCriticalSection函数便使调用线程设置为等待状态EnterCriticalSection函数实

9、际上只做了一些测试,但是这些测试工作都是原子的。尝试获取权限函数TryEnterCriticalSection:WINBASEAPIBOOLWINAPITryEnterCriticalSection(_Inout_LPCRITICAL_SECTIONlpCriticalSection);TryEnterCriticalSection区别于EnterCriticalSection函数的地方在于,它会立即返回共享资源当前是否被占用,根据返回值来决定调用线程是继续等待尝试还是做其他事务。访问共享资源结束后必须调用LeaveCriticalSection:WINBASEAPIVOIDWINAPILea

10、veCriticalSection(_Inout_LPCRITICAL_SECTIONlpCriticalSection);1.2.2自旋锁相关的代码段BOOLWINAPIInitializeCriticalSectionAndSpinCount(_Out_LPCRITICAL_SECTIONlpCriticalSection,_In_DWORDdwSpinCount);该函数作用是初始化阶段为一个关键段设置一个自旋锁,期以提高关键段的性能。这样的关键段在后续尝试进/EnterCriticalSection的时候会用一个自旋锁循环尝试获取权限。函数中参数dwSpinCount为自旋次数。目前路

11、人说法是进程关键段性能最佳自旋次数为4000(该数值引用自windows核心编程8.4.2关键代码段与循环锁)。dwSpinCount的高信息位有数据时,它会创建对应的事件内核对象,初始化时与该代码段关联起来。这样可以避免InitializeCriticalSection在内存不足的情况可能初始化失败的问题,如果确认程序后续会争用关键代码段或进程在内存短缺的环境中运行的话。DWORDWINAPISetCriticalSectionSpinCount(_Inout_LPCRITICAL_SECTIONlpCriticalSection,_In_DWORDdwSpinCount);这个函数是对上一

12、个的补充,设置自旋次数。二、内核模式线程同步机制在内核模式下,等待函数是非常常用的一类,用于使调用线程主动进入等待状态。WINBASEAPIDWORDWINAPIWaitForSingleObject(_In_HANDLEhHandle,_In_DWORDdwMilliseconds);WaitForSingleObject函数相对使用频率比较高,针对单一内核对象的进入等待函数,参数分别为内核对象句柄及等待时长ms)。相应的有等待多个内核对象信号的等待函数:WINBASEAPIDWORDWINAPIWaitForMultipleObjects(_In_DWORDnCount,_In_reads

13、_(nCount)CONSTHANDLE*lpHandles,_In_BOOLbWaitAll,_In_DWORDdwMilliseconds);参数说明:nCount,等待内核对象句柄数量,windows定义不超过MAXIMUM_WAIT_OBJECTS,即64个IpHandles,句柄数组首地址bWaitAll,TRUE需要所有对象有信号后返回,FALSE任何一个对象有信号即可返回dwMilliseconds,等待时长(ms)通常用法是在等待时长处传入INFINITE,该宏定义为永远等待,WaitForSingleObject函数返回值只有3种可能:WAIT_OBJECT_0句柄对象WAI

14、T_TIMEOUTWAIT_FAILED等待到了对应信号,在WaitForMultipleObjects情况下,WAIT_OBJECT_0为数组首元素下标,以此+1取后续等待超时,通常只有设置一个具体等待时长才会触发该返回值调用失败,通常为句柄无效2.1、事件内核对象(Event)事件内核对象是最基本的对象。它包含一个使用计数,一个用于指明该事件是个自动重置的事件还是一个手动重置的事件的布尔值,一个用于指明该事件处于已通知状态还是未通知状态的布尔值。WINBASEAPI_Ret_maybenull_HANDLEWINAPICreateEvent(_In_opt_LPSECURITY_ATTRI

15、BUTESlpEventAttributes,_In_BOOLbManualReset,_In_BOOLbInitialState,_In_opt_LPCSTRlpName);lpEventAttributes,内核对象安全性描述结构,同其他所有内核对象函数参数bManualReset,TRUE-手动重置;FALSE-自动重置-bInitialState,初始化状态:TRUE-已通知状态;FALSE-未通知状态lpName,内核对象在操作系统同一名字空间中的唯一名称通过CreateEvent函数创建事件内核对象,调用成功返回与进程相关的对象句柄,该进程中的其他线程可以取得对该对象的访问权,可以

16、使用继承性,由DuplicateHandle函数等来调用CreateEvent,或者调用OpenEvent,在lpName参数中设定一个与调用CreateEvent时设定的匹配名字:WINBASEAPI_Ret_maybenull_HANDLEWINAPIOpenEvent(_In_DWORDdwDesiredAccess,_In_BOOLbInheritHandle,_In_LPCSTRlpName);dwDesiredAccess,需要请求的事件对象的访问权限,DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER-blnheritHan

17、dle,TRUE被此进程创建的进程继承该对象的访问权限,否则不继承lpName,内核对象在操作系统同一名字空间中的唯一名称OpenEvent函数可以在全局范围内调用,其他进程只要知道pName样可以获取该对象句柄。其他关于事件对象使用基本上就只有置状态的两个函数SetEvent和ResetEvent,功能分别将事件置为已通知和未通知状态。可以重复置位。WINBASEAPIBOOLWINAPISetEvent(_In_HANDLEhEvent);WINBASEAPIBOOLWINAPIResetEvent(_In_HANDLEhEvent)2.2、等待定时器内核对象(WaitableTimer)

18、等待定时器是在某个时刻或按规定的时间间隔发出自己的信号通知的内核对象,通常用于在某个时刻执行某个操作。WINBASEAPI_Ret_maybenull_HANDLEWINAPICreateWaitableTimer(_In_opt_LPSECURITY_ATTRIBUTESlpTimerAttributes,_In_BOOLbManualReset,_In_opt_LPCSTRlpTimerName);IpTimerAttributes,同其他内核对象一致的安全性描述符,通常无特殊设定传NULLbManualReset,同事件内核对象,说明其是自动置位还是手动置位;TRUE-手动重置;FALS

19、E-自动重置IpTimerNam,内核对象在操作系统同一名字空间中的唯一名称通过CreateWaitableTimer函数来创建一个等待定时器,类似可以使用OpenWaitableTimer函数来取得一个已创建的等待定时器句柄。等待定时器对象总是在未通知状态中创建,创建完成后必须使用SetWaitableTimer函数来告诉定时器何时称为已通知状态。WINBASEAPIBOOLWINAPISetWaitableTimer(_In_HANDLEhTimer,_In_constLARGE_INTEGER*lpDueTime,_In_LONGlPeriod,_In_opt_PTIMERAPCROUT

20、INEpfnCompletionRoutine,_In_opt_LPVOIDlpArgToCompletionRoutine,_In_BOOLfResume);hTimer,等待定时器句柄lpDueTime,指明定时器何时开始第一次报时时刻。lPeriod,指明定时器第一次报时后应多长时间间隔报时,单位为mspfnCompletionRoutine,lpArgToCompletionRoutine,-fResume,TRUE-触发时若操作系统处于休眠状态会被唤醒;FALSE触发时若操作系统处于休眠状态则不会唤醒lpDueTime参数的些转换方式:SystemTimetoFileTime将SYS

21、TIME结构转换为FILETIME格式,LocalFileTimeToFileTime将FILETIME结构转换为UTC时间后,再赋值给LARGE_INTEGER结构传入。常见用法:1给定初始时刻,后续按周期报时。不给定初始时刻,让定时器以SetWaitableTimer时刻作为初始时刻,后续按周期报时。此时初始时刻需要传入一个负值,且以100ns为时间间隔。仅一次报时。lPeriod传0即可。同一个等待定时器可以重复调用SetWaitableTimer来改变其报时时间及间隔。最后,线程不应该等待定时器的句柄,也不应该以待命的方式等待定时器2.3、信标内核对象(Semaphore)信标内核对象

22、用于对资源进行计数。与其他内核对象一样,拥有一个使用计数,同时它还有另外两个有符号的B2位值,一个是最大资源数量,一个是当前资源数量。最大资源数量标识信标能够控制的资源最大数量,而当前资源数量则用于标识当前可使用资源数量。WINBASEAPI_Ret_maybenull_HANDLEWINAPICreateSemaphore(_In_opt_LPSECURITY_ATTRIBUTESlpSemaphoreAttributes,_In_LONGlInitialCount,_In_LONGlMaximumCount,_In_opt_LPCSTRlpName);lpSemaphoreAttribut

23、es,同其他内核对象一致的安全性描述符,通常无特殊设定传NULLllnitialCount当前可用资源数lMaximumCount,最大资源数lpName,内核对象在操作系统同一名字空间中的唯一名称WINBASEAPI_Ret_maybenull_HANDLEWINAPIOpenSemaphore(_In_DWORDdwDesiredAccess,_In_BOOLbInheritHandle,_In_LPCSTRlpName);dwDesiredAccess,访问权限bInheritHandle,是否继承IpName,内核对象在操作系统同一名字空间中的唯一名称CreateSemaphore函数

24、用于创建信标内核对象,同样还有对应的OpenSemaphore函数获取已存在的信标句柄。线程获取到信标内核对象句柄后,通过等待函数确认信标当前是否可用,等待函数在这里会检测信标的当前可用资源数是否大为,大于0则使可用资源数减1,调用线程保持其可调度状态。信标的特点在于它能够以原子操作的方式来执行测试和设置操作,这意味着当使用等待函数申请一个信标保护的资源时,操作系统会检测这个资源是否可用,同时负责对该资源可用计数递减,而不让其他线程在这个过程中产生干扰。当可用资源计数递减后,系统才会允许另个线程申请对资源的访问权。如果等待函数确认信标的可用资源数为0时,那么调用线程就会进入等待状态,当其他某个

25、线程对信标保护资源释放后,信标可用资源计数递增了,系统会重新使等待线程进入可调度状态(相应递减它当前资源数量)。WINBASEAPIBOOLWINAPIReIeaseSemaphore(_In_HANDLEhSemaphore,_In_LONGIReIeaseCount,_Out_opt_LPLONGIpPreviousCount);hSemaphore,信标句柄IReleaseCount,可用资源计数需要增加的数量,传入值+当前值创建信标时最大资源计数时,函数返回FALSEIpPreviousCount返回当前资源可用计数的原始值信标保护资源使用完毕后,需要调用ReleaseSemaphor

26、e函数来递增信标可用资源计数以便操作系统调度其他可能存在的等待访问资源的线程。目前没有方法可以获取信标当前可用计数。必须通过等待函数获取到信标访问权限后再ReleaseSemaphore函数取得可用计数的原始值加以计算。2.4、互斥对象内核对象(Mutex)互斥对象内核对象能够确保线程(可以是不同进程的线程)拥有对单个资源的互斥访问权限。互斥对象包含一个使用计数,一个线程ID,个递归计数器。ID用于标识系统当前哪个线程拥有互斥对象,递归计数器用于指明线程拥有互斥对象的次数。互斥对象的行为特性与关键代码段相同,但是互斥对象属于内核对象,而关键代码段则属于用户方式的对象。这意味着互斥对象在运行速度

27、上比关键段要慢,同时意味着不同进程重的多个线程能够访问单个互斥对象,并且线程在等待访问资源时可以设定一个超时值。通常意义下,互斥对象用于保护多个线程使用的内存块的访问权限,以保证数据的完整性。其使用规则如下:如果线程ID为0,互斥对象不被任何线程拥有,并且发出该互斥对象的通知信号如果线程ID非0,那么意味着该ID的线程用有互斥对象,并且不发出该互斥对象的通知信号-与其他内核对象不同,互斥对象在操作系统中拥有特殊的代码,语序它们违反正常的规则WINBASEAPI_Ret_maybenull_HANDLEWINAPICreateMutex(_In_opt_LPSECURITY_ATTRIBUTES

28、lpMutexAttributes,_In_BOOLbInitialOwner,_In_opt_LPCSTRlpName);lpMutexAttributes同其他内核对象一致的安全性描述符,通常无特殊设定传NULL-bInitialOwner,初始化时是否被拥有。TRUE则其线程ID为调用线程ID,递归计数器置为1,;FALSE则其线程ID为0,递归计数器置为0lpName,内核对象在操作系统同一名字空间中的唯一名称WINBASEAPI_Ret_maybenull_HANDLEWINAPIOpenMutex(_In_DWORDdwDesiredAccess,_In_BOOLbInheritH

29、andle,_In_LPCWSTRlpName);dwDesiredAccess,要请求的访问权限blnheritHandle,是否继承IpName,内核对象在操作系统同一名字空间中的唯一名称必须通过CreateMutex函数创建一个互斥对象,或者OpenMutex函数请求一个已创建的互斥对象。其线程D和递归计数等都是原子操作方式。异常规则:互斥对象有一个比较特殊的情况,互斥对象当前状态为未通知,线程D为123,而123的线程当前执行到了等待函数等待获取互斥对象的访问权限时,操作系统会允许该线程进入可调度状态且互斥对象进入到正常流程,状态为已通知,递归计数1。若要使递归计数大于1,唯一的方法是让线程多次等待相同的互斥对象,以便利用这个异常规则。旦线程使用完互斥资源时,必须调用函数ReleaseMutex释放该互斥对象以便操作系统调度其他等待访问互斥资源的线程。WlNBASEAPlBOOLWlNAPlReleaseMutex(_ln_HANDLEhMutex);该函数将互斥对象递归计数器减1,如果线程多次成功等待一个互斥对象,在互斥对象的递归计数器变成)之前,该线程必须调用同样次数的ReleaseMutex函数直到递归计数为0此时该线程ID被置为0且互斥对象变为已通知状态。异常规则也适用于释放互斥对象的线程,当调用ReleaseMutex函数的线程ID与互斥对象的线

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论