Реклама:

info.krc.karelia.ru

win -:|:- koi -:|:- iso -:|:- dos -:|:- mac

Start -:|:- Проекты -:|:- О нас

RT-Linux.

1 Введение

1.1 Что такое система реального времени

В последнее время все чаще приходится сталкиваться с задачами, требующими управления какими-то сложными процессами или оборудованием при помощи ЭВМ. При этом все события в этих процессах происходят тогда, когда они происходят. Компьютер же может выполнять лишь конечное число операций в конечное время, поэтому возникает вопрос: а успеет ли компьютер с нужной скоростью обсчитать ситуацию и выдать определенные управляющие действия. Ясно, что сигналы в природе распространяются с конечной скоростью, скорость работы тоже конечна, поэтому мгновенных действий (вызванных неким событием) от компьютера ожидать не приходится. Таким образом время реакции системы на некоторое событие строго больше нуля. Реальные задачи допускают некоторого запаздавания действий и если наша система имеет время реакции меньше, чем эта допустимая задержка то ее можно называть системой реального времени. Так как в природе разные процессы протекают с разной скоростью, одна и таже система может укладываться в заданные рамки для одного процесса и не укладываться для другого. Таким образом о системе реального времени имеет смысл говорить применительно к конкретной задаче. Например чтобы построить зависимость средней температуры воздуха за день от дня недели в качестве системы реального времени сойдет практически любой компьютер с практически любым програмным обеспечением. Если же мы управляем посадкой самолета, где существенную роль играют микросекунды, нам придется внимательно выбирать аппаратное и програмное обеспечение.

Кроме рассмотренной задачи о реагировании на некоторое событие, существуют еще другие классы задачь реального времени. Одной и часто встречаемых является задача постоянного наблюдения или управления процессом. Т.е. когда требуется непрерывно обмениваться сигналами с внешним миром. Ясно, что компьютер - это дискретная система и мы вынуждены осуществлять некоторые действия с некоторыми конечными промежутками времени, считая что в эти малые промежутки времени внешний мир остается неизменным. Если наша система способна обрабытывать информацию и выдавать управляющие сигналы с требуемой частотой, то ее можно назвать системой реального времени. Нетрудно видеть, что эту задачу легко свести к предыдущей, используя в качестве события начало очередного оитервала времени, время реакции должно быть меньше времени дискретизации процесса. Таким образом описанная в предыдущем параграфе задача является наиболее важной, когда речь идет о системах реального времени. Отметим, неудовлетворительная по запаздаванию работа системы в некоторых задачах может привести к фатальным последствиям, а в некоторых не произойдет больших неприятностей. Например если система измерения температуры из описанного выше примера случайно опоздает на непозволительное время, то это значит что мы просто изменили выборку точек съема температуры и все равно получим правельный результат, если же на секунду случайно задержится автомат захода на посадку в пассажирском самолете при внезапном порыве ветра, самолет может не попасть на полосу и десятки людей погибнут. Таким образом следует делить системы на системы жесткого реального времени (провалы недопустимы) и системы магкого реального времени (ограниченное число провалов допустимо).

1.1 Основные сложности при реализации систем реального времени в среде UNIX

Как мы видели выше, основной является задача реагирования на некоторое внешнее событие в заданный промежуток времени. В ЭВМ внешнее событие обычно с точки зрения программиста выглядит как аппаратное прерывание. В совремеммых многозадачных операционных системах первым на аппаратное прерывание реагурует ядро. Затем это прерывание через драйвера устройств коким-то образом может попасть и к прикладной задаче. Но в многозадачной системе должны одновременно работать сразу несколько задач и для того, чтобы доставить прерывание ядро должно перевести процесс выполняющийся в данный момент в состояние сна, пробудить нужный процесс и передать ему прерывание. Для этого нужно переключать контексты, что требует много времени, поэтому прерывание будет доставлено процессу со значительным опазданием. Кроме того после получения прерывания прцессом нельзя быть уверенным что обработка информации будет завершена в минимальные сроки, т.к. если компьютер оснащен всего одним процессором а в системе запущено больше одной задачи то в любой момент может произойти переключение задач с очередным переключением контекстов. В результате время реакции может получиться неоправданно большим на достаточно производительном компьютере.

