Почему процесс 1cv8.exe грузит процессор на 100% в RDP и как решить проблему?

Системный администратор 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При работе пользователей в терминальном режиме (RDP) нередко возникает ситуация, когда клиентский процесс 1cv8.exe внезапно начинает непрерывно потреблять 100% одного процессорного ядра (или эквивалентную долю от общей мощности многоядерного сервера). При этом интерфейс программы может продолжать откликаться, либо окно зависает, а закрытие всех вкладок внутри сеанса не снимает аномальную нагрузку. Разберем основные причины такого поведения, способы точной диагностики и готовые сценарии автоматического реагирования.

Основные причины зацикливания и высокой нагрузки процессора

Выясним, какие факторы приводят к непрерывной загрузке процессора потоком клиентского приложения 1С:

  1. Внешние компоненты и драйверы торгового оборудования: Использование устаревших библиотек (например, модулей торгового оборудования, драйверов сканеров, фискальных регистраторов или общих библиотек наподобие rtl70.bpl) способно вызывать бесконечный цикл ожидания событий в фоновом потоке клиентского приложения.
  2. Некорректная работа таймеров и обработчиков ожидания: Использование метода ПодключитьОбработчикОжидания (или ОбработкаОжидания) с минимальным интервалом вызова для выполнения тяжелых алгоритмов, проверки оповещений или фонового обновления данных.
  3. Отрисовка динамических списков и табличных полей: В обычных формах размещение ресурсоемких вычислений, запросов или обращений к базе данных внутри событий ПриВыводеСтроки и ПриПолученииДанных приводит к лавинообразному потреблению ресурсов процессора при автообновлении или прокрутке.
  4. Сбои в подсистеме печати через Easy Print: Несовместимость драйверов перенаправленных локальных принтеров в сессии RDP способна зацикливать опрос очереди печати в 32-разрядном процессе 1С.
  5. Утечка системных дескрипторов (GDI / USER Objects): Превышение лимита дескрипторов графического интерфейса при частом создании динамических элементов интерфейса нарушает работу системного цикла сообщений Windows (Message Pump).

Диагностика зависшего процесса

Для выявления конкретной процедуры или сторонней библиотеки, вызвавшей зависание, выполним следующие шаги:

  1. Анализ стека вызовов активных потоков: При помощи системных утилит мониторинга процессов открываем свойства зависшего экземпляра 1cv8.exe, переходим на вкладку потоков и определяем поток, потребляющий процессорное время. Просмотр стека вызовов (Call Stack) покажет, в какой именно библиотеке (библиотека платформы, драйвер оборудования или модуль печати) выполняется непрерывный цикл.
  2. Снятие дампа памяти: Через диспетчер задач создаем дамп памяти зависшего процесса для последующего анализа стека в отладчике.
  3. Настройка технологического журнала: Размещаем файл logcfg.xml в папке conf клиентской платформы на терминальном сервере, настроив сбор событий CALL и EXCP с фильтром по длительности для фиксации последней выполненной клиентской операции.

Автоматический мониторинг и перезапуск зависших сессий через PowerShell

Если проблему вызывает внешняя компонента или периодический сбой, который сложно исправить мгновенно, организуем автоматический контроль за сессиями пользователей на терминальном сервере. Реализуем сценарий, который рассчитывает нагрузку на ядро процессора каждым процессом 1cv8.exe, предупреждает пользователя и при отсутствии реакции завершает зависший процесс.

Создадим основной скрипт 1cControl.ps1:


# 1cControl.ps1 - Мониторинг зависших процессов 1C
$core = Get-WmiObject -Class Win32_ComputerSystem | Select-Object NumberOfLogicalProcessors
$logging = $true
$screen = $false
$procname = "1cv8"
$logfile = ".\1cControl.log"
$ExecuteTimer = 60 # Время задержки перед принудительным завершением (в секундах)
$CollectTimer = 20 # Время замера интервала загрузки процессора (в секундах)

