ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
Государственное образовательное учреждение

высшего профессионального образования

ОБНИНСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
АТОМНОЙ ЭНЕРГЕТИКИ

(ИАТЭ)

 

 

 

 

Факультет «Кибернетики»

Кафедра «Компьютерные Системы, Сети и Технологии»

 

 

 

 

 

 

 

ОТЧЕТ ПО ПРОИЗВОДСТВЕННОЙ ПРАКТИКЕ

На тему : «Разработка и создание элементов ОС на основе микроядра»

 

 

 

 

 

                                                                                             

 

 

Выполнил:

 

 

 

_______________

Студент  5  курса

Группы ВТ-3-00

Специальности 220100

Дневного отделения

Степанов А. В.

Email:

andrusha [at] gmail [dot] com

 

 

Руководитель:

 

_______________

Зав лабораторией

ЛТТ ГУ ВНИИГМИ-МЦД

к.т.н.

Шаймарданов В.М.

 

                                                                      

 

 

М.П.

 

 

 

 

Обнинск 2005 г.


Содержание

Содержание.. - 2 -

Реферат.. - 3 -

Определения.. - 4 -

Введение.. - 5 -

1. Микроядро L4Ka::Pistachio.. - 6 -

1.2. Kenge. - 7 -

1.2. Iguana. - 7 -

2. Рабочая среда.. - 7 -

3. Платформа проведения тестов и опытов.. - 8 -

4. Реализация семафоров POSIX для приложений микроядра L4. - 9 -

4.1. Понятие семафора. - 9 -

4.2. Функции для работы с семафорами Posix: sem_init и sem_destroy. - 10 -

4.3. Функции для работы с семафорами Posix: sem_wait и sem_trywait - 11 -

4.4. Функции для работы с семафорами Posix: sem_post и sem_getvalue. - 12 -

4.5. Особенности реализации. - 12 -

5. Сервер ethernet карт.. - 14 -

5.1. Преследуемая цель создания. - 14 -

5.2. Основополагающая база. - 15 -

5.3. Общая структура драйвера сетевой карты.. - 16 -

5.4. Архитектура Ethernet сервера. - 18 -

6. Click роутер. - 24 -

6.1. Ознакомление. - 25 -

6.2. Портирование. - 25 -

6.3. Проверка работоспособности Click роутера как приложения микроядра. - 26 -

7. Заключение.. - 28 -

Список использованных источников.. - 29 -

Приложение 1. - 30 -

Приложение 2. - 41 -

Приложение 3. - 48 -


Реферат

 

 53 стр., 6 рис., 4 ист., 3 прил..

 

Способы построения и проектирования ОС, ОС основанные на микроядре, L4::Pistachio, разработка сервисов и приложений для работы в окружении микроядра, программный роутер Click, семафоры POSIX.

Данная преддипломная практика посвящена изучению основ разработки сервисов и подсистем ОС, основных возможностей микроядра L4Ka::Pistachio, а также методов оптимизации и защиты работы приложений. Целью данной работы является освоение возможностей микроядра L4Ka::Pistachio и предоставляемый им интерфейс. Задачи, решены в ходе выполнения работы:

§        Проанализированы научные доклады исследовательских групп и определены ведущие принципы проектирования и взаимосвязи элементов ОС.

§        Изучены инструментальные средства построения сервисов ОС (С++, GNU make, scons, GNU arch, IDL, vim, cscope и др).

§        Спроектированы сервисы ОС.

Для реализации поставленных задач используется рабочая станция на платформе многозадачной многопользовательской операционной системы Linux; микроядро L4Ka::Pistachio X.2 (rev. 6), рабочее окружение Kenge/Iguana, спецификации и рекомендации стандарта POSIX для реализации семафоров, сетевое ПО EtherBoot.


Определения

 

В настоящей преддипломной практике применяют следующие термины с соответствующими определениями:

§        ООП – Объектное Ориентированное Программирование

§        DMADirect Memory Access (прямой доступ к памяти)

§        CPU – Central Process Unit (процессор)

§        ЗД – Защитный домен

§        NATNetwork Address Translation (сетевое преобразование адресов)

§        BIOSBasic Iput Output System (базовая система ввода/вывода)

§        IPSEC – Сетевой транспортный протокол, передающий данные в зашифрованном виде

§        NIC – Network Interface Card (сетевая карта)


Введение

 

Мы живем во время эффективного развития информационных, электронных,

коммуникационных технологий. В нашей повседневной жизни дне мы сталкиваемся с множеством различных технических устройств. С каждым новым днем появляются все новые сетевые, фото, аудио, видео, охранные, коммуникационные устройства. Прогресс начинает затрагивать очень специфические области человеческой деятельности. Каждое новое устройство наделено новыми особенными функциями, которые зависят от своей области применения. Такие системы выполняют одно или некое маленькое семейство приложений со специализированными нуждами. В целях долгой, экономичной, непрерывной работы нагрузка на память, CPU и периферийные устройства должна быть сведена к минимуму.  Требования к программному и аппаратному обеспечению постоянно и очень быстро меняются. От систем требуется гибкой поддержки в контроле, диагностике и оптимизации. Становиться очевидно, что писать каждый раз или переносить старое ПО на новую аппаратную платформу должно происходить с максимальной скоростью. При написании ПО необходимо избегать включения и использования низкоуровневых функций для работы с периферией или управлением работы CPU. Также для обеспечения гибкости, должна быть предоставлена возможность по изменению рабочей конфигурации системы. В каждой системе есть участок кода, который занимает определенное привилегированное место, и выполняет базовые функции синхронизации, коммуникации, защиты и взаимодействует с аппаратным слоем. Поэтому наиболее целесообразно выделить и создать некую общую платформу, которая бы предоставляла в независимости от аппаратной платформы набор базовых функций. Так было введено понятие микроядра.

Микроядро функционально несет необходимою, но не избыточную нагрузку, которая позволяет реализовать различные в зависимости от требований системы. Причем при проектировании конкретной системы, не накладывается никаких ограничений на архитектуру. Введение понятия микроядра, дало толчок к возникновению новых направлений в исследованиях по построению ОС.

 

 

1.    Микроядро L4Ka::Pistachio

 

Микроядро L4 имеет много реализаций, оно разрабатывается несколькими исследовательскими группами университетов:

§         System Architecture Group университета Karlsruhe

§         DiSy Group университета New South Wales, Австралия

На его основе создано не мало научных разработок, начиная от простой студенческой ОС  (Minix А. С. Таненбаума на основе L4), заканчивая реально действующей и использующейся системой для управления полетами спутниками. Микроядро L4 находится в постоянном развитии и модернизации. На сегодняшний день последняя реализация носит индекс X2 (eXperimental 2). Оно работает на различных архитектурных платформах, начиная от процессоров для супер производительных мейнфреймов, заканчивая встраиваемыми процессорами для наладонных и мобильных систем:

§         Alpha

§         AMD64

§         ARM

§         IA-32

§         IA-64

§         MIPS64

§         PowerPC

§         PowerPC64

§         SPARCv9

Ядро свободно распространяется под условиями BSD лицензии. Т.е. любой желающий может скачать из Интернета исходные коды и использовать по своему усмотрению, оставляя ссылку на разработчика. Написано на С++, используя в своей реализации методы ООП. Ядро представляет механизмы защиты и передачи сообщений (способ взаимодействия между исполняемыми единицами), и не несет в себе никаких драйверов устройств. Весь другой функционал выносится на непривилегированный пользовательский уровень (драйвера, сетевые протоколы и т.д.). Можно начать с нуля разрабатывать систему для L4, а можно использовать уже готовые наборы базовых библиотек и серверов (IO, naming, timer и т.д.). Во время проведения практики было принято решение остановится на рабочем окружении для микроядра Pistachio Kenge/Iguana.

1.2. Kenge

 

Kenge – свод правил и ПО с помощью которого компилируются, линкуются и строятся зависимости между компонентами ОС. Сердцем Kenge является система выявления зависимостей Scons (функционально похоже на ПО make), и система ведения и отслеживания изменений в исходном коде компонентов GNU arch (по своим функциональным возможностям не уступает CVSCurrent Version System). А также набор вспомогательных инструментов необходимый при разработке ПО:

o      IDL4 Magpie – компилятор интерфейсов описанных в формате Corba ,

o      Grub позволяющего загружать образ созданной ОС с различных устройств, включая сетевые карточки

o      qemu – эмулятора различных аппаратных платформ (ia32, x86_64, SPARC64)

1.2. Iguana

 

Iguana набор библиотек и серверов выполняющихся на основе микроядра. В наличии доступны следующие компоненты:

§         Сервер Naming – предоставляет возможность поиска сервера по имени.

§         Сервер Serial – содержит драйвер COM-порта. Используется для управления и отладки процессов через консоль, подключенной к последовательному порту.

§         Сервер Server – дополняет базовые системные вызовы L4. Содержит средства для управления/создания/удаления/ защитных доменов, нитей, участков памяти, сессий, внешних адресных пространств. Предоставляет доступ к порам В/В и участкам DMA.

§         Сервер Init – выполняет начальную инициализацию и запускает на выполнение все последующие сервера.

§         Тестовые программки – главная цель которых, наглядно продемонстрировать, как использовать выше перечисленные сервера.

Во время практики этот набор был расширен такими серверами как ethernet и semaphore.

2. Рабочая среда

 

Весь процесс исследования, обучения и разработки проводился в ОС Linux, так как эта ОС как нельзя лучше подходит для этих целей, предоставляя такие мощные инструменты как:

§         GdbGNU Debugger позволяет вести отладку исполняемых программ

§         Scons – система контроля версий

§         vim – мощнейший текстовый редактор исходных файлов

§         gcc – компилятор языка C/C++