1.3 Идея RT-Linux

Linux - это UNIX-like система и поэтому ей присущи все выше перечисленные сложности при реализации систем реального времени. Но все же в структуре UNIX можно найти место где могла бы существовать система реального времени. Даже в стандартной системе есть просессы почти удовлетворяющие нашим требованиям - это драйвера устройств. Драйвера устройств имеют привелегии ядра и могут непосредственно получать прерывания, кроме того они находятся в адресном пространстве ядра и всегда отображаются в память процесса выполняющегося в данный момент. Поэтому когда проичходит прерывание переключения контекстов не требуется, более того т.к. драйвера не являются обычными процессами UNIX при обработке переключения задачь не произойдет. Драйвера устройств могут обмениваться информацией с обычными процессами UNIX через файлы символьных и блочных устройств. Это означает что все долгие операции такие как выведение данных на экран, взаинодействие с человеком пересылка данных через сеть может быть легко осуществлена при помощи обычнех следств UNIX.

Таким образом в UNIX уже имеется некая сабсистема реального времени. Это и понятно. Ведь драйвера работают непосредственно с аппаратной частью и эта работа должно проводиться в реальном времени. Казалось бы задача решена и для создания системы реального времени достаточно написать соответствующий драйвер. Но у такого подхода есть ряд недостатков.

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

1.4 Реализация RT-Linux

Посмотрим как же реализовано ядро RT-Linux. Для этого кратко опишем как работает стандартное ядро Linux.

При старте системы управление получает функция start_kernel. В ней происходит вся необходимая инициализация:

        setup_arch(&command_line, &memory_start, &memory_end);

        memory_start = paging_init(memory_start,memory_end);

        trap_init();

        init_IRQ();

        sched_init();

        time_init();

        parse_options(command_line);

#ifdef CONFIG_MODULES

        init_modules();

#endif

	.....



        dquot_init();

        arch_syms_export();

        sti();

        check_bugs();



        printk(linux_banner);

#ifdef __SMP__

        smp_init();

#endif

        sysctl_init();

        /*

         *      We count on the initial thread going ok

         *      Like idlers init is an unlocked kernel thread, which will

         *      make syscalls (and thus be locked).

         */

        kernel_thread(init, NULL, 0);

Здесь выставляются вектора прерываний в том числе и вектор прерывания таймера. Когда происходит прерывание таймера вызывается функция do_timer(), которая обнавляет статистику по процессам и при необходимости вызывает функцию schedule(), запускающую нужные процессы Linux.

В RT-Linux изменена функции запроса прерывания setup_x86_irq() и вместо непосредственной установки прерывания заполняет соответствующее поле в массиве прерываний Linux static void (*linux_interrupt[16])(void) . На самом же деле вектора прерывание устанавливает функция request_RTirq. Изменены также функции:

Эти изменения позволяют задачам реального времени всегда получать свои прерывания вне зависимости от того что происходит в данный момент в Linux. Этот же механизм позволяет установить вектор прерывания таймера на свой обработчик и сделать свой планеровщик, который бы переключал задачи реального времени. Так и реализован планировщик загружаемый в виде модуля ядра.
int init_module(void)

{

       rt_tasks = &rt_linux_task;

       rt_current = &rt_linux_task;

       rt_linux_task.priority = RT_LOWEST_PRIORITY;

       rt_linux_task.next = 0;

       rt_linux_task.state = RT_TASK_READY;

       rt_request_timer(&rt_schedule);

       return 0;

}



При загрузке он создает список задач реального времени в который он включает стандартное ядро Linux. И переопределяет прерывание таймера на функцию rt_schedule(). Теперь при срабатывании прерывания таймера наш планировщик будет передавать управление одной из задач реального времени, и если в данный момент ни одна из задач не требует выполнения, управление будет передано Linux (в то место где было прервано ядро Linux или обычный процесс Linux). При этом планирование задач Linux по прежнему будет осуществлять функция schedule, получая прерывания таймера, когда задачи реального времени не требуют выполнения.

