24/05/2018, 20:39

Những thao tác bên trong điều khiển IO (Internal IO Control Operations)

Hệ thống sử dụng IRP_MJ_DEVICE_CONTROL để thực hiện một sự gọi DeviceIoControl từ kiểu người sử dụng. Những trình điều khiển đôi khi cũng cần nói chuyện với nhau, và họ sử dụng IRP_MJ_INTERNAL_DEVICE_CONTROL liên quan để làm. Một chuỗi mã tiêu biểu Đi theo ...

Hệ thống sử dụng IRP_MJ_DEVICE_CONTROL để thực hiện một sự gọi DeviceIoControl từ kiểu người sử dụng. Những trình điều khiển đôi khi cũng cần nói chuyện với nhau, và họ sử dụng IRP_MJ_INTERNAL_DEVICE_CONTROL liên quan để làm. Một chuỗi mã tiêu biểu Đi theo Như sau:

ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);

KEVENT event;

KeInitializeEvent(&event, NotificationEvent, FALSE);

IO_STATUS_BLOCK iostatus;

PIRP Irp = IoBuildDeviceIoControlRequest(IoControlCode,

  DeviceObject, pInBuffer, cbInBuffer, pOutBuffer, cbOutBuffer,

  TRUE, &event, &iostatus);

NTSTATUS status = IoCallDriver(DeviceObject, Irp);

if (NT_SUCCESS(status))

  KeWaitForSingleObject(&event, Executive, KernelMode,

    FALSE, NULL);

The IoControlCode argument to IoBuildDeviceIoControlRequest là một mã điều khiển biểu thị thao tác bạn muốn bộ điều khiển thiết bị đích thực hiện. Mã này là cùng loại mã Bạn sử dụng với những thao tác điều khiển bình thường.DeviceObject là một con trỏ tới DEVICE_OBJECT mà có trình điều khiển sẽ thực hiện chỉ báo thao tác. Đầu vào và những tham số bộ đệm ra phục vụ cùng mục đích với những bản sao của họ trong một sự gọi DeviceIoControl kiểu người sử dụng. Đối số(thứ) bảy, mà Tôi chỉ rõ như TRUE trong đoạn này, chỉ ra rằng bạn xây dựng một thao tác điều khiển (phép toán kiểm tra) bên trong. (Bạn đã có thể nói FALSE ở đây để tạo ra một IRP_MJ_DEVICE_CONTROL thay vào đó.) Tôi sẽ mô tả mục đích (của) sự kiện và những đối sốiostatus trong một mẩu.

IoBuildDeviceIoControlRequest builds an IRP và khởi tạo sự định vị (vị trí) chồng đầu tiên để mô tả mã tác vụ và những bộ đệm bạn chỉ rõ. Nó trả lại con trỏ IRP cho bạn để bạn có thể làm bất kỳ sự khởi tạo bổ sung nào mà có lẽ đã được yêu cầu. Trong Chương 12, chẳng hạn, Tôi sẽ chỉ ra bạn làm sao để sử dụng một yêu cầu kiểm soát nội bộ để gợi ý một khối yêu cầu USB (URB) tới trình điều khiển USB. Một phần mà quá trình bao gồm đặt một lĩnh vực tham số chồng tới điểm tới URB. Bạn gọi là IoCallDriver để gửi IRP cho thiết bị đích. Nếu giá trị trở lại đi qua sự thử NT_SUCCESS, bạn đợi trên đối tượng sự kiện bạn chỉ rõ như đối số(thứ) tám Tới IoBuildDeviceIoControlRequest. The I/O Manager sẽ đặt sự kiện khi kết thúc IRP, và nó sẽ cũng điền vào cấu trúc iostatus (của) các bạn với tình trạng cuối và thông tin những giá trị. Cuối cùng nó sẽ gọi là IoFreeIrp để giải phóng IRP. Vậy thì, Bạn không muốn truy nhập con trỏ IRP chút nào sau khi bạn gọi là IoCallDriver.

Sẽ không phải là một ý tưởng tốt khi sử dụng cùng sự liên lạc thường lệ (cho) điều khiển bên trong và ngoài, nhân tiện, ít nhất không phải không có việc kiểm tra mã chức năng chính (của) IRP. ở đây là một ví dụ của tại sao không: Giả thiết trình điều khiển (của) các bạn có một giao diện điều khiển ngoài mà cho phép một ứng dụng để hỏi giao diện kiểm soát nội bộ số lưphaanjboj phận điều khiển (của) các phiên bản mà cho phép một người gọi kiểu nhân có thể tin cẩn được xác định một bí mật sống còn mà bạn không muốn chia sẻ với người sử dụng- những