§         ld – линкер объектных файлов, позволяет использовать карту (скрипт) будущего размещения объектов в оперативной памяти.

§         cscope – утилита для навигации по файлам с исходным кодом программ, также позволяет быстро понять внутреннюю структуру больших проектов.

3. Платформа проведения тестов и опытов

 

Полученный скомпилированный файл не может непосредственно выполняться как исполняемый файл ОС Linux. Главным препятствием является тот факт, что микроядро непосредственно управляет CPU, его работой, например механизмом защиты памяти и прерываниями от внешних устройств. То есть, ему необходим доступ к 0-му кольцу защиты. А любое исполняемое приложение в ОС выполняется на 3-м кольце защиты, в своем адресном пространстве и не имеет никакой возможности прямого доступа к оборудованию. Выход из сложившейся ситуации представляется двумя путями:

§         Запускать скомпилированный файл непосредственно на «голом» оборудовании. Образ ОС можно загрузить для выполнения с помощью ПО GRUB по сети из FTPF сервера, или с жесткого диска.

§         Использовать ПО которое полностью эмулирует работу некой аппаратной платформы (ia32, arm  и т.д.). Данное ПО выполняется на ОС Linux и позволяет запускать гостевые ОС, эмулируя все аппаратные части. Во время практичной работы, такую неоценимую помощь оказал эмулятор qemu. Так как отладка ОС, а особенно ядра и его компонентов почти неосуществима в силу того, что само ядро является самым нижнем уровнем ПО, то ни о каком отладчике говорить не приходится. Мельчайшая ошибка в таком ПО ведет к полному зависанию платформы. Частые перезагрузки просто неизбежны. qemu позволяет значительно экономить время, выполняя гостевую ОС на машине где ведется разработка.

Стоит заметить, если во время программирования была допущена ошибка, то единственный ход действий по ее выявлению заключается в следующих шагах:

§         Скомпилировать все ПО с отладочной информацией

§         Получить от внутреннего встроенного отладчика ядра указатели SP и IP, чтоб выявить на каком шаге и где произошла критическая ошибка

§         Вернутся в окружение ОС Linux

§         Дизассемблировать  полностью программу, в которой произошла ошибка (objdumpdfailed.program.elf.reloc).

§         Загрузить дамп в текстовый редактор.

§         Найти по указателю инструкций (IP) в каком именно участке кода произошла ошибка.

§         Попытаться понять, почему произошла ошибка.

Никаких других отладочных средств для перехода вперед или назад по давшей сбой программе нет. В этом и заключается одна из сложностей разработки ПО такого класса.

4. Реализация семафоров POSIX для приложений микроядра L4

 

4.1. Понятие семафора

 

Семафор представляет собой простейшие средство синхронизации процессов и потоков. Семафоры могут использоваться как отдельных процессов, так и потоков одного процесса. Процесс в окружении ядра L4 можно рассматривать как набор всех нитей исполняющихся в одном адресном пространстве. Главная задача семафоров заключается в предоставлении механизма, который бы позволял организовывать взаимоисключающий доступ к некому исполняемому участку кода. То есть, под этим термином скрывается великое множество способов и реализаций, которые предоставляют упомянутую выше функциональность. Семафоры бывают двоичные, целочисленные («со счетчиком») и взаимного исключения (которые, между прочим, программисты называют между собой «мьютексами», стихийно стремясь избегать недоразумений). В современных ОС более всего популярны два варианта семафоров:

§         Семафоры определены стандартом POSIX (Portable Operating System Interface), которые были установлены PASC (Portable Applications Standarts Committe) (Комитет по стандартам переносимых приложений).

§         Семафоры используемее в ОС System V. Данный класс семафоров получил также очень широкое распространение, по той причине, что почти все современные варианты ОС UNIX наследуют подсистему IPC именно от System V.

В нашем случае семафор представить как счетчик доступа программ к публичным ресурсам.

Во время проведения практики было решено реализовать часть подсистемы IPC, которая управляет семафорами, причем чтобы она полностью удовлетворяла стандарту POSIX.

Семафоры POSIX разделяются на:

§         Именованные, идентифицируемые именами, соответствующими стандарту Posix для IPC.

§         Размещаемые в разделяемой памяти.

Именованные семафоры Posix не обязательно должны обрабатываться ядром. Их особенностью является наличие имен, которые могут соответствовать именам реальных файлов в файловой системе. В силу этого, реализовать их в настоящие время не предоставляется возможности (так как нет файловой системы). (На самом деле, для достижения данной цели разумно было бы использовать сервер имен, но при будущем добавлении файловой системы, понадобилась бы модернизация клиентского ПО).

Для использования размещаемых в памяти семафоров, приложение должно выделить для них участок памяти, а инициализируются они системой.

4.2. Функции для работы с семафорами Posix: sem_init и sem_destroy

 

#include <semaphore.h>

int sem_init(sem_t *sem, int shared, unsigned int value);

/* Возвращает -1 в случае ошибки */

int sem_destroy(sem_t *sem);

/* Возвращает 0 в случае успешного завершения. -1 – в случае ошибки */

Размещенный в памяти семафор инициализируется вызовом sem_init. Аргумент sem указывает на переменную типа sem_t, место под которую должно быть выделено приложением. Если аргумент shared равен 0, семафор используется потоками одного процесса (в одном ЗД), в противном случае доступ к нему могут иметь несколько процессов. Если аргумент shared равен ненулевой, семафор должен быть размещен в разделяемой памяти и должен быть доступен всем процессам использующим его. Аргумент value задает начальное значение семафора. После завершения работы с размещаемым в памяти семафором его можно уничтожить, вызвав sem_destroy. Размещаемый в памяти семафор не утрачивает функциональности до тех пор, пока память, в которой он размещен, еще доступна какому-либо процессу.

Если размещаемый в памяти семафор совместно используется потоками одного процесса (аргумент shared при вызове sem_init равен 0), семафор обладает живучестью процесса и удаляется при завершении последнего.

Если размещаемый в памяти семафор совместно используется несколькими процессами (аргумент shared при вызове sem_init равен 1), он должен располагаться в разделяемой памяти, и в этом случае семафор существует столько, сколько существует эта область памяти. Это значит, что сервер может создать область разделяемой памяти, инициализировать в ней размещаемый в памяти семафор Posix, а затем завершить работу. Некоторое время спустя один или несколько клиентов могут присоединить эту область к своему адресному пространству и получить доступ к хранящемуся в ней семафору.

На рисунке изображен размещенный в разделяемой памяти семафор, используемый двумя процессами. Общий сегмент памяти принадлежит адресному пространству обоих процессов:

 

 

Рис.1 Семафор в разделяемой памяти

4.3. Функции для работы с семафорами Posix: sem_wait и sem_trywait

 

Функция sem_wait проверяет значение заданного семафора на положительность, уменьшает его на единицу и немедленно возвращает процессу. Если значение семафора при вызове функции равно нулю, процесс приостанавливается до тех пор, пока оно снова не станет больше нуля, после чего значение семафора будет уменьшено на единицу и произойдет возврат из функции. Операция «проверка и уменьшение» должна быть атомарной по отношению к другим потокам, работающим с этим семафором:

#include <semaphore.h>

int sem_wait(sem_t *sem);

int sem_trywait(sem_t *sem);

/* функции возвращают 0 в случае успешного завершения. -1 в случае ошибки */

Разница между sem_wait и sem_trywait заключается в том, что последняя не приостанавливает выполнение процесса, если значение семафора равно нулю, а просто немедленно возвращает ошибку EGAIN.

4.4. Функции для работы с семафорами Posix: sem_post и sem_getvalue

 

После завершения работы с семафором поток вызывает sem_post. Этот вызов увеличивает значение семафора на единицу и возобновляет выполнение любых потоков, ожидающих изменения значения семафора:

#include <semaphore.h>

int sem_post(sem_t *sem);

int sem_getvalue(sem_t *sem, int *valp);

/* функции возвращают 0 в случае успешного завершения. -1 в случае ошибки */

Функция sem_getvalue возвращает текущее значение семафора, помещая его в целочисленную переменную, на которую указывает valp. Если семафор заблокирован, стандартом указано возвращать либо 0, либо отрицательное число, модуль которого соответствует количеству потоков, ожидающих разблокирования семафора. В ходе практической работы был выбран второй вариант. Один из потоков может ожидать изменения значения семафора, чтобы потом уменьшить его с 1 до 0, а другой поток может изменить значение семафора с 0 до 1.

4.5. Особенности реализации

 

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

Основная проблема при реализации семафоров состояла в том, что операция по проверке текущего состояния семафора и выбор последующего действия в зависимости от состояния должна быть атомарная (т.е. переключение контекста в процессоре во время исполнения этой операции должно быть предотвращено).  Для достижения этой цели была использована единственная команда микропроцессора семейства x86 cmpxchg r/m32, r32. Была создана функция следующего вида:

inline int cmpxchg(volatile L4_Word_t * dest, L4_Word_t cmp_val, L4_Word_t new_val)

{

L4_Word_t tmp;

__asm__ __volatile__

(

“cmpxchgl %1, %3 \n\t”

:

“=a” (tmp)                ///< 0 EAX, return val

:

“r”  (new_val),          ///< 1 reg, new value

“0”  (cmp_val),         ///< EAX, compare value

“m”  (*dest)               ///< mem, destination operand

:

“memory”, “cc”

);

return tmp == cmp_val;                   ///< return true if change is succesful

}

 

Алгоритм работы заключается в следующем: сравнивается значение регистра EAX со значением расположенным по адресу в памяти или в другом регистре. Если равны, выставляется флаг ZF, и r32 загружается в r/m32. Иначе, флаг ZF сбрасывается и в EAX загружается r/m32. Наглядно можно представить следующей схемой:

 

 

Рис.2. Функция процессора cmpxchg r/m32, r32

 

