Архив за ‘Кодинг’ категория

А программер ли ты?

Сентябрь 28th, 2008

Вот такая незатейливая игрушка на логику. Прошёл её за 181 команду. Говорят, можно меньше. Но сама идея понравилась очень.

http://www.poparcade.net/swf/light-bot-2205.swf

Первый патч в Open Source-проекте.

Сентябрь 10th, 2008

Сделал первый патч для проекта OpenWatcom.
Пофиксил баги в утилите exe2bin, без которой дальше не мог писать вторичный загрузчик. Ну, заодно, и добавил пару нужных лично мне фич. Баг был серьёзный — buffer overflow, связан с тем, что линкер помещает в relocation table записи в неотсортированном порядке.

Целый день потратил на поиск ошибки. Открыл баг, выложил патч, жду подтверждения…

Баг + патч

Новый релиз

Июнь 15th, 2008

Выпущен июньский релиз ядра под названием jun.2008 Fishes: Pike.
Основные нововведения релиза:
- Добавлена информация о лицензиях.
- Добавлена поддержка PS/2 мыши (экспериментальное).
- Добавлено обнаружение и чтение ATA-устройств в режиме PIO.
- Добавлено устройство /dev/apic/local, ссылающееся на Local APIC.
- Добавлено устройство /dev/apic/timer, ссылающееся на Local APIC Timer.
- Добавлено устройство чтения шины PCI /dev/pci (экспериментальное).
- Добавлены функции sys_* для будущей защиты функций k_* от хаков из user-space.
- Добавлена возможность классом TConsole выводить данные в порт COM1.
- Добавлен класс трассировки ввода-вывода в аппаратные порты для возможности получения лога протокола обмена с устройством.
- Добавлен шлюз 0×07:0×00000000 для организации системных вызовов.
- Добавлены макроопределения для вывода отладки непосредственно в порт COM1.
- Добавлен класс быстрых мьютексов (фьютексов) для возможного исключения взаимоблокировок при разделении ресурса между задачей и обработчиком прерывания.
- Добавлено простое разделение нагрузки между процессорами в SMP.
Изменения:
- Исправлены ошибки в первичном загрузчике, связанные с неправильным кэшированием таблицы FAT.
- Все существующие до этого функции sys_* были переименованы в функции k_*.
- Заменён планировщик на планировщики, ориентированные на SMP.
- Исправлены ошибки в драйвере floppy.
- Исправлены ошибки в хранении данных текстового режима.

Linux RPM

Июнь 13th, 2008

Научился создавать RPM-ки. Занятная вещь :) .
Теперь существует OpenWatcom 1.7.1 в RPM-пакете для openSUSE Linux.

Также Дрон собирался сделать ebuild ваткома. И что-то даже получается ;) .

А вот, кажется, и решение…

Июнь 9th, 2008

Как был такой анекдот:
Когда-то студент подумал. Ему понравилось, и он подумал ещё раз.

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

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

В чём суть? Да в том, что прерывание, вызываемое через interrupt gate, обладает свойством атомарности на процессоре. То есть, не произойдёт никаких других прерываний от внешних устройств, пока сброшен флаг IF (ситуации с исключениями в расчёт не берём).
Ситуацию с прерываниями мы научились решать. Если вектор прерывания доставляется всем процессорам, то чтобы заставить его обработать только один процессор, необходимо воспользоваться Try-Lock конструкцией:

в обработчике прерываний:
TMutex Mutex;

interrupt_handler:
  if (Mutex.TryLock()) // Защита от запуска обработчика на других процессорах
  {
    // Обработчик прерывания
    Mutex.Unlock(); // Разблокировка мьютекса
  }

Таким образом, код обработчика выполняется тем процессором, который первым смог захватить объект Mutex (напомню, метод TryLock() — это блокировка без ожидания. То есть, если не удалось заблокировать мьютекс, то сразу же функция возвращает false, удалось — true).

Хорошо. Теперь необходимо решить обратную задачу. Необходимо параллельный код превратить в последовательный. Для этого достаточно воспользоваться некоторой модификацией приведённой ранее схемы.

В данном случае надо уже использовать не попытку блокировки ресурса, а уже полноценную блокировку с ожиданием, но при этом гарантировать, что обработчик и задача будут вызываться на разных процессорах. Осуществить это можно, как я уже говорил, сведя задачу к обработчику прерывания. А отличие между ними очень небольшое: в обработчике флаг IF сброшен, а в задаче — установлен. Поэтому код блокировки ресурса задачей надо обернуть в CLI + STI блок.

Итого получим асимметричную схему блокировки объекта Mutex1:
в обработчике прерывания:
  if (Mutex.TryLock()) // Защита от запуска обработчика на других процессорах
  {
    // Обработчик прерывания
    Mutex1.Lock();
    // Код, использующий общие данные, требующие блокировки
    Mutex1.Unlock(); // Разблокировка мьютекса блокировки данных

    Mutex.Unlock(); // Разблокировка мьютекса прерываний
  }
