Asynchronous device I/O is suitable for large data volumes and high performance scenarios, such as servers.
To use Asynchronous device I/O, when calling createfile to open or create a device, make the parameter dwflagsandattributes include file_falg_overlapped, which means that the device to be opened can be accessed asynchronously.
To send an I/O Request to a device, that is, to put an I/O request into the I/O queue, you can use the readfile and writefile functions:
Bool readfile (
Handle hfile,
Pvoid pvbuffer,
DWORD nnumbytestoread,
Pdword pdwnumbytes,
Overlapped * poverlapped ); Bool writefile (
Handle hfile,
Const void * pvbuffer,
DWORD nnumbytestowrite,
Pdword pdwnumbytes,
Overlapped * poverlapped );
When these two functions are called, the system uses the first hfile parameter to check whether file_flag_overlapped is used when the device specified by the handle is opened. If yes, these two functions execute the asynchronous device I/O, and vice versa, the synchronous device I/O is executed. When the asynchronous I/O mode is used, the null value can be passed to the pdwnumbytes parameter when the two functions are called, because you do not know when the device I/O is complete, therefore, using this parameter does not make much sense.
Note that the last parameter is a pointer to the overlapped structure:
Typedef struct _ overlapped {
DWORD internal; // error code (exit parameter, return)
DWORD internalhigh; // data size transmitted, in bytes (exit parameter, returned)
DWORD offset; // low 32-bit offset (Entry parameter, input)
DWORD offsethigh; // high 32-bit offset (Entry parameter, input)
Handle hevent; // event kernel object handle (Entry parameter, input)
} Overlapped, * lpoverlapped;
The structure contains five members. Three of them, namely offset, offsethigh, and hevent, should be initialized before calling readfile and writefile, the other two -- Internal and internalhigh will be set by the device driver when I/O is completed. The following details:
- Offset and offsethigh -- when an asynchronous device I/O is used to manipulate a "file device", the file read/write pointer is ignored, the offset of I/o is determined by the offset and offsethigh In the overlapped structure. In addition, in "non-file devices", these two members are not ignored. Generally, they must be set to 0.
- Hevent-an event kernel object handle, which can be used in multiple ways, will be discussed later.
- Internal -- save the I/O error code. When you send an I/O request, this parameter is set to status_pending, indicating that no error has occurred because the operation has not started. You can use the hasoverlappediocompleted macro to check whether an asynchronous device I/O is complete. This structure accepts an overlapped structure pointer and returns true if the I/O request is complete. If the I/O request is still not started, false is returned.
- Internalhigh -- when the asynchronous I/O request is complete, the member stores the number of bytes of data transferred.
After the asynchronous I/O request is complete, you can receive a pointer in the overlapped structure. Generally, a C ++ class can be derived from the overlapped structure, and some other information can be added to the class to make it easier to process. Then, when using the readfile and writefile functions, you can pass the pointer of the C ++ Class Object. When I/O is complete and the structure is accepted, you can convert it to a C ++ class object to obtain not only five members, but also other information in the class.
When using asynchronous device I/O, pay attention to the following three points:
1. the device driver may not necessarily process the device I/O requests in the first-in-first-out (FIFO) Order. Therefore, the following encoding will not ensure that the device is read first and then written:
Overlapped O1 = {0 };
Overlapped O2 = {0 };
Byte bbuffer [100];
Readfile (hfile, bbuffer, 100, null, & O1); // read
Writefile (hfile, bbuffer, 100, null, & O2); // write
2. If I/O requests are made asynchronously, the driver may select the synchronous method. When you read a file, if the system finds that the read data is in the cache and the data is valid, the I/O request does not need the driver, instead, you can directly copy the data in the cache to your buffer. The driver has been using Synchronization Methods for some operations, such as file compression in the NTFS format, extending the file length, and adding file information.
At this time, if readfile and writefile return non-0 values, it indicates that it is synchronized. If flase is returned, an error occurs. In this case, you can use getlasterror to obtain information. If error_io_pending is returned, the I/O request is successfully submitted but not completed.
3. The data buffer and overlapped structure cannot be moved or released before the asynchronous I/O request is completed. When the device driver is preparing to process your I/O request, it transfers the data to the address corresponding to the pvbuffer parameter and accesses the offset and other Members in the overlapped structure. After the I/O request is complete, the device driver updates the internal and internalhigh members in the overlapped result. Therefore, the data buffer and overlapped structure cannot be moved or released before the I/O request is completed. Otherwise, the memory data will be damaged. In addition, each time readfile or writefile is called, A separate overlapped structure must be allocated.
For example, the following code has a bug:
Void readdata (handle hfile)
{
Overlapped o = {0 };
Byte B [100];
Readfile (hfile, B, 100, null, & O );
} // At this time, both the buffer B and overlapped structure o are released
You can cancel a device I/O request to cancel the queue. You can use the following methods:
1. Call the cancelio function in a thread to cancel all I/O requests sent by the thread to the specified device, except that the specified device is the "I/O completion port ".
Bool cancelio (handle hfile); // The parameter is the device object handle.
2. Cancel all I/O requests related to a device and close the device handle.
3. When a thread ends, the system automatically cancels all I/O requests sent by the thread, except for the I/O requests sent to the "I/O completion port.
4. If you want to cancel a specific I/O request, you can use the cancelioex function to pass an overlapped structure pointer to it:
Bool cancelioex (handle hfile, lpoverlapped poverlapped );
This function can be used across threads, that is, I/O requests sent in thread T1. It can end with this function in thread T2. Because each I/O request requires a unique overlapped structure, the overlapped structure identifies an I/O Request. If null is passed to the 2nd parameter of the cancelioex function, all I/O requests of devices corresponding to the hfile will be canceled.
Cancels an I/O request. The I/O Request ends and the error code is set to error_operation_aborted.