Данная реализация семафоров POSIX позволяет использовать взаимное исключение или  разграниченный доступ к критическому объекту в разных ЗД. Единственное что нужно, так это домену владельцу дать доступ к телу семафора (например, через совместно-разделяемую память).

Для тестирования работоспособность семафоров была написана программка, работа которой заключалась в следующих шагах:

§         Создание и инициализация семафора.

§         Создание 10 нитей микроядра.

§         Каждая нить пыталась войти в критический важный участок с помощью семафора.

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

§         После выхода из критически важного участка, нить сама себя уничтожала.

Исходные коды библиотеки, сервера, и тестовой программки можно просмотреть в дополнении.

5. Сервер ethernet карт

 

5.1. Преследуемая цель создания

 

Сервер ethernet карт проектировался с целью возможности использования ethernet карточек в приложениях работающих на основе микроядра. На первый план ставились следующие требования:

§         Поддерживать  максимальное количество карт от различных производителей и модификаций.

§         Предоставить пользователю простой, доступный и унифицированный интерфейс контроля и использования сетевых карт.

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

§         Предоставляемый интерфейс должен наследовать классические концепции UNIX систем и отображать устройство в виде файла.

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

§         Должны быть приняты меры для предотвращения вхождения сервера в тупиковые ситуации

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

§         Кроссплатформенность (сервер должен работать на различных аппаратных архитектурах)

5.2. Основополагающая база

 

Разработка  нового драйвера, для какого либо аппаратного продукта выпускаемого производителем на рынок может потребовать значительных затрат. К ним относятся изучение технической документации устройства. Некоторые производители выпускают продукт уже с готовыми драйверами для конкретной ОС (например, Windows), закрывая все спецификации работы производимого устройства. Заставить работать такое устройство в другом окружении является довольно проблематичным занятием. А если и удается добиться от производителя техническою спецификацию на сетевую карточку, то как правило она насчитывает не одну сотню страниц (Например описание работы карточки 3Com 905-B насчитывает 300 страниц). На обработку информации такого объема нужно много усилий и времени. И разработку драйвера для такого класса устройства сможет провести высоко квалифицированный специалист. Для решения проблем этого плана было решено найти ПО, которое содержит базу драйверов сетевых карт. Как возможные варианты рассматривались следующие продукты:

Ядра ОС Linux, FreeBSD, и т.п. – данные ядра совершенно открыты, включают драйвера для многих и различных устройств, постоянно обновляют и добавляют новые версии. Но как один большой недостаток подсистема работы с Ethernet карточками очень тесно связана с другими подсистемами, такими как TCP/IP стек, обработкой времени, планирования, абстракции оборудования и т.д. То есть просто взять и вырезать код драйверов и приспособить его для работы в другом незнакомом окружении практически невозможно. К счастью, мир открытого ПО не ограничивается одними ОС. После тщательного поиска было найдено ПО Etherboot. Главная задача Etherboot-а – загрузка ОС посредством сети. В частности Etherboot используется для организации бездисковых рабочих станций. Вся работа ПО Etherboot сводится к загрузке образа ОС через сеть с FTPF сервера, и передачи управления ядру этой ОС. Во время проведения практики был исследован исходный код Etherboot и выявлено, что главными компонентами являются следующие подсистемы:

§         Код для работы с протоколом TFTP

§         Код для работы с PCI шиной

§         Код инициализирующий систему В/В конкретной архитектуры.

§         Код драйверов сетевых карт (которые архитектурно независимые)

Единственный обнаруженный недостаток драйверов был выявлен уже при написании Ethernet сервера, и заключается в том, что все доступные драйвера спроектированы для работы в монопольном режиме. Этого и следовало ожидать: так как при загрузке ядра некой ОС не требуется участие более одной сетевой карты. Препятствие в использовании кода драйверов из Etherboot было легко решено упаковыванием глобальных переменных драйвера в структуру данных, отвечающую за обнаруженную сетевую карту. То есть, если в системе будет обнаружено две сетевых сетевые карты, и если даже одной модели, будут созданы две структуры, которые содержат индивидуальные настройки каждой сетевой карточки в отдельности. В частности, большинство драйверов сохраняют там базовый адрес В/В, адрес участка DMA, MAC адрес, режим работы (HalfDuplex, FullDuplex) и какие типы пакетов можно применять (broadcast, multicast, unicast). Итак, была найдена база драйверов, сетевых карт, которая постоянно обновляется и совершенствуется и находится в свободном доступе (под лицензией GPL).

5.3. Общая структура драйвера сетевой карты

 

Каждый драйвер представляет стандартный набор функций, в независимости какое оборудование оно обслуживает. Будь то драйвер для карточек построенных на чипсете Tulip, или на 3C90x и т.д., все  они наследуют следующие функции:

1.                  static int probe (struct nic *nic, struct pci_device *pci);

Функции передаются номер PCI шины и номер функции. Если по этим реквизитам драйвер находит знакомое ему устройство (по одному из полей VendorID, DeviceID, ClassID) то эта функция возвращает положительный результат, свидетельствующий, что данное PCI устройство распознано и драйвер знает и умеет как им управлять.

2.                  static void transmit(

            struct nic *nic,

            const char d,                          / Destination */

            unsigned int t,                        /* Type */

            unsigned int s,                        /* size */

            const char p)                          / Packet */);

Этой функции передается указатель на участок памяти, где содержится пакет (фрейм, кадр) который необходимо отправить. Понятно, что за содержимое пакета драйвер ответственности не несет, он даже не знает какие данные хранит этот пакет, то есть, он никак не интерпретирует его содержимое. Единственная задача испустить в Etherner-сеть данный пакет через знакомое ему устройство. Также функция получает размер посылаемого пакета.

Существует два способа реализации драйверов в ПО Etherboot. Они зависят от того, как содержимое пакета попадает в буфер самого устройства:

§         С использованием команд ассемблера inb/outb, inw/outw, inl/outl

§         С использованием DMA

Выбор способа реализации драйвера в первую очередь зависит от конкретного устройства. Но многие современные NIC поддерживают два способа (например, чип Tulip). В каком режиме будет происходить передача данных между карточкой и памятью компьютера устанавливается в конфигурационном регистре устройства.

У каждого способа были выявлены свои недостатки и преимущества. Во время использования in/out команд идет постоянное использование CPU в пересылке байтов между оперативной памятью и PCI шиной. Если же драйвер спроектирован с использованием DMA, то участие CPU в передаче данных не требуется. Для устройства достаточно указать адрес, где начинается пакет, его размер и дать отмашку, что пакет сформирован. Тогда сетевая карточка без участия CPU считает весь пакет, используя, контролер DMA и выставит соответствующий флаг, свидетельствующий, что память где находится тело пакета можно модифицировать.

Программное обеспечение Etherboot использует защищенный режим работы процессора для возможности использования 32-х битной адресации. Но инициализирует работу подсистемы виртуальной памяти таким образом, что виртуальный адрес прямо соответствует линейному физическому адресу. То есть при работе драйверов, которые используют режим DMA, нет необходимости дополнительно преобразовывать виртуальный адрес  в физический адрес. Но в нашем случае, т.е. при работе драйвера как нити микроядра виртуальный адрес не соответствует физическому адресу. Необходимо вводить следующие функции:

uintptr_t phys_to_virt(uintptr_t);

uintptr_t virt_to_virt(uintptr_t);

Данные функции должен предоставлять другой сервер микроядра, который ведает выделением памяти.

3.                  static int poll(struct nic *nic);

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

Методика постоянного опроса имеет свои области применения и использования. Как противоположный метод взаимодействия с сетевыми карточками, является использование аппаратных прерываний от устройства. Следует сказать пару слов защиты в сторону выбранного нами способа (опроса). Так как ethernet сервер является нитью микроядра, можно очень гибко управлять его работой и приспосабливать к конкретным задачам. Используя системные вызовы микроядра можно проектировать системы, от которых требуется реакция в реальном времени. Например, если задать опрашивающей нити более высокий приоритет, чем остальным, а в самой нити использовать системный вызов L4_ThreadSwitch(l4_nilthread); (освободить процессор), можно реализовать разные схемы работы, и устанавливать жесткие временные границы. Даная методика также задействована в архитектурах построения драйверов mkLinux (модифицированной версии Liunx), которая отвечает требованиям RealTime системам. При будущем проектировании QoS систем на основе данного сервера не должно возникнуть никаких затруднений.

4.                  static void disable(struct nic *nic);

Драйвер при вызове этой функции, выставляет флаги в конфигурационном регистре устройства линий Rx и Tx. То есть, в таком состоянии карточка отказывается от посылки и приема новых пакетов.

5.4. Архитектура Ethernet сервера.

 

Ethernet сервер является сервером микроядра L4. Он выполняется в своем защитном домене, в своем адресном пространстве. Никакой другой компонент ОС не сможет повредить работе данного сервера. Сервер состоит из двух нитей микроядра. Первая нить постоянно опрашивает открытые пользовательским кодом сетевые карточки (для которых была вызвана библиотечная функция open()). Другая же нить находится в замороженном состоянии, в состоянии ожидания запроса от клиентского приложения. Эта нить использует процессорное время только при поступлении запроса от клиента.

Рис.1 Две нити ethernet сервера

 

Основные узлы во время работы Ethernet сервера можно отобразить следующей схемой:

Рис.2. Взаимодействие между сервером и клиентами

 

В примере рассмотрена работа двух клиентов, которые взаимодействуют с сервером. Каждому клиенту библиотека предоставляет следующие функции:

int n_init();
eth_t * n_open(int nic_num);
int n_close(eth_t *nic);
int n_found();
size_t n_write(eth_t *nic, const void *tx_packet, size_t count);
size_t n_read(eth_t *nic, void *rx_packet, size_t count);
void n_mac(eth_t *nic, unsigned char *mac);
 