Рассмотрим подробнее как работает планировщик реального времени:


void rt_schedule(void)

{

       RTIME now;

       RTIME preemption_time;

       RT_TASK *task;

       RT_TASK *new_task;

       RT_TASK *preemptor;

       int prio;

       int flags;



       r_save_flags(flags);

       r_cli();

Запрещаем прерывания. Это практически единственное место где прерывания действительно запрещены.
       now = rt_get_time();

       for (task = rt_tasks; task; task = task->next) {

               if (task->state == RT_TASK_DELAYED &&

                                       task->resume_time < now + 10) {

                       task->state = RT_TASK_READY;



               }

       }

Просматриваем все задачи реального времени и если находим приостановленную задачу, которую пора запускать помечаем ее как готовую к запуску.
       new_task = &rt_linux_task;

       prio = rt_linux_task.priority;

Далее пойдет поиск готовой к запуску задачи с наивысшим приоритетом. Если таковой не найдется то управление будет передано Linux.
        for (task = rt_tasks; task; task = task->next) {

               if (task->state == RT_TASK_READY && task->priority < prio) {

                       new_task = task;

                       prio = task->priority;

               }

       }

       preemptor = 0;

       preemption_time = RT_TIME_END;

Далее пойдет поиск приостановленого процесса реального времени с приоритетом выше, чем у выбранного для передачи выполнения. И таймер будет запрограммирован на прерывание в тот момент времени, когда найденный процесс не потребует выполнения. Если такого процесса не окажется т.е. все процессы либо готовы к выполнению, но имеют приоритет ниже, чем у выбранной, либо приостановлены, но имеют приоритет ниже чем у выбранной, либо просто остановлены, выбранный процесс будет выполняться пока не пройзойдет аппаратного прерывания или он не вызовет функцию rt_task_wait. Если был выбран процесс реального времени не являющийся Linux и он не успевает выполнить свою работу да момента времени в который ему снова нужно работать и, соответственно не вызовет rt_task_wait, при следующем вызове rt_schedule управление будет снова передано ему, либо другому процессу реального времени, приостоновленного на данном шаге и имеющего приоритет выше чем у выбранного. В этом случае Linux никогда не получит управления, т.к. имеет самый низкий приоритет и компьютер повиснет.
       for (task = rt_tasks; task; task = task->next) {

               if (task->state == RT_TASK_DELAYED && task->priority < prio &&

                               task->resume_time < preemption_time) {

                       preemption_time = task->resume_time;

                       preemptor = task;

               }

       }



Если нашли приостановленный процесс с приоритетом выше чем у выбранного программируем таймер на прерывание в момент, кгда этот процесс требует выполнения. Иначе следующее в следующий раз функция rt_schedule будет вызвана в одном из случаев:
       if (preemptor) {

               rt_set_timer(preemption_time);

       } else {

               rt_no_timer();

       }

Если выбранный прцесс является текущим заканчиваем планирование.
       if (new_task == rt_current) {

               r_restore_flags(flags);

               return;

       }

Если переключаем Linux восстанавливаем/сохраняем флаги прерываний Linux.
       if (new_task == &rt_linux_task) {

               SFIF = linux_irq_state;

       } else if (rt_current == &rt_linux_task) {

               linux_irq_state = SFIF;

               SFIF = 0;

       }





       new_task->state = RT_TASK_READY;

Переключаем задачи.
       rt_switch_to(new_task);

       r_restore_flags(flags);

}



Часто используются задачи управление которым передается переодически. Такие задачи создаются при помощи функции rt_task_make_periodic(). Рассмотрим ее работу подробнее:
int rt_task_make_periodic(RT_TASK *task, RTIME start_time, RTIME period)

