25/05/2018, 08:06

Một số phương pháp đồng bộ khác (Other Kernel-Mode Synchronization Primitives )

Nhân XP Windows đưa ra một số phương pháp bổ sung để đồng bộ hóa sự thực hiện giữa những luồng hay để bảo vệ được truy nhập để chia sẻ những đối tượng. Trong mục này, tôi sẽ bàn luận mutex nhanh, là một đối tượng mà những đề xuất loại trừ lẫn nhau nhanh hơn ...

Nhân XP Windows đưa ra một số phương pháp bổ sung để đồng bộ hóa sự thực hiện giữa những luồng hay để bảo vệ được truy nhập để chia sẻ những đối tượng. Trong mục này, tôi sẽ bàn luận mutex nhanh, là một đối tượng mà những đề xuất loại trừ lẫn nhau nhanh hơn sự thực hiện so với một nhân mutex vì nó tối ưu hóa cho trường hợp trong đó không có thật sự tranh dành đang xuất hiện. Tôi cũng sẽ mô tả phạm trù của những chức năng hỗ trợ mà bao gồm từ Interlocked trong tên của chúng . Những chức năng này thực hiện những thao tác chung nhất định như tăng hoặc giảm một số nguyên hay việc chèn hoặc loại bỏ một mục từ một bản kê liên kết trong một cách nguyên tử điều đó ngăn ngừa giao thoa đa nhiệm hay đa xử lý.

Một mutex nhanh thực hiện cung cấp một giải pháp tới một nhân mutex để bảo vệ một tiết diện tới hạn của mã. Bảng 4-6 tổng kết những chức năng công tác bạn thường làm việc với loại đối tượng này.

Bảng 4-6. Những chức năng công tác cho sự sử dụng với thực hiện Mutexes nhanh
Chức năng dịch vụ Mô tả
ExAcquireFastMutex Thu nhận quyền sở hữu của mutex, đợi nếu cần thiết
ExAcquireFastMutexUnsafe Thu nhận quyền sở hữu của mutex, đợi nếu cần thiết, trong hoàn cảnh trong đó người gọi đã vô hiệu hóa xác nhận của APCs
ExInitializeFastMutex Khởi tạo đối tượng mutex
ExReleaseFastMutex Thoát khỏi mutex
ExReleaseFastMutexUnsafe Thoát khỏi mutex không có sự truyền ra reenabling APC
ExTryToAcquireFastMutex Thu nhận mutex nếu khả dĩ để làm vì vậy không có việc đợi

So sánh với nhân mutexes, mutexes nhanh có những sức mạnh và những sự yếu kém được tổng kết trong Bảng 4-7. Trên cạnh dấu cộng, một mutex nhanh là nhanh nhiều hơn để thu nhận và giải phóng nếu ở đó thực tế không có sư tranh chấp cho nó. Trên cạnh trừ, một luồng mà thu nhận một mutex nhanh sẽ không có khả năng để nhận được những kiểu nhất định của sự gọi thủ tục không đồng bộ, phụ thuộc vào những chức năng chính xác mà bạn gọi, và sự ràng buộc làm sao bạn gửi IRPs cho những trình điều khiển khác.

Bảng 4-7. So sánh Kernel và những đối tượng Mutex nhanh
Kernel Mutex Fast Mutex
Có thể được thu nhận một cách đệ quy bởi một luồng đơn (hệ thống bảo trì một máy đếm tuyên bố) Không thể được thu nhận một cách đệ quy
Một cách tương đối chậm Một cách tương đối nhanh hơn
Chủ nhân sẽ chỉ nhận được nhân APCs " đặc biệt " Chủ nhân không nhận được chiến thắng bất kỳ APCs nào trừ phi bạn sử dụng những chức năng XxxUnsafe
Có thể bộ phận của một nhiều đối tượng đợi Không thể được sử dụng như một đối số tới KeWaitForMultipleObjects

