OSDev http://osdev.su/ |
|
Реализация многозадачности/многопоточности http://osdev.su/viewtopic.php?f=6&t=778 |
Страница 15 из 16 |
Автор: | vlad9486 [ 02 сен 2013, 22:10 ] |
Заголовок сообщения: | Re: Реализация многозадачности/многопоточности |
Цитата: Тогда непонятно как технически, скажем один байт, из ВАП одного процесса попадает в ВАП другого? На примитивном уровне - один процесс куда-то этот байт кладет, другой его оттуда берет. Программа пишет в регистры, дергает int (или что там на данной архитектуре), ядро переключает ВАПы не затирая те регистры, переходит на другую программу, все. По-моему это самый быстрый способ. |
Автор: | Nable [ 02 сен 2013, 23:01 ] |
Заголовок сообщения: | Re: Реализация многозадачности/многопоточности |
maisvendoo писал(а): то с приложениями не хорошо - пользователь не обязна применять каких то мер по выходу из программы, кроме return Это кто сказал? Точнее, смотря как посмотреть. Ведь main - обычно не точка входа в программу (особенно если используются всякие argv), а функция, вызываемая из libc (с которой линкуются программы) для твоей системы.Собсно, в тех же линухах если не используешь libc, то нужно завершать программу с помощью системного вызова (того самого exit), ибо делать ret некуда. Так что пихать на стек адрес возврата при создании процесса совсем не обязательно - просто сделай вызов exit (заодно сможешь реализовать всякие atexit) в своей libc. При создании потока - аналогично, просто вместо stdlib/libc говорим о libpthread (точнее, её аналоге в твоей системе). Хотя с другой стороны, нынче модны всякие VDSO, так что организовывать выход из программы/треда как ret на адрес вызова соответствующего системного вызова - выглядит в чём-то даже и красиво. |
Автор: | maisvendoo [ 02 сен 2013, 23:14 ] |
Заголовок сообщения: | Re: Реализация многозадачности/многопоточности |
Nable писал(а): Собсно, в тех же линухах если не используешь libc, то нужно завершать программу с помощью системного вызова (того самого exit), ибо делать ret некуда. С программами не сталкивался, а вот с потоками - без pthread_exit() вылезают всякие нехорошести и программа аварийно завершается. А вот в венде, насколько я помню, потоки завершаются автоматом - за счет библиотек тоже, получается? Nable писал(а): Собсно, в тех же линухах если не используешь libc Да, без libc никогда не писал под линукс. Nable писал(а): Хотя с другой стороны, нынче модны всякие VDSO, так что организовывать выход из программы/треда как ret на адрес вызова соответствующего системного вызова - выглядит в чём-то даже и красиво. Ценность этого хотя бы в получении полезного опыта разбора кода в отладчике и понимании происходящего |
Автор: | phantom-84 [ 03 сен 2013, 00:56 ] |
Заголовок сообщения: | Re: Реализация многозадачности/многопоточности |
vlad9486 писал(а): Программа пишет в регистры, дергает int (или что там на данной архитектуре), ядро переключает ВАПы не затирая те регистры, переходит на другую программу, все. По-моему это самый быстрый способ. В конечном счете все равно получится через память, причем скорее всего не самым простым путем.maisvendoo писал(а): Тот leave который попортил мне крови... Так вот - в начале в стек проталкивается EBP, затем в него записывается значение ESP. Команда LEAVE делает наоборот - записывает в ESP значение сохраненное ранее в EBP. То есть в конце ESP указывает место в стеке, лежащее на 4 байта глубже той позиции, на которую указывал ESP после передачи управления на данную задачу. И если перед запуском задачи мы поместим в эту позицию адрес возврата, то при завершении функции потока уйдем контролируемо в нужное нам место ядра. Если по-простому: подложить в стек адрес возврата из функции потока - я тебе давно говорил об этом способе - хорошо подходит для потоков ядра, позволяя сэкономить несколько байт на коде завершения основной функции потока.Для ядерный потоков и процессов можно сразу передать управление в функцию уничтожения потока/процесса, с последующим переходом на планировщик. Что такое ядерные процессы, я не совсем понимаю. У меня ядерный процесс только один - первичная задача. Кстати это единственный процесс, в контексте которого вызов exit не приводит к безусловному уничтожению процесса. Возможен и перезапуск исполняемого модуля в контексте этого же процесса. Естественно, код исполняемого модуля работает в user mode. Цитата: Для прикладных потоков сначала надо перейти в Ring 0, ибо при обычном прыжке мы останемся в Ring 3. Для этого я использовал системный вызов такого вида... А я бы использовал такой:Код: .global kernel_mode_switch Или в крайнем случае такой:kernel_mode_switch: ; destroy_proc: Код: .global kernel_mode_switch .extern destroy_proc kernel_mode_switch: jmp destroy_proc Цитата: После этого управление переходит к функции уничтожения потока/процесса. Вообще-то это две разные функции, хотя конечно можно договориться считать выход из первичного потока процесса завершением процесса, но зачем? Чтобы завершать процесс в контексте других его потоков через трехэтажные костыли что ли?Естественно, exit(process) есть везде. У меня так вообще на самом первом месте (SI_EXIT=0). |
Автор: | maisvendoo [ 03 сен 2013, 07:45 ] |
Заголовок сообщения: | Re: Реализация многозадачности/многопоточности |
phantom-84 писал(а): Или в крайнем случае такой: Код: .global kernel_mode_switch .extern destroy_proc kernel_mode_switch: jmp destroy_proc Дальний переход, для загрузки CS? Да я так и хотел в начале, просто я запутался с привилегиями при загрузке DS (не мог понять почему его загрузка вызывает #GP, потом разобрался) и сделал так "академично" через iret phantom-84 писал(а): Вообще-то это две разные функциих имел в виду как раз две разные функции - одна у меня thread_exit, другая destroy_proc. phantom-84 писал(а): Что такое ядерные процессы, я не совсем понимаю. у меня есть возможность запустить процесс в ring 0. Осталась опционально после того как начал с ними работать, для тестирования оставил пока phantom-84 писал(а): Естественно, exit(process) есть везде. Можно предусмотреть оба механизма, тем более что это место в стеке - куда помещается адрес перехода на destroy - всё равно пустует. Просто автоматическое завершение средствами системы - мне такой подход нравится. P.S.: Переделал вот так (как и хотел с самого начала) Код: .global kernel_mode_switch .extern destroy_proc kernel_mode_switch: ljmp $KERNEL_CS,$destroy_proc просто я не учел, что без прерывания не смогу загрузить DS из Ring 3. |
Автор: | phantom-84 [ 03 сен 2013, 10:09 ] |
Заголовок сообщения: | Re: Реализация многозадачности/многопоточности |
maisvendoo писал(а): Дальний переход, для загрузки CS? Да я так и хотел в начале, просто я запутался с привилегиями при загрузке DS (не мог понять почему его загрузка вызывает #GP, потом разобрался) и сделал так "академично" через iret Что-то я запутался. Я думал, речь идет о реализации системного вызова (в смысле "системной функции") внутри ядра, поэтому использовал именно ближний переход. Ты же вроде писал, что используешь для входа в ядро int 50h. Использовать iret или прямой far call/far jump для этих целей нельзя, все вызовы нужно выполнять через шлюзы.Цитата: Можно предусмотреть оба механизма, тем более что это место в стеке - куда помещается адрес перехода на destroy - всё равно пустует. Почему пустует? Для этого адреса нужно отдельно предусмотреть место в стеке: под вершиной стека будет располагаться адрес destroy, а ниже (стековый) стартовый кадр потока.
|
Автор: | maisvendoo [ 03 сен 2013, 11:30 ] |
Заголовок сообщения: | Re: Реализация многозадачности/многопоточности |
phantom-84 писал(а): Что-то я запутался. Я думал, речь идет о реализации системного вызова (в смысле "системной функции") внутри ядра, поэтому использовал именно ближний переход. Ты же вроде писал, что используешь для входа в ядро int 50h. Использовать iret или прямой far call/far jump для этих целей нельзя, все вызовы нужно выполнять через шлюзы. Так и есть. Эта функция - kernel_mode_switch() - выполняется внутри обработчика int 50h. В стек помещается не её адрес, а адрес syscall_kernel_mode_switch() А первоначально я её просто пробовал выполнять, находясь в ring 3 сразу после возврата из завершаемого процесса. У меня же большая часть ядра написана на C, ассемблерными выполнены только функции, критичные к чистоте кода (в смысле без лишних телодвижений типа leave). Обработчик сделан так Код: /*----------------------------------------------------------------------------- * System call handler *---------------------------------------------------------------------------*/ void syscall_handler(registers_t regs) { if (regs.eax >= NUM_CALLS) return; void* syscall = calls_table[regs.eax]; syscall_entry_call(syscall); } Таблица системных функций Код: /*----------------------------------------------------------------------------- * System calls table *---------------------------------------------------------------------------*/ void* calls_table[NUM_CALLS] = { /* System calls for test */ &print_text, &print_dec_value, &vprint_text, &get_digit, &in_byte, &out_byte, &kernel_mode_switch }; Функция, передающая управление на табличную Код: /*----------------------------------------------------------------------------- / / Call system call function / (c) maisvendoo, 21.08.2013 / /----------------------------------------------------------------------------*/ .global syscall_entry_call syscall_entry_call: push %edi push %esi push %edx /* Push params into stack */ push %ecx push %ebx mov 24(%esp), %edx /* entry_point --> EDX */ call *%edx /* Call function at address in EDX */ add $20, %esp /* Clean stack */ ret Да собсна в блоге всё описано P.S.: Я понял тебя - можно обойтись ближним переходом - мы же в предалах сегмента кода ядра, при вызове kernel_mode_switch() P.P.S.: Пока читал лекцию, посетила идея как переделать механизм системных вызовов. Переделал - теперь код компонуемый с приложениями можно реально сократить в разы. Кроме того, наконец появилась возможность включить защиту памяти на уровне страниц - станицы ядра и видеопамять с атрибутами 0x03. А до этого всё было 0х07 phantom-84 писал(а): Мне не нравятся твои cli/sti с достаточно большим объемом кода между ними Убрал. Для большинства мест где это делается достаточно просто остановить планировщик phantom-84 писал(а): Я большую часть работы выполняю в контексте первичного потока нового процесса - не нужно делать лишние переключения ВАП. Сделал и без лишних переключений. Теперь в стеке создаваемого потока адрес функции, которая сначала заряжает ELF в память, потом включает user mode с переходом на entrypoint программы. Вся работа с памятью процесса ведется в его контексте, загрузка CR3 производится только планировщиком, при переключении задач. И, надо сказать, когда выполнялись эти лишние переключения каталога было невозможно запустить процесс из процесса - вылетал #PF. Теперь всё ок - убрал лишние переключения - дочерний процесс сразу взлетел |
Автор: | phantom-84 [ 03 сен 2013, 20:44 ] |
Заголовок сообщения: | Re: Реализация многозадачности/многопоточности |
По мелочам... Функция вызова системного вызова - совершенно ненужное усложнение. Попробуй избавиться от лишних вложенных вызовов. Нет необходимости использовать промежуточный регистр, т.е. допустима команда call dword [esp+24]. Но а в общем лучше выйти на такую команду вызова: call [calls_table+eax*4]. У меня и с 0x07 страничная защита работает, т.к. пространство ядра защищается на уровне входов каталога страниц. Чем меньше различий в распределении прикладных и ядерных страниц, тем удобнее. |
Автор: | maisvendoo [ 03 сен 2013, 20:54 ] |
Заголовок сообщения: | Re: Реализация многозадачности/многопоточности |
phantom-84 писал(а): Попробуй избавиться от лишних вложенных вызовов. Попробую. Кстати - офигенная идея запуска процесса в его же контексте. Ни прерывания выключать не надо, ни планировщик останавливать. Задача запускает сама себя |
Страница 15 из 16 | Часовой пояс: UTC + 3 часа |
Powered by phpBB® Forum Software © phpBB Group http://www.phpbb.com/ |