
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.3] |
![]() |
|
Страницы: (21) « Первая ... 18 19 [20] 21 ( Перейти к последнему сообщению ) |
Сообщ.
#286
,
|
|
|
Цитата rcz, 09.10.03, 23:29:35 А еще, кстати, надо рассмотреть такую ситуацию. Приходят 2 сообщения. (у второго приоритет выше). Первое сообщение начинает выполняться, дле него создается поток, с приоритетом выше, чем у потока, отправивешего 2-е сообщение. Значит 2-е сообщение(приоритет будет реалтайм) не начнет выполняться, будет ждать. Если оба потока real time, то оба и запустятся, и между ними будет делиться время. Цитата А в варианте создания потока для каждого сообщения - о расположении стека не стоит волноваться. Можно сделать так. При регистрации интерфейса указывается размер стека,адрес функции(ну и доп..инфа.для создания потоков). Неявным образом задается адресное пространство - процесс. При появлении сообщения, ядро просто обычный образом создает поток, как это бы делал сам процесс(параметры были переданы при объявлении интерфейса). И передает ему параметры (сообщение). Это получается отложенное создание потока процессом Как базовую схему я это и имел в виду. Только тут возникает проблема с тем, что на одном интерфейсе может быть много потоков, значит нужно много стеков. И для этих стеков нужно определить адресное пространство. Самое примитивное решение: при формировании процесса задавать начало и размер стековой области и размер стека - а при создании потоков для них стеки будут размещаться подряд в стековой области. Но это не очень красивое решение - поэтому я и придумываю как все стеки "посадить" на один адрес. |
Сообщ.
#287
,
|
|
|
Цитата Нет проблем. В двух параллельных потоках. Проблемма в том, какой из них будет выполняться ![]() Цитата Единственный способ обеспечить немедленную обработку в Вашей схеме - это если поток обработки будет только сортировать сообщения: для срочных запускать новый поток, несрочные - перемещать в новую очередь. Значит меня не поняли. Обработчик очередями не занимается. Цитата Только тут возникает проблема с тем, что на одном интерфейсе может быть много потоков, значит нужно много стеков. И для этих стеков нужно определить адресное пространство. Это как раз не проблема. Дан размер стека. Под него всегда будет вызываться выделение памяти (от лица этого процесса) динамически. фиксировать адрес не надо. доапустим как создается поток. create_thread (addr,params,stack_size...) Процесс выделяет память (виртуальные страницы ) размером stack_size. стек нового потока устанавливает на эту выделенную память. Туда копирует параметр. Так и в обработке сообщения. Будет известны адрес обработчика, размер стека, а параметр - сообщение. |
Сообщ.
#288
,
|
|
|
Цитата rcz, 10.10.03, 12:06:27 Проблемма в том, какой из них будет выполняться ![]() По-очереди! Согласно стратегии планирования вроцессорного времени. Цитата Значит меня не поняли. Обработчик очередями не занимается. Значит не будет гарантированного времени отклика (до начала обработки) на сколь угодно срочные сообщения. Цитата Это как раз не проблема. Дан размер стека. Под него всегда будет вызываться выделение памяти (от лица этого процесса) динамически. фиксировать адрес не надо. До этого Вы согласились с тезисом 7. А теперь его фактически отвергаете. На самом деле, как раз это есть нарушение инкапсуляции: когда кто-то со стороны (ядро) решает за процесс, где (в адресном пространстве) будет располагаться его стек. Цитата доапустим как создается поток. create_thread (addr,params,stack_size...) Так как с тезисом 10 ? Два способа создания потока заведомо лишнее. Либо create_thread, либо ForkMessage (в том чисте и для создания потока внутри процесса - через отправку сообщения самому себе). |
Сообщ.
#289
,
|
|
|
Цитата Значит не будет гарантированного времени отклика (до начала обработки) на сколь угодно срочные сообщения. Писал вариант работы посылки сообщения, ( Цитата )время отклика там тоже не гарантируется.Приходят 2 сообщения... Цитата До этого Вы согласились с тезисом 7. А теперь его фактически отвергаете. На самом деле, как раз это есть нарушение инкапсуляции: когда кто-то со стороны (ядро) решает за процесс, где (в адресном пространстве) будет располагаться его стек. Если рассматривать эту систему, в ней уже нет ОО. Привязать стек к определенному адресу - тоже нарушение. (Разрешить работу с локальными переменными надо). Пример func (struct *somestruct){ handle thr=create_thread(thr_func,somestruct..); //...some work wait(thr); } Если родитель передаст в func или локальную структуру, или структуру с указателями на локальные переменные... при фиксировании стека - они потеряются. А вызываеющий func вполне может и не знать о создании потока. Т.е. он или должен сам копировать всю структуру в Хип (со всеми полями, разименовыванием указателей. считай сериализация всех объектов), или всем отказаться от стековых переменных и параметров вообще. Цитата 7. Любое отображение памяти на виртуальное пространства производится только по явному запросу процесса. А указание в создании потока размера стека - считай явный запрос . Или по явным Вы подразумеваете. stack_addr = alloc_mem (stack_size) create_thread(func.stack_addr,params); Цитата Два способа создания потока заведомо лишнее. Либо create_thread, либо ForkMessage (в том чисте и для создания потока внутри процесса - через отправку сообщения самому себе). Суть у них одна. В одном случае параметры - размер стека указывается при создании интерфейса. В втором - при явном создании потока. Поэтому просто надо решить - делать ли боступным create_thread как syscall Для оптимизации - лучше syscall, но для сохранение лучшей архитектурной целостности (микроядерность) - через сообщения. |
Сообщ.
#290
,
|
|
|
Цитата rcz, 10.10.03, 15:09:16 Писал вариант работы посылки сообщения, ( )время отклика там тоже не гарантируется. ??? Если придут два real time сообщения, то одному естественно потребуется ждать (не более кванта). ..Но я не понял утверждения.. Проблемная ситуация такая: объект обрабатывает низкоприоритетное сообщение, а в это время приходит real time. В Вашей схеме оно будет ждать (причем сколь угодно долго) окончания обработки сообщения с низким приоритетом. А в том, что я предлагаю, его обработка начнется немедленно. Цитата Если рассматривать эту систему, в ней уже нет ОО. Привязать стек к определенному адресу - тоже нарушение. Где же нарушение?! Стек принадлежит процессу и процесс имеет право привязать его к любому адресу. Вполне нормальная работа объекта со своими внутренними сущностями. Цитата Если родитель передаст в func или локальную структуру, или структуру с указателями на локальные переменные... при фиксировании стека - они потеряются. Они потеряются только если использовать идею стека "со слоями". Эта проблема как раз обсуждалась, и сносное решение найдено. А если нет "слоев", то и проблемы нет в принципе. Цитата А указание в создании потока размера стека - считай явный запрос . Или по явным Вы подразумеваете. stack_addr = alloc_mem (stack_size) create_thread(func.stack_addr,params); Я же 100 раз писал, что для функции выделения памяти адрес - это входной, а не выходной параметр! handle= alloc_mem (stack_addr,stack_size). А точнее, на входе у allocate целая структура section, где указываются в том числе и адреса. Цитата Суть у них одна. В одном случае параметры - размер стека указывается при создании интерфейса. В втором - при явном создании потока. Проблема в указании не размера, а адреса. Цитата Для оптимизации - лучше syscall, но для сохранение лучшей архитектурной целостности (микроядерность) - через сообщения. syscall в эффективности не выигрывает - действия точно те же. |
Сообщ.
#291
,
|
|
|
Цитата Если придут два real time сообщения, то одному естественно потребуется ждать (не более кванта). А ведь поэтому не можем гарантировать максимальное время отклика. (Но это уже вопрос философский). Поэтому это Цитата А в том, что я предлагаю, его обработка начнется немедленно. немного ложь. Итак. При каждом сообщении будет создаваться поток. Но стек резервируется не ядром. Будет отложенное создание потоков. Так же не согласен с Цитата Я же 100 раз писал, что для функции выделения памяти адрес - это входной, а не выходной параметр! handle= alloc_mem (stack_addr,stack_size). |
Сообщ.
#292
,
|
|
|
Цитата rcz, 15.10.03, 13:44:56 Поэтому это (А в том, что я предлагаю, его обработка начнется немедленно.) немного ложь. Так можно договориться, что и обычный вызов функции происходит с задержкой, так как может случиться, что в момент вызова квант времени закончится, и придется ждать следующего. Поэтому переформулирую: форма RunMessage гарантирует, что обработка сообщения начнется тогда же, как если бы отправитель делал функциональный вызов. Собственно, это и означает "немедленно", в наиболее естественном смысле этого слова. Цитата Итак. При каждом сообщении будет создаваться поток. Но стек резервируется не ядром. Будет отложенное создание потоков. Так же не согласен с .. В общем, понятно, что к одному мнению не прийти - аргументы высказаны, остались личные предпочтения. Есть смысл перейти пока к базовой технической части, которая не зависит от конкретики ОС. Это библиотека внутренних функций ядра. |
Сообщ.
#293
,
|
|
|
Кто-нибудь знает такой момент: если в обработчике прерывания первой инструкцией стоит cli, то может ли теоретически так случиться, что эта инструкция еще не успеет выполниться, а обработчик будет прерван еще одним прерыванием ?
Также симметричный вопрос: если sti - последняя команда в обработчике прерывания, то может ли получиться, что новое прерывание произойдет до iret ? И еще забыл одну вещь (изучал 12 лет назад и сейчас не вспомню): команда cli запрещает немаскируемые прерывания (и используются ли они вообще в современных процессорах) ? |
Сообщ.
#294
,
|
|
|
Цитата Кто-нибудь знает такой момент: если в обработчике прерывания первой инструкцией стоит cli, то может ли теоретически так случиться, что эта инструкция еще не успеет выполниться, а обработчик будет прерван еще одним прерыванием ? нет. Вызов прерывания очищает флаг прерывания(их запрещает). Т.е cli не нужен Цитата Также симметричный вопрос: если sti - последняя команда в обработчике прерывания, то может ли получиться, что новое прерывание произойдет до iret ? может. Но iret ведь из стека выталкивает регистр флагов и этим сама неявно делает sti. о cli из Intel Architecture Software Developer’s Manual Vol ume 2 :Instruction Set Reference Цитата Clears the IF flag in the EFLAGS register. No other flags are affected. Clearing the IF flag causes the processor to ignore maskable external interrupts. The IF flag and the CLI and STI instruction have no affect on the generation of exceptions and NMI interrupts. |
Сообщ.
#295
,
|
|
|
ftp://ftp.tsu.ru/pub/techdocs/Architecture/INTEL/IA32/24319201.PDF
5.10.1.2 FLAG USAGE BY EXCEPTION- OR INTERRUPT-HANDLER PROCEDURE второй абзац: The only difference between an interrupt gate and a trap gate is the way the processor handles the IF flag in the EFLAGS register. When accessing an exception- or interrupt-handling procedure through an interrupt gate, the processor clears the IF flag to prevents other interrupts from interfering with the current interrupt handler. A subsequent IRET instruction restores the IF flag to its value in the saved contents of the EFLAGS register on the stack. Accessing a handler procedure through a trap gate does not affect the IF flag. Несколько слов о защите флага IF. В случае IRET и POPF, флаг IF будет восстанавливаться только если CPL<=IOPL. Если CPL>IOPL, то флаг IF не изменяется. В случае CLI и STI, будет возникать #GP, если CPL>IOPL. В V86 mode, CPL=3 см. также: 5.5 NONMASKABLE INTERRUPT (NMI) В семействе PC/AT до сих пор есть одно NMI для обработки сбоя в электропитании. При инициализации системы, его желательно запретить. ftp://ftp.tsu.ru/pub/techdocs/Hardware/Intel/Chipset/Ports/PORTS.TXT строка 768: The AT and PS/2 uses port 70H bit 7 to disable 'Non-Maskable' Interrupts (NMI) by setting bit 7 to 0. Enable NMI by setting bit 7 to 1. Note: the PCs use port 0A0H for this purpose. Port 70H on ATs is also used to set a CMOS register index (00-3FH), which is then read from port 71H. |
Сообщ.
#296
,
|
|
|
Спасибо за информацию.
В техническом плане, структура ядра вырисовывается. Общая структура: - ассемблерное наноядро (из двух функций), - ядро на С, состоящее из функции main и функций - обработчиков прерываний. Работа наноядра. Интерфейс: - функция set_interrupt_handler, - глобальная переменная current_TSS, - глобальная переменная current_interrupt. Сценарий работы. Загрузчик запускает main, которая выполняет инициализацию и завершается. При инициализации устанавливаются обработчики значительной части прерываний и всех исключений. Функция set_interrupt_handler прописывает (клонирует) код пред- и постобработки прерывания, а внутри этого кода вызывается обработчик из ядра. Обработчик прерываний (в-частности обработчик таймера) перед выходом может установить в current_TSS другую задачу. Код постобработки полностью очищает стек, помещает в него селектор current_TSS и делает iret. Весь код ядра выполняется с флагом запрещения всех прерываний (NMA запрещается раньше). Внутри ядра (С-части) все вызовы функций - near. Вся деятельность ядра осуществляется в обработчиках прерываний (main служит только инициализации). |
Сообщ.
#297
,
|
|
|
Цитата - ассемблерное наноядро (из двух функций) А вторая это что? А стоит ли так строго разделять по языкам. Точнее получится так: - все архитектурно зависимое (что нельзя выполнить н ЯВУ) или модуль асма. Или асм вставка. (тут будет "код пред- и постобработки прерывания") Цитата При инициализации устанавливаются обработчики значительной части прерываний и всех исключений. Точнее только таймер, и временный жесткий диск(или флоп). (ну и прерывание для syscall'a) По идее далее должны загружаться драйвера и они захватят все остальное. Цитата Функция set_interrupt_handler прописывает (клонирует) код пред- и постобработки прерывания, а внутри этого кода вызывается обработчик из ядра. Для обязательный прерываний (исключения, IRQ, syscall) можно сделать статическим. Без копирования(копирование кода не очень хорошо. Появится ограничение на использование относительной адресации). будет что-то вроде. Для IRQ. intrruptN pushad push N call _CommonInterruptHandler add esp,4 popad iret void (*intrFuncs)() [0x10]; // Обработчики - IRQ. void CommonInterruptHandler (DWORD N) { <pre-code> if(intrFuncs[N]!=NULL) //Есть ли у нас его обработчик? intrFuncs[N](); <post-code> } set_IRQ_handler(Int N, handler) { .. //всякие проверки на занятость. intrFuncs[N]=handler; .. } Для исключений по-другому. Там надо учитывать, что некоторые идут с кодом ошибки, а некоторые без. Тут другой способ обработки. А set_interrupt_handler инициализирует только IDT. Ну так же надо по аналогии ввести функцию set_gdt_entry. И их можно реализовать и на C. Без ASMа. Цитата Код постобработки полностью очищает стек, помещает в него селектор current_TSS и делает iret. Здесь имеется в виду переключение на новую задачу? И нужно ли такое делать для syscall'a? Прерывания будут шлюзами задач, чтоб переключались контексты при входе в ядро? (стек в этом случае, вроде, не надо очищать. Селектор current_TSS будет один и тот же, чтоб не было ограничений на количество задач. Изменяется сам дескриптор в GDT (изменяется база на TSS новой задачи)). |
Сообщ.
#298
,
|
|
|
Цитата rcz, 22.10.03, 01:07:51 А вторая это что? Код пред-постобработки. Цитата А стоит ли так строго разделять по языкам. Точнее получится так: - все архитектурно зависимое (что нельзя выполнить н ЯВУ) или модуль асма. Или асм вставка. (тут будет "код пред- и постобработки прерывания") Вот две функции и нельзя сделать на ЯВУ - остальное, вроде, можно. На С также присутствует архитектурно-зависимая часть - это таблицы всех дескрипторов. И асемблерных вставок в С лучше обойтись только обращениями к портам - остальное вынести в отдельный асемблерный блок (пока из двух функций, но вероятно что-то еще добавится для обработки исключений). Цитата Точнее только таймер, и временный жесткий диск(или флоп). (ну и прерывание для syscall'a) По идее далее должны загружаться драйвера и они захватят все остальное. Все равно предобработка всех прерываний делается ядром, поэтому можно предобработчики поставить сразу - но это несущественный момент, можно и потом. Кстати, ядро видимо должно всегда частично обрабатывать клавиатуру - для отслеживания комбинации перехода в системный монитор (диспетчер задач). Цитата Для обязательный прерываний (исключения, IRQ, syscall) можно сделать статическим. Без копирования(копирование кода не очень хорошо. Появится ограничение на использование относительной адресации). будет что-то вроде. Для IRQ. intrruptN pushad push N call _CommonInterruptHandler add esp,4 popad iret Так этот код как раз и придется клонировать на каждое прерывание. Ведь если на все прерывания сделать один предобработчик, то в нем не получится узнать номер прерывания? Цитата Для исключений по-другому. Там надо учитывать, что некоторые идут с кодом ошибки, а некоторые без. Тут другой способ обработки. Наверное, все различия можно перенести на этап предобработки. Цитата А set_interrupt_handler инициализирует только IDT. ..И их можно реализовать и на C. Без ASMа. Подключить обработчик можно и из С - но для этого нужно раздобыть клон ассемблерного кода предобработки (причем модифицированный). Цитата Здесь имеется в виду переключение на новую задачу? И нужно ли такое делать для syscall'a? А какая разница - на новую задачу, или на ту, из которой был вызов. Технически это одно и то же. Цитата Прерывания будут шлюзами задач, чтоб переключались контексты при входе в ядро? Да. Это некоторая потеря в эффективности (но ведь небольшая ? - оба контекста будут в кэше), зато очень большое упрощение. Цитата Селектор current_TSS будет один и тот же, чтоб не было ограничений на количество задач. Изменяется сам дескриптор в GDT (изменяется база на TSS новой задачи)). Да, пожалуй. |
Сообщ.
#299
,
|
|
|
Цитата Да. Это некоторая потеря в эффективности (но ведь небольшая ? - оба контекста будут в кэше), зато очень большое упрощение. Потеря в эффективности небольшая (Загрузка TSS вроде занимает 80-100 тактов, точно не помню), но в любом случае обработчик должен сохранять регистры. Так что это по эффективности сравнимо. В этом я вижу только один недостаток (наверно поэтому так не делают). Нельзя внутри обработчика разрашить прерывания. (Хотя у нас это микроядро и обработчики ничем кроме отсылки сообщений не занимаются). Но задачи ядра(выполняются ведь в прерываниях) - Определение какая задача будет выполняться следующей, доставка сообщения и т.п. - могут занять время и следовательно время отклика всей системы увеличится. Хотя это можно выкинуть в отдельную задачу ядра, переключение из прерывания будет идти в эту задачу. Она определяет следующий Current_TSS. И как-то (как? отдельное прерывание ?) переключается. Хотя как недостаток(некритический) еще можно считать и выделение TSS на каждое прерывания (трата памяти). Цитата Наверное, все различия можно перенести на этап предобработки. И тогда еще задействовать и постобработку. Исключения с кодом ошибки кидают в стек код ошибки. Его надо после очищать. Цитата Так этот код как раз и придется клонировать на каждое прерывание. Ведь если на все прерывания сделать один предобработчик, то в нем не получится узнать номер прерывания? Клонировать точно придется. Только считаю, что не динамически. Лучше сразу вшить в ядро. (Макросы АСМа помогут) Цитата И асемблерных вставок в С лучше обойтись только обращениями к портам - остальное вынести в отдельный асемблерный блок (пока из двух функций, но вероятно что-то еще добавится для обработки исключений). Но если маленький код, то зачем выносить? И выносить получается надо только тот код обработки прерывания. |
Сообщ.
#300
,
|
|
|
Цитата rcz, 22.10.03, 16:06:56 В этом я вижу только один недостаток (наверно поэтому так не делают). Нельзя внутри обработчика разрашить прерывания. (Хотя у нас это микроядро и обработчики ничем кроме отсылки сообщений не занимаются). Но задачи ядра(выполняются ведь в прерываниях) - Определение какая задача будет выполняться следующей, доставка сообщения и т.п. - могут занять время и следовательно время отклика всей системы увеличится. Хотя это можно выкинуть в отдельную задачу ядра, переключение из прерывания будет идти в эту задачу. Она определяет следующий Current_TSS. И как-то (как? отдельное прерывание ?) переключается. Этот вопрос технический и заведомо решаемый. Видимо нужно предусмотреть в ядре не только режим обработки прерываний, но и режим "обслуживания", который имеет отдельный стек и может прерываться. При этом можно обойтись и без задачного переключения (все можно свести к усложнению постобработки). Но в эти сложности сейчас предлагаю не погружаться, а первую версию делать без гарантий на время реакции (но если пользоваться простым алгоритмом планирования и ограничить размер сообщений, то время реакции будет ограниченным). Все равно сразу все сделать хорошо невозможно, и придется писать не одну версию - потом и можно усовершенствовать. Цитата И тогда еще задействовать и постобработку. Исключения с кодом ошибки кидают в стек код ошибки. Его надо после очищать. Можно в предобработке переносить код ошибки в глобальный параметр и очищать стек сразу. Это мне кажется более безопасным, чем когда С-функция будет брать параметр из стека - нужно будет следить за согласованностью форматов. Потом, когда все обработчики без параметров - это единообразие. Цитата Клонировать точно придется. Только считаю, что не динамически. Лучше сразу вшить в ядро. (Макросы АСМа помогут) А стоит ли? Ведь прерываний теоретически может быть до 2^12=4096 (или меньше?). Если так, то придется сразу выделить на обработчики около 100кб. С другой строны, реально используется прерываний гораздо меньше. Поэтому динамическая генерация обработчиков будет экономнее. Правда получится модификация кода, что считается нехорошо (хотя используя модификацию кода я как-то реализовал очень быструю и компактную процедуру отрисовки линии - так что вещь иногда полезная). Цитата Но если маленький код, то зачем выносить? И выносить получается надо только тот код обработки прерывания. Если встроенный ассемблер достаточно хорош, что можно и не выносить. |