Bất ngờ, tài liệu DDK về những đối tượng mutex nhân đã lâu nói rằng nhân đưa cho một quyền ưu tiên sự tăng tới một luồng mà đòi hỏi một mutex. Tôi có những thông tin đáng tin cậy rằng điều đó thật sự không đúng từ năm 1992 (năm, nghĩa là, không phải Windows xây dựng số). Tài liệu cũng đã lâu nói rằng một luồng giữ một mutex không thể được loại bỏ từ sự cân bằng được thiết lập(điều đó, được chinh phục tới việc cho di chuyển tất cả những trang của nó ra khỏi bộ nhớ vật lý) đây đúng khi Windows NT còn mới mẻ nhưng không đúng trong một thời gian dài.

Để tạo ra một mutex nhanh, đầu tiên bạn phải cấp phát một cấu trúc dữ liệu FAST_MUTEX trong số trang kí ức. Rồi bạn khởi tạo đối tượng bằng việc " gọi là " ExInitializeFastMutex, mà thật sự là một macro trong WDM.H:

ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);

ExInitializeFastMutex(FastMutex);

Ở đâu FastMutex là địa chỉ của đối tượng FAST_MUTEX của các bạn. Mutex bắt đầu cuộc sống trong trạng thái không có chủ. Để thu nhận quyền sở hữu sau này, gọi là một trong số những chức năng này :

ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);

ExAcquireFastMutex(FastMutex);

or

ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);

ExAcquireFastMutexUnsafe(FastMutex);

Những chức năng đầu tiên này đợi mutex để trở nên sẵn có, gán quyền sở hữu tới luồng gọi, và sau đó nâng bộ xử lý IRQL hiện thời tới APC_LEVEL. Việc nâng IRQL có hiệu ứng của sự kết khối của tất cả APCs. Những chức năng thứ hai này không thay đổi IRQL.

Sự bế tắc khả dĩ khác có thể xuất hiện với một trình điều khiển trong đường dẫn trang-trong từ khác, một trình điều khiển mà được gọi giúp đỡ quản lý kí ức xử lý một lỗi trang. Giả thiết bạn đơn giản gọi KeEnterCriticalRegion và sau đó ExAcquireFastMutexUnsafe. Bây giờ giả thiết hệ thống thử để thực hiện một kernel-mode APC đặc biệt trong cùng luồng, mà khả dĩ vì KeEnterCriticalRegion không chặn trước nhân APCs đặc biệt. APC thường lệ có lẽ đã đánh số trang lỗi, mà có lẽ đã dẫn dắt tới bạn là reentered và bế tắc vào một giây thử đòi hỏi cùng mutex. Bạn tránh hoàn cảnh này bằng việc nâng IRQL tới APC_LEVEL trước khi thu nhận mutex trong chỗ đầu tiên hay, đơn giản hơn, bằng cách sử dụng KeAcquireFastMutex thay vì KeAcquireFastMutexUnsafe. Cùng vấn đề có thể nếu bạn sử dụng một KMUTEX bình thường hay một sự kiện đồng bộ hóa, tất nhiên.

Nếu bạn không muốn đợi nếu mutex không ngay lập tức sẵn có, sự sử dụng " sự thử để thu nhận " vận hành:

ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);

BOOLEAN acquired = ExTryToAcquireFastMutex(FastMutex);

Nếu giá trị trở lại là TRUE, bạn bây giờ sở hữu mutex. Nếu nó là FALSE, một người khác sở hữu mutex và đã cản trở bạn thu nhận nó. Để giải phóng điều khiển của một mutex nhanh và cho phép luồng khác nào đó đòi hỏi nó, gọi cho phiên bản chức năng tương ứng đối với cách mà bạn thu nhận nhanh mutex:

ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);

ExReleaseFastMutex(FastMutex);

or

ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);

ExReleaseFastMutexUnsafe(FastMutex);

