Windows asynchronous io (asynchronous Io) (2)

Source: Internet
Author: User
Tags apc readfile

In the previous article, we learned how to send asynchronous IO requests to the device driver. Obviously, it is not enough to only know this. The user thread must receive a complete notification from the device driver when necessary to execute related tasks. Otherwise, asynchronous IO does not make any sense. Windows provides four methods to receive completion notifications from the device driver.

Some may have come up with a method. As mentioned in the previous article, we can use the internal Member of overlapped to judge the status of the IO request, so we can implement a busy loop to check whether the internal value is status_pending. Isn't that enough? In theory, it is acceptable, but it does not seem practical. As we all know, busy loop is a waste of CPU! How can I easily use the CPU time saved from a slow device in an empty loop ?! In fact, Windows provides four methods for us to receive the completion notification from the device driver. Here we will first introduce the first three methods.

1. singaling a device Kernel Object)

Before the readfile/writefile function adds an IO request to the Request queue, the function sets the kernel object of the device to a nonsignaled state ). After the device driver completes the I/O Request, the driver sets the device kernel object to signaled ). Sample Code:

# Include <windows. h> <br/> # include <stdio. h> </P> <p> int main () <br/>{< br/> handlehfile = NULL; <br/> bytebuff [1024]; <br/> overlappedol = {0}; <br/> boolbret = false; <br/> dworddwcode = 0; </P> <p> hfile = createfile (text ("data.txt"), generic_read, file_1__read, <br/> 0, open_existing, file_flag_overlapped, null ); </P> <p> ol. offset = 16; // read data from file starting at byte 16 <br/> Bret = readfile (hfile, buff, 1024, null, & ol ); </P> <p> If (BRET) // Io request is completed immediately <br/>{</P> <p >}< br/> else if (dwcode = getlasterror () = error_io_pending) // Io request is queued successfully <br/>{< br/> waitforsingleobject (hfile, infinite); // The Io is being stored med asynchronously; wait for it to complete <br/>}< br/> else <br/>{< br/>/error handling here <br/>}</P> <p> return exit_success; <br/>}

The above code is almost the same as the previous Code, but the waitforsignalobject function is called using hfile as the parameter in the second if statement. This call will suspend the user thread until the hfile kernel object changes to the trigger state. After the device driver completes the IO request, it will trigger the hfile kernel object so that the waitforsignalobject function can return, so that the user thread can execute the corresponding task. At this point, it seems that the problem has been solved, but careful friends may find that this method seems to be effective, in fact it is not very useful. Because it cannot process the situation where the user thread sends multiple IO requests at the same time. After the device driver completes any Io request, it will trigger the device kernel object, causing the waitforsignalobejct function to return, but the user thread does not get any useful information to identify which Io request is completed. :)

2. Signaling an event Kernel Object)

Now we know that the first method is not practical, but some friends may have come up with a solution! As mentioned in the previous article, each Io request has a corresponding overlapped structure, and each overlapped has a hevent member. That is to say, each Io request corresponds to an event. Isn't this solved! Sample Code:

# Include <windows. h> <br/> # include <stdio. h> </P> <p> int main () {<br/> handlehfile = NULL; <br/> bytebreadbuff [1024]; <br/> bytebwritebuff [16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };< br/> overlappedolread = {0 };< br/> overlappedolwrite = {0 };< br/> handlehevents [2] = {0 }; <br/> dworddwwaitret = 0; </P> <p> hfile = createfile (text ("data.txt"), generic_read, file_assist_read, <br/> 0, open_existing, file_flag_overlapped, null); </P> <p> olread. offset = 16; // read data from file starting at byte 16 <br/> olread. hevent = createevent (null, true, false, null); <br/> readfile (hfile, breadbuff, 1024, null, & olread); </P> <p> olwrite. hevent = createevent (null, true, false, null); <br/> writefile (hfile, bwritebuff, 16, null, & olwrite ); </P> <p> hevents [0] = olread. hevent; <br/> hevents [1] = olwrite. hevent; <br/> dwwaitret = waitformultipleobjects (2, hevents, false, infinite); // wait multiple IO requests <br/> switch (dwwaitret-wait_object_0) <br/>{< br/> case 0: // read completed <br/> break; <br/> case 1: <br/> break; // write completed <br/>}< br/> return exit_success; <br/>}

