gpt4 book ai didi

c - 如果WriteFile同步完成,是否发出信号通知事件

转载 作者:可可西里 更新时间:2023-11-01 09:27:37 25 4
gpt4 key购买 nike

如果WriteFile函数同步完成并成功,则是否通过lpOverlapped参数传递了通过事件传递的信号?如果事件同步失败,是否会向事件发出信号?我已经打开了带有FILE_FLAG_OVERLAPPED标志的文件的句柄。我无法从文档中弄清楚这一点,也无法在代码中轻松地复制这种情况。

最佳答案

首先,这个问题不仅与WriteFile有关,而且与任何异步I/O函数有关-几乎所有获得 OVERLAPPED 结构指针的函数。因为为所有这些功能分配了IRP(I/O请求数据包)(在wdm.h中查看它的定义)。 hEvent 中的OVERLAPPED句柄转换为对象指针,并存储在PKEVENT UserEvent;IRP成员中。该事件是在IRP例程中完成IopCompleteRequest时设置(或未设置)的。 IRP完成函数对于所有I/O api都是通用的,因此,规则和规则(当完成触发时)与所有人都相关。不幸的是,这是非常糟糕的记录。 win32层(比较NT层)在此处添加了额外的,非常薄的问题。

基于wrk src code,我们可以看到异步io的I/O Manager触发完成(事件,apc和iocp(互斥)三种)是!NT_ERROR( irp->IoStatus.Status )irp->PendingReturned

如果我们使用 native api,则直接返回NTSTATUS-当(ULONG)status < 0xc0000000时。但是在不清楚的情况下,这里的0x80000000 <= status < 0xc0000000NT_WARNING(status)范围非常有问题-将会设置完成(甚至设置,apc或iocp队列的数据包)。这是因为在分配IRP I/O管理器之前,请进行一些基本检查,并可以从此处返回错误。通常,I/O管理器从NT_ERROR(status)返回错误,该错误意味着不会完成(不会设置事件),但存在且很少异常(exception)。例如对于 ReadDirectoryChangesW (或ZwNotifyChangeDirectoryFile),lpBuffer指针必须是DWORD对齐的(与 FILE_NOTIFY_INFORMATION 完全对齐),否则I/O Manager从STATUS_DATATYPE_MISALIGNMENT范围返回NT_WARNING(0x80000002)。但在这种情况下将不会完成操作(事件集),因为在分配IRP之前,函数失败。在另一种情况下,如果我们在缓冲区不足的情况下调用 FSCTL_FILESYSTEM_GET_STATISTICS ,则文件系统驱动程序(不是I/O Manager)将返回STATUS_BUFFER_OVERFLOW(0x80000005)。但是由于此时IRP已经分配并且代码不在NT_ERROR范围内,因此将被设置为事件。