Một mutex nhanh nhanh bởi vì sự thu nhận và những bước phiên bản được tối ưu hóa cho trường hợp thông thường trong đó không có tranh dành nào cho mutex. Bước phê bình trong việc thu nhận mutex tới một cách nguyên tử sự giảm bớt và kiểm tra những máy đếm số nguyên mà chỉ báo bao nhiêu luồng hay sở hữu hay đang đợi mutex. Nếu sự thử chỉ báo không có luồng khác nào đó Sở hữu mutex, không có công việc bổ sung được yêu cầu. Nếu sự thử chỉ báo luồng khác mà sở hữu mutex, Những khối luồng hiện thời trên một sự kiện đồng bộ hóa đó là đối tượng bộ phận của FAST_MUTEX. Giải phóng những thế tập mutex một cách nguyên tử là tăng và kiểm tra máy đếm. Nếu sự thử chỉ báo rằng không có luồng hiện thời đang đợi, không có công việc bổ sung được yêu cầu. Nếu luồng khác đang đợi, tuy nhiên, chủ nhân gọi là KeSetEvent để giải phóng một trong những chờ đợi.

Bạn có thể gọi cho vài chức năng công tác trong một trình điều khiển WDM để thực hiện số học trong một cách đó là luồng- an toàn và bộ đa xử lý- an toàn. (Xem ở Bảng 4-8.) Những thủ tục này tới hai hương vị. Kiểu đầu tiên của thủ tục có một tên bắt đầu với Interlocked và thực hiện một thao tác nguyên tử trong một cách như vậy mà không có luồng khác nào hay CPU có thể can thiệp. Hương vị khác cho phép một tên bắt đầu với ExInterlocked và sử dụng một sự khóa quay tròn.

Bảng 4-8. Những chức năng dịch vụ cho khóa lồng số học
Chức năng dịch vụ Mô tả
InterlockedCompareExchange So sánh và có điều kiện trao đổi
InterlockedDecrement Trừ 1 từ một số nguyên
InterlockedExchange Trao đổi hai giá trị
InterlockedExchangeAdd Thêm hai giá trị và tổng những trở lại
InterlockedIncrement Thêm 1 vào một số nguyên
InterlockedOr ORs những bit vào trong một số nguyên
InterlockedAnd ANDs những bit vào trong một số nguyên
InterlockedXor Exclusive-ORs những bit vào trong một số nguyên
ExInterlockedAddLargeInteger Thêm giá trị tới 64- bit số nguyên
ExInterlockedAddLargeStatistic Thêm giá trị vào ULONG
ExInterlockedAddUlong Thêm giá trị vào ULONG và giá trị ban đầu những trở lại
ExInterlockedCompareExchange64 Trao đổi hai giá trị 64- bit

Những chức năng InterlockedXxx có thể được ghé thăm bất kỳ IRQL nào; chúng cũng có thể xử lý dữ liệu trang ở PASSIVE_LEVEL vì chúng không yêu cầu một sự khóa quay tròn. Mặc dù những thủ tục ExInterlockedXxx có thể được ghé thăm bất kỳ IRQL nào, họ vận hành trên dữ liệu đích tại hoặc ở trên DISPATCH_LEVEL và bởi vậy đòi hỏi đánh một số trang lý lẽ. Lý do duy nhất để sử dụng một chức năng ExInterlockedXxx là nếu bạn có một dữ liệu biến thiên mà bạn đôi khi cần phải tăng dần hay giảm bớt và đôi khi cần để truy nhập khắp cả loạt nào đó của những chỉ dẫn. Bạn rõ ràng đòi hỏi sự khóa quay tròn xung quanh những sự truy nhập nhiều chỉ dẫn và sử dụng ExInterlockedXxxfunction để thực hiện những sự tăng dần đơn giản hay những sự giảm bớt.

InterlockedIncrementadds 1 tới một số nguyên dài trong kí ức và những sự trở lại giá trị sau tăng dần tới bạn:

LONG result = InterlockedIncrement(pLong);

