OSDev

для всех
Текущее время: 27 апр 2024, 20:22

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 160 ]  На страницу 1, 2, 3, 4, 5 ... 16  След.
Автор Сообщение
СообщениеДобавлено: 12 авг 2013, 11:52 
Аватара пользователя

Зарегистрирован: 25 июл 2013, 08:45
Сообщения: 141
Откуда: Новочеркасск
Решил вынести это в отдельную тему.

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

Приведу код
Инициализация планировщика
Код:
/*-----------------------------------------------------------------------------
 *
 *---------------------------------------------------------------------------*/
void init_task_manager(void)
{
   /* Запоминаем текущий контекст выполнения */
   u32int   eip = read_eip();
   u32int   esp = read_esp();
   u32int   ebp   = read_ebp();

        /* Выход, если прошла инициализация */
   if (multi_task)
   {
      return;
   }

   /* Вырубаем прерывания */
   asm volatile ("cli");

   list_init(&process_list);
   list_init(&thread_list);

   /* Создаем процесс ядра */
   kernel_proc = (process_t*) kmalloc(sizeof(process_t));

   memset(kernel_proc, 0, sizeof(process_t));

   kernel_proc->pid = next_pid++;
   kernel_proc->page_dir = get_kernel_dir();
   kernel_proc->list_item.list = NULL;
   kernel_proc->threads_count = 1;
   strcpy(kernel_proc->name, "Kernel");
   kernel_proc->suspend = false;

   list_add(&process_list, &kernel_proc->list_item);

   /* Создаем главный поток ядра */
   kernel_thread = (thread_t*) kmalloc(sizeof(thread_t));

   memset(kernel_thread, 0, sizeof(thread_t));

   kernel_thread->process = kernel_proc;
   kernel_thread->list_item.list = NULL;
   kernel_thread->id = next_thread_id++;
   kernel_thread->stack_size = 0x4000;
   kernel_thread->suspend = false;
   kernel_thread->state.eip = eip;
   kernel_thread->state.esp = esp;
   kernel_thread->state.ebp = ebp;

   list_add(&thread_list, &kernel_thread->list_item);

   current_proc = kernel_proc;
   current_thread = kernel_thread;

   /* Включаем прерывания */
   multi_task = true;

   asm volatile ("sti");
}


Переключение потоков
Код:
/*-----------------------------------------------------------------------------
 *
 *---------------------------------------------------------------------------*/
void switch_task(registers_t regs)
{
   if (multi_task)
   {
      /* Вырубаем прерывания */
      asm volatile ("cli");

      /* Запоминаем состояние текущего потока */
      current_thread->state.eip = regs.eip;
      current_thread->state.esp = regs.esp;

      /* Берем новый поток из кольцевой очереди */
      current_thread = (thread_t*) current_thread->list_item.next;

      /* Помещаем адрес перехода в новую задачу в ECX */
      asm volatile ("mov %0, %%ecx"::"a"(current_thread->state.eip));
      /* Меняем директорию страниц */
      asm volatile ("mov %0, %%cr3"::"a"(current_proc->page_dir));
      /* Устанавливаем ESP */
      asm volatile ("mov %0, %%esp"::"a"(current_thread->state.esp));

      /* Включаем прерывания */
      asm volatile ("sti");

      /* Прыгаем на новый поток */
      asm volatile ("jmp *%ecx");
   }
}


Создание потока
Код:
/*-----------------------------------------------------------------------------
 *
 *---------------------------------------------------------------------------*/
void create_thread(process_t* proc,      /* Process */
                  void* entry_point,   /* Entry point of thread */
                  size_t stack_size,   /* Size of thread's stack */
                  bool kernel,         /* Kernel CPL */
                  bool suspend,      /* Suspended thread */
                  thread_t* thread)   /* Thread's handler */
{
   void* stack = NULL;

   /* Выключаем прерывания */
   asm volatile ("cli");

   /* Создаем новый описатель потока */
   thread_t* tmp_thread = (thread_t*) kmalloc(sizeof(thread_t));

   /* Чистим выделеную память */
   memset(tmp_thread, 0, sizeof(thread_t));

   /* Инициализируем поток  */
   tmp_thread->id = next_thread_id++;
   tmp_thread->list_item.list = NULL;
   tmp_thread->process = proc;
   tmp_thread->stack_size = stack_size;
   tmp_thread->suspend = suspend;/* */
   tmp_thread->state.eip = (u32int) entry_point;

   /* Создаем свой стек для потока */
   stack = kmalloc(stack_size);

   tmp_thread->state.esp = (u32int) stack + stack_size;
   tmp_thread->state.ebp = 0;

   /* Ставим поток в очередь на выполнение */
   list_add(&thread_list, &tmp_thread->list_item);

   /* Увеличиваем счетчик потоков процесса которому принадлежит данный поток */
   proc->threads_count++;

   /* Возвращаем описатель */
   thread = tmp_thread;

   /* Включаем прерывания */
   asm volatile ("sti");
}