因此,如果来自I/O管理器的错误(在分配IRP之前)将无法完成。否则,如果函数返回IRP导致驱动程序错误(传递了!NT_ERROR(status))出错,则完成。结果,如果我们得到:

  • NT_SUCCESS(status)(STATUS_PENDING(0x103)是其中的一部分)-将

    完成
  • NT_ERROR(status)将不会完成
  • NT_WARNING(status)-不清楚,取决于I/O管理器的此错误
    (否)或驱动程序(是)

  • 但是使用win32层会使情况变得更糟。因为不清楚如何解释NT_WARNING(status)-大多数win32 api将此解释为错误-返回false并设置最后一个错误(从状态转换)。但是某些API(例如 ReadDirectoryChangesW 将此解释为成功代码)返回true,但未设置上一个错误。结果是,如果我们使用对齐错误的缓冲区(但有效的其他参数)调用 ReadDirectoryChangesW ,则返回-。 true 且未设置任何错误。但是api调用确实失败了。 ZwNotifyChangeDirectoryFile内部在这里返回STATUS_DATATYPE_MISALIGNMENT

    从另一面来看,如果DeviceIoControlFSCTL_FILESYSTEM_GET_STATISTICS失败(返回false),代码为ERROR_MORE_DATA(从STATUS_BUFFER_OVERFLOW转换),则将设置事件(完成)。

    也通过win32错误代码我们无法理解-初始状态是NT_ERROR还是NT_WARNING代码-转换( RtlNtStatusToDosError )状态到win32错误丢失了此信息

    如果我们使用IOCP补全(而非事件)并在文件上设置 NT_WARNING(status) ,则从vista开始的FILE_SKIP_COMPLETION_PORT_ON_SUCCESS范围问题可以解决,在这种情况下,当且仅当返回STATUS_PENDING时,I/O管理器将完成条目排队到端口通过本地api调用。对于win32层,这通常意味着api返回false,最后一个错误是ERROR_IO_PENDING。异常(exception)-WriteFileExReadFileEx,在此处返回true。但是无论如何ReadDirectoryChangesW都无济于事(我假设这是Windows错误)

    也请阅读 FILE_SKIP_SET_EVENT_ON_HANDLE 部分-这是隐式的说明,如果在异步功能的情况下设置了显式事件(重叠),则请求将返回成功代码,或者返回的错误是ERROR_IO_PENDING。但是这里的问题是-成功代码是什么?是否由win32 api返回true?并非总是如此,从FSCTL_FILESYSTEM_GET_STATISTICS可见-ERROR_MORE_DATA(STATUS_BUFFER_OVERFLOW)也是成功代码。或 STATUS_NO_MORE_FILES 返回的NtQueryDirectoryFile以及成功代码-事件(apc或iocp完成)将被设置。但是当NtQueryDirectoryFile对齐错误时,相同的 STATUS_DATATYPE_MISALIGNMENT 可以返回FileInformation-这是失败代码,因为在分配IRP之前从I/O Manager返回

    在大多数情况下,NT_WARNING状态为成功代码(将完成),但是在大多数情况下,win32层将其解释为失败代码(返回false)。

    测试的代码示例:
    ULONG BOOL_TO_ERROR(BOOL fOk)
    {
    return fOk ? NOERROR : GetLastError();
    }

    void CheckEventState(HANDLE hEvent, ULONG err, NTSTATUS status = RtlGetLastNtStatus())
    {
    DbgPrint("error = %u(%x)", err, err ? status : STATUS_SUCCESS);

    switch (WaitForSingleObject(hEvent, 0))
    {
    case WAIT_OBJECT_0:
    DbgPrint("Signaled\n");
    break;
    case WAIT_TIMEOUT:
    DbgPrint("NON signaled\n");
    break;
    default:
    DbgPrint("error=%u\n", GetLastError());
    }
    #if 1
    EVENT_BASIC_INFORMATION ebi;
    if (0 <= ZwQueryEvent(hEvent, EventBasicInformation, &ebi, sizeof(ebi), 0))
    {
    DbgPrint("EventState = %x\n", ebi.EventState);
    }
    #endif
    }

    void demoIoEvent()
    {
    WCHAR sz[MAX_PATH];
    GetSystemDirectoryW(sz, RTL_NUMBER_OF(sz));

    HANDLE hFile = CreateFileW(sz, 0, FILE_SHARE_VALID_FLAGS, 0,
    OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_BACKUP_SEMANTICS, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
    FILESYSTEM_STATISTICS fs;

    OVERLAPPED ov = {};

    if (ov.hEvent = CreateEvent(0, TRUE, FALSE, 0))
    {
    FILE_NOTIFY_INFORMATION fni;
    IO_STATUS_BLOCK iosb;

    // STATUS_DATATYPE_MISALIGNMENT from I/O manager
    // event will be not set
    NTSTATUS status = ZwNotifyChangeDirectoryFile(hFile, ov.hEvent, 0, 0, &iosb,
    (FILE_NOTIFY_INFORMATION*)(1 + (PBYTE)&fni), 1, FILE_NOTIFY_VALID_MASK, FALSE);

    CheckEventState(ov.hEvent, ERROR_NOACCESS, status);

    // windows bug ! ReadDirectoryChangesW return .. true and no last error
    // but really api fail. event will be not set and no notifications
    ULONG err = BOOL_TO_ERROR(ReadDirectoryChangesW(hFile,
    (FILE_NOTIFY_INFORMATION*)(1 + (PBYTE)&fni), 1, 0, FILE_NOTIFY_VALID_MASK, 0, &ov, 0));

    CheckEventState(ov.hEvent, err);

    // fail with ERROR_INSUFFICIENT_BUFFER (STATUS_BUFFER_TOO_SMALL)
    // NT_ERROR(c0000023) - event will be not set
    err = BOOL_TO_ERROR(DeviceIoControl(hFile,
    FSCTL_FILESYSTEM_GET_STATISTICS, 0, 0, 0, 0, 0, &ov));

    CheckEventState(ov.hEvent, err);

    // ERROR_MORE_DATA (STATUS_BUFFER_OVERFLOW)
    // !NT_ERROR(80000005) - event will be set
    // note - win 32 api return false and error != ERROR_IO_PENDING
    err = BOOL_TO_ERROR(DeviceIoControl(hFile,
    FSCTL_FILESYSTEM_GET_STATISTICS, 0, 0, &fs, sizeof(fs), 0, &ov));

    CheckEventState(ov.hEvent, err);

    if (err == ERROR_MORE_DATA)
    {
    SYSTEM_INFO si;
    GetSystemInfo(&si);

    ULONG cb = si.dwNumberOfProcessors * fs.SizeOfCompleteStructure;

    union {
    PVOID pv;
    PBYTE pb;
    PFILESYSTEM_STATISTICS pfs;
    };

    pv = alloca(cb);

    // must be NOERROR(0) here
    // !NT_ERROR(0) - event will be set
    err = BOOL_TO_ERROR(DeviceIoControl(hFile, FSCTL_FILESYSTEM_GET_STATISTICS, 0, 0,
    pv, cb, 0, &ov));

    CheckEventState(ov.hEvent, err);

    if (!err && GetOverlappedResult(hFile, &ov, &cb, FALSE))
    {
    do
    {
    // use pfs here
    } while (pb += fs.SizeOfCompleteStructure, --si.dwNumberOfProcessors);
    }
    }

    CloseHandle(ov.hEvent);
    }
    CloseHandle(hFile);
    }
    }

    并输出:
    error = 998(80000002)NON signaled
    EventState = 0
    error = 0(0)NON signaled
    EventState = 0
    error = 122(c0000023)NON signaled
    EventState = 0
    error = 234(80000005)Signaled
    EventState = 1
    error = 0(0)Signaled
    EventState = 1

    关于c - 如果WriteFile同步完成,是否发出信号通知事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49959547/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com