При таком подходе построения архитектуры, от клиента полностью спрятаны все технические и аппаратные аспекты работы сетевой карты. Сетевая карточка отображается как обычный файл. То есть клиенты взаимодействуют с сервером ether_server косвенно, посредством библиотеки ether_lib.

Сервер ether_server, и библиотека ether_lib построены с использованием IDL4 (Magpie). IDL сокращение к Interface Description Language. Этот язык берет свои корни от кластерной, многопроцессорной, мультикомплексной обработки информации в параллельном режиме, и используется при описании интерфейсов некого узла.

На этом языке описано как работает сервер. В нашем случае сервер экспортирует один интерфейс, который может обслуживать следующие запросы:

int init(out unsigned nic_base, out int total_nics);
int transmit(in int nic_num);
int open(in int nic_num);

int close(in int nic_num);

После обработки файла описывающего интерфейсы IDL компилятор создает два заголовочных файла для написания библиотеки и самого сервера. Тем самым отпадает необходимость непосредственного кодирования механизма передачи сообщений между библиотекой и сервером. То есть IDL компилятор прячет в заголовочных файлах все аспекты использования системных вызовов микроядра.

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

Во время открытия сетевой карты, сервер отображает два участка памяти из своего ЗД в ЗД клиента. Один с возможностью записи и чтения другой только с правами на чтение. Участок памяти, который доступен только на чтение, содержит очередь с прочитанными (полученными) пакетами. То есть сервер получает пакеты постоянно, в независимости запрашивает или нет клиент, следующий пакет. В случае если, очередь переполнена, и клиент не успевает выбирать из очереди полученные пакеты, Ethernet сервер начинает все последующие поступившие пакеты отбрасывать до тех пор, пока в очереди не появится свободное место.

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

Рис.3. Участки памяти отображены в адресное пространство клиента

 

Во время обращения к серверу, происходит проверка сервером на соответствие корректности запроса. Сервер спрашивает ID защитного домена клиента подавшего запрос на обслуживание и сравнивает со своими внутренними структурами, в которых фиксируется, какой защитный домен вызвал функцию open для данной сетевой карточки.

При такой организации взаимодействия сервера и клиента только один ЗД монопольно владеет сетевой карточкой, начиная от команды open, заканчивая командой close. Освободивши карточку, другое приложение в другом защитном домене сможет открыть ее и стать новым монопольным владельцем.

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

Каждый драйвер сетевой карточки компилируется в виде отдельной единицы. В любом из драйверов выделяется секция, в которой содержатся указатели на стандартные для сетевой карты операции (probe, poll, transmit, disable). Эта секция помечается специфическим для компилятора методом.  И во время компоновки все эти участки располагаются в одном сегменте, последовательно друг за другом. Во время инициализации Ethernet сервера происходит последовательное сканирование этого участка, в итоге сервер знает, какие в его расположении присутствуют драйвера. Для достижения выше описанной схемы был написан скрипт на специфическом языке, который передавался на вход линкеру (ld) вместе с другими объектными файлами. В итоге операция добавления или изъятия нового драйвера устройства сводится к операции копирования \ удаления исходного файла из папки компилируемых файлов.

Во время загрузки ethernet сервера происходит полное сканирование PCI-шины, каждое найденное устройство последовательно передается функции probe из каждого драйвера. В случае положительного ответа данной функции заводится служебные структуры.

Также следует заметить одну особенность при реализации ethernet сервера. Каждому из драйверов необходимо иметь доступ к функциям реализующие точные временные задержки с точностью до 1 микросекунды. Данные функции в Etherboot были реализованы  с использованием стандартной микросхемы i8254 и подсчета количества тиков центрального микропроцессора за одно переключение таймера. Во время портирования драйверов было решено непосредственно использовать системный вызов микроядра L4_SystemClock(); возвращающий текущее значение времени в микросекундах с начала работы системы. (Переполнение счетчика микроядра происходит раз в 1000 лет). Такой подход способствует более легкому и безболезненному портированию приложения на новые архитектуры.

Сканирование устройств расположенных на шине PCI решено производить непосредственно, без использования функций BIOS-а.

Для тестирования работоспособности предложенного сервера было написано маленькое приложение, представляющие собой клиента ethernet сервера, и выполняющего одну единственную функцию – выдачу ответов на ICMP запросы. Алгоритм работы данного приложения достаточно примитивен и представлен следующей диаграммой:

Рис.4. алгоритм работы приложения, отвечающего на ICMP запросы

 

Следует напомнить, что такой простенький клиент создан только в тестовых рамках и предназначен для тестирования ethernet сервера. Он имеет два недостатка:

§         Размер ICMP пакета не может превышать 1500 байт (с учетом ethernet заголовка), в связи с ограничением размера на ethernet кадр.

§         Будет работать только в рамках одного Ethernet домена. То есть, кто посылает ICMP – запросы и наш пример должны выполняться на компьютерах расположенных в одном домене коллизий. Если же они находятся в разных сегментах сети, работоспособность нашего примера не возможна. Данное ограничение вводится тем, что клиент только меняет местами адреса сетевых карт отправителя и получателя (контрольная сумма пакета от этого не меняется).

 

6. Click роутер

 

Click представляет собой модульный, программный роутер разработанный Computer Science and Artificial Laboratory  при MIT (Massachusetts institute of technology). В дальнейшем разрабатывался Mazu Networks, потом в Center for Internet Research при ICSI (International Computer Science Institute) а теперь в Computer Science Department в UCLA (University of California Los Angeles).

Роутеры построены на базе Click очень гибки, конфигурируемые, и легки в понимании. Также они показывают очень высокою производительность по сравнению с другими программными роутерами. Как наглядный пример на Pentium 3 работающем на 700 Mhz Click IP роутер может обслуживать до 435000 64-х байтных пакетов за 1 сек.

Роутер представляет собой коллекцию взаимосвязанных модулей называемыми элементами. Элементы контролируют каждый аспект поведения роутера, начиная от коммуникации с аппаратными устройствами, до модификации пакета, до создания очередей, построения политик отказа и отброса пакетов, а также планирования приоритетов для пакетов. Индивидуальные элементы могут иметь удивительно чудовищное поведение, и их легко реализовать на языке C++. Работа роутера задается конфигурационным файлом, в котором описываются взаимосвязи между элементами. Файл конфигурации пишется на простеньком специфическом языке. Имея такую инфраструктуру легко описать рабочую конфигурацию, которая бы соответствовала поведению стандартного IP роутера 2-го/3-го уровня. Для Click роутера созданы элементы, которые могут, выполняют NAT, IPSEC, и т.д.

Click роутер умеет работать в следующих режимах:

§         На уровне пользователя (как обычное прикладное ПО) используя библиотеку для захвата пакетов libpcap

§         Как модуль ядра Linux версии 2.2 и 2.4 (управление роутером в данном случае производится через файловую систему /proc)

§         Как часть ядра FreeBSD.

В ходе выполнения практики было решено спортировать Click роутер для работы на микроядре L4. Для решения поставленного задания были сделаны следующие шаги:

6.1. Ознакомление

 

§         С сайта производителя была загружена последняя версия Click роутера.

§         Была полностью проанализирована работа Click роутера, в частности были запущенны тестовые конфигурации в ОС Linux в пользовательском режиме.

§         Был разобран синтаксис написания конфигурационного файла.

§         Изучение внутренней структуры Click роутера.

§         Была изучена система построения роутера для конкретной платформы (Linux UM, Linux module, FreeBSD).

Для построения бинарного исполняемого файла (модуля) из исходных кодов разработчики использовали систему GNU autotools (Automake, Autoconf, Libtool). Для каждой платформы разработчикам пришлось написать отдельные специфические элементы роутера (участвуют при написании конфигурации) которые бы получали / отсылали пакеты со специфического устройства. Также для каждой платформы созданы конфигурационные заголовочные файлы, в которых определены на языке C++ необходимые макросы и константы.

6.2. Портирование

 

§         для запуска Click роутера как клиента микроядра были проведены следующие шаги:

§         Система сбора, компиляции, и выявления зависимостей была перенесена на Scons.

§         Были добавлены заголовочные конфигурационные файлы, в которых задавались параметры и пути взаимодействия с рабочим окружением микроядра.

§         Были адаптированы некоторые необходимые функции (которые отсутствуют в Kenge/Iguana), а именно:

o      gettimeofday для определения текущего времени

o      ntohl, ntohs, htonl, htons для перевода следования байтов между машиной и сетью

§         Добавлены элементы tonic, pollnic - для работы Click роутера с ethernet сервером, представленным выше.

§         Изменены загрузочные и функции инициализации с целью возможности подгрузки конфигурационного файла роутера как некого модуля.

6.3. Проверка работоспособности Click роутера как приложения микроядра.

 

Click роутер был запущен на компьютере Pentium 3 со следующей конфигурацией:

in :: PollNic(0);

out :: Print(p_out) -> ToNic(0);

c :: Classifier(12/0806 20/0001, // ARP request

12/0806 20/0002,  // ARP reply

12/0800,                     // IP packet

-);               // All other

//ns8390 00:00:1c:09:37:71

//3Com            00:01:02:de:53:5e

//arpq :: ARPQuerier(195.178.197.241, 00:00:1c:09:37:71);

arpq :: ARPQuerier(195.178.197.241, 00:01:02:de:53:5e);

 

in -> Print(p_in) -> c;

arpq[0] -> out;

 

c[2]

Strip(14)

CheckIPHeader(INTERFACES 195.178.197.241/24)

IPPrint(ICMP)

ICMPPingResponder

[0]arpq;

 

c[0]

//          -> ARPResponder(195.178.197.241 00:00:1c:09:37:71, 10.0.0.1 52:54:00:12:34:57)