{

       long flags;

       if (task->magic != RT_TASK_MAGIC) {

               return -EINVAL;

       }

       r_save_flags(flags);

Запрещаем прерывания. Недопустимо чтобы в этот момент был вызван планировщик.
       r_cli();

Процесс будет запущет в требуемое время
       task->resume_time = start_time;

Функция rt_task_wait() прибавляет к resume_time period. Поэтому процесс будет получать управление через требуемый промежуток времени.
       task->period = period;

       task->state = RT_TASK_DELAYED;

       r_restore_flags(flags);

       rt_schedule();

       return 0;

}



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

2 Что такое RT-Linux

RT-Linux - это операционная система в, которой небольшое ядро реального времени сосуществует с Posix-like ядром Linux. Основная цель - сделать доступными сложные службы и оптимизированное поведение системы в стандартных ситуациях, для системы с разделением времени и в то же время выполнять задачи реального времени. В прошлом операционные системы реального времени примитивны - простые программы которые предлагали пользователю чуть больше, чем просто бублиотека основных функций. Но в наше время требуют доступ к TCP/IP, графическому дисплею и системе окон, бызам данных и другим службым, которые не являются ни примитевными ни простыми. Одно из решений - добавить non-real-time службы к базовому ядру реального времени, это и было проделано в VXworks и, немного подругому в микроядре QNX. Вторая возможность - модифицировать стандартное ядро и сделать его полностью прерываемым. Ею воспользовались разработчики RT-IX(Modcomp). RT-Linux устроен треьим способом, в котором простое ядро реального времени запускает обычное ядро как одну из задач реального времени с самым низким приоритетом, используя виртуальную машину для того, чтобы сделать стандартное ядро полностью прерываемым.

В RT-Linux все прерывания обслуживаются ядром реального времени а затем передаются стандартному ядру, но только в том случае, если нет нодобность запустить одну из задач реального времени. Для того, чтобы минимизировать количество изменений в стандартном ядре, этот механизм реализован при помощи эмулирования ICH (Interrupt Control Hardware). Ядро реального времени и пользовательские задачи Linux могут обмениваться данными через неблокируемые очериди и сегменты разделяемой памяти. С точки зрения программиста очериди выгледят как стандартные последовательные устройства UNIX, доступ к которым возможен при помощи системных вызовов POSIX read/write/open/ioctl. разделяемая память доступна через системный вызов mmap. RT-Linux использует Linux для загрузки, доступа к большинству устройств, работы с сетью, файловыми системами, Управлением процессами Linux, и загрузки модулей ядра, что дает возможность легко модифицировать систему реального времени. Программа реального времени состоит из двух частей - задачи реального времени, которая представляет собой модуль ядра и обыкновенный UNIX/Linux процесс, который заботится об обработке данных, доступу к дусплею и сети, и о любых других функциях которые не требуют таких жестких временных рамок.

На практике оказалось, что идея RT-Linux - очень удачна. В самом худшем случае запаздавание прерываний на 486/33Mhz PC оказалось менее 30 мкс, что близко к аппаратному пределу. Для прикладных задач симбиоз межбу системой реального времени и системой, оптимизированной для 'общего случая' оказался очень удачным. Наиболее часто используемая конфигурация RT-Linux - примитивные задачи реального времени со статически распределяемой памятью, без защиты памяти, простым плянировщиком с фиксированными приоритетами без защиты от нереализуемых планов, аппаратным запрещением прерываний, разделяемая память - единспвенный механизм для синхронизации задач реального времени, и ограниченным набором операций над FIFO очередями подсоединенными о обычным процессам Linux. Но все не так сурово, как может показаться, благодаря богатой коллекции служб стандартного ядра Linux.

Ядро Linux позволяет в дунамике загружать и выгружать модули ядра. Это позволяет, сделав отдельные части ядра реального времени в виде модулей, легко изменять ядро реального времени. Уже написаны альтернативные планировщики и модуль семафоров. Во время работы системы можно загрузить модуля с задачами реального времени, затем выгрузить стандартный планировщик и загрузить, например EDF планировщик. Можно пробовать разные комбинации модулей, пока не будет найдена оптимальная.

3 Возможности RT-Linux

Обзор

Этот вариант Linux позволяет выполнять задачи в реальном времени. Это, в основном, достигается путем всталки ядра реального времени между стандартным ядром Linux и аппаратными прераваниями. Что позволяет избавиться от главной причины непригодности Linux для задачь реального времени - большого запаздывания прерываний.

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