chương trình kiểu. Rồi giả thiết bạn sử dụng một thủ tục để xử lý cả hai giao diện, như trong ví dụ này:

NTSTATUS DriverEntry(...)

  {

  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =

    DispatchControl;

  DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =

    DispatchControl;

  }

NTSTATUS DispatchControl(...)

  {

  switch (code)

    {

  case IOCTL_GET_VERSION:

  case IOCTL_INTERNAL_GET_SECRET:

           // <== exposed for user-mode calls

    }

  }

Nếu một ứng dụng có thể bằng cách nào đó xác định giá trị số của IOCTL_INTERNAL_GET_SECRET, nó có thể phát hành một sự gọi DeviceIoControl bình thường và đi vòng an toàn dự định trên chức năng đó.

ý tưởng cơ bản đằng sau phương pháp chia sẻ sự kiện là ứng dụng tạo ra một sự kiện bằng việc gọi là CreateEvent và sau đó sử dụng DeviceIoControlto gửi tới ô điều khiển sự kiện cho bộ phận điều khiển

DWORD junk;

HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

DeviceIoControl(hdevice, IOCTL_REGISTER_EVENT, &hEvent,

  sizeof(hEvent), NULL, 0, &junk, NULL);

NOTE Trình điều khiển mẫu EVWAIT minh họa phương pháp sự kiện dùng chung của việc thông báo một ứng dụng về một sự kiện thú vị. Bạn phát sinh sự kiện " thú vị " bằng việc nhấn (nén) một chìa khóa trên bàn phím, do đó việc gây ra sự thử lập trình để gọi là một IOCTL cửa sau trong bộ phận điều khiển. Trong cuộc sống thực sự, một biến cố phần cứng thực tế sẽ phát sinh sự kiện.

Sự gọi tới CreateEvent tạo ra a kernel-mode KEVENT object và làm một mục vào vào trong quá trình ứng dụng xử lý bảng mà trỏ vào KEVENT. Giá trị HANDLE trở lại đối với ứng dụng thực chất là một chỉ số vào trong bảng Ô điều khiển . Ô điều khiển tuy nhiên không trực tiếp hữu ích đối với một trình điều khiển WDM, bởi hai lý do. Trước hết, ở đó không lấy một tài liệu giao diện kiểu nhân để đặt một sự kiện, được đưa cho đúng (là) một ô điều khiển . Thứ hai, và quan trọng nhất, Ô điều khiển chỉ hữu ích trong một luồng mà thuộc về cùng quá trình. Nếu mã trình điều khiển chạy trong một luồng chuyên quyền ( Như nó thường làm ), nó sẽ không có khả năng đối với sự tham khảo sự kiện bằng cách sử dụng ô điều khiển .

Để có xung quanh hai vấn đề này với sự kiện ô điều khiển , trình điều khiển phải " chuyển đổi " ô điều khiển tới một con trỏ tới underlying KEVENT object. Để xử lý một thao tác điều khiển (phép toán kiểm tra) METHOD_BUFFERED ứng dụng đó thường đăng ký một sự kiện với trình điều khiển, mã sử dụng giống điều này

HANDLE hEvent = *(PHANDLE) Irp->AssociatedIrp.SystemBuffer;

PKEVENT pevent;

NTSTATUS status = ObReferenceObjectByHandle(hEvent,

  EVENT_MODIFY_STATE, *ExEventObjectType, Irp->RequestorMode,

  (PVOID*) &pevent, NULL);

ObReferenceObjectByHandle xem hEvent trong bảng ô điều khiển (cho) quá trình hiện thời và cất giữ địa chỉ (của) đối tượng nhân có liên hệ trong biến pevent. Nếu RequestorMode trong IRP là UserMode, chức năng này cũng kiểm tra rằng hEvent thật sự là một ô điều khiển tới cái gì đó, mà cái gì đó là một đối tượng sự kiện, và ô điều khiển đó được mở trong một cách mà bao gồm đặc quyền EVENT_MODIFY_STATE.