ARPResponder(195.178.197.241 00:01:02:de:53:5e, 10.0.0.1 52:54:00:12:34:57)

Print(Something_want_to_know_my_MAC)

out;

 

c[1]

Print(Answer_to_my_APR_Request) -> [1]arpq;

 

c[3]

Discard;

 

//Comments:                                       

//-> CheckICMPHeade

 

Любой компьютер, пославший Echo - запрос получал Echo – ответ. При запуске команды ping, варьировали параметрами -i (время между посылками ICMP запросов)  и -s (размер посылаемого ICMP пакета). На все запросы, при любых параметрах были получены ответы.


7. Заключение

 

В итоге выполнения настоящей проектно-эксплуатационной практики были получены следующие основные результаты и выводы:

1.                  Получено представление о построении ОС на основе микроядра и его окружения.

2.                  Приобретены навыки разработки компонентов работающих в окружении микроядра.

3.                  Получены глубокие знания о  работе микроядра L4::Pistachio, его архитектуре.

4.                  Разработаны службы, работающие на основе микроядра, и предоставляющие стандартный интерфейс семафоров POSIX, а также отображающие сетевые устройства в виде файла.

5.                  Написаны тестовые программы для проверки работоспособности представленных выше серверов.

6.                  Перенесен программный роутер Click для работы в окружении микроядра.

Микроядро представляет гибкую инфраструктуру, для проведения опытов в построении операционных систем будущего поколения. Было показано как можно без больших затрат времени создать набор необходимых компонентов ОС. Радует тот факт, что удалось заставить работать ПО Click роутер в окружении микроядра. На данном этапе тестированию подверглись лишь базовые возможности данного ПО. В будущем, особый интерес представляет сравнение производительности по пересылке пакетов роутером Click как в родной исполнительной среде (Linux, FreeBSD) так и в новой рабочей обстановке микроядра. Для полной картины, также следует сравнить функциональные и производительные характеристики между роутером Click и аппаратным роутером Cisco. Данные вопросы являются очень актуальными в настоящие время,  в связи широким распространением телекоммуникационных технологий во всех сферах человеческой деятельности.


Список использованных источников

 

1.      Стивенс У. Unix: Взаимодействие процессов. - СПб.: Питер, 2002. – 576 с.: ил

2.       Gernot Heiser. Iguana User Manual. http://www.ertos.nicta.com.ua/Software/Iguana/userman.pdf

3.      L4 X.2 Rev 6 Reference Manual http://l4hq.org/docs/manuals/l4-x2-20050608.pdf

4.      Nicholas FitzRoy-Dale. Magpie — An Extensible Interface Generator. http://nicta.com.au/ertos.html


Приложение 1

 

Исходный код:

§        сервера семафоров POSIX

§        библиотеки для его использования.

§        приложения для тестирования работоспособности

iguana\sem\src\sem_server.c :

#include <stdio.h>

#include <assert.h>

#include <l4/ipc.h>

#include <l4/kdebug.h>

#include <l4/thread.h>

#include <naming/naming.h>

#include <sem_common/sem_common.h>

 

#define L4_SEM_NSEMS_MAX (255)                     ///< maximum semaphores

 

int main(int argc, char **argv)

{

            L4_ThreadId_t caller;

            L4_ThreadId_t me;

            L4_ThreadId_t wakeup_thrd;

            L4_MsgTag_t tag;

            L4_Msg_t msg;

            L4_Word_t label;

            int nsems;                                           ///< Activated semaphores presents in system

 

            printf("Semaphore thread startup\n");

            nsems = 0;

            L4_KDB_SetThreadName(L4_Myself(), "sem_srv");

            L4_Accept(L4_UntypedWordsAcceptor);     ///< We accept only untyped words

           

            me = L4_Myself();

            assert(naming_insert("semaphore", me.raw) == 0);

           

            while(1)

            {

                        tag = L4_Wait(&caller);

 

                        if(L4_IpcFailed(tag))

                        {

                                   continue;

                        }

                       

                        L4_MsgStore(tag, &msg);

                        label = L4_Label(tag);          ///< In the label we save the type of request

 

                        switch(label)

                        {

                                   case L4_SEM_INIT:

                                               if(nsems == L4_SEM_NSEMS_MAX)

                                                           label = L4_SEM_CALL_FAILED;

                                               else

                                                           label = L4_SEM_CALL_SUCCESS;

                                               L4_MsgClear(&msg);

                                               L4_Set_Label(&msg.tag, label);

                                               L4_MsgLoad(&msg);

                                               tag = L4_Send(caller);

                                               if(L4_IpcSucceeded(tag) && label == L4_SEM_CALL_SUCCESS)

                                               {

                                                           nsems++;

                                               }

                                               break;

                                   case L4_SEM_DESTROY:

                                               L4_MsgClear(&msg);

                                               L4_Set_Label(&msg.tag, L4_SEM_CALL_SUCCESS);

                                               L4_MsgLoad(&msg);

                                               tag = L4_Send(caller);

                                               if(L4_IpcSucceeded(tag))

                                               {

                                                           nsems--;

                                               }

                                               break;

                                   case L4_SEM_POST:

                                               wakeup_thrd.raw = L4_MsgWord(&msg, 0);

 

                                               if(wakeup_thrd.raw == L4_nilthread.raw)

                                               {

                                                           label = L4_SEM_NO_THRD_TO_UBLOCK;

                                               }

                                               else

                                               {

                                                           L4_MsgClear(&msg);

                                                           L4_Set_Label(&msg.tag, L4_SEM_CALL_SUCCESS);

                                                           L4_MsgLoad(&msg);

                                                           tag = L4_Send(wakeup_thrd);

                                                           if(L4_IpcFailed(tag))

                                                           {

                                                                       label = L4_SEM_NO_THRD_TO_UBLOCK;

                                                           }

                                                           else

                                                           {

                                                                       label = L4_SEM_CALL_SUCCESS;

                                                           }

                                               }

                                               L4_MsgClear(&msg);

                                               L4_Set_Label(&msg.tag, label);

                                               L4_MsgLoad(&msg);

                                               L4_Send(caller);

                                               break;

                                   default:

                                               break;

                        }

            }

 

            assert(!"Should reach here\n");

 

            return 0;

}

 

libs\sem\include\semaphore\semaphore.h :

#ifndef L4_SEMAPHORE_H

#define L4_SEMAPHORE_H

 

extern L4_ThreadId_t semaphore_thread;   ///< must be defined and initialised by application

 

typedef struct

{

            int counter;

            L4_ThreadId_t running_thrd;

            L4_ThreadId_t blocked_thrd;

            int valid;

} sem_t;

 

int sem_init(sem_t *sem, int pshared, unsigned int value);

int sem_destroy(sem_t *sem);

int sem_post(sem_t *sem);

int sem_getvalue(sem_t *sem, int *valp);

int sem_wait(sem_t *sem);

int sem_trywait(sem_t *sem);

#endif ///< end L4_SEMAPHORE_H

 

libs\sem\src\sem_lib.c :

#include <stdio.h>

#include <l4/types.h>

#include <l4/ipc.h>

#include <l4/message.h>

#include <sem_common/sem_common.h>

#include <semaphore/semaphore.h>

 

#define L4_SEM_VALUE_MAX (32767)

//#define l4_SEM_VALID_SEM 0xA6CEOCB3FB9B13AE

#define L4_SEM_VALID_SEM 0x26ce0cb3

 

inline int cmpxchg(volatile L4_Word_t * dest, L4_Word_t cmp_val, L4_Word_t new_val);

 

inline int cmpxchg(volatile L4_Word_t * dest, L4_Word_t cmp_val, L4_Word_t new_val)

{

            L4_Word_t tmp;

            __asm__ __volatile__

                        (

                         "cmpxchgl %1, %3 \n\t"

                         :

                         "=a" (tmp)                ///< 0 EAX, return val

                         :

                         "r"  (new_val),          ///< 1 reg, new value

                         "0"  (cmp_val),         ///< EAX, compare value

                         "m"  (*dest)              ///< mem, destination operand

                         :

                         "memory", "cc"

                         );

            return tmp == cmp_val;                   ///< return true if change is succesful

}

 

///< flag pshared isn't used. semaphore can be used in all PD, if it in the common memory.

int sem_init(sem_t *sem, int pshared, unsigned int value)

{

            L4_Msg_t msg;

            L4_MsgTag_t tag;

            L4_Word_t label;

 

            if(value > L4_SEM_VALUE_MAX || value < 0)

                        return -1;        ///< errno = EINVAL

 

            if(sem->valid == L4_SEM_VALID_SEM)

                        return -1;        ///< semaphore already inited

           

            L4_MsgClear(&msg);

            L4_Set_Label(&msg.tag, L4_SEM_INIT);

            L4_MsgLoad(&msg);

            tag = L4_Call(semaphore_thread);

            label = L4_Label(tag);

 

            if(L4_IpcFailed(tag) || label != L4_SEM_CALL_SUCCESS)

                        return -1;

 

            sem->counter = value;

            sem->blocked_thrd = L4_nilthread;

            sem->running_thrd = L4_nilthread;

            sem->valid = L4_SEM_VALID_SEM;

 

            ///< if value = 0, first sem_wait call will be blocked

 

            return 0;

/*

   The function will fail if:

   [EINVAL]

   The value argument exceeds SEM_VALUE_MAX.

   [ENOSPC]

   A resource required to initialise the semaphore has been exhausted, or the limit on semaphores (SEM_NSEMS_MAX) has been reached.

   [EPERM]

   The process lacks the appropriate privileges to initialise the semaphore.

*/

}

 

int sem_destroy(sem_t *sem)