в задаче:
  _asm cli; // Запрещение прерываний, мы как бы в обработчике
  Mutex1.Lock();
  // Код, использующий общие данные, требующие блокировки
  Mutex1.Unlock(); // Разблокировка мьютекса блокировки данных
  _asm sti; // Разрешение прерываний, мы как бы вышли из обработчика

Вот, в принципе, и всё решение, которое гарантирует, что:
1. Обработчик прерывания и задача будут выполняться одновременно на разных процессорах.
2. Код доступа к общим данным будет выполняться последовательно в зависимости от того, кто первым захватил ресурс.

UPD:
Соответственно, всё это выполняется в Kernel Mode (ring 0), а для асимметричной блокировки можно написать отдельный специальный класс блокировки.

Вот так всегда…

Июнь 9th, 2008

То, что работает на одном процессоре, может гарантированно не работать на нескольких.
Стоило мне только разделить нагрузку между процессорами, и драйвер клавиатуры+мыши стал глючить.
В чём же прикол? А вот в следующем:
1. на одном проце задачи пытается модифицировать содержимое портов.
2. на другом проце в это время срабатывает прерывание, которому тоже нужны порты.
Следствие: оба проца пытаются биться за один и тот же порт.

Как разрешить данную ситуацию?
1. Ввести мьютекс и блокировать его при попытке обращения к портам. Данный метод очень опасен. Например, в случае, если обращение к портам со стороны задачи и прерывание происходят на одном и том же процессоре. В таком случае Dead Lock обеспечен.
2. Разрешить прерывания в обработчике. Опасна эта вещь повторной входимостью а также Dead Lock’ом, так как в случае однопроцессорной системы не будет приходить прерывание от таймера, пока не будет послан EOI в PIC. Если же слать EOI при входе в обработчик, Dead Lock так же обеспечен (повторная попытка заблокировать мьютекс).
3. Блокиовать прерывания от устройства-инициатора прерываний на время его программирования. Также чревато тем, что, возможно, ответ от устройства приходит именно с прерыванием.

Таким образом, все виды блокировок являются недопустимыми из-за их неполноценности. Обдумываю возможные другие варианты…

Первое приложение под Qt — уже прогресс!

Апрель 23rd, 2008

Вот так выглядит моя программа по расчёту циклических кодов к курсовой работе:

Free Image Hosting at www.ImageShack.us

QuickPost

Смени мозги.

Март 26th, 2008

Задача: написать shell-скрипт, который выкидывает в файл список файлов в директории, претерпевших изменение после такой-то даты. Я придумал такую конструкцию:

find . -type f -printf «%TY-%Tm-%Td %TH:%TM:%TS %p\n» | awk ‘{ if (sprintf(«%s %s», $1, $2) > «2008-03-24 20:00:00″) { printf(«%s», $3); for (i=4; i<=NF; i++) printf(» %s», $i); printf(«\n»); } }’ >out.txt

Интересно, есть ещё какие-нибудь «более красивые» варианты?

Внимательность прежде всего.

Март 25th, 2008

Что-то сдаётся мне, я стал терять внимательность. Багу искал очень долго и, наконец, мегазавихляцким методом нашёл её. А ошибка была такая дурная, что аж обидно. Итак, вопрос на засыпку: какая ошибка в этом коде:

bool TATADirectory::ReadPIO(void *buffer, size_t count)
{
    if (WaitInterrupt(ATA_PIO_TIMEOUT))
    {
        if (WaitDevice(ATA_PIO_TIMEOUT, ATA_SR_DRQ))
        {
            TIntHandler::ClearInterrupts();
            inportwv(FPort.GetAddress()+ATA_REG_DR, buffer, count);
            TIntHandler::SetupInterrupts();
            return true;
        }
    }
    return false;
}

А вот и правильный ответ:
inportwv(FPort.GetAddress()+ATA_REG_DR, buffer, count>>1); // It is a word register!!!
Регистр ATA — двухбайтовый, и читать из него следует по два байта (что и отражено в имени функции — inportwv, которая читает массив слов из порта (rep insw)). Однако, передаётся в качестве счётчика число байт, а не слов. Поэтому происходит переполнение буфера и, как следствие, порча данных в ядре.

Крутой PR

Март 3rd, 2008

Вот все всегда задумываются, как лучше всего организовать свою собственную рекламу? Одни придумывают слоганы, вторые организуют якобы бесплатные подарки (цена которых включена в стоимость товара, за который дают подарок :) ))), третьи покупают/подкупают СМИ, а четвёртые… А четвёртые придумывают оригинальные, полезные и приятные решения, и хороший пример тому — Intel.

Пример грамотной рекламы…