Bất cứ khi nào mà bạn yêu cầu đối tượng quản lý viên giải quyết một ô điều khiển thu được từ kiểu người sử dụng, sự truy nhập yêu cầu và sự kiểm tra kiểu bằng việc chỉ báo UserMode (cho) đối sốkiểu accessor tới đối tượng quản lý viên nào vận hành bạn gọi (hướng tới). Rốt cuộc, số bạn có từ người sử dụng kiểu Có lẽ đã không là một ô điều khiển nào, hay là nó Có lẽ đã là một ô điều khiển tới kiểu khác nào đó (của) đối tượng. Ngoài ra, tránh chức năng undocumented ZwSetEvent Trong mệnh lệnh không phải để tạo ra lỗ an ninh sau đây: dù là bạn chắc chắn ô điều khiển ngẫu nhiên nào đó (cho) một đối tượng sự kiện, người gọi kiểu người sử dụng (của) các bạn đã có thể đóng ô điều khiển và nhận được sau cùng ô điều khiển số (cho) một kiểu khác nhau của đối tượng. Bạn không cố ý gây ra cái gì đó xấu xảy ra vì bạn là một người gọi có thể tin cẩn được (của) ZwSetEvent. ứng dụng có thể đợi sự kiện để xảy ra:

WaitForSingleObject(hEvent, INFINITE);

The driver signals the event in the usual way:

KeSetEvent(pevent, EVENT_INCREMENT, FALSE);

Dần dần, ứng dụng dọn sạch lên trên bằng việc gọi là CloseHandle. Trình điều khiển có một sự tham khảo riêng biệt tới đối tượng sự kiện, mà nó phải giải phóng bằng việc gọi là ObDereferenceObject. Đối tượng quản lý viên không được phá hủy đối tượng sự kiện cho đến khi cả hai thứ này xuất hiện.

Using a Pending IOCTL for Notification

ý tưởng trung tâm trong phương pháp thông báo IOCTL đang xem xét là khi ứng dụng muốn nhận được những thông báo sự kiện từ trình điều khiển, nó gọiDeviceIoControl:

HANDLE hDevice = CreateFile(".<driver-name>", ...);

BOOL okay = DeviceIoControl(hDevice, IOCTL_WAIT_NOTIFY,

);

(IOCTL_WAIT_NOTIFY, nhân tiện, là mã điều khiển Tôi được dùng trong mẫu NOTIFY trong nội dung hướng dẫn.)

Trình điều khiển chưa quyết định IOCTL này và hoàn thành nó sau đó .Nếu những xem xét khác không xâm nhập mã trong trình điều khiển có lẽ đã đơn giản như điều này:

NTSTATUS DispatchControl(...)

  {

  switch (code)

    {

  case IOCTL_WAIT_NOTIFY:

    IoMarkIrpPending(Irp);

    pdx->NotifyIrp = Irp;

    return STATUS_PENDING;

    }

  }

VOID OnInterestingEvent(...)

  {

  CompleteRequest(pdx->NotifyIrp,

    STATUS_SUCCESS, 0); // <== don't do this!

  }

" Những sự xem xét khác " Tôi thấy đúng như vậy tiện lợi gấp lại ở dưới Thảm, tất nhiên, hết sức quan trọng ở crafting một trình điều khiển chế độ làm việc.

Bộ khởi đầu (của) IRP có lẽ đã quyết định hủy bỏ nó. ứng dụng có lẽ đã gọi là CancelIo, hay điểm tận cùng (của) luồng ứng dụng có lẽ đã gây ra một thành phần kiểu nhân để gọi là IoCancelưIrp. Trong mọi trường hợp, chúng tôi phải cung cấp một sự hủy bỏ thường lệ để IRP được hoàn thành. Phải chăng sức mạnh được loại bỏ từ thiết bị (của) chúng ta, hoặc Nếu thiết bị (của) chúng ta được loại bỏ thình lình từ máy tính, chúng tôi có thể muốn để làm sẩy (hỏng) bất kỳ yêu cầu IOCTL nổi bật nào. Nói chung, bất kỳ số lượng IOCTLs nào có lẽ đã cần sa sẩy. Vậy thì, Chúng tôi sẽ cần một bản kê liên kết của chúng. Từ những luồng có lẽ đã đang thử để truy nhập bản kê liên kết này, chúng tôi cũng sẽ cần một sự khóa quay tròn để chúng tôi có thể truy nhập danh sách an toàn.

Helper Routines

