Alertable I/O

1. Alertable I/O 的概念

Alertable I/O 是異步I/O操作完成之後,對其結果進行處理的一種機制,爲了理解Alertable I/O,讀下面的文章(來自MSDN

Alertable I/O is the method by which application threads process asynchronous I/O requests only when they are in an alertable state.

To understand when a thread is in an alertable state, consider the following scenario:

1. A thread initiates an asynchronous read request by calling ReadFileEx with a pointer to a callback function.

2. The thread initiates an asynchronous write request by calling WriteFileEx with a pointer to a callback function.

3. The thread calls a function that fetches a row of data from a remote database server.

In this scenario, the calls to ReadFileEx and WriteFileEx will most likely return before the function call in step 3. When they doReadFileEx and WriteFileEx完成數據的讀寫), the kernel places the pointers to the callback functions on the thread's Asynchronous Procedure Call (APC) queue. The kernel maintains this queue specifically to hold returned I/O request data until it can be processed by the corresponding thread(調用ReadFileEx and WriteFileEx函數的那個線程).

 

When the row fetch is complete and the thread returns from the function, its highest priority is to process the returned I/O requests on the queue by calling the callback functions. To do this(爲了使kernel維護的APC隊列中的CALLBACK函數得到調用,實際上就是指對I/O操作結果進行處理), it (線程)must enter an alertable state. A thread can only do this by calling one of the following functions with the appropriate flags:

SleepEx

WaitForSingleObjectEx

WaitForMultipleObjectsEx

SignalObjectAndWait

MsgWaitForMultipleObjectsEx

When the thread enters an alertable state(也就是thread調用以上的函數並設置適合的標誌,線程調用了這些函數並不立即返回,並且此時線程進入

Alertable狀態), the following events occur:

1. The kernel checks the thread's APC queue. If the queue contains callback function pointers, the kernel removes the pointer from the queue and sends it to the thread.

2. The thread executes the callback function.

3. Steps 1 and 2 are repeated for each pointer remaining in the queue.

4. When the queue is empty, the thread returns from the function that placed it in an alertable state.(當kernel維護的APC隊列變爲空隊列時,線程就從把該線程變成可提醒狀態的函數(SleepEx等)中返回)

In this scenario, once the thread enters an alertable state it will call the callback functions sent to ReadFileEx and WriteFileEx, then return from the function that placed it in an alertable state.

If a thread enters an alertable state while its APC queue is empty, the thread's execution will be suspended by the kernel until one of the following occurs:

1. The kernel object that is being waited on becomes signaled.

2. A callback function pointer is placed in the APC queue.

A thread that uses alertable I/O processes asynchronous I/O requests more efficiently than when they simply wait on the event flag in the OVERLAPPED structure to be set, and the alertable I/O mechanism is less complicated than I/O completion ports to use. However, alertable I/O returns the result of the I/O request only to the thread that initiated it. I/O completion ports do not have this limitation.

2. Alertable I/Ocompletion routines被調用的實現機制(實際上就是APC的實現機制)

When an I/O request is issued, a structure is allocated to represent the request. This structure is called an I/O request packet (IRP). With synchronous I/O, the thread builds the IRP, sends it to the device stack, and waits in the kernel for the IRP to complete. With asynchronous I/O, the thread builds the IRP and sends it to the device stack. The stack might complete the IRP immediately, or it might return a pending status indicating that the request is in progress. When this happens, the IRP is still associated with the thread, so it will be canceled if the thread terminates or calls a function such as CancelIo. In the meantime, the thread can continue to perform other tasks while the device stack continues to process the IRP.

 

There are several ways that the system can indicate that the IRP has completed:

1. Update the overlapped structure with the result of the operation so the thread can poll to determine whether the operation has completed.

2. Signal the event in the overlapped structure so a thread can synchronize on the event and be woken when the operation completes.

3. Queue the IRP to the thread's pending APC so that the thread will execute the APC routine when it enters an alertable wait state and return from the wait operation with a status indicating that it executed one or more APC routines.

4. Queue the IRP to an I/O completion port, where it will be executed by the next thread that waits on the completion port.

Threads that wait on an I/O completion port do not wait in an alertable state. Therefore, if those threads issue IRPs that are set to complete as APCs to the thread, those IPC completions will not occur in a timely manner; they will occur only if the thread picks up a request from the I/O completion port and then happens to enter an alertable wait.(摘自MSDN)

3.可提醒I/O的使用示例:

#include"../Common/UnicodeSupport.h"

#include<windows.h>

#include<stdio.h>

#include<process.h>

VOID CALLBACK FileIOCompletionRoutine(DWORD, DWORD, LPOVERLAPPED);

#define BUFFER_SIZE 46104

BYTE buffer[BUFFER_SIZE];

int _tmain()

{

         HANDLE hFile;

         if(INVALID_HANDLE_VALUE == (hFile = CreateFile(TEXT("test.txt"), GENERIC_READ |                          GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)))

    {

                   _tprintf_s(TEXT("open file data.txt fail."));

                   return -1;

         }

         

         OVERLAPPED overLapped = {0};

         ReadFileEx(hFile, buffer, BUFFER_SIZE, &overLapped, FileIOCompletionRoutine);

         if(WAIT_IO_COMPLETION == WaitForSingleObjectEx(GetStdHandle(STD_INPUT_HANDLE), 0,           TRUE))

         {

                   _tprintf_s(TEXT("Thread %d: WAIT IO COMPLETION."), GetCurrentThreadId());

         }

         return 0;

}

 

VOID CALLBACK FileIOCompletionRoutine(DWORD, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)

{

         for(int i = 0; i < BUFFER_SIZE; i++)

         {

                   _tprintf_s(TEXT("%c"), buffer[i]);

         }

         _tprintf_s(TEXT("Thread %d: CALLBACK-%d\n"), GetCurrentThreadId(), dwNumberOfBytesTransfered);

         putchar('\n');

}


通過以上示例,證明① Wait* 類函數在將bAlertable參數設置爲TRUE時,如果APC隊列中有回調函數指針,那麼Wait*類函數內部就會調用這些回調函數,此時,Wait*函數的時間限制參數dwMilliseconds,就不起作用了,也就是說wait*類函數一定會等回調函數調用完畢之後纔會返回,即使此時處理回調函數花費的時間已經超過dwMilliseconds參數所指定的時間;②回調函數時間上是由wait*類函數來調用的所以它的執行線程ID和調用I/O操作的線程ID是一樣的,也就是說它們是在同一個線程中被調用的。③ 調用一次Wait*類函數,kernel就會將APC隊列中所有的回調函數調用完畢,直到線程的APC隊列爲空時,Wait*函數纔會返回。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章