Как стать автором
Обновить

Дорожная карта навыков разработчика на C++

Уровень сложностиСредний
Время на прочтение16 мин
Количество просмотров56K

Джеймс Гослинг как‑то сказал, что Java — это C++, из которого убрали все пистолеты, ножи и дубинки, однако практика показывает, что «ножи и дубинки» становятся классным инструментом в руках опытных разработчиков. В общем, немалая часть проклятий в адрес C++ объясняется элементарным «вы просто не умеете его готовить». Мы в «Лаборатории Касперского» умеем готовить «плюсы» и поэтому любим их. C++ — низкоуровневый язык, который позволяет работать с железом и писать быстрый код и при этом содержит массу возможностей. В экосистеме «плюсов» куча проработанных паттернов, best practices и готовых библиотек под разные задачи. Язык динамично развивается — но сохраняет обратную совместимость.

В этой статье мы с помощью карты покажем, какие навыки и знания нужны разработчику на C++. Естественно, разбирать путь развития «плюсистов» будем на собственном примере — тем более что у нас в «Лаборатории Касперского» много очень разных проектов с отличающимися задачами. Однако наша карта по большей части универсальна и будет полезна всем, кто хочет развиваться в разработке на C++.


В этой карте мы разбили по блокам навыки, которые считаем необходимыми для реальной коммерческой разработки на C++, и показали, для каких задач и продуктов подходят их разные сочетания. За точку отсчёта приняли отличное знание C++: когда разработчик знает нюансы и тонкости языка и понимает, как он работает. Этот опыт формируется за 3–4 года работы с C++ на реальном проекте.

Что будет в статье

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

Что входит в эту схему

Общие знания и навыки

Какое бы направление работы ни выбрал разработчик на C++, всегда будет универсальный набор знаний и навыков, без которых не обойтись. Каждый «плюсист» должен знать основы языка и Computer Science, писать хороший код, грамотно работать с многопоточкой и иметь базовый набор софт‑скиллов для комфортного взаимодействия с командой.

Отличное знание C++ 

Это как раз об универсальной основе. По нашему опыту, многие разработчики пишут на C++ так, как будто это условный C#, — то есть совсем не погружаясь в тонкости языка и не зная, как он работает «под капотом». C++ — это язык для низкоуровневых задач, который выбирают за возможность быть ближе к железу и оптимизировать скорость выполнения кода. А значит, доскональное понимание того, как на самом деле работает язык, на чём он основан, как исполняется код, совершенно необходимо. 

Это подразумевает, что разработчик знает и понимает:

  • как устроены и чем отличаются структуры данных, какие проблемы они решают и какую из них стоит использовать в той или иной задаче; 

  • как внутри устроены стандартные контейнеры std::vector, std::string, std::list, std::map;

  • как работают исключения, какие у них есть преимущества и недостатки;

  • современные стандарты C++ — 17 и 20. Необходимо не только знать их, но и уметь обоснованно применять на практике нововведения языка. То есть там, где это действительно необходимо и помогает сделать код лучше; 

  • разницу между design-, compile- и runtime — а также не боится шаблонов.

Конечно, все эти навыки отслеживаются не каким-то тестом с галочками и вариантами ответов. Мы практикуем формат открытого общения — по ходу беседы глубина понимания всех необходимых областей знания быстро становится очевидна. Конечно, всегда есть шанс кого-то обмануть, даже интервьюера-эксперта, но, как правило, когда общаешься с кандидатом один на один, ты все равно чувствуешь, поверхностные у него знания или глубокие.

Алексей Тотмаков

(@atotmakov) Head of Core Technologies and B2B Development, «Лаборатория Касперского»

Умение писать код

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

