转载请注明出处
作者:小马
前些天发现自己写的一个基于wosa/xfs的读卡器驱动有一个小的bug,读某种IC卡失败时,读卡器串口没有成功释放掉, 导致其它应用程序无法访问该读卡器. bug的原因和XFS中的WFSLock,WFSUnlock有关.
XFS要求是设备(比如,读卡器, 打印机等)当前只能被一个应用程序访问, 其它应用必须等设备被释放掉控制权后才能访问. 所以基于XFS的设备驱动具备上锁和解锁的接口.XFS里的lock和unlock的原理是通过互斥信号量机制来实现进程或线程间的互斥,达到对硬件访问的独立控制权. 信号量这里就不多说了,操作系统的基本知识.
下面通过举例说明.
现在我假设当前有两个app,分别是app1, app2. 不考虑多线程, 这两个app都是单线程.
当前有个device没有被任何应用控制, 现在app1要访问这个device, 由于它是第一个访问该device的App, 这个时候还没有信号量, 首先这个app1创建一个互斥信号量, 代码类似下面这样:
HANDLE g_hMutex = NULL; g_hMutex= CreateMutex( NULL,FALSE,"name_test");
CreateMutex函数创建一个互斥信号量, 第一个参数直接置为NULL就可以了,第三个参数是给这个互斥信号量起一个名字. 重点要说一下第二个参数, 这个参数决定当前调用线程是否拥有该信号量, 如果为参数值是FALSE,当前创建的这个信号量不为任何线程拥有. 如果是TRUE, 这个信号量被当前的调用线程拥有.在XFS中, 这个参数一般用FALSE,因为CreateMutex一般是在XFS open时创建, 信号量的拥有和释放交给lock和unlock来处理, 这样的结构才是清晰的.当互斥信号量没有被任何线程拥有时, 它的状态为signaled。
当app1调用lock接口时, 它申请对信号量的控制, lock的实现原理是, 它调用WaitForSingleObject申请对信号量的控制权, 调用方法类似如下:
<span style="font-size:13px;">DWORD dwWaitObject = WaitForSingleObject(g_hMutex, dwWaitTime);</span>
如果g_hMutex为signaled, 函数返回,当前应用就获得了该信号量. 同时g_hMutex变量unsignaled(注意这里). 上面说到这个g_hMutex是signaled的, 所以, app1成功获取g_hMutex. 接着就可以访问device了,类似下面这样:
<span style="font-size:13px;">switch( dwWaitObject ){ case WAIT_OBJECT_0: //访问device break; case WAIT_TIMEOUT: //超时 break; }</span>
当需要释放device时, app1调有unlock接口, unlock实现的原理比较简单, 就是调用ReleaseMutex释放当前对信号量的拥有.成功调用ReleaseMutex后,g_hMutex又变回signaled的状态.
如果在app1调用unlock之前, app2要访问硬件, 它首先要调用lock, 这时因为g_hMutex是unsignaled状态, app2获取不到访问权限, 只能阻塞. 为了防止死锁现象的发生, WaitForSingleObject的第二个参数一般都传一个超时时间, 这样app2仅会在超时时内发生阻塞.
XFS还提供了一种比较灵活的事件通知机制. XFS中有一个名为WFS_SYSE_LOCK_REUESTED的系统通知事件. 这个事件会在app2要求取得控制权时通知app1. 这样就比较灵活, app1可选择是在得到通知后马上释放或者等到自己不用的时候再释放g_hMutex.
当然, lock和unlock不是一定要出现在你的驱动接口中, 如果你的系统只有一个应用访问设备, Lock和Unlock这对兄弟可以不必出现.
需要WOSA,XFS,SPI相关代码的可以私信发给我qq号。