用户模式及驱动模式同步

   中断优先级,线程优先级的概念及关系:

   在一个多任务的系统中,可以同时运行多个线程。事实上并不是真正的同时,而是每个线程运行一段时间,然后切换另一个线程,运行一段时间后,又切换到别的线程。只不过这个切换的速度特别快,人是感觉不到的,感觉到的就你是同时在运行一样。

    在系统中有一个“线程调度内核组件”。它来负责管理进行线程的切换工作。是否进行线程的切换及切换到哪个线程,要根据“中断请求优先级” 和“线程优先级”来综合考虑。 可以把“中断请求优先级”比喻成病人的病重指数,把“线程优先级”比喻成病人想得到医治的心情。假设只有一个医生(CPU),把“线程调度内核组件”当作一个安排看病次序的人,它有权力在医生进行治疗一个病人的时候让医生停止转而去治疗其他的病人(这个相当于中断)。

    那么这个调度原则一定应该是: 在“中断请求优先级”一样的情况下,线程不间断的进行切换,都可以得到一段cpu时间,只是优先级高的得到的机会多。但是当一个线程运行在一个DISPATCH_LEVEL级别时,不再发生线程的切换,直到该函数执行完或中断请求级降低。(这个规则适用于单cpu的PC,如果是多cpu,就不能使用这个办法了,因为另外的CPU可以去执行其他的线程。)

    利用这种性质,我们可以得到一种同步的方法: 当想让某段代码不被其他线程打断的话,就把它的“中断请求优先级”提升到DISPATCH_LEVEL.这样的话,其他的代码就不能得到机会执行了。

 

以上的方法是对于单cpu的PC机来使用的方法,那么对于多CPU,上面的方法就不管用了,需要使用自旋锁.

自旋锁: 自旋锁是驱动程序所使用的一种同步方法。

自旋锁主要用于驱动程序中各个派遣函数之间的同步。

自旋锁的数据是KSPIN_Lock, 我们把它放在“设备扩展”里。如下代码:

    typedef struct _DEVICE_EXTENSION

{......

    kSPIN_LOCK My_SpinLock;

 .......

}

  在使用之前,我们需要使用KeInitializeSpinLock()对它进行初始化,这个过程一般在DriverEntry()或者AddDevice()中进行。

 

使用自旋锁可以利用内核函数KeAcquireSpinLock(),而释放它可以利用内核函数DeAcquireSpinLock().

当你认为某段代码非常重要,在它被执行完之前,不允许别的线程来执行这段代码(不管是来自于哪个CPU),那么你可以使用上面的两个函数将这段代码包含住。

 

 

  下面总结一下用户模式下的同步方法:

     1 事件.在多线程的情况下,如果一个线程必须等另外一个线程中的某些代码执行完后自己才能执行(比如处理数据的线程一定要等接收数据的线程接到数据后才应该执行)。那么就使用等待方法。比如有一个主线程(primary),两个分线程(deal和receive).在primary中调用CreateEvent()创建一个事件,且为未激发状态。在deal中调用WaitForSingleObject(),该函数如果发现事件是未激发的话,线程就休眠,一直等到事件被激发,才会醒来继续执行下面的代码。在另外的receive线程中,当成功接收到数据后,调用SetEvent()中,将事件设为激发状态。

     2 信号灯。信号灯中有几个灯泡,如果全部都是熄灭状态的话,则整个信号灯是处于熄灭状态,如果其中有一个灯泡是亮着的话,信号灯就处于点亮状态。如果上例,主线程调用createSemaphore(NULL,2,2,)创建一个信号灯,第二个参数代表里面有两个灯是亮着的,那么该信号灯是处于激发状态的.调用一次waitForSingleObject(),它检查该灯内是否有灯泡可以被熄灭,如果发现没灯可吹了,则该线程进入等待模式.如果发现信号灯里面有灯泡是亮着,就熄灭一个灯泡,程序继续向下走. 程序可以利用ReleaseSemaphore()来点亮信号灯里的灯泡.

 

 

          总结事件和信号灯的区别就是, 在线程多于两个时, 事件中的waitForSingleObject()所在的线程在事件被激活后苏醒过来后向下执行(不改变事件的状态),另外的线程看到事件的状态仍然是激发的.而信号灯的waitForSingleObject()所在的线程在信号灯被激发后苏醒过来后继续向下执行后熄灭一个灯泡,如果该信号灯中只有一个灯泡是亮的,却被它吹灭了,那么后来的其他线程如果在执行waitForSingleObject()时看到的信号灯是灭的,那个线程会继续等待.

   Using Semaphore Objects