Вот какие моменты мы стараемся проверить на интервью:

  • Умение читать чужой код, находить ошибки в коде и дизайне. Каждый разработчик больше времени тратит на чтение чужого кода, а не на создание своего. Если разработчик не умеет отлавливать ошибки в коде и архитектуре, процесс код-ревью будет неэффективным, а готовый продукт быстро потребует серьёзного рефакторинга.

  • Соответствие принципам KISS. Хороший код — простой код. Излишнее усложнение ведёт к проблемам поддержки кодовой базы. Да и на код-ревью разбирать такой код и отлавливать ошибки будет гораздо сложнее. Что опять же приведёт к необходимости скорого рефакторинга и багам в продукте. 

  • Умение упрощать и декомпозировать сложные задачи. Проще изготовить 500 стандартных кирпичей и сложить из них стену, чем произвести один большой пятисоткратный кирпич. То же и с программированием: сложные недекомпозированные задачи влекут за собой сложный некрасивый код и неудачные архитектурные решения.

  • Умение разбивать код на сущности. Собственно, стандартное требование ООП: функции и классы не должны решать посторонних задач (Single-responsibility principle), но и не должны быть слишком «специализированными». Обе ошибки усложняют чтение и понимание кода, делают его плохо поддерживаемым и потенциально забагованным.

  • Умение понятно именовать сущности. В идеале по названию функции должно быть понятно, что она делает (даже без комментариев), по названию класса — какое место в иерархии он занимает, какие свойства и методы объекта может описывать, по названию переменной — что в ней хранится и для чего она вообще нужна.

Работа с многопоточными приложениями

Самое важное в работе с многопоточкой — это внимательность и умение смотреть на задачу в целом, а не на отдельные её блоки. Необходимо чётко понимать, какие операции в коде стоит защищать, а какие — необязательно (область действия «лочки» должна быть минимально возможной). Пригодится и знание сложных примитивов синхронизации — однако применяя их, особенно важно помнить о принципе KISS.

Опыт коммерческой разработки

Увы, без этого никак не отработать важные для профессионального программиста навыки: отлавливание ошибок в коде, работа итерациями, понимание требований к продукту, умение поддерживать продукт после релиза.

С опытом коммерческой разработки всё может быть довольно гибко — тут многое зависит от кандидата. Да, в среднем понадобится опыт от двух лет. Однако бывает, что на собеседование приходят люди с двадцатилетним стажем и не подходят нам, а бывает, что приходит стажёр-студент старших курсов — и к окончанию института уже становится крепким мидлом.

Алексей Тотмаков

(@atotmakov), Head of Core Technologies and B2B Development, «Лаборатория Касперского»

Универсальные требования

В этот раздел мы включили набор универсальных требований, которые не относятся к C++, но важны для работы в любой нашей команде:

  • знание базовых алгоритмов и структур;

  • хороший технический английский;

  • софт-скиллы. Тут достаточно просто «взрослого» подхода к работе: предупреждать заранее, если что-то не получается сделать в оговорённый срок, не замалчивать личные и рабочие проблемы и т. п. 

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

Знания и навыки разработки под разные операционные системы

Разница между операционками на уровне прикладного программиста — не очень большая. А знания, полученные на одной платформе, легко конвертируются в другую: например, у нас есть опыт, когда разработчики, писавшие драйверы и низкоуровневые компоненты под Windows, безболезненно переходили на Mac-направление. 

С библиотеками примерно то же самое — например, знание специфических Win-библиотек типа MFC, ATL, WinSock вряд ли понадобится. Чаще всего мы используем универсальные библиотеки, подходящие для разных операционок (например, STL или Boost), а задачи у прикладных программистов универсальные: многопоточность, межпроцессное взаимодействие.

Windows

С WinAPI и пониманием Windows ситуация неоднозначная: в некоторых командах достаточно знаний на уровне продвинутого пользователя операционки, в других — понадобится глубокое знание WinAPI, а командах, которые разрабатывают драйверы и исследуют вредоносное ПО, надо понимать устройство Windows в мельчайших деталях. Но независимо от команды точно понадобится умение работать с WinDbg и PerfLog.

