jjzjj

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

coder 2024-06-05 原文

如果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/

    有关c - 如果WriteFile同步完成,是否发出信号通知事件的更多相关文章

    1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

      给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

    2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

      大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

    3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

      我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

    4. ruby - 检查数组是否在增加 - 2

      这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

    5. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

      如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

    6. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

      我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

    7. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

      我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

    8. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

      我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

    9. ruby - 检查日期是否在过去 7 天内 - 2

      我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/

    10. ruby - 如何验证 IO.copy_stream 是否成功 - 2

      这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

    随机推荐