Ở chỗ Long là địa chỉ của một biến được đánh máy như một LONG (điều đó là, một số nguyên dài). Nhận thức, thao tác của chức năng tương đương đối với sự trở lại phát biểu Long trong C, trừ phi sự thi hành khác với sự phát biểu đơn giản đó để cung cấp sự an toàn luồng và sự an toàn bộ đa xử lý. InterlockedIncrement đảm bảo rằng số nguyên là sự kiện gia tăng thành công nếu mã trên CPUs khác hay trong những luồng xứng đáng khác trên cùng CPU đồng thời đang thử thay đổi cùng biến. Trong bản chất của thao tác, InterlockedIncrement không đảm bảo rằng giá trị nó trở lại là giá trị của biến san bằng một chu kỳ máy sau đó bởi những luồng khác hay CPUs sẽ có khả năng để sửa đổi biến nhanh chóng như thao tác tăng dần nguyên tử hoàn thành.

InterlockedDecrement tương tự như InterlockedIncrement, nhưng nó trừ 1 từ biến đích và trả lại giá trị sau giảm bớt, giống như câu lệnh C trở lại Long với sự an toàn luồng và sự an toàn bộ đa xử lý.

LONG result = InterlockedDecrement(pLong);

You call InterlockedCompareExchange like this:

LONG target;

LONG result = InterlockedCompareExchange(&target,

  newval, oldval);

Ở đây đích là một số nguyên dài được dùng như đầu vào và đầu ra tới chức năng, oldval là phỏng đoán của các bạn về nội dung hiện thời của đích, và newval là giá trị mới mà bạn muốn thiết đặt trong đích nếu phỏng đoán của các bạn đúng. những chức năng thực hiện một thao tác tương tự như vậy được chỉ báo trong mã C sau đây nhưng phải làm qua một thao tác nguyên tử đó là cả luồng an toàn lẫn bộ đa xử lý an toàn:

LONG CompareExchange(PLONG ptarget, LONG newval, LONG oldval)

  {

  LONG value = *ptarget;

  if (value == oldval)

    *ptarget = newval;

  return value;

  }

Nói cách khác, chức năng luôn luôn trả lại giá trị trước đây của biến đích cho bạn. Ngoài ra, nếu giá trị trước đó cân bằng với oldval, nó đặt đích bằng newval mà bạn chỉ rõ. Chức năng sử dụng một thao tác nguyên tử để làm sự so sánh và sự trao đổi sao cho sự thay thế xảy ra chỉ khi bạn đúng trong phỏng đoán của các bạn về nội dung trước đây. Bạn cũng có thể gọi là chức năng InterlockedCompareExchangePointer để thực hiện một thao tác loại so sánh- và- trao đổi tương tự với một con trỏ. Chức năng này cũng được định nghĩa như một người biên tập- bản tính (Nghĩa là, một chức năng cho người biên tập nào cung cấp một sự thi hành inline) hay một chức năng thực sự gọi, tiếp tục phụ thuộc những con trỏ rộng trên nền tảng nào cho bạn biên dịch và trên khả năng của người biên tập để phát sinh mã inline. Chức năng cuối cùng trong lớp này là InterlockedExchange , mà đơn giản sử dụng một thao tác nguyên tử để thay thế giá trị của một biến số nguyên và để trả lại giá trị trước đây :

LONG value;

LONG oldval = InterlockedExchange(&value, newval);

Như bạn có lẽ đã đoán được, có một InterlockedExchangePointer mà trao đổi một giá trị con trỏ nữa (64- bit hay 32-bit, phụ thuộc trên nền tảng). Chắc chắn để ném đích của thao tác trao đổi để ránh một lỗi khi biên tập 64- bit những trình điều khiển:

PIRP Irp = (PIRP) InterlockedExchangePointer(

  (PVOID*) &foo, NULL);

InterlockedOr, InterlockedAnd và InterlockedXor mới với XP DDK. Bạn có thể sử dụng chúng trong những trình điều khiển mà sẽ tiếp tục chạy sớm hơn những phiên bản Windows vì chúng thật sự được thực hiện như những hàm thuộc về chương trình biên tập.

