1 KQUEUE KeInitializeQueue

VOID

KeInitializeQueue(

IN PKQUEUE  Queue,

IN ULONG  Count  OPTIONAL

);

lkd> dt _KQUEUE

nt!_KQUEUE

+0x000 Header           : _DISPATCHER_HEADER

+0x010 EntryListHead    : _LIST_ENTRY

+0x018 CurrentCount     : Uint4B

+0x01c MaximumCount     : Uint4B

+0x020 ThreadListHead   : _LIST_ENTRY

KeInitializeQueue初始化Queue对象,线程可以等待访问队列中的元素,Count 表示同时可以访问队列的线程数(不需要等待),一般使用KQUEUE的时候也会创建对应数量的线程来queue and dequeue its entries

2 KQUEUE KeInsertQueue

LONG

KeInsertQueue(

IN PKQUEUE  Queue,

IN PLIST_ENTRY  Entry

);

如果当前线程的不是处于queue wait状态,或者等待的QUEUE不是要插入的这个QUEUE,并且有其它线程在等待KQUEUE(调用KeRemoveQueue),活跃的线程小于最大的活跃线程,则会唤醒等待KQUEUE的线程来取本次要插入的Entry,否则把Entry插入到Queue的尾部.

3 CreateIoCompletionPort - ExistingCompletionPort为NULL的情况

HANDLE WINAPI CreateIoCompletionPort(

__in          HANDLE FileHandle,

__in          HANDLE ExistingCompletionPort,

__in          ULONG_PTR CompletionKey,

__in          DWORD NumberOfConcurrentThreads

);

内部创建一个完成端口对象,对象开始是一个_KQUEUE对象,然后调用KeInitializeQueue初始化这个KQUEUE,然后返回这个对象的句柄。如果FileHandle不为NULL,则调用NtSetInformationFile把FileHandle和完成端口进行绑定关联

CompletionInfo.Port = ExistingCompletionPort;

CompletionInfo.Key = (void *)CompletionKey;

NtSetInformationFile(FileHandle, &IoSb, &CompletionInfo, 8, FileCompletionInformation);

关于NtSetInformationFile内部的实现,先看FILE_OBJECT对象

lkd> dt _FILE_OBJECT

nt!_FILE_OBJECT

...

+0x06c CompletionContext : Ptr32 _IO_COMPLETION_CONTEXT

lkd> dt  _IO_COMPLETION_CONTEXT

nt!_IO_COMPLETION_CONTEXT

+0x000 Port             : Ptr32 Void

+0x004 Key              : Ptr32 Void

也就是说内部先分配一个IO_COMPLETION_CONTEXT结构体,然后根据CompletionInfo里面的Port句柄获得Port对象,然后填充这个结构体,最后把这个结构体的指针放到FileHandle对应的文件对象的CompletionContext里面。一个CompletionPort可以关联多个FileHandle,可以通过CompletionKey来标识具体的FileHandle

4 PostQueuedCompletionStatus

BOOL WINAPI PostQueuedCompletionStatus(

__in          HANDLE CompletionPort,

__in          DWORD dwNumberOfBytesTransferred,

__in          ULONG_PTR dwCompletionKey,

__in          LPOVERLAPPED lpOverlapped //这里只是个指针,不一定非要指向OVERLAPPED结构体

);

往CompletionPort投递一个I/O completion packet,包含dwNumberOfBytesTransferred, dwCompletionKey和lpOverlapped.

PostQueuedCompletionStatus内部调用NtSetIoCompletion来实现具体的功能

NTSTATUS

NtSetIoCompletion (

__in HANDLE IoCompletionHandle, //CompletionPort

__in PVOID KeyContext,     //dwCompletionKey

__in_opt PVOID ApcContext,      //为0

__in NTSTATUS IoStatus,     //lpOverlapped

__in ULONG_PTR IoStatusInformation //dwNumberOfBytesTransferred

)

NtSetIoCompletion内部大概实现如下

IoCompletionHandle获得完成端口对象IoCompletion,分配一个结构体_IOP_MINI_COMPLETION_PACKET MiniPacket,根据NtSetIoCompletion传进的参数初始化MiniPacket,调用KeInsertQueue((PKQUEUE)IoCompletion, &MiniPacket->ListEntry);插入到完成端口对象的KQUEUE里面_IOP_MINI_COMPLETION_PACKETL里面的PacketType表示类型,比如IopCompletionPacketIrp表示是IRP IO完成时投递的,在NtRemoveIoCompletion里面会跟进这个字段来区分数据