{

            L4_Msg_t msg;

            L4_MsgTag_t tag;

            L4_Word_t label;

            L4_ThreadId_t myself;

            int success;

 

            myself = L4_Myself();

 

            do

            {

                        success = cmpxchg(&sem->running_thrd.raw, L4_nilthread.raw, myself.raw);

                       

                        if(sem->valid != L4_SEM_VALID_SEM)

                        return -1;        ///< errno == EINVAL

                                               ///< The sem argument does not refer to a valid semaphore.

 

                        if(sem->running_thrd.raw != myself.raw)

                                   L4_ThreadSwitch(sem->running_thrd);

            }while(!success);

 

            if(sem->counter < 0 || !L4_IsNilThread(sem->blocked_thrd))

            {

                        sem->running_thrd = L4_nilthread;

                        return -1;        ///< errno == EBUSY

                                               ///< some thread blocked on this semaphore

            }

 

            L4_MsgClear(&msg);

            L4_Set_Label(&msg.tag, L4_SEM_DESTROY);

            L4_MsgLoad(&msg);

            tag = L4_Call(semaphore_thread);

            label = L4_Label(tag);

 

            if(L4_IpcFailed(tag) || label != L4_SEM_CALL_SUCCESS)

            {

                        sem->running_thrd = L4_nilthread;

                        return -1;

            }

 

            sem->valid = 0;

 

            return 0;

/*

   The function will fail if:

   [EINVAL]

   The sem argument is not a valid semaphore.

   [ENOSYS]

   The function sem_destroy() is not supported by this implementation.

   [EBUSY]

   There are currently processes blocked on the semaphore.

*/

}

 

int sem_post(sem_t *sem)

{

            L4_Msg_t msg;

            L4_MsgTag_t tag;

            L4_Word_t label;

            L4_ThreadId_t myself;

            int success;

           

            myself = L4_Myself();

 

            do

            {

                        success = cmpxchg(&sem->running_thrd.raw, L4_nilthread.raw, myself.raw);

                       

                        if(sem->valid != L4_SEM_VALID_SEM)

                        return -1;        ///< errno == EINVAL

                                               ///< The sem argument does not refer to a valid semaphore.

 

                        if(sem->running_thrd.raw != myself.raw)

                                   L4_ThreadSwitch(sem->running_thrd);

            }while(!success);

 

            if(sem->counter == L4_SEM_VALUE_MAX)

            {

                        sem->running_thrd = L4_nilthread;

                        return -1;        ///< Maximum value of semaphore reached

            }

           

            sem->counter++;

           

            if(sem->counter <= 0)

            {

                        sem->running_thrd = L4_nilthread;

                        do

                        {

                                   L4_MsgClear(&msg);

                                   L4_Set_Label(&msg.tag, L4_SEM_POST);

                                   L4_MsgAppendWord(&msg, sem->blocked_thrd.raw);

                                   L4_MsgLoad(&msg);

                                   tag = L4_Call(semaphore_thread);

                                   label = L4_Label(tag);

                                   if(L4_IpcFailed(tag))

                                   {

                                               return -1;

                                   }

                                   else if(label == L4_SEM_NO_THRD_TO_UBLOCK)

                                   {

                                               L4_ThreadSwitch(sem->blocked_thrd);

                                               ///< Maybe wait-thread don't have time to join to

                                               ///< L4_Receive, so give it chanse to do this.

                                   }

                        }while(label != L4_SEM_CALL_SUCCESS);

                        sem->blocked_thrd = L4_nilthread;

            }

            else

            {

                        sem->running_thrd = L4_nilthread;

            }

           

            return 0;

/*

   The function will fail if:

   [EINVAL]

   The sem argument is not a valid semaphore.

*/

}

 

int sem_getvalue(sem_t *sem, int *valp)

{

            if(sem->valid != L4_SEM_VALID_SEM)

                        return -1;        ///< errno == EINVAL

                                               ///< The sem argument does not refer to a valid semaphore.

           

            *valp = sem->counter;

 

           

            return 0;

/*

   The function will fail if:

   [EINVAL]

   The sem argument is not a valid semaphore.

*/

}

 

int sem_wait(sem_t *sem)

{

            int success;

            L4_MsgTag_t tag;

            L4_Word_t label;

            L4_ThreadId_t myself;

 

            myself = L4_Myself();

 

            do

            {

                        success = cmpxchg(&sem->running_thrd.raw, L4_nilthread.raw, myself.raw);

                       

                        if(sem->valid != L4_SEM_VALID_SEM)

                                   return -1;        ///< errno == EINVAL

                                                           ///< The sem argument does not refer to a valid semaphore.

 

                        if(sem->running_thrd.raw != myself.raw)

                                   L4_ThreadSwitch(sem->running_thrd);

            }while(!success);

 

            sem->counter--;

 

            if(sem->counter < 0)

            {

                        sem->running_thrd = L4_nilthread;

 

                        do       

                        {

                                   success = cmpxchg(&sem->blocked_thrd.raw, L4_nilthread.raw, myself.raw);

                                   if(!success)

                                               L4_ThreadSwitch(L4_nilthread);

                        }while(!success);

 

                        tag = L4_Receive(semaphore_thread);

 

                        label = L4_Label(tag);

                        if(L4_IpcFailed(tag) || label != L4_SEM_CALL_SUCCESS)

                        {

                                   return -1;

                        }

            }

            else

            {

                        sem->running_thrd = L4_nilthread;

            }

                       

            return 0;

/*

   The function will fail if:

   [EINVAL]

   The sem argument does not refer to a valid semaphore.

   [EINTR]

   A signal interrupted this function.

*/

}

 

int sem_trywait(sem_t *sem)

{

            L4_ThreadId_t myself;

            int lock_stat;

            int success;

           

            myself = L4_Myself();

 

            do

            {

                        success = cmpxchg(&sem->running_thrd.raw, L4_nilthread.raw, myself.raw);

                       

                        if(sem->valid != L4_SEM_VALID_SEM)

                                   return -1;        ///< errno == EINVAL

                                                           ///< The sem argument does not refer to a valid semaphore.

 

                        if(sem->running_thrd.raw != myself.raw)

                                   L4_ThreadSwitch(sem->running_thrd);

            }while(!success);

 

            if(sem->counter > 0)

            {

                        lock_stat = 0;

                        sem->counter--;

            }

            else

            {

                        lock_stat = -1;           ///< errno = EAGAIN

            }

 

            sem->running_thrd = L4_nilthread;

                       

            return lock_stat;

/*

   The function will fail if:

   [EINVAL]

   The sem argument does not refer to a valid semaphore.

   [EAGAIN]

   The semaphore was already locked, so it cannot be immediately locked by the sem_trywait() operation

   [EINTR]

   A signal interrupted this function.

*/

}

 

example\sem_test\src\sem_example.c :

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

#include <iguana/thread.h>

#include <l4/types.h>

#include <l4/ipc.h>

#include <iguana/memsection.h>

#include <naming/naming.h>

#include <semaphore/semaphore.h>

 

L4_ThreadId_t semaphore_thread;

 

#define THREADS 7

 

sem_t *sem_lock;

int thrd_no = 1;

 

void thread(void);

 

void thread(void)

{

            L4_ThreadId_t l4thrd;

            thread_ref_t thrd;

            int value;

            L4_Time_t sleep;

            char *stack_top;

            int my_no;

            L4_ThreadId_t me;

 

            sleep.period.m = 1023;

            sleep.period.e = 6;

            sleep.period.a = 0;

            my_no = thrd_no;

            me = L4_Myself();

            if(thrd_no < THREADS)

            {

                        thrd_no++;

                        memsection_create(0x1000, (void*) &stack_top);

                        stack_top += 0x1000 - sizeof(uintptr_t);

                        thrd = thread_create(&l4thrd);

                        thread_start(thrd, (uintptr_t) thread, (uintptr_t) stack_top);

            }

            printf("%d Thread:0x%lx Parent:0x%lx Child:0x%lx \n", my_no, (long)me.raw, (long)L4_Pager().raw, (long)l4thrd.raw);

 

            L4_Sleep(sleep);

                       

            if (-1 == sem_wait(sem_lock)) {

                        assert(!"sem_wait didn't return success\n");

            }

                        sem_getvalue(sem_lock, &value);

                        printf("Sem value = %d\n", value);

                        for (int i = 1; i<= 10; i++) {

                                   printf("%d", my_no);

                                   L4_Sleep(sleep);

                        }

 

                        fputc('\n', stdout);

 

            if (-1 == sem_post(sem_lock)) {

//                      assert(!"sem_wait didn't return success\n");

           

            }

 

            if(sem_destroy(sem_lock) == 0)

            {

                        printf("Semaphore destroyed succesfuly by me: 0x%lx\n", me.raw);

            }

           

            thread_delete_self();

 

            return;

}

           

int main(int argc, char** argv)

{

            semaphore_thread.raw = naming_lookup("semaphore");

 

            int shared = 1;

            int value = 1;

 

            sem_lock = (sem_t *)malloc(sizeof(sem_t));

           

            if (-1 == sem_init(sem_lock, shared, value)) {

                        assert(!"sem_init didn't return success\n");

            }

 

            thread();

 

            return 0;

}


Приложение 2

 

Исходный код:

§        Сервера сетевых ethernet карт (не включен, ввиду большего объема)

§        Библиотеки для его использования

§        Тестового приложения ping reply.

libs\ether_lib\include\ether_lib\ether_lib.h :

#ifndef _ETHER_LIB_H

#define _ETHER_LIB_H

 

#define PKT_BUF_SIZE 1536

#define ARP_ADDR_SIZE 6

#define MAX_SERVER_CAN_HOLD_PACKETS 50

 

typedef unsigned char uchar; // Special for MAGPIE IDL

 

typedef struct

{

            unsigned char data[PKT_BUF_SIZE];

            unsigned int size;

} packet_t;

 

struct nic_state

{