Interlocked Fetches và Stores?

Giả thiết bạn viết sau đây về sự phân loại mã để bảo vệ việc truy cập tới một vài dữ liệu dùng chung:

if (InterlockedExchange(&lock, 42) == 0)

  {

  sharedthing++;

  lock = 0;   // == don't do this

  }

Ý định này sẽ thực hiện tốt trên máy tính Intel x86, nơi mọi CPU nhìn thấy kí ức viết trong cùng mệnh lệnh. Trên kiểu CPU khác, tuy nhiên, ở đó đã có thể là một vấn đề. Một CPU có lẽ đã thật sự thay đổi sự khóa biến thiên kí ức thành 0 trước khi việc cập nhật kí ức cho sự phát biểu tăng dần. Hành vi đó đã có thể cho phép hai CPUs đồng thời truy nhập sharedthing. Vấn đề này đã có thể xảy ra vì cách mà CPU thực hiện những thao tác trong đường song song hay vì những thói quen trong bộ điều khiển kí ức. Vậy thì, bạn cần phải Rework mã để sử dụng một thao tác khóa lồng cho cả hai sự thay đổi để hãm lại:

if (InterlockedExchange(&lock, 42) == 0)

  {

  sharedthing++;

  InterlockedExchange(&lock, 0);

  }

Toàn bộ những chức năng ExInterlockedXxx yêu cầu rằng bạn tạo ra và khởi tạo một sự khóa quay tròn trước khi bạn gọi là nó. Ghi nhớ rằng những toán hạng đó của những chức năng này phải có mọi thứ trong bộ nhớ nonpaged bởi vì những chức năng vận hành trên dữ liệu tại IRQL cao.

ExInterlockedAddLargeInteger thêm vào những số nguyên hai 64- bít và trả lại giá trị trước đây của đích:

LARGE_INTEGER value, increment;

KSPIN_LOCK spinlock;

LARGE_INTEGER prev = ExInterlockedAddLargeInteger(&value,

  increment, &spinlock);

Giá trị là đích của phép cộng và một trong những toán hạng. Sự tăng dần là một toán hạng số nguyên được thêm vào đích. Spinlock là một sự khóa quay tròn mà bạn khởi tạo trước đó. Giá trị trở lại là đích của giá trị trước khi thêm. Nói cách khác, thao tác của chức năng này tương tự như chức năng sau đây chỉ có điều nó xuất hiện dưới sự bảo vệ của khóa quay tròn:

__int64 AddLargeInteger(__int64* pvalue, __int64 increment)

  {

  __int64 prev = *pvalue;

  *pvalue += increment;

  return prev;

  }

Chú ý rằng giá trị trở lại là giá trị preaddition, những sự tương phản nào với sự trở lại sau tăng dần từ InterlockedExchange và những chức năng tương tự. (Đồng thời, không phải tất cả chương trình biên dịch hỗ trợ dữ liệu số nguyên __int64, và không phải tất cả máy tính có thể thực hiện một thao tác cộng 64- bit sử dụng những chỉ dẫn nguyên tử.)

ExInterlockedAddUlong tương tự đối với sự loại trừ ExInterlockedAddLargeInteger mà nó làm việc với 32-bit những số nguyên không dấu:

ULONG value, increment;

KSPIN_LOCK spinlock;

ULONG prev = ExInterlockedAddUlong(&value, increment, &spinlock);

Chức năng này cũng trả lại giá trị preaddition của đích của thao tác.

ExInterlockedAddLargeStatistic tương tự như ExInterlockedAddUlongin mà nó thêm một giá trị 32- bit tới một giá trị 64- bit:

VOID ExInterlockedAddLargeStatistic(PLARGE_INTEGER Addend,

  ULONG Increment);

