Эмулятор ядра OS Windows и правки ядра Колибри

Internal structure and you change requests/suggestions
  • Этот пост будет про приоритеты потоков

    Винда интенсивно применяет политику приоритетов потоков при своей работе. Эти приоритеты мной игнорировались при разработке эмулятора, что не мешало работе компонентов Винды. Однако, когда я заметил, что число созданных потоков приближается к двустам (а будет еще больше), то резонно задуматься о внедрении в ядро Колибри поддержки системы приоритетов пользовательских потоков.

    Нашел тему в форуме "Приоритеты в планировщике задач"
    http://board.kolibrios.org/viewtopic.php?t=2316

    В последнем сообщении этого форума CleverMouse сообщает о логике работы планировщика.
    CleverMouse wrote: Fri Jun 07, 2013 8:09 pm r3615+r3617 вносят дополнение: теперь при поступлении IRQ, отличного от таймера, также вызывается планировщик, но со специальным флагом "смотри только на потоки, которые строго более приоритетны, чем текущий". Флаг также меняет логику продвижения очереди: если пользовательский поток A упорно работал и был прерван по таймеру, то следующим пользовательским потоком будет B, следующий в кольце после A; если тот же пользовательский поток A упорно работал и был прерван по другому прерыванию, то следующим пользовательским потоком останется A - возможно, после того, как вмешавшийся ядерный поток что-нибудь сделает.

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


    Предлагаю следующие изменения в ядре Колибри:

    1.) Добавить в структуру TASKDATA в файле const.inc два поля в свободные ячейки:

    struct taskdata.jpg
    struct taskdata.jpg (89.44 KiB)
    Viewed 945 times
    Примечание:
    Я пытался просто добавить поля в структуре TASKDATA, но в итоге перестает работать кнопка "ПУСК" у Колибри. Видимо происходит затирание других данных если увеличить размер структуры.


    2.) Внедрить код проверки ядром локальных приоритетов пользовательских потоков в файл sched.inc после метки .task_found:

    scheduler.jpg
    scheduler.jpg (168.45 KiB)
    Viewed 945 times

    3.) В процедуре set_app_params в файле taskman.inc внедрить код установки начального приоритета потока:

    taskman.jpg
    taskman.jpg (54.81 KiB)
    Viewed 945 times


    Уровень локальных приоритетов пользовательских потоков может быть установлен от 0 до 255 (максимально возможное значение в байте).
    0 - самый высокий локальный приоритет. Этот приоритет установлен по умолчанию при запуске потока.

    Продолжение следует......
  • Продолжение поста ....

    Для регулировки приложением локальных приоритетов запущенных потоков созданы новые функции:
    51,3 - получить приоритет потока
    51,4 - установить приоритет потока.
    Заодно я создал функцию 51,2 - получить pid текущего потока чтобы не обращаться к функции 9 только для того, чтобы получить потоком свой pid.

    kernel_func.jpg
    kernel_func.jpg (203.55 KiB)
    Viewed 941 times
    kernel_func2.jpg
    kernel_func2.jpg (140.58 KiB)
    Viewed 941 times


    Если не будет возражений или замечаний то, приблизительно через месяц, я внесу код в SVN и пояснения в справочник по функциям 51,2 - 51,4
  • Этот пост будет про обработку "контролируемых исключений" в ядре Kолибри

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

    Если разработчик ядра, драйвера или приложения желает обезопасить механизм обработки данных при обращении к памяти для записи/чтения (например, к памяти полученной от пользователя), то он может сделать следующие действия:
    1.) Перед обращением к памяти, установить следующие данные для обработчика "контролируемых исключений" ядра:
    - В регистр edi занести сигнатуру - текст 'EXPT'
    - В регистр esi занести адрес, куда должен передать управление обработчик "контролируемых исключений".
    2.) По адресу, указанном в регистре esi разработчик прописывает необходимые действия в случае получения некорректных данных.


    В итоге:
    1. Значительно повышается стабильность (отказоустойчивость) ядра Колибри или приложения при обработке данных.
    2. Упрощается алгоритм проверки данных на их корректность.
    3. Этот алгоритм могут использовать разработчики приложений без обращения к функции 68,24.


    Ниже показан пример применения механизма обработки некорректных данных.
    Когда нам в ядро приходят непроверенные данные от пользователя, то мы устанавливаем данные для обработчика "контролируемых исключений" (строка 5593-5594).
    Теперь если произойдет исключение из-за обращениия к несуществующей памяти (в строке 5596), то исполнение кода начнется с метки .err_exit: (строка 5619) с корректным завершением обработки запроса от пользователя.

    example_ctrl_ex.jpg
    example_ctrl_ex.jpg (171.13 KiB)
    Viewed 935 times


    В заключение предлагаю внедрить в файл sys32.inc в процедуре page_fault_exc: код обработчика "контролируемых исключений":

    ctrl_exc.jpg
    ctrl_exc.jpg (53.46 KiB)
    Viewed 935 times

    Если не будет возражений или замечаний, то приблизительно через месяц я внесу код в SVN, а также внесу пояснения в справочник по функции 68,24.
  • Этот пост будет про обработку исключений переполнения стека

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

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


    Действие пользователя
    """"""""""""""""""""""""""""""""""""""
    Если пользователь (приложение) желает обрабатывать исключения переполнения стека, то он должен сделать следующее:
    1. Выделяет область памяти для аварийного\резервного стека с размером, достаточным для обработки данного исключения.
    2. При вызове функции 68,24 передает стартовый адрес резервного стека в регистр ESI.
    3. Ставит флаг 12 в маске обработки исключений в функции 68,24
    4. При возникновении исключения с кодом 12 в аварийном стеке будет представлена нижеследующая инфа (см. структуру EXCEPT_STACK)
    5. Обработчик приложения делает то, что нужно и при завершении обработки этого исключения сбрасывает (обнуляет) бит занятости аварийного стека. (структура EXCEPT_STACK.LockAccess)

    структура EXCEPT_STACK
    struct except_stack.jpg
    struct except_stack.jpg (45.11 KiB)
    Viewed 908 times




    Алгоритм действий системы ядра Колибри
    """""""""""""""""""""""""""""""""""""""""""""""""""""
    При возникновении исключения со стеком, по логике должно генерироваться исключение 12. Однако, из-за настроек (системы или процессора), при переполнении стека чаще всего генерируется исключение 14 (page fault).
    У меня генерируется исключение 14 даже при явном применении команд типа push pushad со стеком. :shock:
    Поэтому, при возникновении исключения 14, предлагаю сделать в ядре дополнительные проверки для корректного определения причины исключения.

    Принцип проверки:
    Перед базовым адресом стека всегда будет незанятая область памяти в 1000h и поэтому, если обращение к памяти было в пределах этого размера, то значит была попытка обратиться к несуществующей памяти стека.


    Ниже предлагается:
    1. Внедрить в файл sys32.inc (после метки IRetToUserHook:) код дополнительной проверки причины исключения. (см ниже)
    2. Внести изменение в структуре APPDATA (добавить дополнительное поле - exc_reserve_stack) в const.inc (см ниже)


    Изменение в структуре APPDATA
    struct APPDATA.jpg
    struct APPDATA.jpg (116 KiB)
    Viewed 908 times

    Код дополнительной проверки после метки IRetToUserHook:
    except_stack.jpg
    except_stack.jpg (200.15 KiB)
    Viewed 908 times
    except_stack2.jpg
    except_stack2.jpg (194.44 KiB)
    Viewed 908 times

    Продолжение следует .......
  • Продолжение поста .......

    В заключение предлагается добавить код в функцию 68,24 в файле memory.inc:

    memory f68.jpg
    memory f68.jpg (52.62 KiB)
    Viewed 899 times


    Если не будет возражений, то я планирую через месяц внести этот код в SVN, а также прописать в справочнике по функциям sysfuncr.txt у функции 68,24 дополнительную инфу по исключениям.


    №№№№№№№№№№№№№
    Примечание по коду проверки исключения 14

    Библиотеки Винды используют два варианта увеличения стека через исключение:
    1. Простое переполнение в ходе использования стека
    2. Динамическое выделение области памяти в стеке размером более чем в 1000h

    Ниже показан пример процедуры динамического выделения памяти в стеке в модуле dmserver.dll

    stack dmserver_dll.jpg
    stack dmserver_dll.jpg (73.05 KiB)
    Viewed 899 times

    Так вот, при выделения памяти в стеке размером более чем 1000h, мой код может не определить это исключение, так как разница между адресом в регистре esp и адресом в регистре cr2 может составить более 1000h.

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

    Я пока не нашел в ядре Колибри функцию определения базы памяти по его адресу. Видимо потом придется прописывать дополнительный код.
    №№№№№№№№№№№
  • Приветствую, Jurgen !
    Интересно следить за тем, что вы делаете, спасибо за описание некоторых процессов.
    Есть несколько моментов, на которые хотел бы обратить внимание:
    1) Присылайте, пожалуйста, код примеров и исправлений в виде кода, а не скриншотов, т.к. не очень удобно смотреть на изменения и использовать их для сверки/проверки.
    2) Также вместо написания фразы "Если не будет возражений, то я планирую через месяц внести этот код в SVN..." можете обратиться в группу Телеграм https://t.me/kolibri_os и спросить мнение там, а не только тут на форуме.
  • 1. I generally agree on your design of thread priorities, why not. It would be nice to see a patch though. Same for other changes.

    2.1. Maybe '*_local_priority' names are a bit too long. Something like 'niceness' is shorter and refers to POSIX.

    2.2. Also, variable names starting with the words 'check' and 'set' look misleading. These words usually name functions. Please, consider using other prefixes like 'current' and 'default' or 'cur' and 'def'.

    I didn't get into the other your changes yet.

    You don't have to repost to telegram.
  • Здравствуйте, не могу не отметить что в так называемом "патче" виднеется использование структуры TASKDATA, что лично для меня, говорит что вы используете достаточно старую версию ядра и это неприемлемо.

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

    По поводу API хотелось бы не на словах читать, а видеть интерфейс того, как это будет выглядеть(подробное описание сисфункций НЕ в виде кода)

    Ну и чисто своё мнение: Перед любым патчем проверяйте всё ли работает и всё ли после него будет нормально (хорошая аналитика должна быть), про то как надо патч оформлять уже выше писали