Để đơn giản hóa cuộc sống (của) riêng mình, Tôi viết một sự đông cứng (của) người giúp đỡ những thủ tục để quản lý IOCTLs không đồng bộ. Hai thứ Quan trọng nhất của những thủ tục này có tên CacheControlRequest và UncacheControlRequest. Họ giả thiết điều bạn sẽ chấp nhận chỉ có một IOCTL không đồng bộ có một mã điều khiển đặc biệt trên thiết bị phản đối và mà bạn có thể, bởi vậy, dự trữ một ổ con trỏ trong mở rộng thiết bị để chỉ tới IRP điều đó hiện thời nổi bật. Trong NOTIFY, Tôi gọi là ổ con trỏ NotifyIrp này. Bạn chấp nhận IRP không đồng bộ cách này:

switch (code)

  {

case IOCTL_WAIT_NOTIFY:

  if (<parameters invalid in some way>)

    status = STATUS_INVALID_PARAMETER;

  else

    status = CacheControlRequest(pdx, Irp, &pdx->NotifyIrp);

  break;

  }

return status == STATUS_PENDING ? status :

  CompleteRequest(Irp, status, info);

Sự phát biểu quan trọng ở đây là sự gọi tới CacheControlRequest, những sổ đăng ký IRP này trong một cách mà chúng tôi sẽ có khả năng để hủy bỏ nó sau đó nếu cần thiết. Nó cũng ghi địa chỉ (của) IRP này trong thành viên NotifyIrp (của) mở rộng thiết bị (của) chúng ta. Chúng tôi chờ đợi nó để trả lại STATUS_PENDING, tại trường hợp nào chúng tôi tránh hoàn thành IRP và đơn giản trả lại STATUS_PENDING cho người gọi (của) chúng ta.

NOTE Bạn có thể dễ dàng khái quát hóa sơ đồ Tôi mô tả để cho phép một ứng dụng để có một IRP (của) mỗi kiểu nổi bật (cho) mỗi ô điều khiển mở. Thay vì việc mang những con trỏ IRP hiện thời vào mở rộng thiết bị (của) các bạn, đặt chúng vào trong một cấu trúc mà bạn thấy liên quan đến FILE_OBJECT mà tương ứng tới ô điều khiển . Bạn sẽ đưa một con trỏ tới FILE_OBJECT này trong sự định vị (vị trí) chồng I/O (cho) IRP_MJ_CREATE, IRP_MJ_CLOSE, và, thật ra, tất cả IRPs khác sinh ra (cho) ô điều khiển hồ sơ. Bạn có thể sử dụng mọi FsContext hay lĩnh vực đối tượng hồ sơ (cho) bất kỳ (cái) nào FsContext2 Bạn dự định chọn.

Later , khi sự kiện nào ứng dụng đang đợi cho xuất hiện, chúng tôi thực hiện mã như điều này

PIRP nfyirp = UncacheControlRequest(pdx, &pdx->NotifyIrp);if (nfyirp)

  {

  <do something>

  CompleteRequest(nfyirp, STATUS_SUCCESS, <info value>);

  }

Lôgic này khôi phục địa chỉ (của) yêu cầu IOCTL_WAIT_NOTIFY đang xem xét, àm cái gì đó để cung cấp dữ liệu quay trở lại ứng dụng, và sau đó hoàn thành gói yêu cầu I/O đang xem xét.

I hid a wealth of complications inside the CacheControlRequest and UncacheControlRequest functions. Hai chức năng này cung cấp một luồng và cơ chế an toàn bộ đa xử lý để theo dõi những yêu cầu IOCTL không đồng bộ. Họ sử dụng một sự biến đổi trên kỹ thuật Chúng tôi tranh luận nơi khác trong sách để an toàn xếp hàng và ra khỏi hàng IRPs đôi khi một người khác có lẽ đã đang dời chỗ về việc thử để hủy bỏ IRP. Tôi thật sự đóng gói những thủ tục này trong GENERIC.SYS, and mẫu NOTIFY trong nội dung hướng dẫn chỉ ra làm sao để gọi chúng. Here’s how those functions work (but note that the GENERIC.SYS versions have ‘Generic’ in their names):

typedef struct _DEVICE_EXTENSION {

  KSPIN_LOCK IoctlListLock;

  LIST_ENTRY PendingIoctlList;

  } DEVICE_EXTENSION, *PDEVICE_EXTENSION;