Chức năng mới này nhanh hơn ExInterlockedAddUlong vì nó không cần trả lại giá trị preincrement của biến Addend. Bởi vậy nó không cần thuê một sự khóa quay tròn cho sự đồng bộ hóa. Hóa trị do chức năng này cung cấp, tuy nhiên, chỉ với sự chú ý tới những đối tượng gọi khác của cùng chức năng. Nói cách khác, nếu bạn có mã trên một CPU gọi hướng tới ExInterlockedAddLargeStatistic cùng lúc như mã trên CPU khác đang truy nhập biến Addend cho đọc hoặc viết, bạn có thể có những kết quả mâu thuẫn. Tôi có thể giải thích tại sao như vậy bằng việc cho bạn thấy sự diễn đạt lại này của sự thi hành Intel x86 của chức năng(không phải mã nguồn thực tế):

mov eax, Addend

mov ecx, Increment

lock add [eax], ecx

lock adc [eax+4], 0

Mã này làm việc chính xác cho những mục đích của incrementing Addend bởi vì những tiền tố khóa bảo đảm hóa trị của mỗi thao tác bổ sung và bởi không mang theo mệnh lệnh- thấp 32 bít nào có thể bị mất.Giá trị tức thời của Addend mẩu 64 luôn luôn không nhất quán tuy nhiên, bởi một sự tăng dần có lẽ đã được thăng bằng giữa ADD và ADC tại lúc người nào đó làm một sự sao chép giá trị hoàn thành của mẩu 64. Bởi vậy, thậm chí một đối tượng gọi của ExInterlocked CompareExchange64 trên CPU khác đã có thể thu được một giá trị mâu thuẫn.

Bạn có thể khởi tạo những danh sách này như được cho thấy ở đây:

LIST_ENTRY DoubleHead;

SINGLE_LIST_ENTRY SingleHead;

SLIST_HEADER SListHead;

InitializeListHead(&DoubleHead);

SingleHead.Next = NULL;

ExInitializeSListHead(&SListHead);

Đừng quên rằng bạn cũng phải cấp phát và khởi tạo một sự khóa quay tròn cho mỗi danh sách. Hơn nữa, lưu giữ cho những danh sách đầu và tất cả các tiết mục bạn đặt vào trong những danh sách phải đến từ việc đánh số trang kí ức bởi vì những thủ tục hỗ trợ thực hiện những sự truy nhập của chúng tại IRQL cao. Chú ý rằng sự quay tròn không được dùng trong việc khởi tạo của danh sách đầu vì nó không làm bất kỳ nghĩa nào để cho phép tranh dành cho sự truy nhập danh sách trước khi danh sách đã được khởi tạo.

Bạn có thể chèn những tiết mục tại đầu và đuôi của gấp hai – một bản kê liên kết và tại cái đầu của một đơn độc - bản kê liên kết hay một S-List:

PLIST_ENTRY pdElement, pdPrevHead, pdPrevTail;

PSINGLE_LIST_ENTRY psElement, psPrevHead;

PKSPIN_LOCK spinlock;

pdPrevHead = ExInterlockedInsertHeadList(&DoubleHead,

  pdElement, spinlock);

pdPrevTail = ExInterlockedInsertTailList(&DoubleHead,

  pdElement, spinlock);

psPrevHead = ExInterlockedPushEntryList(&SingleHead,

  psElement, spinlock);

psPrevHead = ExInterlockedPushEntrySList(&SListHead,

  psElement, spinlock);

Những giá trị trở lại là những địa chỉ của những phần tử trước đó tại cái đầu (hay cái đuôi) của danh sách trong câu hỏi. Ghi nhớ những địa chỉ phần tử mà bạn sử dụng với những chức năng này là những địa chỉ của những cấu trúc mục vào danh sách mà thông thường nhúng trong những cấu trúc lớn hơn của cách thức nào đó, và bạn sẽ cần sử dụng CONTAINING_RECORD vỹ mô để khôi phục địa chỉ của cấu trúc lân cận.

Bạn có thể loại bỏ những tiết mục từ cái đầu của bất kỳ danh sách nào:

