Всем привет!

Предыстория (можете пропустить)

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

Но недавно обнаружил, что «очередное решение несуществующей проблемы» в виде моего личного Android порта Blender 5.3.0 Alpha, ушло в массы и начало жить своей жизнью, а люди на Reddit выкладывают видосы с тем, как они работают на своих Android планшетах. Причём, мой оригинальный пост собрал еле‑еле 3 стрелочки вверх на Reddit, а пост с видосами да и форк моего репозитория получили дикие отклики по меркам их сабреддитов.

Потому решил, таки рассказать, про самописные вещи, которые лежат в опенсорсе и которыми я активно пользуюсь.

Вступление

Гуглил я как‑то опенсорс C++ библиотеку для мониторинга, температуры и нагрузки компонентов ПК. И не нагуглил. Вот такие вот дела. Есть HWiNFO, платно и закрыто. Есть OpenHardwareMonitor — написан C# и библиотека мертва. Есть форк предыдущей библиотеки — LibreHardwareMonitor — живее всех живых, но C#, да ещё и по дефолту требует 10.NET.

Зачем нужен C++, когда можно подцепить DLL из C# напрямую? Ну вот не хочу я таскать.NET определенной версии с собой из бандла в бандл. AOT компиляция вообще не поможет, тут как раз то самое использование специфических вещей, которые AOT не компилит. Finita la comedia! А ещё есть один косяк у последней библиотеки: мака нет в списке поддержки от слова совсем.

Одно кольцо, чтоб править всеми” Windows, Linux, MacOS — x64, arm64. Вот тут я и решил сесть и написать её. В очередной раз. Тем более что меня уволили и времени теперь вагон, можно потрогать все свои пэт проджекты. Тем более что есть референс в виде LibreHardwareMonitor.

Правда то, что он есть, хорошо и плохо одновременно. Нельзя сделать, как у них, потому что «лицензия, детка»! Мне их лицензия не нравится. Надо избавлятся, делать надо не так.

Встречайте HardwareMonitorCpp (hardware_monitor_cpp)!

Пытался я получить температуру процессора без повышения привилегий. Гайки, никак. One Ring0 to rule them all! Ну, на винде и макоси, по крайней мере, Ring0 обязателен, Linux может и без этого.

Остальное работает и без sudo. Что такое остальное? Общая информация о железе: как железка называется, сколько у неё частота, память занятая/свободная, текущая температура и текущая нагрузка. Полноценный мониторинг, одним словом. Понятно, что у процессора нет занятой свободной памяти, а у SSD нет текущей частоты, но есть количество активного времени и IO Bound.

Чем классная библиотека?

Ну, во‑первых, к ней сразу идёт Python сервер, которые все эти данные выдает в сеть! Так же есть консольная приложуха, которая выдаёт эти данные визуально в консоль (со своими собственными настройками, котрые сохраняются между запусками). Писать 100 500 утилит не стал. Я пользуюсь только консольной приложухой на винде и маке. Очень нравится, легким движением руки переключаюсь между нагрузкой и температурой (кнопка «t»), а таймскейл на колонку меняю с 1 секунды на 5 минут (кнопки «‑» и «=»). Очень удобно, особенно, чтобы прикинуть визуально, сколько ещё проекту собираться, и сколько он собирался в прошлый раз. И сколько он выжрал оперативы, например.

А ещё на Windows и MacOS наконец‑то есть внятный график и размер файла подкачки. Я сколько не смотрел в TaskManager и ActivityMonitor, так надо найти 2 числа и дальше математикой заниматься. Не круто! Тут сразу видно, что к чему.

Все написано на C++. Потому что я плюсовик и я могу! Консольное приложение в ходе разработки раздулось с 7 мб оперативы до 30 мб на винде. Да и только потому, что я решил больше снапшотов системы хранить в истории отображения.

Из плюшек библиотеки: можно запросить по всему оборудованию только нагрузку или только температуру.

Из минусов: для Windows нужно отдельно ставить PawnIO для цифер процессора, но это нормально. LibreHardwareMontior тоже драйвер таскает и запускатет от имени администратора, для того же самого. Судьба такая. HWiNFO тоже, вот, не просто так просит поднять привелегии.

Что я делать не стал: мониторить температуру материнки и обороты вентилятора. Мне самой температуры железок хватает, чтобы понять, что к чему. А писать код вслепую для 100 500 вендоров не хочется, хоть и есть куда подглядеть.

Консольное приложение и Python сервер

Вместе с самой библиотекой в комплекте идёт консольное приложение и отдельно сервер на Python, который выдаёт эту информацию в сеть.

Скрины из консольного приложения на MacOS
Скрины из консольного приложения на MacOS
Внешний вид UI питоновского сервера
Внешний вид UI питоновского сервера