            packet_t * rx_packets;                                  // buffer for recived packets, if no free space packet will be droped

            int * read_flags;                                // array, edited by client, and show what packets was read

            unsigned char *tx_buf;                                 // Client write here packets body for transmit

            unsigned int *tx_buf_sz;                    // Size of transmited packet

            unsigned char *arp_dest_addr;                    // Client fill this field.

            unsigned char mac[6];                                  // NIC's MAC address

};

 

typedef struct nic_state nic_state_t;

 

typedef struct

{

            int num;          //nic number in system

            int rx_i;           //index of next rx_packet

} eth_t;

 

int n_init(void);

eth_t * n_open(int nic_num);

int n_close(eth_t *nic);

int n_found(void);

size_t n_write(eth_t *nic, const void *tx_packet, size_t count);

size_t n_read(eth_t *nic, void *rx_packet, size_t count);

void n_mac(eth_t *nic, unsigned char *mac);

 

#endif /* _ETHER_LIB_H */

 

libs\ether_lib\src\ether_lib.c :

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include <assert.h>

#include <iguana/types.h>

#include <iguana/object.h>

#include <naming/naming.h>

#include <ether_lib/ether_lib.h>

#include <interfaces/ether_client.h>

 

static nic_state_t *nics_state;

static object_t *ether_object;

static int found_nics;

 

int n_init()

{

            objref_t ether_server;

            uintptr_t info_base;

           

            ether_server = naming_lookup("ether_server");

            assert(ether_server != 0);

            ether_object = object_get_interface(ether_server);

            assert(ether_object !=0 );

 

            nic_init(ether_object->server, &info_base, &found_nics, NULL);

            nics_state = (nic_state_t *) info_base;

 

            return 0;

}

           

eth_t * n_open(int nic_num)

{

            eth_t *eth_ptr;

                       

            if(nic_open(ether_object->server, nic_num, NULL) != 0)

                        return NULL;

           

            if((eth_ptr = malloc(sizeof(eth_t))) == NULL)

                        return NULL;

 

            eth_ptr->num = nic_num;

            eth_ptr->rx_i = 0;

 

            return eth_ptr;

}

 

int n_close(eth_t *nic)

{

            if(nic_close(ether_object->server, nic->num, NULL) !=0)

                        return -1;

 

            free(nic);

 

            return 0;

}

 

int n_found()

{

            return found_nics;

}

 

size_t n_write(eth_t *nic, const void *tx_packet, size_t count)

{

            memcpy(nics_state[nic->num].tx_buf, tx_packet, count);

            *nics_state[nic->num].tx_buf_sz = count;

 

            return nic_transmit(ether_object->server, nic->num, NULL);

}

 

size_t n_read(eth_t *nic, void *rx_packet, size_t count)

{

            size_t size = 0;

            int i;

           

            i = nic->rx_i;

 

            if(!nics_state[nic->num].read_flags[i])

            {

                        size = nics_state[nic->num].rx_packets[i].size;

                        memcpy(rx_packet, nics_state[nic->num].rx_packets[i].data, size);

                        if(size)

                        {

                                   nics_state[nic->num].read_flags[i] = 1;

                                   if(i == MAX_SERVER_CAN_HOLD_PACKETS-1)

                                               nic->rx_i = 0;

                                   else

                                               nic->rx_i++;

                        }

            }

                       

            return size;

}

 

void n_mac(eth_t *nic, unsigned char *mac)

{

            memcpy(mac, nics_state[nic->num].mac, 6);

           

            return;

}

 

example\ether_ping\src\ping-reply.c :

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <assert.h>

#include <l4/ipc.h>

#include <iguana/thread.h>

#include <ether_lib/ether_lib.h>

 

#define ECHO_REPLY   0x00

#define IP_PACKET    0x0800

#define ARP_PACKET   0x0806

#define ARP_REPLY    0x02

#define ICMP_PACKET  0x01

#define ECHO_REQUEST 0x08

 

#define __bswap_constant_16(x) \

     ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))

 

#define __bswap_constant_32(x) \

     ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |                        \

      (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))

 

#define __bswap_32(x) \

     (__extension__                                                                                  \

      ({ register unsigned int __v, __x = (x);                                            \

             if (__builtin_constant_p (__x))                                                  \

               __v = __bswap_constant_32 (__x);                                                   \

             else                                                                                             \

               __asm__ ("bswap %0" : "=r" (__v) : "0" (__x));                             \

             __v; }))

 

#  define __bswap_16(x) \

     (__extension__                                                                                  \

      ({ register unsigned short int __v, __x = (x);                                   \

             if (__builtin_constant_p (__x))                                                  \

               __v = __bswap_constant_16 (__x);                                                   \

             else                                                                                             \

               __asm__ ("rorw $8, %w0"                                                      \

                            : "=r" (__v)                                                                \

                            : "0" (__x)                                                                   \

                            : "cc");                                                                        \

             __v; }))

 

#define ntohl(x)          __bswap_32(x)

#define htonl(x)          __bswap_32(x)

#define ntohs(x)         __bswap_16(x)

#define htons(x)         __bswap_16(x)

 

static unsigned int convert_ip_string_to_int(const char *ip, int len);

static void prepare_icmp_echo_reply(unsigned char *out_buf, unsigned char *in_buf, int len);

static void prepare_arp_reply(unsigned char *out_buf, unsigned char *in_buf, int len, int sender_ip, unsigned char *sender_ha);

 

/*-----------------------------------------------------------------------------------*/

int main(int argc, char **argv)

{

            const char ip[] = "192.168.0.1";

            const int nic_num = 1;

            eth_t *nic;

            unsigned char mac[6];

            unsigned int my_ip;

            unsigned char rx_packet[PKT_BUF_SIZE];

            unsigned char tx_packet[PKT_BUF_SIZE];

            int len;

 

            n_init();

           

            if((nic = n_open(nic_num)) == NULL)

            {

                        printf("Error opening %d nic\n", nic_num);

                        thread_delete_self();

            }

 

            n_mac(nic, mac);

 

            printf("My IP address: %s, send to me ping request!\n", ip);

 

            my_ip = convert_ip_string_to_int(ip, strlen(ip));

 

            while(1)

            {

                        // Obtain the size of the packet and put it into the "len" variable

                        len = n_read(nic, rx_packet, PKT_BUF_SIZE);

                        if(len == 0)

                        {

                                   L4_ThreadSwitch(L4_nilthread);

                                   continue;

                        }

   

                        //Is an IP Packet??

                        if(htons(*(unsigned short *)&(rx_packet[12])) == IP_PACKET)

                        {

                                   //Is an ICMP Packet??

                                   if(rx_packet[23] == ICMP_PACKET){

                                               //Is an ICMP echo request??

                                               if(rx_packet[34] == ECHO_REQUEST){

                                                           //Let's prepare the ICMP echo reply

                                                           prepare_icmp_echo_reply(tx_packet, rx_packet, len);

                                                           //Send the reply

                                                           n_write(nic, tx_packet, len);

                                               }

                                   }

                        }

                        else

                        { //It is not an IP packet

                                   //Is an ARP Packet??

                                   if(htons(*(unsigned short *)&(rx_packet[12])) == ARP_PACKET){

                                               //Asks for my IP address??

                                               if(htonl(*(unsigned int *)&(rx_packet[38])) == my_ip){

                                                           //Let's prepare the ARP reply

                                                           prepare_arp_reply(tx_packet, rx_packet, len, my_ip, mac);

                                                           //Send the reply

                                                           n_write(nic, tx_packet, len);

                                               }

                                   }

                        }

            }

 

            assert(!"How we here?");

            return 0;

}

 

/*-----------------------------------------------------------------------------------*/

static unsigned int convert_ip_string_to_int(const char *ip, int len){

  unsigned int integer_ip=0, last_dot_pos=-1;

  unsigned short i, n_dots = 0;

  unsigned char part[3];

 

  for(i = 0; i<len; i++)

    if(ip[i] == '.'){

      memcpy(part, &ip[last_dot_pos+1], i-last_dot_pos-1);

      n_dots++;

      last_dot_pos = i;

      integer_ip |= atoi(part) << (32-(n_dots*8));

      part[0] = part[1] = part[2] = 0x00;

      if(n_dots == 3) break;

    }

 

  memcpy(part, &ip[last_dot_pos+1], len-last_dot_pos+1);

 

  integer_ip |= atoi(part);

 

  return integer_ip;

}

 

/*-----------------------------------------------------------------------------------*/

void prepare_icmp_echo_reply(unsigned char *out_buf, unsigned char *in_buf, int len){

  //Copy the incoming packet into the out buffer

  memcpy(out_buf, in_buf, len);

 

  //Swap ethernet source and destination addresses

  memcpy(out_buf, &in_buf[6], 6);       //ethout_dst = ethin_src

  memcpy(&out_buf[6],in_buf, 6);        //ethout_src = ethin_dst

 

  //Swap IP source and destination addresses

  memcpy(&out_buf[26], &in_buf[30], 4); //ipout_src = ipin_dst

  memcpy(&out_buf[30], &in_buf[26], 4); //ipout_dst = ipin_src

 

  //The IP checksum does not need to be recomputed because

  //swaping source and destination addresses does not change

  //the result of the checksum computation.

 

  //Change the type of the ICMP packet

  out_buf[34] = ECHO_REPLY;            

 

  //Although in the case of ICMP we change some fields, the

  //checksum field does not need to be recomputed because

  //the Linux TCP/IP stack does not check if it is right.

}

 

/*-----------------------------------------------------------------------------------*/