Чтобы лучше изучить подкапотное устройство Windows и то, как писать драйверы под Win-системы, я бы порекомендовал, классику жанра — «Внутреннее устройство Microsoft Windows. Основные подсистемы ОС» Алекса Ионеску и Дэвида Соломона. Что касается WinAPI, то тут я не могу посоветовать чего-то конкретного — нет какой-то книги, которая отвечает на все вопросы. Однако есть ряд ресурсов, которые очень пригодятся: например, специализированные форумы на msdn, онлайн «Книгу внутренних компонентов Windows» и поизучать раздел Sysinternals на сайте Microsoft.

Алексей Тотмаков

(@atotmakov), Head of Core Technologies and B2B Development, «Лаборатория Касперского»

В каких продуктах нужны эти знания и навыки:

Linux

Linux — стандарт для серверов, да и на десктопах он встречается часто. Поэтому у нас есть продукты и для этой операционки. Кстати, мы делаем его под разные аппаратные платформы, в том числе x86, ARM и «Эльбрус» (а разработка под разные аппаратные платформы — крутой опыт любого «плюсовика»).

C Linux связаны логичные задачи: работа с бизнес-логикой продукта, создание новой и поддержание уже существующей функциональности (кстати, какого-то древнего легаси у нас нет, всё современно и приятно), сертификация продукта в госорганах, анализ различных инцидентов (надо разбирать дампы памяти и логи продукта, мониторить производительность и стабильность ПО).

В каких продуктах нужны эти знания и навыки:

macOS

Ситуация с разработкой под macOS похожа на разработку под Linux и Windows: хотя обычно программирование под macOS подразумевает использование нативных Objective-C и Swift, в реальности нам никуда не деться от от C++ — всё-таки это основной язык для наших продуктов и переписывать логику продукта на Swift просто потому, что это нативно, конечно же, никто не станет. 

То есть разработчикам всё равно придётся использовать внешний код на C++, чтобы не раздувать кодовые базы и переиспользовать готовые кросс-платформенные решения. К тому же на плюсах написаны многие готовые библиотеки, предоставляемые сообществом или сторонними вендорами. 

Именно поэтому бизнес-логику, системные перехват и интеграцию выгодно писать на C++, а нативный стек Apple использовать только там, где без него не обойтись: пользовательский интерфейс, интеграция с системными интерфейсами. Есть и ещё одна особенность: у Swift отсутствует интероп с C++, а значит, не обойтись и без старичка Objective-C.

В каких продуктах нужны эти знания и навыки:

Работа с железом

Про KasperskyOS мы недавно писали на Хабре. ОС мы разрабатываем с нуля и сейчас активно расширяем её и портируем на разные платформы, поэтому ищем людей, которые могут создавать с нуля драйвера и сервисы. C++ используется для разработки драйверов для разных платформ и продуктов, которые будут работать на KasperskyOS.

Здесь можно поучаствовать в разработке ядра KasperskyOS. 


Какие продукты пилим

Общие компоненты защиты для всех пользовательских продуктов

Это ядро всей защиты продуктовой линейки «Лаборатории Касперского». За этим направлением стоит CoreTech — R&D-направление, которое выстраивает фундамент для всех продуктов компании. Внутри CoreTech — несколько команд, каждая со своими задачами. Объединяет их то, что они пишут кросс-платформенный код, который переиспользуется в продуктах под все операционные системы: 

  1. Traffic processing. Команда анализирует трафик на машине конечного пользователя, предоставляет удобные интерфейсы для работы с ним, и разрабатывает компоненты защиты на базе этих интерфейсов. Результат работы команды: анализаторы сетевых протоколов (SSL, HTTP/1.1, HTTP/2, HTTP/3, QUIC, WebSocket, IMAP, POP3 и т. п.), веб-антивирус, антиспам, почтовый антивирус, родительский контроль, защита от фишинга.

  2. Crypto. Команда пишет решения для криптографической защиты информации, которые работают в различных средах выполнения: пользовательский режим ОС, режим ядра, Preboot. Разработчики анализируют подписи файлов различных форматов, создают собственный стандарт подписи на основе ГОСТов, пишут крипто-подсистему для KasperskyOS, занимаются сертификацией и реализуют стандарты PKCS11, PKI, FIPS.

  3. Instrumental PDK. Команда разрабатывает библиотеки C++ общего характера, в том числе платформенно-зависимые. Например, внутреннюю библиотеку стандартных примитивов, собственные фреймворки, которые связывают компоненты во всех продуктах и универсальные компоненты для обновления антивирусных баз.