Реализована кольцевая очередь, спасибо phantom-84 за идею :)

При тестировании всё это запускается вот так
Код:
/*------------------------------------------------------------------------------
//
//   PhantomEx main kernel module
//   (c) maisvendoo, 04.07.2013
//
//----------------------------------------------------------------------------*/
#include   "main.h"

u32int init_esp;

thread_t* thread01;
thread_t* thread02;
thread_t* thread03;

void task01(void)
{
   print_text("I'm task #1\n");

   while (1);
}

void task02(void)
{
   print_text("I'm task #2\n");

   while (1);
}

void task03(void)
{
   print_text("I'm task #3\n");

   while (1);
}

/*------------------------------------------------------------------------------
//   Startup function
//----------------------------------------------------------------------------*/
int main(multiboot_header_t* mboot, u32int initial_esp)
{
   init_esp = initial_esp;

   init_descriptor_tables();

   /* Регистрация обработчиков процессорных исключений */
   register_interrupt_handler(INT_0, &division_by_zero);
   register_interrupt_handler(INT_6, &fault_opcode);
   register_interrupt_handler(INT_8, &double_error);
   register_interrupt_handler(INT_10, &invalid_tss);
   register_interrupt_handler(INT_11, &segment_is_not_available);
   register_interrupt_handler(INT_12, &stack_error);
   register_interrupt_handler(INT_13, &general_protection_error);
   register_interrupt_handler(INT_14, &page_fault);

   /* Инициализация менеджера памяти */
   check_memory_map((memory_map_entry_t*) mboot->mmap_addr, mboot->mmap_length);
   init_memory_manager(init_esp);

   /* Инициализация таймера */
   init_timer(BASE_FREQ);

   /* Врубаем прерывания */
   asm volatile ("sti");

   /* Инициализируем планировщик */
   init_task_manager();

        /* Получаем описатель текущего процесса */
   process_t* kern_proc = get_current_proc();
       
        /* Создаем потоки */
   create_thread(kern_proc,
               &task01,
               0x4000,
               true,
               false,
               thread01);

   create_thread(kern_proc,
              &task02,
              0x4000,
              true,
              false,
              thread02);

   create_thread(kern_proc,
              &task03,
              0x4000,
              true,
              false,
              thread03);

   while (1);

   return 0;
}


Ну вот такой результат...
Изображение


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 12:44 

Зарегистрирован: 10 май 2007, 11:33
Сообщения: 1206
maisvendoo писал(а):
осталась одна проблема - через некоторое время машина перезагружается, основное подозрение - переполнение стеков потоков, так как в процессе переключения esp потока сползает всё ниже и ниже. При увеличении интервала таймера и размера стека всё это живет чуть дольше.
Этой проблемы не должно быть, т.к. у каждого потока свой стек.

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 13:02 
Аватара пользователя

Зарегистрирован: 16 май 2007, 23:46
Сообщения: 1126
У меня лично ESP никуда не ползёт. Но стек портится.
Работаю в нулевом кольце. Думаю из-за этого. Диапазоны разные, но вот таймер лезет в оба диапазона.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 13:10 

Зарегистрирован: 10 май 2007, 11:33
Сообщения: 1206
Мы пока только о нулевом кольце и говорим. Последнее предложение не понял. Если речь идет о затрагивании таймерным обработчиком обоих стеков, то это вполне нормально. При переключении по таймеру таймерный обработчик начинает свою работу в контексте одного потока (оставляет кадр возобновления в его стеке), а завершает свою работу в контексте другого потока (извлекает кадр возобновления из его стека).


Последний раз редактировалось phantom-84 12 авг 2013, 13:26, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 13:18 