Такая структура накладывает некоторые ограничения на задачи реального времени. Они не могут легко использовать различные драйвера Linux, не исеют доступ к сети и т.д. Зато они могут обмениваться данными с стандартнами задачими Linux.

Простые очереди FIFO реализованы для обмена данными между процессами реального времени и процессами Linux.

Типичное приложение состоит из двух частей - задачи реального времени, непосредственно работающей с аппаратурой и обыкновенно задачи Linux, которая выполняет остальные операции, такие как сохранение данных на диск, пересылка их по сети, работа с пользователем (GUI) и т.д.

Самый короткий период для переодически вызываемых задачь чеального времени в RT-Linux на Pentium 120 - менее 150 мкс. Задачи, вызаваемые по прераванию могут иметь намного меньший период.

Ядро реального времени не защищает от перегрузок. Если одна из задачь реального времени полностью утилизурует процессор, ядро Linux, имея самый низкий приоритет, не получит управления и система повиснет.

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

3.1 Планеровщик

Модуль ядра kernel/rt_prio_cshed.c реализует простой планеровщик реального времени. Планировщик позволяет создавать, запускать на выполнение, приоста навливать, пробуждать и удалять задачи реального времени.

файл <linux/rt_sched.h> содержит прототипы интерфейсных функкий и определения типов планеровщика:

typedef struct rt_task_struct RT_TASK;

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

extern int rt_task_init(RT_TASK *task, void (*fn)(int data), int data, int stack_size, int priority);

Инициализирует задачу реального времени. 'fn' - код новой задачи, 'data' - целое число, которое будет передано fn на старте. 'stack_size' - размер стека для этой задачи. 'priotity' - приоритет. Максимальный приоритет - 1, минимальный - RT_LOWEST_PRIORITY.

extern int rt_task_make_periodic(RT_TASK *task, RTIME start_time, RTIME period);

Помечает задачу, как переодическую. Чтобы задача стартовала немедленно, используйте значение возвращаемое rt_get_time() из asm/rt_time.h в качестве start_time.

extern int rt_task_delete(RT_TASK *task);

Удаляет задачу из списка задач.

extern int rt_task_wait(void);

Ждет начала следующего периода для переодических задач.

extern int rt_task_suspend(RT_TASK *task);

Приостанавливает задачу.

3.2 Очериди FIFOs

файл <linux/rtf.h> содержит функции для чтения и записи в RT-FIFO.

extern int rtf_create(unsigned int fifo, int size);

Создает очередь. Возвращает size в случае успеха, иначе -1.

extern int rtf_destroy(unsigned int fifo);

Уничтожает очередь. Возвращает 0 в случае успеха.

extern int rtf_resize(unsigned int minor, int size);

Изменяет размер очереди. Возвращает size в случае успеха и меньше 0 в противном случае.

extern int rt_fifo_put(unsigned int fifo, char * buf, int count);

Пытается записать в очередь fifo count баутов из buf. Возвращает count в случае успеха или -1 если в очереди кончилось место.

extern int rt_fifo_get(unsigned int fifo, char * buf, int count);

Пытаетса прочитать count байтов из fifo в buf. Возвращает -1 если в очереди недостаточно данных или count в случае успеха.

extern int rtf_create_handler(unsigned int fifo, int (*handler)(unsigned int fifo))

Позволяет функции 'handler' быть вызванной когда _Linux_ процесс читает или пишет в fifo. При вызове функции ей передается номер fifo в качестве аргумента.

3.3 Прерывания

int request_RTirq(unsigned int irq, void (*handler)(void));

Устанавливает вектор для прерывания номер ``irq''.

void free_RTirq(unsigned int irq);

освобождает преравание ``irq''.

Для того чтобы лучше понять как пользоваться RT-Linux приведеи сдесь несколько примеров:


Подробнее об RT-Linux можно узнать на домашней странице RT-Linux


Оглавление

 

Rambler's Top100 Service Яндекс цитирования