NTSTATUS CacheControlRequest(PDEVICE_EXTENSION pdx, PIRP Irp,

  PIRP* pIrp)

  {

  KIRQL oldirql;

  KeAcquireSpinLock(&pdx->IoctlListLock, &oldirql);

  NTSTATUS status;

  if (*pIrp)

    status = STATUS_UNSUCCESSFUL;

  else if (pdx->IoctlAbortStatus)

    status = pdx->IoctlAbortStatus;

  else

    {

    IoSetCancelRoutine(Irp, OnCancelPendingIoctl);

    if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))

      status = STATUS_CANCELLED;

    else

       {

       IoMarkIrpPending(Irp);

       status = STATUS_PENDING;

       Irp->Tail.Overlay.DriverContext[0] = pIrp;

       *pIrp = Irp;

       InsertTailList(&pdx->PendingIoctlList,

         &Irp->Tail.Overlay.ListEntry);

       }

    }

  KeReleaseSpinLock(&pdx->IoctlListLock, oldirql);

  return status;

  }

VOID OnCancelPendingIoctl(PDEVICE_OBJECT fdo, PIRP Irp)

  {

  KIRQL oldirql = Irp->CancelIrql;

  IoReleaseCancelSpinLock(DISPATCH_LEVEL);

  PDEVICE_EXTENSION pdx =

    (PDEVICE_EXTENSION) fdo->DeviceExtension;

  KeAcquireSpinLockAtDpcLevel(&pdx->IoctlListLock);

  RemoveEntryList(&Irp->Tail.Overlay.ListEntry);

  PIRP pIrp = (PIRP) Irp->Tail.Overlay.DriverContext[0];

  InterlockedCompareExchange((PVOID*) pIrp, Irp, NULL);

  KeReleaseSpinLock(&pdx->IoctlListLock, oldirql);

  Irp->IoStatus.Status = STATUS_CANCELLED;

  IoCompleteRequest(Irp, IO_NO_INCREMENT);

  }

PIRP UncacheControlRequest(PDEVICE_EXTENSION pdx, PIRP* pIrp)

  {

  KIRQL oldirql;

  KeAcquireSpinLock(&pdx->IoctlListLock, &oldirql);

  PIRP Irp = (PIRP) InterlockedExchangePointer(pIrp, NULL);

  if (Irp)

    {

    if (IoSetCancelRoutine(Irp, NULL))

      {

      RemoveEntryList(&Irp->Tail.Overlay.ListEntry);

      }

    else

      Irp = NULL;

    }

  KeReleaseSpinLock(&pdx->IoctlListLock, oldirql);

  return Irp;

  }

1. Chúng tôi sử dụng một sự khóa quay tròn bảo vệ danh sách (của) IOCTLs đang xem xét và cũng để bảo vệ tất cả các ổ con trỏ mà được dự trữ trỏ vào thể hiện hiện thời của mỗi yêu cầu kiểu IOCTL không đồng bộ khác nhau

2. Điều này là nơi chúng ta Giám sát việc thi hành quy tắc-nó là của một quyết định thiết kế nữa, thật sự-chỉ có một IRP đó (của) mỗi kiểu có thể (thì) nổi bật tại một thời gian

3. Điều này nếu sự phát biểu điều tiết thực tế mà chúng tôi có thể cần bắt đầu bỏ quên đầu vào IRPs tại điểm nào đó bởi vì PnP hay những sự kiện sức mạnh.

4. Từ đó Chúng tôi nối vào cuối đuôi IRP này cái gì có lẽ đã là một thời gian dài, chúng tôi cần phải có một sự hủy bỏ thường lệ (cho) nó. Tôi bàn luận lôgic hủy bỏ rất nhiều thời gian trong sách này mà Tôi cảm thấy chắc chắn bạn không khá hơn khi đọc về nó một lần nữa.

5. Ở đây Chúng tôi đã được giải quyết để tiến lên và cạc IRP này vì thế mà chúng tôi có thể hoàn thành nó sau đó. Từ đó Chúng tôi đi đến kết luận trả lại STATUS_PENDING từ chức năng DispatchControl (của) chúng ta, chúng tôi cần gọi là IoMarkIrpPending.

6. Chúng tôi cần có một cách tới NULL ở ngoài ổ con trỏ bộ đệm khi Chúng tôi hủy bỏ IRP. Từ đó ở đó không có cách để có một tham số văn cảnh chuyển cho sự hủy bỏ thường lệ (của) chúng ta , Tôi quyết định kết nạp một trong những lĩnh vực DriverContext trong IRP để giữ một con trỏ tới ổ con trỏ bộ đệm.