Несмотря на то, что они могут не покрыть ваши нужды, приложение и сервер — хорошая точка старта. Потом с помощью молотка и напильника можно получить то, что бы вам хотелось. Ну и да, сборка питоновского сервера сразу оборачивает биндинги библиотеки в питоновское расширение, так что можно будет использовать бинарь и в других местах.

Билдится всё очень просто. Всё, что нужно — установленные ninja и cmake, и чтобы они были доступны в Path. Дальше достаточно запустить один из python скриптов сборки. Их три:

  • python build_lib.py

  • python build_console.py

  • python build_server.py

Сборки самодостаточные. После подтягивания саб модулей ничего дополнительно кроме запуска скрипта сборки делать не нужно. В README.md всё подробно описано.

Если впадлу билдить, уже есть готовые сборки релизов (но там не всё).

Примеры использования кода

Вывод температуры ядер:

#include <iostream>
#include <HardwareMonitorCpp/HardwareMonitor.hpp>

int main() {
    // Инициализация мониторинга
    HardwareMonitor::HardwareManager monitor;
    monitor.Update();

    // Получаем список всех доступных процессоров
    auto cpus = monitor.GetCpuHardware();

    for (const auto& cpu : cpus) {
        std::cout << "Процессор: " << cpu->GetName() << "\n";

        // Выводим только температуру ядер
        for (const auto& sensor : cpu->GetSensors()) {
            if (sensor->GetSensorType() == HardwareMonitor::SensorType::Temperature) {
                std::cout << "  " << sensor->GetName() 
                          << ": " << sensor->GetValue().value_or(0) << " °C\n";
            }
        }
    }

    return 0;
}

Монитор загрузки системных ресурсов:

#include <iostream>
#include <thread>
#include <chrono>
#include <HardwareMonitorCpp/HardwareMonitor.hpp>

void PrintSystemStatus(HardwareMonitor::HardwareManager& monitor) {
    monitor.Update();

    // 1. Загрузка CPU
    for (const auto& cpu : monitor.GetCpuHardware()) {
        for (const auto& sensor : cpu->GetSensors()) {
            if (sensor->GetSensorType() == HardwareMonitor::SensorType::Load &&
                sensor->GetName() == "CPU Total") {
                std::cout << "[CPU] Загрузка: " << sensor->GetValue().value_or(0) << "%\n";
            }
        }
    }

    // 2. Использование GPU и температуры
    for (const auto& gpu : monitor.GetGpuHardware()) {
        std::cout << "[GPU] " << gpu->GetName() << ":\n";
        for (const auto& sensor : gpu->GetSensors()) {
            if (sensor->GetSensorType() == HardwareMonitor::SensorType::Temperature) {
                std::cout << "  - Температура: " << sensor->GetValue().value_or(0) << " °C\n";
            }
            if (sensor->GetSensorType() == HardwareMonitor::SensorType::Load) {
                std::cout << "  - Загрузка ядра: " << sensor->GetValue().value_or(0) << "%\n";
            }
        }
    }
}

int main() {
    HardwareMonitor::HardwareManager monitor;

    // Цикл мониторинга раз в секунду
    for (int i = 0; i < 5; ++i) {
        std::cout << "=== Обновление данных (" << i + 1 << ") ===\n";
        PrintSystemStatus(monitor);
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    return 0;
}

Безопасный опрос датчиков

Иногда датчики у той или иной железки могут быть недоступны. Например у одного SSD: есть мониторинг температуры, у дргого нет.

#include <iostream>
#include <HardwareMonitorCpp/HardwareMonitor.hpp>

// Вспомогательная функция для безопасного получения значения
void PrintSensorValue(const std::shared_ptr<HardwareMonitor::ISensor>& sensor) {
    std::cout << "Датчик: " << sensor->GetName() << " | ";

    if (auto value = sensor->GetValue(); value.has_value()) {
        std::cout << "Значение: " << *value;
    } else {
        std::cout << "Данные недоступны";
    }
    std::cout << "\n";
}

int main() {
    HardwareMonitor::HardwareManager monitor;
    monitor.Update();

    // Получаем оперативную память
    for (const auto& ram : monitor.GetRamHardware()) {
        std::cout << "Модуль RAM: " << ram->GetName() << "\n";
        
        for (const auto& sensor : ram->GetSensors()) {
            PrintSensorValue(sensor);
        }
    }

    return 0;
}

Спасибо за внимание!

Библиотеку можно скачать здесь https://github.com/simfeo/HardwareMonitorCpp или

git clone https://github.com/simfeo/HardwareMonitorCpp.git
cd HardwareMonitorCpp
git submodule update --init --recursive

Как и в прошлой статье про другую мою библиотеку, готов к конструктивной критике, и делать апдейты. После отзывов библиотека, кстати, немного подросла, но серьезно улушилась в синтаксисе использования. Если кому интересно, есть статья на медиум, но пока что на английском, так как большая половина критики прилетела с Reddit.