$data_start = Get-Date
if ($logging -eq $true) {
    $data_start | Out-File -FilePath $logfile -Append
    "Скрипт мониторинга запущен." | Out-File -FilePath $logfile -Append
}

# Получение списка владельцев процессов
$owners = @{}
Get-WmiObject Win32_Process -Filter "name='$procname.exe'" | ForEach-Object {
    $owners[$_.handle] = $_.GetOwner().User
}

# Первый замер процессорного времени
$data_start = Get-Date
[System.Collections.ArrayList]$process_start = @(Get-Process $procname | Select-Object TotalProcessorTime, Id, PrivateMemorySize, @{Name="Owner"; Expression={$owners[$_.Id.ToString()]}})

# Интервал ожидания для оценки динамики
Start-Sleep -Seconds $CollectTimer

# Второй замер процессорного времени
$data_stop = Get-Date
[System.Collections.ArrayList]$process_stop = @(Get-Process $procname | Select-Object TotalProcessorTime, Id, PrivateMemorySize)

$Data_delta = $data_stop.Ticks - $data_start.Ticks
$msg = "Ваш сеанс 1С перегружает процессор. Пожалуйста, сохраните результаты работы и перезапустите программу, иначе сеанс будет завершен через 1 минуту."
$sign = "Автоматический мониторинг производительности."

$lstop = $process_stop.Count
for ($i = 0; $i -lt $lstop; $i++) {
    $proc = $process_stop[$i]
    $lstart = $process_start.Count
    for ($j = 0; $j -lt $lstart; $j++) {
        $procs = $process_start[$j]
        if ($proc.Id -eq $procs.Id) {
            if ($proc.PrivateMemorySize -eq $procs.PrivateMemorySize) {
                $CPU_delta = $proc.TotalProcessorTime.Ticks - $procs.TotalProcessorTime.Ticks
                if ($Data_delta -gt 0 -and $CPU_delta -gt 0) {
                    # Расчет процента загрузки относительно общего числа ядер
                    $CPU_Load = ($CPU_delta * 100) / ($Data_delta * $core.NumberOfLogicalProcessors)
                    
                    # Проверка загрузки эквивалента одного полного ядра
                    if ($CPU_Load -gt ((100 / $core.NumberOfLogicalProcessors) - 0.5)) {
                        c:\windows\system32\msg.exe $procs.Owner $msg $sign
                        $id = $procs.Id
                        $powershellArguments = "-file .\killWithTimer.ps1", $id, $ExecuteTimer
                        Start-Process powershell.exe -ArgumentList $powershellArguments
                        
                        if ($logging -eq $true) {
                            "Запущен таймер завершения для процесса пользователя " + $procs.Owner | Out-File -FilePath $logfile -Append
                        }
                    }
                }
            }
            $process_start.RemoveRange($j, 1)
            break
        }
    }
}

Рядом разместим вспомогательный скрипт killWithTimer.ps1, выполняющий завершение процесса после истечения заданного интервала ожидания:


# killWithTimer.ps1
param(
    [int]$ProcessId = $args[0],
    [int]$DelaySeconds = $args[1]
)

Start-Sleep -Seconds $DelaySeconds
Stop-Process -Id $ProcessId -Force

Рекомендации по устранению причин сбоя

  1. Ревизия обработчиков событий: Проанализируем конфигурацию на наличие процедур ПриВыводеСтроки в обычных формах и исключим из них обращение к тяжелым функциям и серверным контекстам.
  2. Отключение неиспользуемых обработчиков ожидания: Убедимся, что при закрытии экранных форм вызывается процедура ОтключитьОбработчикОжидания для всех ранее запущенных таймеров.
  3. Обновление и выгрузка внешних драйверов: Переведем работу с внешними компонентами в изолированные фоновые процессы либо обеспечим гарантированную выгрузку библиотек оборудования при завершении торговой операции.
  4. Настройка политик RDP: Если проблема связана с подсистемой печати, настроим изоляцию драйверов печати на стороне сервера и отключим автоперенаправление неиспользуемых локальных портов.
← На главную