Io_stack_location is very important. It is okay to talk more. The last time we talked about io_stack_location and the several important functions. Of course, my goal is not literacy, but to write down some mistakes that are easy to make (in fact, they are all nails that have been encountered at work) to facilitate your review. My memory is so bad that I will forget it if I don't watch it for months. If you have no idea about this, I suggest you check the wdk document first.
The last time we talked about the differences between iocopycurrentirpstacklocationtonext and ioskipcurrentirpstacklocation (you can see whether my memory is poor, but it is actually the last one), we forgot the core content to talk about. Io_stack_location is mainly used to solve the problem in asynchronous Io. The so-called asynchronous Io refers to the request sent by the IRP, instead of finishing all the tasks in the same thread context, we put the IRP in the queue first, and then return pending. After doing the real tasks in other threads, we get the IRP in complete. This involves an important function iomarkirppending, which setsThe flag of sl_pending_returned tells iomanager that the task is not finished. Do not rush to recycle resources. When iomanager calls the IRP Distribution Function to return the value, it checks the return value of the distribution function. If the returned value is not status_pendingSl_pending_returned flag has been set, then bsod will occur. I don't know what you think. Anyway, I saw two domains and two threads. When these words are consistent, I felt the trouble was coming again. It turns out that this topic is really troublesome.
Let's look at specific issues. "Put the IRP in the queue first, then return pending, and finish the real thing in other threads.CodeThis is probably the case. <1>
Driverdispatch: iomarkirppending (1) inserttolist return status_pendingwork item routine: ioskipcurrentirpstacklocation (2) iocalldriver
These codes seem to be correct, but they actually imply a problem: After iocalldriver delivers the IRP to the lower-layer driver, you cannot control the behavior of the lower-layer driver. Let's assume that the underlying driver's processing function is written in this way.
IRP-> iostatus. Status = STATUS_SUCCESS; iocompleterequest (IRP, 0); return STATUS_SUCCESS; (3)
In code (1 ),Sl_pending_returned flag is set, code (2) calls Skip, that is, the lower driver still uses the same layer location,Sl_pending_returned also exists. Code (3) returns a STATUS_SUCCESS, bang! Bsod occurred. Your driver is okay, but you have made a blue screen. Maybe you want to complain about why the lower-level driver is synced, but I am also honest with you, don't make any assumptions about people's code, especially when people's logic is completely correct. What should I do? The first thing I think of is to replace the Skip function with the copy function, as shown below <2>
Driverdispatch: iomarkirppending inserttolist return status_pendingthread: iocopycurrentirpstacklocationtonext (4) iosetcompleteroutine iocalldriver
I am still worried that the underlying driver will bsod this way, because code (4) copies all the information on this layer to the underlying layer, and sl_pending_returned still exists. It may be feasible to change to the following form: <3>
Driverdispatch: iocopycurrentirpstacklocationtonext iosetcompleteroutine iomarkirppending insertintolist return status_pending; thread: iocalldriver
This code can work, but it is ugly. The logic of iocopycurrentirpstacklocationtonext is obviously related to what thread is going to do. In fact, this function is generally used together with iocalldriver. Now I have no choice but to do anything wrong, so I have to go to the newsgroup to ask questions. The result is indeed unexpected. Someone has already asked this question and the answer is quite simple. <2> actually, It is correct. I have incorrect understanding of iocopycurrentirpstacklocationtonext, I thought it was just a simple copy of the content. In fact, in addition to copying, this function has done other things. Its implementation is as follows:
Pio_stack_location irpsp; pio_stack_location nextirpsp; irpsp = kernel (IRP); nextirpsp = kernel (IRP); rtlcopymemory (nextirpsp, irpsp, field_offset (io_stack_location, completionroutine )); nextirpsp-> control = 0; (5)
Take a closer look at code (5). It clears the flag in the control field. So far, problems caused by iomarkirppending have been successfully solved.
I want to talk about it with emotion: I want to copy a call stack and how difficult it is to make it work in a multi-threaded environment. Here I have to say something hard: these functions have not been well designed. Let's look at the long and scary name and there are so many side effect, correctness is guaranteed by word of mouth rules. The word "shanzhai" cannot be used here. By referencing a comment from a newsgroup, when I see the ioskipcurrentirpstacklocation function, I guess its behavior should be to tell iomanager that the location at the current layer is completely inactive, the lower-layer driver should never change my content. Who will think that the lower-layer driver can change the content of this layer. If we design a layered module by ourselves, it should be common sense that the data between layers is independent of each other. I am afraid that the MVC writing won't allow C to keep a V pointer for free. And <2> This code snippet should be an asynchronous Io paradigm, and almost all other statements are wrong. Why not abstract a function directly to do these tasks? What's the matter with word of mouth.
I have a new feeling, I don't appreciate the practice of Program personnel calling, because it hinders the progress of programmers. If such a function really exists, I am afraid I have no chance to write it in this blog post. I may not even know that this is the case. Look. net, Java, and other frameworks with well-designed and well-encapsulated strict interfaces. Who knows what stories are hidden in them, there are several other people who care about the amount of sweat and helplessness behind an API with correct behaviors. If you want to counter it, encapsulated functions will force you to understand the nature of the problem, which is good for the programmer's self-improvement. I admit that if we want to take it as an industrialization, encapsulation, and other things, we must do it. The programmer's level is very different. There is no way to estimate productivity on a unified scale, and productivity cannot estimate the progress, there is no way to estimate the quality, and the function of encapsulation is to bring all programmers down to the same level as much as possible, so that the estimation work can be implemented as little as possible. From the boss's point of view, I certainly hope that programmers are completely different, just as best as the robot arm on the assembly line. But I am not the boss. I am a programmer. I don't want to be a good robot arm. From a certain point of view, I want to know everything without a ready-made API. Of course, this idea is a bit extreme. Industrialization is still to be done. Otherwise, the failure rate of IT projects cannot be lowered. Fortunately, the compromise method still exists, that is, using open source: industrialization, API encapsulation, and robot arm, however, we have time and energy to look at the Source Code and see what is behind those APIs. This is good for ourselves. A well-designed class library is a good friend of the boss, while open source is a good friend of the programmer. Don't go to xxbeta or somewhere to get sprayed, there are already too many people talking to the boss in this world, but there are very few people talking to the programmer. Sometimes you should think about it for yourself.