As mentioned in the back, it is very inappropriate to use spin lock without moving. We should try our best to use other synchronization mechanisms. The NT kernel provides a family of locks collectively referred to as dispatcher locks. They have their own characteristics and adapt to different application scenarios. Understanding their characteristics can help you find the most suitable synchronization mechanism, avoid spin lock abuse. The data structure that represents dispatcher loco has a common header called dispatcher_header. Any lock with this structure can enter the critical section through kewaitforsingleobject (or kewaitformultipleobjects. During the time when the NT kernel was born, the object-oriented concept was widely used. Therefore, the NT design also incorporates a lot of object-oriented ideas. For example, the dispatcher_header data structure can be viewed as kevent, kmutex and so on. kewaitforsingleobject can be regarded as the public method in the parent class, and can be inherited by the quilt class. Dispatcher_header is defined as follows:
1 Typedef Struct _ Dispatcher_header { 2 Union { 3 Struct { 4 Uchar type; 5 Union { 6 Uchar absolute; 7 Uchar npxirql; 8 9 }; 10 Union { 11 Uchar size; 12 Uchar hand; 13 }; 14 Union { 15 Uchar inserted; 16 Boolean debugactive; 17 }; 18 }; 19 Volatile Long lock; 20 }; 21 Long signalstate; 22 List_entry waitlisthead; 23 } Dispatcher_header;
We mainly care about the signalstate and waitlisthead fields. Signalstate indicates whether the lock is in use. waitlisthead is a list that records all threads waiting for the lock, after the lock is released, select one or more threads from the list to wake up. Its relationship with threads is roughly as follows:
The following are some typical dispatcher locks:
The simplest of all dispatcher loch is event, which only contains the dispatcher_header data structure and has no additional content. A thread can use kewaitforsingleobject to obtain the lock (put the signalstate in the un-signal state) and release it with the kesetevent (put the signalstate in the signal State ). There is a small problem here: Because dispatcher_header does not record which thread gets the lock currently, once a deadlock occurs when the event is used, it is impossible for you to use tools such as windbg to analyze who has occupied the lock. In contrast, mutex can be used. Because there is no owner thread information, we can't know how many times a thread gets the lock. If the same thread gets the lock twice and only releases the lock once, the lock will be in the signal state, you writeCodeYou must be careful with this. I don't know whether this is a design mistake or something. There are already so many members in dispatcher_header. Will there be much difference if I have one more owner thread? I don't know what Ms thinks.
Mutex is similar to é vent, but it adds some additional information to record the information of the thread currently obtaining the lock. If the same thread obtains mutex twice, one count is set to 2. Unless you release mutex twice, other threads cannot obtain the mutex. In addition, as a side effect, when the thread obtains mutex, the kernel APC will be disabled, so when yourProgramWhen there are APC mechanisms such as timer and mutex, pay more attention to them. In addition, the thread used to release the lock must be the same as the thread used to obtain the lock. Otherwise, bsod will occur.
The emergence of semaphore aims to solve this problem: some resources are not only one, it allows and only allows (different) threads to lock a specific number of times, when the resources are not exhausted, kewaitforsingleobject is returned immediately. When it reaches the upper limit, it enters the waiting state. To achieve this, semaphore requires two additional domains to manage information: the limitation domain represents the upper limit of resources, and the count domain represents the amount of resources currently used. When we call kewaitforsingleobject, the Count value is reduced by 1. If count>-limitation is called, the Count value enters the un-signal state. When kereleasesemaphore is called, the Count value increases by 1. If count> = 0, the Count value enters the signal.
In addition, some dispatcher locks are not listed. You can check them on msdn. These locks are similar. The biggest difference is the rule that determines when the un-Signal Status enters the Signal status. Here, a table lists most of the dispatcher lock rules:
Lock type |
Signal Status rules |
Affected thread |
Mutex |
After release mutex |
Wake up a thread |
Semaphore |
Count = 0 |
Wake up all threads |
Synchronization type event |
After set event |
Wake up a thread |
Notification Type event |
After set event |
Wake up all threads |
Notification Timer |
Time |
Wake up all threads |
Process |
After the process is destroyed |
Wake up all threads |
Thread |
After the thread is destroyed |
Wake up all threads |
File |
Io complete |
Wake up all threads |
The above is the general description of the dispatcher lock. Next time we will continue to talk about the two Special locks: Resource and executive lock.