调用 QTcpSocket::setSocketDescriptor() 时崩溃

Crashing when calling QTcpSocket::setSocketDescriptor()

我的项目使用 QTcpSocket 和函数 setSocketDescriptor()。代码很正常

QTcpSocket *socket = new QTcpSocket();
socket->setSocketDescriptor(this->m_socketDescriptor);

此编码大部分时间都运行良好,直到我 运行 在 Windows Server 2016 上进行性能测试时,崩溃发生了。我使用故障转储进行调试,这是日志

0000004f`ad1ff4e0 : ucrtbase!abort+0x4e
00000000`6ed19790 : Qt5Core!qt_logging_to_console+0x15a
000001b7`79015508 : Qt5Core!QMessageLogger::fatal+0x6d
0000004f`ad1ff0f0 : Qt5Core!QEventDispatcherWin32::installMessageHook+0xc0
00000000`00000000 : Qt5Core!QEventDispatcherWin32::createInternalHwnd+0xf3
000001b7`785b0000 : Qt5Core!QEventDispatcherWin32::registerSocketNotifier+0x13e
000001b7`7ad57580 : Qt5Core!QSocketNotifier::QSocketNotifier+0xf9
00000000`00000001 : Qt5Network!QLocalSocket::socketDescriptor+0x4cf7
00000000`00000000 : Qt5Network!QAbstractSocket::setSocketDescriptor+0x256

在 stderr 日志中,我看到了那些日志

CreateWindow() for QEventDispatcherWin32 internal window failed (Not enough storage is available to process this command.)
Qt: INTERNAL ERROR: failed to install GetMessage hook: 8, Not enough storage is available to process this command.

这是函数,代码在 Qt 代码库中停止

void QEventDispatcherWin32::installMessageHook()
{
    Q_D(QEventDispatcherWin32);

    if (d->getMessageHook)
        return;

    // setup GetMessage hook needed to drive our posted events
    d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());
    if (Q_UNLIKELY(!d->getMessageHook)) {
        int errorCode = GetLastError();
        qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %s",
               errorCode, qPrintable(qt_error_string(errorCode)));
    }
}

我做了研究,错误 Not enough storage is available to process this command. 可能是 OS (Windows) 没有足够的资源来处理这个函数 (SetWindowsHookEx) 并且创建失败一个钩子,然后 Qt 发出一个致命信号,最后我的应用程序被杀死了。 我在 Windows Server 2019 上对此进行了测试,该应用程序运行良好,没有出现崩溃。 我只是想更多地了解错误消息 (stderr) 的含义,因为我真的不知道什么是“存储空间不足”?我认为这可能是 Windows Server 2016 的限制或错误?如果是,是否有任何方法可以解决 Windows Server 2016 上的这个问题?

当注册表值设置不正确或在最近重置或重新安装后,配置设置不正确时,Windows 服务器中通常会出现“没有足够的存储空间来处理此命令”错误。

以下是针对此问题的验证程序:

  1. 单击“开始”> 运行 > regedit 并按 Enter
  2. 找到这个键名HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\LanmanServer\Parameters
  3. 找到 IRPStackSize
  4. 如果此值不存在,请右键单击“参数”键并单击“新建”>“双字值”,然后在名称下键入 IRPStackSize。
  5. 值的名称必须与我上面的完全相同(大小写字母的组合)。
  6. 右键单击 IRPStackSize,然后单击“修改”
  7. Select Decimal 输入大于 15 的值(最大值为 50 decimal)并单击 Ok
  8. 您可以关闭注册表编辑器并重新启动计算机。

Reference

经过几天的研究,我终于可以配置 Windows Server 2016 设置(注册表)来防止崩溃。 所以基本上它是 OS 本身的限制,它被称为 desktop heap limitation. https://docs.microsoft.com/en-us/troubleshoot/windows-server/performance/desktop-heap-limitation-out-of-memory

(有趣的是错误信息是 Not enough storage is available to process this command 但真正的问题是 desktop heap limitation.

所以对于解决方案,请按照 link 中的步骤进行:https://docs.microsoft.com/en-us/troubleshoot/system-center/orchestrator/increase-maximum-number-concurrent-policy-instances

我将 SharedSection 的第三个参数增加到 2048 并解决了这个问题。

步骤总结:

non-interactive 桌面的桌面堆由以下注册表值的 SharedSection= 段的第三个参数标识:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SubSystems\Windows

此注册表值的默认数据如下所示:

%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16

要输入到 SharedSection= 段的第三个参数 的值应基于以下计算:

(所需的并发策略数)* 10 =(第三个参数值)

示例:如果希望有 200 个并发策略实例,则 200 * 10 = 2000,四舍五入到一个合适的内存数字为您提供 2048 作为第三个参数,导致对注册表值进行以下更新:

SharedSection=1024,3072,2048