Подробности о продукте и команде — по ссылке.

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки.

Консоль безопасности

Основной продукт — Kaspersky Security Center. Это консоль для удобного управления безопасностью на уровне предприятия, то есть такой аналог ЦУПа для сложных IT-систем. Security Center охватывает все платформы: облачные, физические, виртуальные машины и мобильные устройства. Основные задачи:

  • разработка транспортной подсистемы;

  • разработка подсистемы управления патчами и обновлениями.

Подробности о продукте и команде — по ссылке.

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки.

Продукты и драйверы под Windows

Основное и самое большое направление. Это и общая для всех продуктов кодовая база, и драйверы под Windows, и виндовые продукты вроде Kaspersky Endpoint Security и Kaspersky Anti-Virus (кстати, это разные решения: первое предназначено для бизнеса, а второе — для личного использования). Windows по-прежнему остаётся платформой, на которую так или иначе равняются решения под остальные ОС: это основа всех приложений и идей, локомотив продуктовой линейки Kaspersky. Разработчикам приходится постоянно разбирать системные и продуктовые дампы, писать User-Mode-компоненты, драйверы, бизнес-логику, новые продуктовые фичи.

Откликнуться на вакансии можно по ссылкам:

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки, Windows

Продукты и драйверы для Linux

Под Linux у нас сразу несколько продуктов. Основной — это Kaspersky Endpoint Security (KES) для Linux, рассчитанный на рабочие станции и серверы в малом и среднем бизнесе. Внутри продукта есть несколько команд разработки, которые пишут как сам Linux-клиент, опираясь на наработки и кросс-платформенную кодовую базу основного продукта, так и драйверы под Linux. Причём приложение работает на разных архитектурах: x86, ARM и «Эльбрус».

Другой — Kaspersky Secure Mail Gateway для Linux. Он дистанционно управляет IT-инфраструктурой и защищает серверы электронной почты от угроз: спама, фишинга, вредоносных вложений. KSMG имеет кластерную архитектуру для удобства масштабирования и централизованного управления всеми серверами кластера через веб-интерфейс. А также значительно большее, чем у коллег, количество фичей в продукте. Из особенностей работы над KSMG — необходимость кодить на Python, обеспечение совместимости продукта с различными почтовыми серверами (Postfix, Exim, Sendmail), встраивание open-source-программ в комплексное решение на базе Linux, обеспечение совместимости продукта с различными дистрибутивами Linux.

Какие ещё задачи решают команды:

  • разработка новой логики приложения (да, у приложений под Linux есть свои уникальные фичи — а не только переиспользование общей кодовой базы);

  • написание автотестов и unit-тестов для нового функционала;

  • поддержка выпущенных версий продукта;

  • анализ дампов системы и самого KES, работа над стабильностью и производительностью продукта;

  • мониторинг и визуализация состояния сети.

Откликнуться на вакансии можно по ссылкам:

  • разработка системы автоматизированной защиты рабочих станций;

  • разработка комплексной системы защиты почтовых серверов;

  • разработка системы мониторинга и визуализации состояния сети.

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки, Linux

Продукты и драйверы для macOS

Основной продукт — Kaspersky Endpoint Security для Mac. Задачи — как и у команд клиента и драйверов под Linux, только работа идёт исключительно с платформой Mac. Хотя тут есть и разные архитектуры процессоров (и x86, и пару лет назад завезённый в десктопы и ноутбуки ARM). Как и в линуксовых командах, команда разработки под Mac добавляют в продукт свои уникальные фичи, разбирают и анализируют дампы системы и KES, пишут тесты и поддерживают уже выпущенные версии ПО.

Подробности о продукте и команде — по ссылке

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки, macOS

Ядро KasperskyOS

В этом проекте разработчики создают собственную защищённую микроядерную операционную систему KasperskyOS (не очередной форк Linux, а ОС с собственным ядром), да ещё и под разные платформы. Она используется в IoT, VDI, транспорте, корпоративных мобильных устройствах. Тут уже понадобится не только C++, но и Си с Ассемблером для следующих задач:

  • прототипирование решений;

  • написание отдельной, real-time-версии системы;

  • разработка и имплементация компонент для промышленной разработки ПО;

  • подготовка набора тестов для верификации разработанных решений;

  • разработка и ревью архитектуры;

  • подготовка набора тестов (юнит, фаззинг) для верификации разработанных решений;

  • анализ и улучшение эффективности, стабильности, безопасности и масштабируемости разрабатываемых решений.

Откликнуться на вакансии можно по ссылкам:

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки, Работа с железом

Драйверы для KasperskyOS

Разработка драйверов для KasperskyOS под разные устройства — самостоятельное направление в рамках разработки операционной системы. Это продуктовое направление делает так, чтобы KasperskyOS работала на самом разном железе, а также занимается системными сервисами и библиотеками. 

Здесь от программистов требуется умение разрабатывать ПО под разные аппаратные платформы, что тянет за собой знание не только C++, но и более низкоуровневых C и Ассемблера, деталей аппаратной реализации различных компонентов современных вычислительных систем (видеокарты, процессоры, оперативная память и т. п.). 

Подробности о продукте и команде — по ссылке.

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки

Продукты для KasperskyOS

ПО под KasperskyOS и SDK для партнёров, которое позволяет создавать новые собственные продукты под эту операционку или интегрировать уже существующие решения. Задача того же уровня сложности, которую в своё время решали Apple и Google, разрабатывая для своих мобильных платформ как ПО, так и SDK для сторонних разработчиков. Только у KasperskyOS больше платформ, версий и типов устройств.

Собственное ПО пишется для разных направлений: 

  • встроенная безопасность (in-chip security), 

  • мобильные устройства, 

  • тонкие клиенты, 

  • интернет вещей и промышленный интернет вещей (Connected Industry, умные города),

  • электроэнергетика.

Откликнуться на вакансии можно по ссылкам:

  • разработка безопасного шлюза для электронных блоков автомобиля;

  • разработка безопасной платформы для IoT и embedded-устройств с поддержкой облачных платформ;

  • разработка мобильной версии KasperskyOS для разных устройств.

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки

Система контентной фильтрации

Это направление покрывает все задачи, связанные с безопасностью в текстах, — защита от спам-писем, переходов по фишинговым ссылкам, родительский контроль. Для этого приходится детектировать аномалии в поведении пользователя, искать shadow-it-сервисы, плотно заниматься защитой персональных данных пользователя. Всё это портируется на Win, Linux, BSD. 

Компоненты CFR в основном реализованы на современных стандартах языка C++. Сборка компонент происходит в распараллеленной среде под самые различные win*/*nix платформы и архитектуры процессоров. Они интегрируются в продукт в виде плагинов, которые можно обновлять «на горячую» без обновления самого продукта (кстати, эти плагины как раз основаны на тех самых «универсальных компонентах для обновления антивирусных баз», которые входят в блок «Общие компоненты защиты», универсальные для всех платформ). Бизнес-логика, ответственная за детектирования, реализована в виде набора скриптов и обновляется тоже отдельно и независимо от самих компонент. Задачи разнообразные:

  • работа над перформансом (скорость/потребление ram и т. д.);

  • применение эффективных алгоритмов поиска сигнатур;

  • поддержка скриптов бизнес-логики, интерфейсов между движком и скриптами;

  • интеграция в продукты на различных ОС (Win, Linux, BSD);

  • сетевое low-level-взаимодействие;

  • парсинг различных форматов (MIME, HTML, RichText, TNEF и т. д.).

Подробности о продукте и команде — по ссылке.

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки, Windows, Linux

Модуль эвристического анализа

Главный продукт — System Watcher. Это компонент проактивной защиты, который на лету определяет трояны, эксплойты и шифровальщики по шаблонам поведения, а также умеет восстанавливать пользовательские файлы и реестр после заражения. У System Watcher есть версии для Windows, Linux и macOS. Помимо разработки новых версий модуля, разработчики занимаются и поддержкой уже выпущенных версий.

Подробности о продукте и команде — по ссылке.

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки, Windows/Linux/macOS

Компонент безопасности для виртуальных сред

Легкий агент — это решение для защиты облаков и виртуальных сред под платформы VMware vSphere, Microsoft Hyper-V, Citrix XenServer и KVM и интерфейс для более удобного управления компонентами защиты. Разработчики создают продукт, пишут новые фичи и поддерживают существующий код на C++. Ещё одно большое направление — разработка базовых кроссплатформенных компонентов защиты под различные типы ОС.

Подробности о продукте и команде — по ссылке

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки, Linux

Kaspersky Security Network

Kaspersky Security Network объединяет распределённую по всему миру инфраструктуру: несколько дата-центров и сотни серверов, включая виртуальные в публичных облаках. Через неё проходят аналитические данные об угрозах, которые затем преобразуются, актуализируются и используются в остальных наших продуктах. Команда KSN должна обеспечить обработку полутора миллионов запросов в секунду (медианное значение) и трафика в 5000 Тб данных в месяц, а также оперативно разбираться с запросами от продуктовых команд. Серверы работают на Linux, внутри используется несколько языков под разные задачи, но основной — C++ 17.

Основные задачи команды:

  • Оптимизация кода для того, чтобы ПО более рационально использовало ресурсы серверного оборудования.

  • Как можно быстрее отвечать пользователям, особенно в сценариях, когда антивирус пытается проверить объекты в реальном времени.

Требуется понимание принципов работы интернета и основные протоколы, так как иногда приходится отлаживать крайне интересные и занятные вещи: все простые баги мы уже починили :) 

Подробности о продукте и команде — по ссылке

Нужные навыки: Отличное знание C++, Умение писать код, Работа с многопоточными приложениями, Опыт коммерческой разработки, Linux.


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

C++ несмотря на все шуточки про выстрелы в ноги, в том числе и от самого Бьерна Страуструпа, остаётся мощным и незаменимым инструментом для системного программирования. Разработка на нём идёт быстрее, чем на C и Ассемблере, при этом язык позволяет напрямую управлять памятью и работать с железом. К тому же при должной квалификации программистов код получается вполне безопасным и даже красивым. Именно поэтому он стал основной для системного программирования в KasperskyOS.

По нашей дорожной карте видно, насколько разные и крутые задачи умеют решать толковые разработчики на C++. При этом сложно представить программиста, который мог бы разом покрыть все эти области и стать этаким «универсальным юнитом» — даже в пределах одной компании. 

Конечно, наша карта в первую очередь отражает специфику задач «Лаборатории Касперского» и не включает некоторые направления разработки на C++ вроде компьютерного зрения. Однако она описывает основную часть задач «плюсовиков» и послужит полезным источником информации как для кандидатов, которые хотели бы у нас работать, так и для других компаний, которые задумываются о создании своей карты компетенций. А ещё это практически гимн в честь «плюсов» — языка, на котором уже почти 40 лет держится немалая часть системного ПО и у которого пока так и не смогли забрать корону Go, Rust или Carbon.

Теги:
Хабы:
Всего голосов 46: ↑38 и ↓8+30
Комментарии93