Привет!
Рад видеть тебя в своём журнале.
Так случилось, что в очередной раз "зацепился языком" в теме про
Методы синхронизации и блокировки в Linux ядре. После дискуссии, а она, как видно, ещё не закончилась, осталось двоякое впечатление. Первое чувство - "Ну вот, наконец-то удалось донести хоть часть идей до широкой общественности", второе чувство - "Господи, там так много ошибок, опечаток и стилистических промахов, что народ не воспримет всерьёз". Посему, попробую оправдаться у себя в журнале и немного упорядочить тот поток сознания, который я вылил в тему о синхронизациях и блокировках Линукса.
Итак, каркас сервиса:
///////////////////////////////////////////////////////////////////////////////
// Пример каркаса для Хамелеон сервиса
// Автор: Алексей Мандрыкин aka alman (c) 2007
// Рассмотрим случай, когда сервис оформлен в виде обычного исполняемого файла
//
#include // Стандартный заголовочный файл из userspace микроядра L4Ka
int main(
int argc, // Количество аргументов
char * argv[], // Аргументы
char * envp[] ) // Переменные среды
{
L4_ThreadId_t tid; // Идентификатор потока(нити) инициатора сообщения
L4_MsgTag_t tag; // Тэг полученного сообщения
L4_Msg_t msg; // Собственно, структура носитель сообщения
L4_Word_t tmout; // Величина таймаута на приём сообщения.
int irq_number; // Этой переменной присваивается номер прерывания устройства
bool do_confirm; // Признак, отвечающий за необходимость ответа на сообщение
// Следующая функция инициализирует физическое устройство и возврашает номер его прерывания
// Детали реализации данной функции зависят от устройства и выходят за рамки данного примера
initialize_physical_device( &irq_number );
// Первый таймаут - бесконечность, поскольку в очередеях ещё нет данных и экспайриться нечему
tmout = (L4_Word_t) -1;
// Поскольку сервис оформлен в виде потока(нити), заворачиваем всю обработку в бесконечный цикл
// Бесконечный цикл используется для наглядности и простоты понимания
while ( true )
{
// Примитив L4_Wait - это надстройка над системным вызовом IPC микроядра L4.
// В показанном примере, IPC поддерживает только фазу приёма сообщений
// Обратите внимание, что до получения любого сообщения, поток(нить)
// исключается из очереди планировщика задач и,
// соответственно, кванты времени сервиса отдаются другим потокам(нитям) в пределах
// физического процессора
tag = L4_Wait( L4_TimePeriod( tmout ), &tid );
if( L4_IpcFailed(tag) )
{
// По какой-то причине, во время фазы приёма мы получили ошибку.
// Ошибка может быть вызвана следующими причинами:
// 1. Переполнение сообщение
// 2. Ожидание оборвано супервизором
// 3. За время, определённое величиной tmout, не принято ни одного события
// Смотрим, чем вызвана ошибка
if( L4_ErrorCode() == 0x3 )
{
// За период tmout не получили ни одного сообщения.
// Дальнейшая работа заключается в обработке таймаута,
// например, повторная передача пакета или удаление пакета из очереди
// и/или передача статуса операции иниициатору запроса
// После обработки таймаута не забываем назначить переменной tmout актуальное значение.
// Например, "время жизни" пакета, имеющего минимальное "время жизни".
}
else
{
// Здесь можно узнать остальные причины обрыва сообщения
// Пожалуй, любые случаи попадания в этот участок кода,
// за исключением обрыва IPC супервизором, означают ошибку в реализации сервиса
// наиболее распространённая ошибка - вы не использовали примитив L4_Accept()
}
continue;
}
// В соответствии со спецификацией L4, номер прерывания равен номеру потока(нити),
// источника сообщения.
if( L4_ThreadNo(tid) == irq_number )
{
// Получили прерывание. Проверяем статус прерывания устройства и
// в зависимости от статуса выполняем обработку прерывания
// Обратите внимание, что сброс бита активного прерывания и
// сброс бита прерывания в устройстве - ответственность обработчика прерывания
handle_interrupt();
// Затем подтверждаем прерывание на уровне микроядра
L4_LoadMR( 0, 0 );
L4_Send( tid );
// И снова уходим в ожидание события
continue;
}
// Сюда попадаем, когда получаем запрос от внешнего сервиса/приложения
// Переносим сообщение в локальный буффер
L4_Store( tag, &msg );
// Определяем идентификатор полученного сообщения
switch( L4_Label(tag) )
{
case WriteDataToDevice:
// тут всё понятно - кто-то желает записать данные в устройство
do_confirm = write_data_to_device( &msg );
break;
case ReadyToReadDataFromDevice:
// тут тоже всё понятно - кто-то сообщает о готовности принять данные из устройства
do_confirm = read_from_device( &msg );
break;
default:
// По-видимому, чужой запрос или неподдерживаемый протокол
// Чтобы расширить протокол, достаточно добавить новый case и обработчик
// Запрещаем подтверждение - пусть инициатор "битого" сообщения зависнет в фазе ожидания подтверждения
do_confirm = false;
break;
}
// Требуется ли подтверждение? Нет? Тогда ждём следующее сообщение
if( ! do_confirm ) continue;
// Переносим сообщение в системный буффер.
// Обратите внимание, что данные системного буфера в этом примере подготавливаются
// функциями write_data_to_device( &msg ) и read_from_device( &msg );
L4_Load ( &msg );
// Посылаем ответ инициатору сообщения
// Примитив L4_Reply, как и примитив L4_Wait, - является надстройкой над L4_IPC.
// L4_Reply исключает фазу приёма и блокировку.
L4_Reply ( tid );
} // И снова в бой! :)
// Этот пример никогда не попадёт в эту точку. Возвращаем ноль, чтобы не ругался компилятор
return 0;
}
Большое спасибо всем участникам дискуссии, благодаря которой появился этот пост.