7. Trong khóa học bình thường (của) những sự kiện, phát biểu uncaches là một IRP.

8. Bây giờ có điều là Chúng tôi không có vùng nhớ đệm IRP của chúng tôi, chúng tôi không muốn nó cancelled bất kỳ (cái) nào nữa. Nếu IoSetCancelRoutine trả lại NULL ,tuy nhiên, chúng tôi biết IRP này hiện thời trong quá trình (của) cancelled.Chúng tôi trả lại một con trỏ NULLIRP trong trường hợp đó.

NOTIFY cũng Có một trình điều khiển IRP_MJ_CLEANUP về việc xem xét IOCTLs mà trông thì chỉ giống như những trình điều khiển cleanup Tôi đã tranh luận về việc đọc và viết những thao tác. Cuối cùng, nó bao gồm một chức năng giúp đỡ AbortPendingIoctls trong việc sử dụng tại thời gian ngắt điện hay thời gian di chuyển bất ngờ, như sau:

VOID AbortPendingIoctls(PDEVICE_EXTENSION pdx, NTSTATUS status)

  {

  InterlockedExchange(&pdx->IoctlAbortStatus, status);

  CleanupControlRequests(pdx, status, NULL);

  }

CleanupControlRequests là trình điều khiển IRP_MJ_CLEANUP. Tôi viết nó theo một cách mà nó hủy bỏ tất cả IRPs đáng chú ý nếu đối sốthứ ba-bình thường một con trỏ đối tượng tệp tin –là NULL .

NOTIFY hơi quá đơn giản để được dùng làm một mô hình đầy đủ (cho) một trình điều khiển thế giới- thực sự.Ở đây là một số sự xem xét bổ sung (cho) bạn để tham khảo trong quá trình thiết kế (của) riêng mình:

_Trình điều khiển có lẽ đã có vài kiểu những sự kiện mà thúc đẩy thông báo. Bạn đã có thể quyết định thỏa thuận Với chúng bằng cách sử dụng một mã IOCTL đơn, trong trường hợp nào bạn chỉ báo những mã IOCTL kiểu sự kiện bởi loại nào đó (của) dữ liệu ra, hay bằng cách sử dụng nhiều.

_Bạn có lẽ đã muốn cho phép nhiều luồng đăng ký (cho) sự kiện. Nếu mà trường hợp, bạn chắc chắn có một con trỏ IRP đơn trong mở rộng thiết bị- bạn cần một cách theo dõi tất cả IRPs mà liên quan tới một kiểu đặc biệt (của) sự kiện. Nếu bạn chỉ sử dụng một kiểu đơn của IOCTL (cho) tất cả các thông báo, một cách kiểm tra là để chúng xếp hàng trên PendingIoctlList được chỉ ra trước đó. Rồi, khi một sự kiện xuất hiện, bạn thực hiện một vòng mà trong đó bạn gọi ExInterlockedRemoveHeadList và IoCompleteRequestto làm trống rỗng danh sách đang xem xét. (Tôi tránh sự phức tạp này trong NOTIFY bởi mệnh lệnh-Tôi quyết định Tôi chỉ chạy có một chương trình thử tại một thời điểm.) _Sự liên lạc IOCTL (của) Các bạn thường lệ có trong một cuộc đua với hoạt động mà phát sinh những sự kiện. Chẳng hạn, trong mẫu USBINT Tôi tranh luận trong Chương 12, chúng tôi có một cuộc đua tiềm tàng giữa thủ tục liên lạc IOCTL và pseudointerrupt thường lệ mà dịch vụ một ngắt endpoint trên một thiết bị USB. Tới việc tránh mất những sự kiện hay cầm lấy những hoạt động mâu thuẫn, bạn cần một sự khóa quay tròn. Tham chiếu tới mẫu USBINT trong nội dung (cho) một sự minh họa của việc sử dụng sự khóa quay tròn như thế nào phù hợp. (Đồng bộ hóa không chảy ra trong NOTIFY bởi vì bằng thời gian một con người (thì) có thể thực hiện nhấn phím mà thả sự kiện báo hiệu, yêu cầu thông báo gần như chắc chắn đang xem xét. Nếu không phải, yêu cầu tín hiệu có một lỗi.).

0