Зарегистрирован: 31 июл 2013, 09:40
Сообщения: 28
при вызове обработчика система кладет в стек адрес возврата, а также флаги. наверное, потому стек и ползет.

в коде особо не разбирался, так что не кидайтесь тапками


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 13:25 
Аватара пользователя

Зарегистрирован: 16 май 2007, 23:46
Сообщения: 1126
В нуливом кольце, стек прерывания не создаётся, а используется текущая вершина стека.
При прерывание в любом другом кольце, создаётся новый стек.
Т.е. кандидат на порчу стека это прерывание таймера.

Как я говуорил ESP у меня не растёт и это легко проверяется. Пускаю 2 задачи которые просто весят в бесконечном цикле и ничего не делают. И такие задачи нормально висят не падают.
А больше идей о проверки нет.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 13:45 

Зарегистрирован: 10 май 2007, 11:33
Сообщения: 1206
pavia писал(а):
В нуливом кольце, стек прерывания не создаётся, а используется текущая вершина стека.
Если внутри обработчика не происходит переключения, то это так. А если происходит, то см. выше, что я дописал.

Цитата:
При прерывание в любом другом кольце, создаётся новый стек.
У меня используется текущий стек ядра, т.к. он живет постоянно, пока жив связанный с ним поток.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 15:19 
Аватара пользователя

Зарегистрирован: 25 июл 2013, 08:45
Сообщения: 141
Откуда: Новочеркасск
Запуск "немых" потоков-бездельников через некоторое время вызывает исключение
Изображение
INT 06h - неверный код операции.

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 16:21 
Аватара пользователя

Зарегистрирован: 16 май 2007, 23:46
Сообщения: 1126
Это скорее всего не правильно стековый фрейм заполнил для возврата вот он тебя и отправляет чёрти куда.

Код:
Type
 StackFrame=record
   RetIP:Dword;
   EAX:DWord;
   DS:DWord;
   case integer of
   0: IntStackFrame0: record
       EIP:DWord;
       CS:DWord;
       EFlags:DWord;
       end;
  1: IntStackFrame3: record
       EIP:DWord;
       CS:DWord;
       EFlags:DWord;
       ESP:DWord;
       SS:DWord;
       end;   
  end;   
  end;   

Покажи код обработки прерывания что в стек кладёшь и как.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 12 авг 2013, 17:13 
Аватара пользователя

Зарегистрирован: 25 июл 2013, 08:45
Сообщения: 141
Откуда: Новочеркасск
Вообще любое аппаратное прерывание, в том числе IRQ0, обрабатывается у меня вот так
Код:
/*-------------------------------------------
// Общая часть обработчика IRQ
//-----------------------------------------*/
.extern irq_handler
     
irq_common_stub:
     
      pusha                 /* Спасаем РОН в стеке */
     
      mov    %ds, %ax       /* Спасаем селектор сегмента данных */
      push   %eax
     
      mov    $0x10, %ax     /* Загружаем сегмент данных ядра */
      mov    %ax, %ds
      mov    %ax, %es
      mov    %ax, %fs
      mov    %ax, %gs
     
      call   irq_handler    /* Передаем управление обработчику IRQ */
     
      pop    %ebx           /* Выталкиваем из стека селектор */
      mov    %bx, %ds       /* Восстанавливаем сегмент данных */
      mov    %bx, %es
      mov    %bx, %fs
      mov    %bx, %gs
     
      popa                  /* Восстанавливаем РОН */
     
      add    $8, %esp       /* Убираем из стека код ошибки */
                            /* и помещаем туда номер ISR */

      sti                   /* Вновь разрешаем прерывания */

      iret                  /* Возврат из обработчика */
                            /* с восстановлением состояния */
                            /* процессора */

irq_handler - это уже написанный на С обработчик.

Кажется для обработки IRQ0 при переключении задач необходимо поступать несколько иначе, я так понимаю? В нулевом кольце проталкивать нужно
Код:
EIP:DWord;
CS:DWord;
EFlags:DWord;

а в user mode
Код:
EIP:DWord;
CS:DWord;
EFlags:DWord;
ESP:DWord;
SS:DWord;
?


Последний раз редактировалось maisvendoo 12 авг 2013, 17:56, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 160 ]  На страницу 1, 2, 3, 4, 5 ... 16  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 13


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB