• Надежный, безопасный и универсальный бэкап для U2F
    0
    Согласен, но:

    • Если Trezor также используется и по своему самому популярному предназначению (cryptocurrency hardware wallet), то использовать его еще и как аутентификатор уже не так неудобно
    • Для меня лично, лучше не самый удобный форм-фактор с бэкапом, чем удобный без бэкапа. Пока что я вообще продолжаю u2f-zero пользоваться, но Trezor интересен т.к. предлагает бэкап из коробки.

  • Надежный, безопасный и универсальный бэкап для U2F
    0
    Похоже, ваша ссылка работает только с yubi ключами, мои google titan ключи не прошли.
    Работает с моими u2f-zero. А что конкретно означает «не прошли»?

    По поводу проверки с отзывом и перерегистрацией — да, это сработает, но нужны доп шаги. Если мы говорим о сколь-нибудь серьезной стратегии бекапов, то обязательно предусматриваем регулярную проверку. А это противоречит концепции замуровывания в стену.
    Вообще говоря, что именно делать с бэкап-токеном — каждый решает сам, со всеми вытекающими; и эта свобода (и, конечно, ответственность) мне нравится. Если есть желание периодически его перепроверять — можно держать его в сейфе, и это все равно (для меня) было бы значительно лучше, чем независимый токен, т.к. я совсем не хочу добавлять бэкап-токен вручную на каждый сервис.

    Плюс к этому, в соседней ветке обсуждаем Trezor — если с ним все сработает, то я рассматриваю «замуровать в стену» не только девайс, но еще и seed phrase набитой на металлической пластине. Таким образом, даже если с девайсом по прошествии лет что-то случится, то все равно будет возможность его восстановить из seed phrase (можно было бы этим seed и ограничиться, но никто не знает, что станет с этой компанией Trezor впоследствии и насколько легко можно будет купить новый токен, так что предпочитаю просто иметь готовый бэкап-токен)
  • Надежный, безопасный и универсальный бэкап для U2F
    0
    device_secret будет выведен из seed phrase.
  • Надежный, безопасный и универсальный бэкап для U2F
    +1
    Ух ты! Я знал, что Trezor поддерживает U2F с некоторых пор, но ранее не нашел, что counter тоже можно устанавливать. Видимо, я плохо искал: действительно, есть такое, судя по wiki для trezorctl.

    Что ж, спасибо за полезный комментарий! Похоже, заказываю Trezor.
  • Надежный, безопасный и универсальный бэкап для U2F
    0
    Я согласен, что иметь возможность самостоятельно запрограммировать device_secret и константу для увеличения counter в U2F-токен было бы очень здорово, и это именно тот уровень поддержки от производителей токенов, которого я бы хотел видеть, рано или поздно. Опять же, не нужно будет доверяться производителям в том, что они не хранят у себя ключей.

    Такая возможность не решает вашей задачи иметь несколько ключей по подзадачам, но устраняет источник риска в виде электронного устройства.

    (Только для хранения ключа (device_secret) вместо ламинированного листка бумаги я бы использовал что-то вроде этого + punch kit)
  • Надежный, безопасный и универсальный бэкап для U2F
    0
    много поменялось с тех пор
    1. Второй аутентификатор в сейфе, лучше всего
    Это разве что-то новое? Такой подход считался лучшим решением для бэкапа в 2018, и считается до сих пор; хотя это и не принято считать проблемой. В статье я аргументирую, почему мне это не нравится, и предлагаю лучшее решение.
    2. Все ваши андроиды и виндоусы теперь FIDO2 аутентификатор
    Дело предпочтений, но я лично стараюсь воздержаться от использования телефонов и ноутбуков в качестве аутентификаторов; маленький USB-токен, который почти всегда при мне, вместе с другими ключами, и достается гораздо реже, чем телефон или ноутбук — более надежное решение.

    По поводу остального — не вижу, как это делает материал в статье менее актуальным. И делегированное восстановление, и WebAuthn backup extension требуют поддержки со стороны Relying Party (RP), то есть сервисов, и в итоге сервисы могут поддерживать такое восстановление, а могут и не поддерживать. То есть, мы будем просто иметь больше неуниверсальных методов восстановления.

    В статье же предлагается решение, совершенно независимое от сервисов; которое можно применить уже сегодня, и оно сразу оказывается рабочим для абсолютно всех сервисов, поддерживающих U2F.
  • Надежный, безопасный и универсальный бэкап для U2F
    0
    В каком смысле тест бекап-токена невозможен?

    Самое первое, что нужно сделать — это протестировать оба токена на demo.yubico.com/webauthn-technical: регистрируем основной токен, потом аутентифицируемся с ним же, проверяем, что signatureCounter низкий (видно, если раскрыть «Technical Details -> Response from the Relying Party»), потом аутентифицируемся с бэкап токеном, убеждаемся, что это работает, и что signatureCounter высокий.

    После этого можно удостовериться, что все работает и на настоящих сервисах. Берем какой-нибудь сервис (например, Google), добавляем туда основной токен, выходим, аутентифицируемся с основным токеном, выходим, аутентифицируемся с бэкап-токеном (убеждаемся, что это работает), выходим, пытаемся аутентифицироваться с основным токеном (убеждаемся, что это НЕ работает, т.к. бэкап уже засветился), после чего аутентифицируемся с бэкап-токеном, отзываем токен, и добавляем его заново (основной токен, конечно).

    Я провел такую процедуру на нескольких сервисах, и для меня это было достаточным доказательством работоспособности бэкапа в принципе, так что я мог со спокойной душой его закапывать / замуровывать итд. Единственное, что не гарантируется — это аннулирование основного токена после использования бэкапа (т.к. сервис может игнорировать counter), но сам бэкап будет работать на всех сервисах.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    +1
    По-моему, в контроллерах, на которых вообще имеет смысл использовать вытесняющую РТОС, все в порядке с адресной арифметикой. Например, на том же PIC32 (т.е. MIPS), имея в регистре адрес, загрузить слово по данному адресу с 16-битным знаковым смещением — одна инструкция (и один такт). На Cortex-M — тоже. Это в простых 8-битниках, конечно, все плохо с подобными вещами, но там и вообще вытесняющей РТОС делать нечего.

    Что именно вы предлагаете, я так и не понял. Насколько понял — должен быть какой-то отдельный буфер, который будет заранее рассчитан на определенное количество задач. Уже это мне не нравится — гораздо лучше иметь просто связанный список, в который можно динамически добавлять/убирать сколько угодно дескрипторов.

  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    +4
    Для того, чтобы обществу (и в первую очередь, мне самому) было проще переносить на TNeo существующие TNKernel-овские проекты, существует режим совместимости: выключаем рекурсивные мютексы, включаем совместимость с евентами. После этого, чтобы перенести проект с TNKernel на TNeo, потребуются тривиальные изменения в приложении, если вообще потребуются. Обеспечить полную совместимость не удастся как минимум потому, что порты TNKernel под разные платформы уже несовместимы между собой: по-разному выделяется память под системные задачи, по-разному система стартует, по-разному задачи создаются. Все отличия API перечислены на страничке Differences from TNKernel API.

    Более того, в «официальном» TNKernel под Cortex есть архитектурные особенности, сохранять совместимость с которыми нет никакого желания:

    • Там есть Timer Task, который не привносит ничего, кроме оверхеда по производительности и памяти. Подробнее тут, тут.
    • Память для системных вещей (стек для Idle task, Timer task) выделяется статически на этапе компиляции ядра, т.е. нет возможности собрать TNKernel как отдельную библиотеку и просто подключать ее к приложению: надо обязательно собирать TNKernel в составе приложения
    • Поведение макроса MAKE_ALIG не соответствует даже официальной документации. Подробности тут
    • Прочее, изложенное на Differences from TNKernel API


    RTOS для PIC32, пожалуй, поменьше, чем для Cortex, но хватает. API TNKernel не то чтобы уж необычный, но, тем не менее, перенести проект с TNKernel на TNeo — задача тривиальная, в то время как перенести с TNKernel на FreeRTOS — посложнее. Но ничего невозможного, конечно, нет.

    Ну и насчет полезности обществу — простите, если, написав TNeo, я не был достаточно полезным для вас. Я, кстати, думал про блог «я пиарюсь», но решил все-таки написать сюда, т.к. статья, как мне кажется, содержит достаточное количество информации, которая может быть интересна даже тем, кому ни с TNeo, ни с FreeRTOS работать в жизни не придется.

  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    TNeo сохраняет только «callee-saved» регистры, т.е. те, которые должна сохранять вызываемая функция, если эти регистры ей нужны

    Сорри, конечно, имелось в виду, наоборот: сохраняются только «caller-saved» регистры, т.е. те, которые должна сохранять вызывающая функция. А о «callee-saved» регистрах как раз позаботится сам ISR.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Продолжение:
    посмотрел по вашим таймерам. вот вопрос — зачем вообще модифицировать что-то в списке таймеров каждый тик???
    делается так.
    на системном тике сидит isr который просто инкрементит счетчик системных тиков. при старте он равен 0.
    есть общий сортированный по времени срабатывания двусвязный список таймеров.
    в начале он пуст.
    при запуске таймера высчитывается его время срабатывания = текущее значение счетчика тиков+интервал срабатывания заданный таймеру. то есть вы храните не сам интервал(который декрементите там) а сразу значение счетчике систиков когда сработать(вам больше никакие декременты не нужны).
    обьект с посчитанным временем срабатывания заносится в сортированный список. те кто раньше — те в голове.
    isr тамера не просто икрементит счетчик тиков, но после этого смотрит — не больше или равен текущий счетчик, тому значению, при котором должен сработать первый в списке(самый ранний таймер). если текущий счетчик больше — первый таймер извлекается и запускается его хук(или что там у вас). если текущий счетчик меньше времени у первого таймера, то ни один таймер еще не созрел. ибо далее в списке — более поздние.
    псевдокод иср
    void timerISR(){
        ++__SysCLocks; //это типа системное время в тиках
        while(!__TImers.empty() && (__TImers.first()._clocks<__SysClocks)){ //есть созревший таймер
            TImer *lt = __Timers.removeFirst(); //
            lt->timerAction();
            //тут зависит от реализации как реализуется периодичский таймер.
            //сам ли он себя обратно вставляет в список таймеров, или это делает данная функция
            //а этом псевдокоде таймер однократный
            //заметим, что посольку тут while - будут в цикле извлекаьтся все таймеры что созрели к данному тику.
        }
    }
    
    на самом деле если таймеров разумное количесвто — не тыщщи, таймерная иср только проверит первый в списке таймер на готовность и выйдет.
    к чему огород со специальными списками — непонятно.
    остается только в момент старта таймера его поставить в нужное место в списке таймеров(сортировка по времени срабатывания в тиках ).
    псевдокод startTimer(Timer* ft, int fticks){
        ft->_clocks=__SysClocks+fticks; //вычисляем таймеру абс.время его сраатывания в тиках
        Timers.addSorted(ft);
    }
    

    для оптимизации можно организовать несколько списков таймеров. например для которых квант времени 1 мс, 8 мс, 64 мс… если есть опасение что таймеров будет много… но в релаьном устройстве их, активных ну не более 10. в моей практике всегда одного списка хватало в силу небольшого количества запущенных таймеров.

    Ваши доводы насчет таймеров звучат разумно, спасибо. И если включен динамический тик, используется именно такая реализация (потому что там уже и нет периодических тиков, чтобы в них что-то декрементить).

    Через год, я уже не очень хорошо помню, почему решил сделать статические таймеры именно так. Возможно, я был просто под впечатлением от реализации в Linux, и хотел сделать похожим образом. Может, переделаю в будущем.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Прежде всего, скажу следующее: существует два подхода к обработке прерываний в РТОС: «Unified» и «Segmented». (не знаю, есть ли устоявшиеся русские термины, так что пишу их на английском). Вот тут есть отличная статья про это: RTOS Interrupt Architectures.

    Подход, о котором вы говорите, как о «правильном» (в противоположность моему, «наивному») — это как раз Segmented aproach, когда обработка прерываний разделена на два этапа. В ядре Linux эти два этапа называют top half и bottom half. Тогда, действительно, можно сделать так, что РТОС никогда не запрещает прерывания (как, например, AVIX-RT или Q-kernel).

    Но написание приложения под эти два разных типа РТОС существенно отличается (под «Segmented» писать сложнее, подробности в статье по ссылке выше), и, поскольку мне нужна РТОС, максимально совместимая с TNKernel, то этот вариант я не особо рассматривал.

    Я не сомневаюсь, что существуют приложения жесткого realtime, где запрещение прерываний недопустимо. В таких проектах, конечно, нужно использовать что-то вроде AVIX-RT. Но мне, честно говоря, несколько десятков тактов под запрещенными прерываниями никогда не мешали, и таких приложений, по-моему, большинство. Плюс к этому, «Segmented» добавляет оверхед к переключению задач. Процитирую заключение из статьи:

    While both Segmented and Unified Interrupt Architecture RTOSes enable deterministic real-time management of an embedded system, there are significant differences between them with regard to the efficiency and simplicity of the resulting system. Segmented RTOSes can accurately claim that they never disable interrupts within system services. However, in order to achieve this, they instead must delay application threads, introducing a possibly worse consequence. The segmented approach also adds measurable overhead to the context switch process, and complicates application development. In the end, a unified interrupt architecture RTOS demonstrates clear advantages for use in real-time embedded systems development.


    Можно было бы и закончить, но на некоторые вещи отвечу отдельно:

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

    При обработке прерывания, TNeo сохраняет только «callee-saved» регистры, т.е. те, которые должна сохранять вызываемая функция, если эти регистры ей нужны. Если не сохранить их при обработке прерывания, то данные в них будут потеряны. На MIPS TNeo сохраняет их самостоятельно, а, например, Cortex-M делает все за меня, там ядро вообще ничего не делает при обработке прерывания.

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


    Тут я с вами соглашусь, и даже вот дам вам ссылку на мой собственный пост на форуме Microchip, где я выражаю ту же идею. Но, первое: как мне там ответил товарищ andersm, эта функциональность довольно стандартна. И второе: мне эта функциональность не мешает, хоть я ее никогда и не использую, так что, чтобы не ломать без надобности совместимость с TNKernel, эти сервисы были оставлены.

    надо изолировать треды и прерывания вообще. треды ничего не могут знать про прерывания, а прерывания не могут вызывать тредовые функции. обработчики прерываний лишь берут данные(или не берут ничего) по внешнему событию, и кладут их в очереди, кольцевые буферы, и сигналят неким способом о событии в “мир тредов”. такая штука надывается каналом(обычно кольцевой буфер)


    Ну так, data queue именно для этого и нужны, да.

    таймеры у вас какие-от странные — там просто сортированная очередь обьектов(двусвязный список) — ближе к голове — наиболее ранний по срабатыванию… а у вас там какой-то наворот, даже не стал разбираться.

    Ну то, что вы не стали разбираться и написали об этом, по-моему, вам чести не делает. А реализация таймеров, тем временем, описана даже в статье выше: основная идея позаимствована из ядра Linux, и она более эффективна, чем «просто сортированная очередь».

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

    В TNeo есть жесткие таймеры. А мягких (пока) нет, т.к. лишняя задача в контексте вытесняющей ОС — это, как минимум, два переключения контекста каждый раз, и еще, как минимум, несколько сотен байт для ее стека. RAM в моих проектах — очень дорогой ресурс. Может, в будущем добавлю опционально, но пока мне ни разу не было нужно. Если нужно по таймеру разбудить какую-нибудь задачу, то можно из «жесткого» таймерного коллбэка семафором посигналить, или сообщение в очередь отправить.

    обработку прерываний можно разбить на два этапа — isr и dsr…

    Насчет этого ответил в самом начале комментария.

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

    После прочтения этого, стало ясно, что мы с вами, возможно, совсем о разных классах систем говорим. Смартфон на Андроиде — это ведь эмбеддинг, и тупой экранчик в заряднике, на камне с 1 кБ RAM — тоже эмбеддинг.

    Что значит «лучше не давать юзеру»? В эмбеддинге, РТОС и код приложения очень часто выполняется с одинаковым приоритетом — приоритетом ядра. Так что нет никакой защиты памяти, ничего такого. Это не тот класс систем. Можно, конечно, и на тостер Linux ставить, но это слишком дорого. Так что тут юзер имеет все возможности выстрелить в ногу, и никто ему не поможет.

    Синхронизация делается мютексом — отлично. Только не всегда разумно: если нам нужно произвести очень короткое действие (пусть, например, банально поработать с GPIO), то мютекс — это просто колоссальный оверхед, в десятки раз превышающий длительность полезной работы. Мютексом можно ограничивать только сравнительно длительные операции (работа с внешней флешкой, например).

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

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

    все опреации дложны быть с таймаутом, потому дедлоки там в принципе невозможны… дедлоки это у студентов в учебниках.

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

    Если это правило нарушается, то может возникнуть дедлок, и ядро мне об этом тут же скажет. На сегодняшний день, эта возможность помогла мне ровно один раз (помогла быстро найти ошибку) — что ж, уже неплохо.

    Если почувствуете необходимость ответить — пишите на почту (у меня на сайте есть адрес), я скопирую сюда.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Мне тут у английской версии статьи оставил комментарий некто Алекс. Т.к. там — только по-английски, то я оттуда удалил, скопирую сюда, и отвечу.

    Видел статью на хабре, но не зарегистрирован там… и тем не менее.

    Такая реализация как у вас, довольно наивна. (комментирую статью на хабре)

    у вас какие-то архитектурные недостатки видны.

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

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

    далее. надо изолировать треды и прерывания вообще. треды ничего не могут знать про прерывания, а прерывания не могут вызывать тредовые функции. обработчики прерываний лишь берут данные(или не берут ничего) по внешнему событию, и кладут их в очереди, кольцевые буферы, и сигналят неким способом о событии в “мир тредов”. такая штука надывается каналом(обычно кольцевой буфер). Кернел с разделенными мирами прерываниий и тредов запрещает прерывания всего в паре мест

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

    Вообще рилтайм эт оне вполне — немедля запустить тред обработки ивента с железа, а немедля забрать у железа данные и положить в буфер для обработки, — это как минимум.

    таймеры у вас какие-от странные — там просто сортированная очередь обьектов(двусвязный список) — ближе к голове — наиболее ранний по срабатыванию… а у вас там какой-то наворот, даже не стал разбираться.

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

    обработку прерываний можно разбить на два этапа — isr и dsr. dsr — просто активируемые из isr хуки постобрабтоки данных из прерывания, работающие в контексте текущего треда или собственном стеке, например(поскольку в isr вообще не видят тредов и не знают что это такое(в системе строгого разделения прерываний и тредов))…

    ну и так далее…

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

    критическая секция это не запрет прерываний(это убийственно для рантаймовой системы), а запрет переключений тредов. запрет прерываний это вообще стоит особенной секцией оформлять.

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

    все опреации дложны быть с таймаутом, потому дедлоки там в принципе невозможны… дедлоки это у студентов в учебниках.

    вообщем вот так.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Для вложенного запрета/разрешения прерываний есть сервисы tn_arch_sr_save_int_dis() / tn_arch_sr_restore(), а также макросы TN_INT_DIS_SAVE() / TN_INT_RESTORE(), которые, будучи макросами, имеют шансы быть реализованными более эффективно, чем вызов функции.

    1 кБ для ISR — как я написал, это полностью зависит от приложения. Может, где-то и 200 байт достаточно будет. Как минимум, есть 128 байт для контекста, плюс нужно для каждого приоритета прерывания предположить, что все вложенные прерывания вызывались в самом худшем случае. Точно высчитывать, сколько стека нужно, и выделять ровно-ровно — вообще дело неблагодарное, только в крайнем случае можно этим заняться, когда RAM ну вообще нет больше. Я обычно просто смотрю, сколько испачкано, и оставляю раза в два больше, на всякий случай.

    Позволяете ли вы вызывать из ISR все вызовы RTOS, или только подмножество?
    Как можно все сервисы RTOS вызывать из прерывания? В прерывании нельзя вызывать любой сервис, который может ждать. Пока что, по образу и подобию TNKernel и других РТОС, для прерываний существует отдельный набор сервисов, но вообще-то, как минимум на поддерживаемых в данный момент архитектурах, можно было бы просто позволить вызывать любые сервисы, которые не могут ждать. Но пока, как наследие от TNKernel, есть отдельный набор сервисов. Доп. инфу см. тут, тут.

    Как код переноса SP в область ISR после входа (и по все видимости, сохранения минимального контекста — флагов и IP) обрабатывает ситуацию вложенных прерываний? Насколько он эффективен (какая латентность привносится в обработчик прерываний до пользовательского кода)?

    Конечно, от архитектуры зависит. На MIPS — 6-10 лишних инструкций до и после ISR, на PIC24/dsPIC — 3-5 инструкций. А вот Кортекс я люблю за то, что эта платформа была спроектирована «with OS in mind», так что она все делает за меня. Ядро ничего особенного не делает, когда происходит прерывание.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Спасибо за преувеличение.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Вещи правильные говорите, спасибо, я и сам об этом уже думал, но не знаю, когда смогу добраться. Идей больше, чем времени.

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

    Это, конечно, все классно, но пока выделить столько времени нет возможности.

    Подробные формализованные тесты написаны — уже очень хорошо; а эти вероятностные вещи приходится гонять на реальных устройствах, где асинхронных событий сколько хочешь. За последний год проблем не было, так что пока считаю, что все хорошо.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    +5
    Если вы читали статью, то, может быть, заметили: был нужен API, по-максимуму совместимый с TNKernel, чтобы без проблем перенести существующие проекты (оставлять на старой ОС нельзя). Так что изначальный план был — улучшить TNKernel, ну а получилось так глобально в итоге. Это первое.

    Второе: с грустью глядя на найденные баги в TNKernel (которая достаточно популярна, и используется давно), я уже не очень хотел доверять другим РТОСам. Может, конечно, ChibiOS и другие тестируются очень хорошо и подробно — я же не знаю.

    Третье: пока я этим занимался, я неплохо разобрался в матчасти, так что это, в некоторой степени, вклад в будущее. Самообучение для вас — тоже «удовлетворение амбиций»? Ну и, кстати, с работодателем мы в доле: пока я работал над ядром, я реально горел, так что работал по 12 часов в сутки, включая некоторые выходные. И, конечно, в целом работодатели вкладываются в обучение своих сотрудников, это нормально.

    Тон вашего комментария, конечно, такой, малоприятный. К сожалению, когда подобные комментарии оставляют у моих статей, я вынужден отвечать и на них.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    +6
    Конечно, есть. То, о чем вы говорите — это инверсия приоритетов. Только для этого нужно использовать не семафор, а мютекс. Ядро поддерживает два метода обхода инверсии приоритетов: priority ceiling и priority inheritance (и, кстати, это работало криво в TNKernel: с помощью юнит-тестов я нашел две простые ситуации, когда инверсия приоритетов не обрабатывается корректно. Подробнее тут: Bugs of TNKernel 2.7)

    Вообще, эти термины, мютекс и семафор, очень часто путают, даже в серьезных библиотеках и РТОСах. Насчет отличий семафоров и мютексов крайне рекомендую к прочтению две ссылки:



    Вкратце:

    Мютекс

    Когда задача захватывает мютекс, она им владеет. И только она может его разблокировать. Кроме прочего, именно то, что мютекс «принадлежит» задаче, позволяет обойти инверсию приоритетов, о которой вы говорили. Поэтому мютексы нужно использовать именно для защиты разделяемых ресурсов: задача блокирует мютекс, работает с каким-либо ресурсом, разблокирует мютекс. По этой же причине, мютексы нельзя использовать в прерываниях (т.к. у мютекса должна быть задача-владелец).

    Семафор

    С семафором картина совершенно иная: его не блокируют или разблокируют, а ждут и сигналят. И эти действия производятся в разных потоках: например, поток А должен сделать какую-то работу, после которой поток Б может делать свою. Тогда поток Б ждет семафор, а поток А сигналит ему: типа, у меня все готово, теперь давай ты. Также сигналить семафором можно из прерывания.

    По ссылкам выше можно найти более подробное объяснение.

    Конечно, можно использовать семафоры вместо мютексов, работать кое-как будет (без обработки инверсии приоритетов). Но можно и саморезы в стену гвоздями забивать, держаться будут.

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

  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Нет, пока нет.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    map-файл — это легко, вот: tn_pic32_example_basic.X.production.map (сгенерирован из примера в репозитории examples/basic/arch/pic32)

    А вот насчет визуализации: я сам когда-то пытался найти инструмент, который сможет визуализировать map-файл, сгенерированный компилятором; очень полезно иногда было бы; когда, например, где-то память портится. Не нашел такого инструмента. Если знаете — поделитесь, пожалста.

    А про списки, правильно ли я понимаю, что при старте новой задачи память под новый экземпляр struct TN_Task выделяется приложением, а не ядром?
    Да, правильно. Небольшое уточнение только: память выделяется не при старте задачи, а при ее создании (созданную задачу можно останавливать и запускать сколько угодно раз; правда, в реальной жизни мне это ни разу не пригождалось).

    См. tn_task_create(): первый аргумент struct TN_Task *task — это как раз указатель на дескриптор задачи, который приложение должно где-то выделить до вызова этого сервиса.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Спасибо за отзыв! Ссылки на pdf поправил: действительно, кривые были; спасибо, что заметили.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    +1
    Спасибо и вам за отзыв. Я постарался сделать документацию понятной (есть и Quick Guide, и примитивные примеры для разных платформ в репозитории), но если что — спрашивайте.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Хм. Так в статье ведь есть карта распределения памяти: вот. Приложение выделяет экземпляры объектов (т.е. MyBlock примера в статье) самостоятельно и как угодно (в моих приложениях, чаще всего, статически). И передает указатели на эти выделенные объекты ядру. И когда ядро должно добавить этот экземпляр в какой-нибудь список, то оно просто модифицирует несколько указателей, и все.

    В случае же с отдельным (не встроенным) TN_ListItem, для каждого добавляемого объекта в список, ядру пришлось бы отдельно где-то выделить этот TN_ListItem: работает гораздо дольше, нужно больше памяти, cache locality хуже.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Привет, Саша, спасибо за отзыв!

    И за замечание спасибо, поправил. Про PIC32MZ — рад слышать, если что, пишите!
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    +3
    Спасибо за отзыв.

    Один, да, но, на самом деле, это не такая уж большая работа: взгляните на исходники, проект-то сравнительно маленький.

    На тесты уходило очень много времени: наверное, около 30% всего времени, потраченного на ядро — это тесты. Но без них нельзя.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    +4
    Спасибо за отзыв!

    Насчет «слова» — этот термин, к сожалению, обозначает разные вещи. Здесь имеется в виду платформозависимая величина, равная разрядности регистров процессора. Процессор может обрабатывать значения размером в 1 слово за один такт (32-битный процессор может, например, за один такт сложить два 32-битных числа, в то время как 8-битному потребуется для этого гораздо больше тактов)

    Wikipedia: Word (computer architecture)
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    +6
    Диаграммы последовательностей — в PlantUML, гистограммы — в LibreOffice Calc, а вот такие — в yEd.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    0
    Около двух месяцев, но эта первая стабильная версия значительно отличается от текущей версии: только одна архитектура (PIC32, т.е. MIPS), и нет большинства плюшек, добавленных впоследствии.
  • Как я, в итоге, написал новую RTOS, протестированную и стабильную
    +5
    Спасибо за отзыв.

    Ну а распространяться о том, «как платят», все-таки, наверное, ни к чему, и, насколько я знаю, это не очень зависит от того, в какой области работать. Хороших специалистов мало во всех областях.
  • Как работают замыкания (под капотом) в JavaScript
    +6
    Есть одна смысловая неточность в переводе:

    С момента, когда функция возвращает значение, больше никто не обращается к его scope-объекту, поэтому его собирает сборщик мусора. Но что если определить вложенную функцию, вызвать ее и дождаться возвращения?

    Выделенный курсивом текст — это не то, что я имел в виду: But what if we define nested function and return it (or store somewhere outside)?

    Т.е. я имел в виду следующее:

    Но что если определить вложенную функцию и вернуть ее (или сохранить где-то вне текущего scope-объекта)?
  • Как работают замыкания (под капотом) в JavaScript
    +12
    Ничего себе, freetonik перевел мою статью. Спасибо!
    Извините уж, ребята, что сам не добрался.
  • Какая команда в консоли отнимает у вас больше всего времени?
    0
    Я долго не мог найти инструмента для закладок в шелле, все найденные решения меня не очень устраивали: все равно неудобно. А потом нашлась очень классная и универсальная штука: fzf, он позволяет не только быстро искать файлы, но и фильтровать произвольные текстовые данные. Поэтому появилась простая идея: почему бы не держать просто файл со списком директорий-закладок, и fuzzy-фильтровать его. Вот как это выглядит: image

    Я просто пишу «cdg» (от «cd global»), печатаю 3-4 буквы из нужной закладки (или перемещаюсь вверх-вниз по списку), и жму Enter. Очень быстро и удобно, пользуюсь больше года, очень доволен.

    Подробная статья: Fuzzy bookmarks for your shell
  • Автоматическая индексация файлов проекта
    0
    Во-первых, .vimprj , а не .vimproj, так ведь?
    Во-вторых, omnicppcomplete видит ровно столько же, сколько видит сам Vim. Что видит Vim вы можете узнать, набрав :set tags?

    Единственное требование, насколько я знаю — это чтобы теги были сгенерированы с опциями --c++-kinds=+p --fields=+iaS --extra=+q — все это у вас тоже есть, насколько вижу.

    Я не являюсь знатоком omnicppcomplete, поэтому не смогу помочь. У меня проблем с этим не было.
  • Автоматическая индексация файлов проекта
    0
    Блин, опять ответил комментом к статье :-/ Читайте ниже.

    И добавлю еще: вместо omnicppcomplete попробуйте clang_complete. Народ очень хвалит — автодополнение работает четко, без глюков, в отличие от omnicppcomplete. Для его работы не нужны теги вообще.

    Я его только потестировал немного — понравилось, но использовать в своих задачах пока не могу, т.к. пишу под микроконтроллеры, и из-за специфики clang не понимает некоторые вещи. Пока руки не дошли приручить его, да и omnicppcomplete, хоть и неидеально, но работает.
  • Автоматическая индексация файлов проекта
    0
    про этот файл .vimprj вы можете прочитать в доке к плагину vimprj.
  • Автоматическая индексация файлов проекта
    0
    Насчет omnicppcomplete: вроде у меня без проблем все заработало, когда я настраивал его впервые. Вот мои настройки, относящиеся к нему: тынц.

    Насчет генерации тегов для файлов, не входящих в проект:

    Если использовать плагин project, то на данный момент есть только один способ, который я всегда и использую: в вашем случае нужно создать файл /home/victor/development/hist/.vimprj и поместить туда следующий код:

    set tags+=/path/to/my/first_lib/tags
    set tags+=/path/to/my/second_lib/tags


    То есть, перечислить все файлы тегов для инклудов. Недостаток: если эти файлы тегов еще не сгенерированы, то работать, конечно, не будет. То есть, если, например, я настраиваю свое рабочее окружение на новом компе, то мне нужно открыть в виме каждую либу, чтобы indexer сгенерировал теги для всех них.

    У меня в планах сделать возможность подключать другие проекты, чтобы теги для них генерировались автоматически, но пока — так.
  • Автоматическая индексация файлов проекта
    0
    Случайно ответил вам просто комментарием к статье, а не ответом на ваш коммент. Вот мой ответ.
  • Автоматическая индексация файлов проекта
    0
    Выглядит все хорошо. Должны быть проиндексированы следующие 4 файла: algorithm-list.cpp, bgfg-segm.cpp, main.cpp, viewer.cpp.

    Попробуйте навести курсор, например, на имя какого-нибудь метода из этих классов, и нажать Ctrl+].

    Если Вим перешел на определение данного метода — значит, миссия моего плагина выполнена (теги сгенерированы), и проблема где-то в настройке omnicppcomplete.

    Если же нет (выдал ошибку E426, т.е. tag not found) — то это странно, и тогда приведите, пожалуйста, содержимое файла /home/victor/.vimprojects_tags/hist.
  • Автоматическая индексация файлов проекта
    0
    Покажите результаты работы следующих команд:
    :IndexerInfo
    :IndexerDebugInfo
    :set tags?
  • Автоматическая индексация файлов проекта
    0
    Не припомню, чтобы мой плагин когда-нибудь выдавал мне такую ошибку. Расскажите поподробнее-то:
    *) какая версия плагина;
    *) используете с .vimprojects или .indexer_files? желательно выложите куда-нибудь используемый файл, я гляну;
    *) Вим, наверное, еще сказал вам, в какой строке произошла эта ошибка. Напишите.