- PVSM.RU - https://www.pvsm.ru -
Мой рабочий компьютер — iMac 27″ 5 K 2 017 года: Intel Core i5, 32 ГБ памяти, SSD и macOS Sonoma. Он всё ещё справляется с повседневной работой. Менять его только ради того, чтобы утилиты снова считали его «современным Mac», мне не хотелось.
Зато хотелось понимать, чем заняты CPU, две видеокарты и память, почему греется машина и куда исчезло место на диске. Постепенно для этого набирается целая полка приложений: монитор в меню, управление вентиляторами, клинер, деинсталлятор. Одни требуют подписку, другие продаются отдельно, третьи меняют системные требования. И в какой‑то момент я решил собрать нужные мне вещи в одном нативном приложении.
Так появился MacPulse — бесплатный проект на Swift и SwiftUI под MIT. Сейчас это Universal‑приложение для Intel и Apple Silicon с минимальной системой macOS 13 Ventura. Monterey пока не поддерживается. Основная физическая тестовая машина — тот самый Intel iMac; универсальная сборка не означает, что каждый датчик проверен на каждом Mac.
Сразу расшифрую цифру в заголовке: 0,2% CPU и около 28 МиБ RSS — результат конкретного замера в простое, с закрытым окном и только CPU в монохромной строке меню. С цветным графиком финальная сборка показывала примерно 0,28–0,30% и 32 МиБ. Это не обещание такой нагрузки при открытом дашборде, включённых виджетах или стресс‑тесте. Ниже — методика и ограничения.
Монитор на Intel iMac. Адреса на снимках обезличены до отрисовки; показания загрузки взяты из работающих сервисов.
Температуры на моём Intel Mac читаются через IOKit и драйвер AppleSMC. Сервис открывает соединение, получает информацию о ключе, проверяет тип и размер ответа, затем декодирует значение. Например, sp78 — знаковое число с восемью дробными битами:
let bits = UInt16(bytes[0]) << 8 | UInt16(bytes[1])
let celsius = Double(Int16(bitPattern: bits)) / 256
Все обращения сериализованы внутри actor. Метаданные ключей кэшируются, неудачные обращения получают паузу перед повтором, соединение закрывается явно. В самом сервисе чтения нет команд записи.
Это важно: отсутствующий ключ нельзя превращать в 0 °C, а подозрительное значение — в уверенный датчик на экране. На машине с переходником для диска один из ключей мог выдавать отрицательную температуру. Вместо «диск замёрз до −7 °C» поле скрывается.
AppleSMC — не стабильный документированный контракт на все поколения Mac. Для Apple Silicon я не перенёс Intel‑ключи по принципу «авось сработает»: там доступен безопасный fallback с системным термосостоянием. Числовую температуру, которой реализованный API не отдаёт, приложение не придумывает.
Следующая ловушка была у AMD Radeon Pro. На 5 K‑экране драйвер может держать почти всю видеопамять в выделенном пуле. Если вывести его размер как активное использование, пользователь увидит вечные 99%.
Вместо привязки к Radeon Pro 570 и фиксированным 4 ГБ пришлось разделить несколько задач:
находить устройства по идентификаторам реестра, чтобы не смешивать счётчики Intel и AMD;
получать реальный объём памяти из свойств устройства;
разбирать доступные показатели использования по смыслу, а не складывать все похожие ключи;
оставлять значение недоступным, если надёжного счётчика нет.
Отдельная видеопамять и unified memory — тоже разные сущности. Поэтому Apple Silicon показывает объединённую память без выдуманного «выделенного VRAM». Системная загрузка GPU на этой платформе через реализованный fallback может быть недоступна.
Утилита, которая рисует красивые графики и сама постоянно будит процессор, быстро теряет смысл. У меня оптимизация началась не с Mach API, а с интерфейса.
Первый дашборд использовал LazyVGrid. Карточек было мало, но каждые две секунды менялись подписи, сетка пересчитывалась во время прокрутки. При скрытии недоступных датчиков менялась высота документа, и прокрутка могла прыгнуть вверх.
В итоге я заменил сетку на обычные VStack/HStack с двумя или тремя колонками. Карточки сохраняют идентичность, недоступные блоки схлопываются. Одного схлопывания недостаточно: нативный якорь прокрутки сохраняет допустимое смещение после изменения высоты и перехода между разделами.
Во время живой прокрутки новые показания не публикуются в дашборд на каждом тике: после её завершения применяется последний снимок. История ограничена, форматтеры переиспользуются, маленькое изображение строки меню кэшируется до изменения отображаемых значений.
Второе изменение оказалось ещё полезнее: не опрашивать то, что никто не видит.
let snapshot = await collector.sample(selection: requestedMetrics)
Когда закрыты окно, поповер и виджеты, набор requestedMetrics строится по включённым показателям строки меню. Нужен только CPU — не нужно перечислять процессы, обходить GPU, читать SMC, батарею и SMART. Фоновый интервал в таком режиме — три секунды. Открытый интерфейс использует настроенный интервал 1–3 секунды.
Виджеты запрашивают объединение нужных им датчиков. Их общая история ограничена двадцатью точками; для мини‑графика берутся двенадцать. У каждой панели нет собственного таймера опроса. Цикл безопасности активного управления вентиляторами, напротив, не замедляется ради красивого результата профилирования.
Процент ниже считается относительно одного полностью занятого логического ядра, а не всей мощности четырёхъядерного компьютера. Из замеров исключён прогрев в восемь секунд; CPU считался по приращению пользовательского и системного времени процесса.
|
Конфигурация |
Длительность |
CPU |
RSS в конце |
|---|---|---|---|
|
1.0.0, только CPU в меню, прежний опрос |
30 с |
0,733% |
28,4 МиБ |
|
1.0.1, монохромный текст, опрос 3 с |
60 с |
0,200% |
27,7 МиБ |
|
Финальная 1.0.1, цветной график и текст CPU |
60 с |
0,300% |
32,3 МиБ |
То есть путь был примерно от 0,7–1% к 0,2–0,3% в определённом фоновом режиме. Не «приложение всегда ест 0,2%». Короткий замер также не доказывает отсутствие утечек за неделю. Методика, дополнительные замеры и ограничения лежат в репозитории [1].
В клинере самое сложное — не вызвать FileManager.removeItem, а решить, что действительно можно удалить.
Для браузеров по умолчанию сканируется безопасный кэш внутри ~/Library/Caches. Профили, cookies, пароли и активные сессии в этот режим не входят. Глубокая очистка выделена отдельно и требует подтверждения.
Ручной режим сначала показывает категории, размеры и пути. Удаляются только выбранные объекты, прошедшие проверку разрешённых корней. Проверка учитывает симлинки: удалить саму ссылку внутри разрешённого кэша и пойти за ней в чужую папку — разные операции.
Отдельно пришлось исправлять поиск «хвостов». Папка в Application Support без очевидного одноимённого .app ещё не является мусором. Системные com.apple [2].*, установленные владельцы и запущенные приложения требуют защиты. Совпадения только по названию нельзя автоматически считать доказательством.
Быстрая очистка действительно запускается в один клик: доступные безопасные кэши, пользовательские логи и Корзина. Поэтому здесь есть важный компромисс: очистка Корзины необратима. Для выбора конкретных категорий лучше ручной режим. Кэш открытых программ может пропускаться; нейтральный отчёт объясняет причину вместо красной стены технических ошибок.
Это экран подготовки: ради скриншота удаление не запускалось. Домашние пути не публикуются.
Full Disk Access не требуется для обычного мониторинга. Для части очистки он может понадобиться, но приложение не обходит TCC. Индикатор доступа — эвристика, а не официальный API, который достоверно возвращает статус разрешения для всего диска.
Классический текст, компактные подписи, спарклайны или графики вместе с текстом. Можно выбрать монохром, акцент, цвета категорий или динамическую подсветку. CPU и сеть следуют одной выбранной схеме.
Здесь обнаружилась особенность интеграции SwiftUI с системной строкой меню: соседство графика и текста в предпросмотре не гарантировало, что macOS оставит тексту ширину в настоящем status item. Цветные варианты теперь отрисовываются как одна кэшированная композиция; монохромный режим сохраняет нативный текст.
CPU/GPU, память/диск и сеть включаются независимо. Есть отдельная сводная панель. Размеры — 160×160, 340×160 и 340×340 точек; сводка доступна в двух последних.
В маленьком размере помещаются кольца или скорости, в среднем — графики и точные объёмы, в большом — подробности именно выбранной темы. Панели запоминают координаты, размер и закрепление. Можно менять прозрачность и выбирать рабочий стол либо размещение поверх окон.
Реализация — NSPanel с материалом SwiftUI. Оказалось, что уровня чуть выше обоев не всегда достаточно: Finder может перехватывать клики своей областью иконок. Для интерактивных панелей приходится учитывать и этот уровень. А для меню размеров в неактивирующей панели нужно явно задавать target/action и отключать автоматическую валидацию пунктов.
Реальные панели на фоне обоев, без значков, соседних окон и содержимого рабочего стола.
Деинсталлятор ищет программы в /Applications и ~/Applications, собирает связанные пути по Bundle ID и названию и показывает отдельные чекбоксы. Системные, запущенные приложения и общие данные требуют особого обращения. Полного обнаружения каждого стороннего helper я не обещаю.
Для подписи используются SecStaticCodeCreateWithPath и SecStaticCodeCheckValidity: интерфейс различает Apple / Developer ID, Self‑Signed / Ad‑Hoc, Unsigned, а также недействительную или недоступную подпись. Зеленый бейдж не является отдельной проверкой нотарификации и не означает «программа безопасна».
Дата использования берётся из Spotlight; если её нет, запасная дата модификации помечается как приблизительная. Метаданные не дают разрешения на удаление — это информационная часть интерфейса.
На публичном снимке оставлена ограниченная выборка известных приложений, пользовательские пути заменены на ~/Library/….
На поддерживаемых Intel Mac доступны автоматический режим, минимальные обороты и температурные кривые. Изменения выполняет отдельный привилегированный helper после явного разрешения администратора. Обычное чтение метрик не требует повышения прав.
Контрольная сессия имеет heartbeat; её политика безопасности отделена от интерфейса. Это не отменяет осторожности при выборе кривых. На Apple Silicon управление пока read‑only, на безвентиляторных машинах раздел скрывается.
Кроме текущих скоростей и трафика сессии есть сетевой паспорт, история смен IP, IPv6-проверка и трассировка через /usr/sbin/traceroute. Измерение скорости использует системный /usr/bin/networkQuality; тест передаёт реальные данные и запускается отдельно.
Публичный IP запрашивается только после включения функции. Можно задать HTTPS‑сервис и интервал. Выбранный сервис при этом видит публичный IP пользователя. История остаётся локальной; признаки tunnel‑интерфейса не являются гарантией, что весь трафик действительно проходит через VPN.
Отдельная таблица показывает видимые TCP/UDP‑сокеты процессов. Это наблюдение, а не фаервол: наличие соединения само по себе не доказывает передачу данных.
IP заменён на 192.168.1.100, MAC — на 00:1A:2B:3C:4D:5E. Это подстановки для публикации, не реальные реквизиты сети.
В текущем наборе 122 теста: 116 обычных проходят, шесть аппаратных и сетевых запускаются отдельно. CI проверяет Intel и Apple Silicon. Дополнительные нативные проверки покрывают одиннадцать компоновок виджетов, размеры окон, цвета графиков и сохранение прокрутки.
Это не основание обещать постоянные 60 fps на любом Mac или поддержку всех температурных датчиков. Цель проще: честно показывать доступные данные, не выполнять лишнюю работу и не превращать монитор в ещё одну причину нагрева.
Локализаций двенадцать. Телеметрии и аккаунта нет. Есть явно запускаемые сетевые функции и проверка обновлений через GitHub — это тоже сетевые запросы, а не «приложение вообще никогда не выходит в интернет».
Исходники MacPulse на GitHub [3] · Релиз 1.0.1 и Universal DMG [4]
DMG устанавливается обычным перетаскиванием в «Программы». Для обновления сначала завершите запущенный MacPulse, затем замените приложение. Настройки сохранятся.
Через собственный Homebrew tap проекта:
brew tap makskondrasov9-dev/macpulse https://github.com/makskondrasov9-dev/MacPulse.git
brew install --cask makskondrasov9-dev/macpulse/macpulse
Это не официальный cask из homebrew/cask: формула поддерживается в репозитории проекта.
У релиза пока нет сертификата Developer ID и нотарификации Apple: применяется ad‑hoc подпись. Если macOS блокирует запуск, после проверки источника используйте «Открыть» в контекстном меню либо «Системные настройки → Конфиденциальность и безопасность → Всё равно открыть». Apple описывает этот порядок отдельно [5].
Для тех, кто осознанно выбирает терминал и проверил скачанный пакет, можно убрать extended attributes только у установленного приложения:
xattr -cr /Applications/MacPulse.app
open /Applications/MacPulse.app
Эта команда снимает в том числе метку карантина. Она не заменяет проверку доверия к скачанному файлу и не делает приложение нотарифицированным. Контрольная сумма DMG находится в SHA256SUMS рядом с релизом.
Мне интересно развивать MacPulse как небольшой открытый инструмент для реальных машин, включая старые Intel Mac. Не хочется ни подписки за набор базовых датчиков, ни обещаний «ускорить Mac на 300%» удалением кэша.
Если попробуете проект, полезнее всего отчёт с моделью Mac, версией macOS и описанием недоступного датчика или ошибки. Личные пути, IP и содержимое рабочих файлов в issue прикладывать не нужно. А звезда на GitHub поможет проекту найти свою аудиторию.
Код открыт под MIT. Можно изучать, проверять и предлагать изменения — в том числе спорить с архитектурой. Именно за этим я и принёс его на Хабр.
Автор: max_228_0
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/open-source/460430
Ссылки в тексте:
[1] Методика, дополнительные замеры и ограничения лежат в репозитории: https://github.com/makskondrasov9-dev/MacPulse/blob/v1.0.1/docs/PERFORMANCE.md
[2] com.apple: http://com.apple
[3] Исходники MacPulse на GitHub: https://github.com/makskondrasov9-dev/MacPulse
[4] Релиз 1.0.1 и Universal DMG: https://github.com/makskondrasov9-dev/MacPulse/releases/tag/v1.0.1
[5] Apple описывает этот порядок отдельно: https://support.apple.com/102445
[6] Источник: https://habr.com/ru/articles/1096798/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1096798
Нажмите здесь для печати.