void prepare_arp_reply(unsigned char *out_buf, unsigned char *in_buf, int len, int sender_ip, unsigned char *sender_ha){

  unsigned int net_ip = ntohl(sender_ip);

 

  //Copy the incoming packet into the out buffer

  memcpy(out_buf, in_buf, len);

 

  //Swap ethernet source and destination addresses

  memcpy(out_buf, &in_buf[6], 6);       //ethout_dst = ethin_src

  memcpy(&out_buf[6], sender_ha, 6);    //ethout_src = our MAC

 

  //Change the operation type to an ARP reply

  out_buf[21] = ARP_REPLY;

             

  //Swap sender IP and sender MAC with target IP and target MAC

  memcpy(&out_buf[38], &in_buf[28], 4); //target_ip = sender_ip

  memcpy(&out_buf[32], &in_buf[22], 6); //target_ha = sender_ha

  memcpy(&out_buf[28], &net_ip, 4);     //sender_ip = my_ip

  memcpy(&out_buf[22], sender_ha, 6);   //sender_ha = MAC

}


Приложение 3

 

Исходный код:

§        Элементов Click роутера для взаимодействия с ethernet сервером

kenge\example\click\pollnic.hh :

#ifndef CLICK_POLLNIC_HH

#define CLICK_POLLNIC_HH

 

#include <click/element.hh>

#include <click/task.hh>

 

#include <l4/types.h>

#include <l4/ipc.h>

 

#include <click/cxxprotect.h>

CLICK_CXX_PROTECT

#include <ether_lib/ether_lib.h>

CLICK_CXX_UNPROTECT

#include <click/cxxunprotect.h>

 

class PollNic : public Element { public:

 

  PollNic();

  ~PollNic();

 

 

  static void static_initialize();

  static void static_cleanup()             {};

 

  const char *class_name() const       { return "PollNic"; }

  const char *processing() const        { return PUSH; }

 

  int configure(Vector<String> &, ErrorHandler *);

  int initialize(ErrorHandler *);

  void cleanup(CleanupStage);

 

  /* process a packet. return 0 if not wanted after all. */

 

  bool run_task();

 

  void reset_counts();

 

  uint32_t _npackets;

   

 private:

  unsigned _devindex;

  int _burst;

  eth_t *_nic;

  Task _task;

};

 

#endif

 

kenge\example\click\pollnic.сс :

#include <click/config.h>

#include <click/glue.hh>

#include <click/error.hh>

#include <click/confparse.hh>

#include <click/router.hh>

#include <click/skbmgr.hh>

#include <click/standard/scheduleinfo.hh>

#include <nics.hh>

 

 

#include "pollnic.hh"

 

PollNic::PollNic(): _task(this)

{

    MOD_INC_USE_COUNT;

    add_output();

}

 

PollNic::~PollNic()

{

    MOD_DEC_USE_COUNT;

}

 

void PollNic::static_initialize()

{

}

 

int

PollNic::configure(Vector<String> &conf, ErrorHandler *errh)

{

    _burst = 8;

    if (cp_va_parse(conf, this, errh,

                            cpUnsigned, "device index", &_devindex,

                            cpOptional,

                            cpUnsigned, "burst size", &_burst,

                            cpEnd) < 0)

            return -1;

 

            if((_nic = nics_get_nic(_devindex)) == NULL)

            {

                        printf("Error opening %d nic\n", _devindex);

                        return -1;

            }

   

 

    return 0;

}

 

int

PollNic::initialize(ErrorHandler *errh)

{

    // check for duplicate readers

            void *&used = router()->force_attachment("nic_reader_" + String(_devindex));

            if (used)

                return errh->error("duplicate reader for device '%d'", _devindex);

            used = this;

 

/*         if (!router()->attachment("nic_writer_" + String(_devindex)))

                errh->warning("no ToDevice(%d) in configuration\n(\

Generally, you will get bad performance from PollNic unless\n\

you include a ToDevice for the same device. Try adding\n\

'Idle -> ToDevice(%d)' to your configuration.)", _devindex, _devindex);

*/

 

    ScheduleInfo::initialize_task(this, &_task, _nic != 0 , errh);

    reset_counts();

   

    return 0;

}

 

void

PollNic::reset_counts()

{

  _npackets = 0;

}

 

void

PollNic::cleanup(CleanupStage)

{

}

 

bool

PollNic::run_task()

{

            unsigned char rx_packet[PKT_BUF_SIZE];

            int len;

            int got = 0;

 

            do

            {

                        len = n_read(_nic, rx_packet, PKT_BUF_SIZE);

                        if(len)

                        {

                                   //printf("GOT!\n");

                                   got++;

                                    Packet *p = Packet::make(rx_packet, len);

                                   if(p == 0)

                                   {

                                               printf("Can't make packet!!!!\n");

                                               break;

                                   }

                                   _npackets++;

                                   output(0).push(p);

                        }

            }while(len && got < _burst);

 

//          if(got == 0)

//                      L4_ThreadSwitch(L4_nilthread);

           

  _task.fast_reschedule();

  return len > 0;

}

 

EXPORT_ELEMENT(PollNic)

 

kenge\example\click\tonic.hh :

#ifndef CLICK_TONIC_HH

#define CLICK_TONIC_HH

#include <click/element.hh>

#include <click/task.hh>

#include <click/notifier.hh>

#include <click/cxxprotect.h>

CLICK_CXX_PROTECT

#include <ether_lib/ether_lib.h>

CLICK_CXX_UNPROTECT

#include <click/cxxunprotect.h>

CLICK_DECLS

 

 

class ToNic : public Element { public:

 

  ToNic();

  ~ToNic();

 

  const char *class_name() const                  { return "ToNic"; }

  const char *processing() const                    { return PUSH; }

  int configure(Vector<String> &, ErrorHandler *);

  static void static_initialize();

  static void static_cleanup()             {};

 

  int initialize(ErrorHandler *);

  void add_handlers();

 

  void push(int, Packet *);

//  bool run_task();

 

 protected:

 

  Task _task;

  int _devindex;

  NotifierSignal _signal;

  eth_t *_nic;

 

};

 

CLICK_ENDDECLS

#endif

 

kenge\example\click\tonic.сс :

#include <click/config.h>

#include "tonic.hh"

#include <click/error.hh>

#include <click/confparse.hh>

#include <click/standard/scheduleinfo.hh>

#include <nics.hh>

 

ToNic::ToNic()

  : Element(1, 0), _task(this)

{

  MOD_INC_USE_COUNT;

}

 

ToNic::~ToNic()

{

  MOD_DEC_USE_COUNT;

}

 

void ToNic::static_initialize()

{

}

 

int

ToNic::configure(Vector<String> &conf, ErrorHandler *errh)

{

    if (cp_va_parse(conf, this, errh,

                            cpUnsigned, "device index", &_devindex,

                            cpEnd) < 0)

            return -1;

 

            if((_nic = nics_get_nic(_devindex)) == NULL)

            {

                        printf("Error opening %d nic\n", _devindex);

                        return -1;

            }

   

 

    return 0;

}

 

int

ToNic::initialize(ErrorHandler *errh)

{

 

//  if (input_is_pull(0)) {

//    ScheduleInfo::initialize_task(this, &_task, errh);

//    _signal = Notifier::upstream_empty_signal(this, 0, &_task);

//  }

  return 0;

}

 

 

void

ToNic::push(int, Packet *p)

{

  n_write(_nic, p->data(), p->length());

  p->kill();

}

 

/*

bool

ToNic::run_task()

{

            Packet *p = input(0).pull();

            if (p)

            {

                        n_write(_nic, p->buffer_data(), p->buffer_length());

//                      p->kill();

            }

            else if (!_signal)

                        return false;

  _task.fast_reschedule();

  return p != 0;

}

*/

 

void

ToNic::add_handlers()

{

  if (input_is_pull(0))

    add_task_handlers(&_task);

}

 

EXPORT_ELEMENT(ToNic)


ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
Государственное образовательное учреждение

высшего профессионального образования

ОБНИНСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
АТОМНОЙ ЭНЕРГЕТИКИ

(ИАТЭ)

 

               Студент                 Степанов Андрей Вадимович                                          

(фамилия, имя, отчество)

факультета «Кибернетики», специальности 220100, группы ВТ-3-00

 (факультет, специальность, № группы)

 

ОТЗЫВ РУКОВОДИТЕЛЯ

на преддипломную практику

на тему  «Разработка и создание элементов ОС на основе микроядра»

 

Степанов А.В. проходил преддипломную практику в лаборатории телекоммуникационных технологий ГУ «ВНИИГМИ-МЦД»

Степанов А.В. проявил настойчивость, самостоятельность, творческий подход в процессе формирования и достижения поставленной задачи. Его отношение к исследуемой теме проявились в стилистике изложения материала, аргументации выводов и стремлении использовать фактические данные. Главной чертой студента является самостоятельность и убежденность в перспективности поставленной цели.

Для достижения поставленной цели автор выполнил следующие работы:

Проанализировал исследовательские работы в области создания и развития ОС на базе микроядра. На основе анализа были сделаны выводы о перспективности данного направления в ближайшем будущем.

Систематизированы технологии создания компонентов ОС, что позволило автору определить наиболее перспективные методы проектирования сервисов ОС.

В частности были реализованы следующие компоненты ОС:

§         сервер, предоставляющий стандартный интерфейс семафоров POSIX для синхронизации исполняемых потоков

§         сервер, представляющий унифицированный интерфейс для работы с сетевым устройствами типа Ethernet

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

Были выделены позитивные и негативные факторы, влияющие на быстродействие и защищенность серверов ОС. Предложены наиболее рациональные способы реализации компонентов ОС.

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

Считаю, что работа Степанова А.В. Заслуживает оценки “отлично”.

 

Зав лабораторией ЛТТ ГУ ВНИИГМИ-МЦД, к.т.н.

_______________

Шаймарданов В.М.

Подпись Шаймарданов В.М. заверяю, ученый секретарь

_______________

Кутько А.П.

 

Hosted by uCoz