Before sending an IO request, the user thread specifies an event kernel object for each hevent member in the overlapped structure. After completing an IO request, the device driver checks whether the hevent member in its overlapped structure is empty. If not, use the setevent function to set it to the trigger status. Then, the user thread uses the waitformultipleobjects function to wait for the previous event object array. After the waitformultipleobjects function returns, the user thread can identify which Io request is completed by the return value. How about it? It is much easier than the first method. :)

However, it has an obvious drawback: the maximum number of handle that waitformultipleobjects can wait at a time cannot exceed maximum_wait_objects, and the value is 64. However, there is also a way to wait for handle of more than 64 through waitformultipleobjects, which will not be described here.

3. Remind io (alertable Io)

Here, except for the first article, the previous two methods can be forgotten for the moment. Because this method has no similarity with the preceding two methods.

When Windows creates a thread, it creates a queue for each thread, called asynchronous procedure call (APC ). To use this feature, we should use the readfileex/writefileex function to replace the original readfile/writefile function.

Bool readfileex (<br/> handle hfile, <br/> pvoid pvbuffer, <br/> DWORD nnumbytestoread, <br/> overlapped * poverlapped, <br/> define pfncompletionroutine ); </P> <p> bool writefileex (<br/> handle hfile, <br/> const void * pvbuffer, <br/> DWORD nnumbytestowrite, <br/> overlapped * poverlapped, <br/> lpoverlapped_completion_routine pfncompletionroutine );

The two methods are different from the original method. First, * ex does not have a pointer to DWORD to return the number of bytes transmitted. Secondly, the * Ex function needs to specify a callback function address, which is called the completion routine ).

Void winapi completionroutine (<br/> DWORD dwerror, <br/> DWORD dwnumbytes, <br/> overlapped * Po );

When the user thread uses the * Ex function to send an IO request, the function will pass the address of the completed function to the device driver. When the device driver completes an IO request, it adds an item to the APC queue of the thread that sends this Io request. This includes the address for completing the function address and overlapped structure. When a thread is placed in a reminder state, the system will check its APC queue. For each item in the queue, the system will call the completion function and pass in the IO error code to transmit the number of bytes, and overlapped structure address. That is to say, I/O can be reminded to enable the user thread to execute related tasks by calling corresponding completion functions after each Io request is completed. Of course, you cannot expect that the order of the execution completion function is the same as the order in which I/O requests are sent.

Now there is a new problem. What is the thread's reminding status. The reminder status is the state in which the thread can be safely interrupted. Windows provides six functions to enable the thread to be reminded.

DWORD sleepex (<br/> DWORD dwmilliseconds, <br/> bool balertable); </P> <p> DWORD waitforsingleobjectex (<br/> handle hobject, <br/> DWORD dwmilliseconds, <br/> bool balertable); </P> <p> DWORD waitformultipleobjectsex (<br/> DWORD cobjects, <br/> const handle * phobjects, <br/> bool bwaitall, <br/> DWORD dwmilliseconds, <br/> bool balertable ); </P> <p> bool signalobjectandwait (<br/> handle hobjecttosignal, <br/> handle hobjecttowaiton, <br/> DWORD dwmilliseconds, <br/> bool balertable ); </P> <p> bool Merge (<br/> handle hcompport, <br/> lpoverlapped_entry pcompportentries, <br/> ulong ulcount, <br/> Pulong pulnumentriesremoved, <br/> DWORD dwmilliseconds, <br/> bool balertable); </P> <p> DWORD msgwaitformultipleobjectsex (<br/> DWORD ncount, <br/> const handle * phandles, <br/> DWORD dwmilliseconds, <br/> DWORD dwwakemask, <br/> DWORD dwflags); </P> <p>

The last parameter of these six functions is of the bool type, indicating whether the call thread should put itself in a reminder state. In fact, the non-extended versions of these * Ex functions call the corresponding * Ex function internally. Of course, the balertable parameter is set to false.

Here, I/O overview will pop up. However, we do not advocate the use of reminder Io because it is too troublesome. :)

1) The user thread must implement a completion function for each Io request. Too many IO requests will of course lead to code bloated. If a function is shared to complete a function, it is difficult for a function to provide sufficient information to distinguish between different cases. In short, it is troublesome.

2) the thread that sends an IO request must process the completed functions of these requests. If a thread sends too many IO requests, the thread must also process each completed function at will, even if there are other Idle threads in the system.

Yes, the method (iocp) introduced in the next article will solve these two problems very well.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.