5 GetQueuedCompletionStatus

BOOL WINAPI GetQueuedCompletionStatus(

__in          HANDLE CompletionPort,

__out         LPDWORD lpNumberOfBytes,

__out         PULONG_PTR lpCompletionKey,

__out         LPOVERLAPPED* lpOverlapped,

__in          DWORD dwMilliseconds

);

内部只是简单的调用NtRemoveIoCompletion

NTSTATUS

NtRemoveIoCompletion (

__in HANDLE IoCompletionHandle,

__out PVOID *KeyContext,

__out PVOID *ApcContext,

__out PIO_STATUS_BLOCK IoStatusBlock,

__in_opt PLARGE_INTEGER Timeout

)

如果是IO

ApcContext = Irp->Overlay.AsynchronousParameters.UserApcContext;

KeyContext = (PVOID)Irp->Tail.CompletionKey;

IoStatusBlock = Irp->IoStatus;

6 完成端口跟FileHandle的内部逻辑

当一次IO完成时,会调用IopCompleteRequest,IopCompleteRequest内部有这一处逻辑

if (fileObject && fileObject->CompletionContext) {

port = fileObject->CompletionContext->Port;

key = fileObject->CompletionContext->Key;

}

irp->Tail.CompletionKey = key;

irp->Tail.Overlay.PacketType = IopCompletionPacketIrp;

KeInsertQueue( (PKQUEUE) port,

&irp->Tail.Overlay.ListEntry );

也就是说,每一次IO完成时,都会向完成端口投递相关信息

7  BindIoCompletionCallback

BOOL WINAPI BindIoCompletionCallback(

__in          HANDLE FileHandle,

__in          LPOVERLAPPED_COMPLETION_ROUTINE Function,

ULONG Flags //没用

);

微软内部帮你封装好了IO 完成端口的调用,线程池,在文件操作完成时会调用Function

BindIoCompletionCallback内部实现

Status = RtlSetIoCompletionCallback(FileHandle,

(PIO_APC_ROUTINE)Function,

Flags);

RtlSetIoCompletionCallback内部只是把Function当做一个CompletionKey,调用 NtSetInformationFile(FileHandle, &IoSb, &CompletionInfo, 8, FileCompletionInformation); 把Function和文件句柄进行绑定,下次有IO完成时,线程池里面的线程调用ZwRemoveIoCompletion就可以获得CompletionKey,然后进行调用

VOID CALLBACK FileIOCompletionRoutine(

[in]                 DWORD dwErrorCode,  //

[in]                 DWORD dwNumberOfBytesTransfered,

[in]                 LPOVERLAPPED lpOverlapped //ApcContext = Irp->Overlay.AsynchronousParameters.UserApcContext;

);

那Irp->Overlay.AsynchronousParameters.UserApcContext从哪来的,在NtReadFile, NtWriteFile里面会设置

NTSTATUS

NtReadFile (

__in HANDLE FileHandle,

__in_opt HANDLE Event,

__in_opt PIO_APC_ROUTINE ApcRoutine,

__in_opt PVOID ApcContext,

__out PIO_STATUS_BLOCK IoStatusBlock,

__out_bcount(Length) PVOID Buffer,

__in ULONG Length,

__in_opt PLARGE_INTEGER ByteOffset,

__in_opt PULONG Key

)

irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;

对应的就是ReadFile里面的lpOverlapped

BOOL WINAPI ReadFile(

__in          HANDLE hFile,

__out         LPVOID lpBuffer,

__in          DWORD nNumberOfBytesToRead,

__out         LPDWORD lpNumberOfBytesRead,

__in          LPOVERLAPPED lpOverlapped

);

8 APC和完成端口

如果绑定了完成端口,那APC是不能干活的,在NtReadFile, NtWriteFile里面会这么判断

if (fileObject->CompletionContext && IopApcRoutinePresent( ApcRoutine )) {

ObDereferenceObject( fileObject );

return STATUS_INVALID_PARAMETER;

}

也就是意味着绑定了完成端口以后,你不能使用ReadFileEx传入回调