Using Semaphore Objects

In the following example, a process uses a semaphore object to limit the number of windows it creates. First, it uses the CreateSemaphore function to create the semaphore and to specify initial and maximum counts.

HANDLE hSemaphore;

LONG cMax = 10;

LONG cPreviousCount;



// Create a semaphore with initial and max. counts of 10.



hSemaphore = CreateSemaphore(

NULL, // no security attributes

cMax, // initial count

cMax, // maximum count

NULL); // unnamed semaphore



if (hSemaphore == NULL)

{

// Check for error.

}

Before any thread of the process creates a new window, it uses the WaitForSingleObject function to determine whether the semaphore's current count permits the creation of additional windows. The wait function's time-out parameter is set to zero, so the function returns immediately if the semaphore is nonsignaled.

DWORD dwWaitResult; 



// Try to enter the semaphore gate.



dwWaitResult = WaitForSingleObject(

hSemaphore, // handle to semaphore

0L); // zero-second time-out interval



switch (dwWaitResult) {



// The semaphore object was signaled.

case WAIT_OBJECT_0:

// OK to open another window.

break;



// Semaphore was nonsignaled, so a time-out occurred.

case WAIT_TIMEOUT:

// Cannot open another window.

break;

}

When a thread closes a window, it uses the ReleaseSemaphore function to increment the semaphore's count.

// Increment the count of the semaphore.



if (!ReleaseSemaphore(

hSemaphore, // handle to semaphore

1, // increase count by one

NULL) ) // not interested in previous count

{

// Deal with the error.

}

 

 

     在单cpu上如果使用自旋锁,是不是因为升级到dispatch_level,所以任何其他的线程都不能运行?

         是的,此时已经不能进行线程切换了. 所以单cpu的机器上不用使用自旋锁,只要提升中断处理级到dispatch_level即可.

        3   关键代码段(临界区):

              这个名字起的容易误导人, 原来我以为关键代码段就是这段代码特别关键,所以在执行的时候不允许切换到别的线程的意思,其实根本不是那么回事儿. 在程序中声明一个全局变量CRITICAL_SECTION g_cs; 再把你不想同时运行的几段代码分别包含在EnterCriticalSection(&g_cs);和LeaveCriticalSection(&g_cs); 语句中.那么他们绝对不会同时运行的. 一般是用来保护一些资源,比如有两个线程中的函数都会访问同一个全局变量,那么你把这两个函数分别这样包含进去,他们就绝对不会同时执行,总是一先一后的执行,实现了同步.

             当一个线程想执行关键代码段时,它发现已经有其他在线程占用了,那么它将进入等待状态,但是进入这个等待状态过程很耗CPU,甚至有时不等他进入等待状态呢,那边的线程已经释放了关键代码段.这样就造成了浪费.所以windows提供了一个接口函数给我们itializeCriticalSectionAndSpinCount, 即当有其他线程占用时,不立刻进入等待状态,而是不停的询问一段时间.

        4   互斥体:主线程创建一个互斥体(CreateMutex).在各个分线程中分别调用waitforsingleobject(mutex),先得到互斥体的得到运行,后边的那个就必须在一直等,直到前一个调用ReleaseMutex释放了这个互斥体它才有机会能执行.

        5 线程对象.  一个线程可以调用waitForSingleObject(thread)去指定一个线程运行完, 也可以用来等待进程。

 

事件和互斥体都是系统对象, 所以都可以用于进程间的通信.

 

内核模式下的同步方法:

       实际上用户模式的同步对象都是借助于内核模式的同步对象实现的, 只是对他们进行了一次封闭. 比如说用户模式中的CreatEvent函数, 它其实是创建了一个内核事件对象,它返回的值就是该对象的句柄. 因为它无法获取该对象的指针,所以只能用一个句柄表示.句柄就是一个数值.

       1 等待   KeWaitForSingleObject()   KeWaitForMultipleObjects(). 用户模式的WaitForSigngleObject()和WaitForMultipleObjects()其实最终也要调用这两个内核函数的. 只是这两个内核函数的参数比用户模式中的函数参数多了几个.用法都是差不多的.

       2 事件对象  内核模式中的事件对象用结构体KEVENT表示. 使用KeInitializeEvent()函数对事件对象进行初始化. 

windows驱动开发技术详解 8.5没有看.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值