pdElement = ExInterlockedRemoveHeadList(&DoubleHead, spinlock);

psElement = ExInterlockedPopEntryList(&SingleHead, spinlock);

psElement = ExInterlockedPopEntrySList(&SListHead, spinlock);

Những giá trị trở lại là NULL nếu những danh sách tương ứng trống rỗng. Chắc chắn để kiểm tra giá trị trở lại cho NULL trước khi áp dụng CONTAINING_RECORD vỹ mô để phục hồi một con trỏ chứa cấu trúc.

Bạn có thể gọi là S - List chỉ vận hành trong khi chạy tại hoặc ở dưới DISPATCH_LEVEL. Những chức năng ExInterlockedXxx cho truy nhập gấp hai - Được liên kết hay đơn độc- Những bản kê liên kết có thể được ghé thăm bất kỳ IRQL nào dài như tất cả các sự tham khảo tới danh sách sử dụng một sự gọi ExInterlockedXxx. Lý do không cho những sự hạn chế IRQL nào là những sự thi hành của những chức năng này vô hiệu hóa những ngắt, mà tương đương để nâng IRQL tới mức khả dĩ cao nhất. Chỉ một lần ngắt là làm mất khả năng hoạt động, những chức năng này rồi thu nhận sự khóa quay tròn bạn được chỉ rõ. Không từ mã khác nào có thể kiếm được điều khiển trên cùng CPU, và kể từ khi không có mã nào trên CPU khác có thể thu nhận sự khóa quay tròn, những danh sách của các bạn được bảo vệ.

Nó hoàn hảo OK cho bạn để sử dụng ExInterlockedXxx gọi tới sự truy nhập một đơn độc - liên kết hay gấp hai - bản kê liên kết ( nhưng không phải một S -List) trong một số phần của mã của các bạn và để sử dụng không khóa lồng vận hành (InsertHeadList và vân vân) trong những phần khác của mã của các bạn nếu bạn đi theo sau một quy tắc đơn giản. Trước khi sử dụng một nguyên thủy không khóa lồng, thu nhận cùng sự khóa quay tròn mà khóa vào nhau của các bạn gọi là sự sử dụng. Hơn nữa, hạn chế sự truy nhập danh sách tới mã chạy tại hoặc ở dưới DISPATCH_LEVEL. Chẳng hạn:

// Access list using noninterlocked calls:

VOID Function1()

  {

  ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);

  KIRQL oldirql;

  KeAcquireSpinLock(spinlock, &oldirql);

  InsertHeadList(...);

  RemoveTailList(...);

  KeReleaseSpinLock(spinlock, oldirql);

  }

// Access list using interlocked calls:

VOID Function2()

  {

  ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);

  ExInterlockedInsertTailList(..., spinlock);

  }

Chức năng đầu tiên phải được chạy tại hoặc ở dưới DISPATCH_LEVEL vì đó là một yêu cầu của gọi hướng tới KeAcquireSpinLock. Lý do cho sự hạn chế IRQL trên những sự gọi khóa lồng vào giây vận hành như sau: Suppose Function1 thu nhận sự khóa quay tròn trong sự chuẩn bị để thực hiện một số sự truy nhập danh sách Việc thu nhận sự khóa quay tròn nâng IRQL tới DISPATCH_LEVEL. Bây giờ giả thiết một ngắt xuất hiện trên cùng CPU ở một bậc cao IRQL và những sự điều chỉnh tăng ích Function2 để sử dụng một trong những thủ tục ExInterlockedXxx. Nhân bây giờ sẽ thử thu nhận cùng sự khóa quay tròn , và CPU sẽ đình trệ hoàn toàn. Vấn đề này xuất hiện từ việc cho phép mã chạy tại hai IRQLs khác nhau để sử dụng cùng sự khóa quay tròn: Function1 ở DISPATCH_LEVEL, và Function2 là nói thực tế, dù sao đi nữa-ở HIGH_LEVEL khi nó thử một cách đệ quy thu nhận sự khóa.

0