异步机制 - IO完成端口的更多相关文章

  1. windows核心编程---第九章 同步设备IO与异步设备IO之同步IO

    同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...

  2. 异步设备IO OVERLAPPED结构(设备内核对象 事件内核对象 可提醒IO)

    同步IO是指:线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备驱动程序的通知. 一.异步准备与OVERLAPPED结构 ...

  3. 《Windows核心编程系列》九谈谈同步设备IO与异步设备IO之同步设备IO

    同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...

  4. IO完成端口

    从MSDN中翻译了IO完成端口的文章,不得不说翻译的很烂,英语需要继续提高啊... 在一个多处理器系统上,IO完成端口提供一个非常高效的线程模型来处理多个异步IO请求.当一个进程创建了一个IO完成端口 ...

  5. 【windows核心编程】IO完成端口(IOCP)复制文件小例前简单说明

    1.关于IOCP IOCP即IO完成端口,是一种高伸缩高效率的异步IO方式,一个设备或文件与一个IO完成端口相关联,当文件或设备的异步IO操作完成的时候,去IO完成端口的[完成队列]取一项,根据完成键 ...

  6. 异步设备IO:OVERLAPPED和IOCompletionPort

    异步设备IO:OVERLAPPED和IOCompletionPort 本文内容为<windows核心编程>第10章内容的总结,仅记录一些本人感兴趣的内容. 1:OVERLAPPED &qu ...

  7. Windows核心编程:第10章 同步设备IO与异步设备IO

    Github https://github.com/gongluck/Windows-Core-Program.git //第10章 同步设备IO与异步设备IO.cpp: 定义应用程序的入口点. // ...

  8. 《Windows核心编程系列》十谈谈同步设备IO与异步设备IO之异步IO

    同步设备IO与异步设备IO之异步IO介绍 设备IO与cpu速度甚至是内存访问相比较都是比较慢的,而且更不可预测.虽然如此,通过使用异步设备IO我们仍然能够创造出更高效的程序. 同步IO时,发出IO请求 ...

  9. 什么是阻塞、非阻塞、同步和异步以及IO模型

    首先先看如下几个问题,或者说我们经常会遇到的问题. 阻塞是否等于同步?非阻塞是否等于异步?同步一定是阻塞的么?异步一定是非阻塞的么?要把这四个概念讲明白,先从一顿简餐说起.假设你要做一顿便饭:烧土豆: ...

随机推荐

  1. [NOIP2018模拟赛10.25]瞎搞报告

    闲扯 最近有点颓,都修到好晚,早上起来和吔shi一样难受 忍着困意把题面看完,发现啥也不会,又是一场写暴力的模拟赛 T1发现似乎可以DP,顺手码了个 T2像个最小瓶颈路板子,但是只做过N^2算法的.. ...

  2. js大数计算之计算

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. python2.7.5安装docker-compose的方法

    yum -y install epel-release && yum install -y python-pip && pip install --upgrade pi ...

  4. iOS中的分类(category)和类扩展(extension)

    今天在研究swift的时候看到了分类和扩展.这是两个十分重要有用的功能,但是之前用的不多,没有深入了解过,在今天就从头理一遍. 一.分类(Category): 概念: 分类(Category)是OC中 ...

  5. gitlab中的几个常用界面(runner管理、gitlab-ci.yml管理、runner token管理、新建用户、拉用户入工程、拉用户入组、复制工程导入组)

    目录: 1.runner管理 2.gitlab-ci.yml管理 3.runner token管理 4.新建用户 5.拉用户入工程 6.拉用户入组 7.复制工程导入组 1.runners界面 http ...

  6. nginx__的简单搭建和 wsgi

    4 部署 1 配置 uwsig(配置文件) pip3 install uwsgi 1 mkdir uwsgi 2 cd uwsgi 3 touch test_uwsig.ini [uwsgi] # 指 ...

  7. Image Processing and Analysis_21_Scale Space:Feature Detection with Automatic Scale Selection——1998

    此主要讨论图像处理与分析.虽然计算机视觉部分的有些内容比如特 征提取等也可以归结到图像分析中来,但鉴于它们与计算机视觉的紧密联系,以 及它们的出处,没有把它们纳入到图像处理与分析中来.同样,这里面也有 ...

  8. C# 普通的辅助类

    在数字前面补0 /// <summary> /// 在数字前面添加0 /// </summary> /// <param name="num"> ...

  9. CentOS7使用Qemu模拟ARM64

    准备 RPM包安装 yum安装交叉编译工具 yum install -y binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu bison flex gli ...

  10. 09 Windows编程——键盘消息

    焦点窗口:接收到这个键盘事件的窗口称为有输入焦点的窗口.具有输入焦点的窗口要么是活动窗口,要么是活动窗口的子孙窗口. 活动窗口:活动窗口通常是很好鉴别的.它总是最上层的窗口——也就是说,它的父窗口句柄 ...