ИУС_Архитектура и разработка_IoTиОблако. 87 Интернет вещей и облачные технологии Интернет вещей и облачные технологии
Скачать 2.23 Mb.
|
1 В классических операционных системах реального вре‑ мени, как правило, таких возможностей нет. Операционные системы для интернета вещей должны обе‑ спечивать самостоятельность устройства, работающего в об‑ щем контексте. Каждая «вещь» получает информацию, за‑ дания и обновляет данные из общего контекста, не заботясь о наличии других устройств в том же контексте. Например, вновь под‑ ключаемое устройство, благодаря наличию доступа к контексту, со‑ храняющему текущий статус задачи, продолжит выполнение задачи именно с того состояния, на котором оно прекратило свою работу из‑ за некоторой неисправности. Это позволит обеспечить надежность, масштабирование и решение таких задач, как автономная адаптация сетей с высокой плотностью гетерогенных устройств (порядка милли‑ она вещей на квадратный километр). Проект ОС РВ МАКС (операционная система реального времени для мультиагентных когерентных систем) стартовал в 2015 г. В результате ре‑ ализации проекта была создана ОС реального времени, обладающая до‑ полнительными возможностями по обеспечению взаимодействия меж‑ ду устройствами в распределенной среде. Концепция распределенной 1 Internet of Battle Things. 114 Раздел Б. Разработка информационно-управляющих систем общей памяти позволила упростить программирование параллельно функционирующих устройств: разработчик обеспечивает выполнение действий с памятью, а ОС заботится о согласованности данных на всех узлах системы, организовывает обмен данными и самостоятельно ре‑ шает вопросы возможных сбоев устройств или каналов связи. Система поставляется с комплектом русскоязычной документации, поддерживает работу с оборудованием отечественного производства ( продукция «ПКК Миландр» — 32‑разрядные микроконтроллеры се‑ рий 1986 и 1967). Ее техническая поддержка осуществляется россий‑ скими специалистами. К исходным кодам системы прилагаются ша‑ блоны проектов для различных сред разработки и готовые программы, помогающие пользователям быстро освоить ОС, настраивать и со‑ здавать новые приложения. Для обеспечения работы многозадачного приложения, ядро систе‑ мы должно объединять в себе: планировщика (диспетчера), объекты синхронизации (семафоры, события и др.) и средства обеспечения взаимодействия задач (очереди сообщений). 5 . 3 . П р о г р а м м н ы й и н т е р ф е й с с и с т е м р е а л ь н о г о в р е м е н и Наблюдается тенденция использования универсальных ОС в каче‑ стве ОС РВ. С одной стороны, это обусловлено тем, что ОС общего назначения широко распространены, доступны, обладают развиты‑ ми и эффективными средствами разработки. Также для них подготов‑ лено большое количество специалистов. С другой стороны, в состав ядер универсальных ОС включаются средства поддержки реального времени, что отражается в открытых стандартах. Однако на базе та‑ кой ОС может быть реализована только система мягкого реального времени, т. к. в ней отсутствуют необходимые для ОС РВ механизмы, описанные выше. Рассмотрим особенности использования универсальных ОС семей‑ ства Windows в качестве ОС РВ. Система поддерживает всего 32 приоритета (0 …31). При создании процесса указывается только класс приоритета: • IDLE_PRIORITY_CLASS, базовый приоритет 4; 115 5. Системы реального времени • NORMAL_PRIORITY_CLASS, базовый приоритет 9 (в фоку‑ се) или 7; • HIGH_PRIORITY_CLASS, базовый приоритет 13; • REALTIME_PRIORITY_CLASS, базовый приоритет 24. Диспетчер может самостоятельно изменять приоритет процессов всех классов, кроме класса реального времени. Класс приоритета те‑ кущего процесса задается следующим образом: BOOL SetPriorityClass ( HANDLE hProcess,//дескриптор процесса DWORD dwPriorityClass//значение класса приоритета ); При создании потока он принимает приоритет, равный базовому приоритету процесса, и далее он может быть изменен в диапазоне (±2) относительно базового или принимает крайние значения приоритета класса (16 или 31 для класса реального времени): • THREAD_PRIORITY_LOWEST • THREAD_PRIORITY_BELOW_NORMAL • THREAD_PRIORITY_NORMAL • THREAD_PRIORITY_ABOVE_NORMAL • THREAD_PRIORITY_HIGHEST • THREAD_PRIORITY_IDLE • THREAD_PRIORITY_TIME_CRITICAL Приоритет потока задается следующим образом: BOOL SetThreadPriority ( HANDLE hThread,//дескриптор потока i nt nPriority//уровень приоритета потока ); Таким образом, общее количество приоритетов равно 7, что явля‑ ется крайне недостаточным. При обработке прерываний в Windows происходят следующие со‑ бытия: • прерывание; • сохранение регистров и вызов диспетчера; • сохранение контекста; • выполнение ISR (Interrupt Service Routine) — критическая по вре‑ мени работа; • постановка в очередь DPC (Deferred Procedure Call); • восстановление контекста и регистров; 116 Раздел Б. Разработка информационно-управляющих систем • выполнение DPC с приоритетом DISPATCH_LEVEL — окон‑ чательная обработка прерывания; • продолжение выполнения пользовательских процессов. Обработка прерывания подобна процедуре, рассмотренной выше, за исключением того, что все процедуры DPC, представляющие собой части драйвера (как и ISR), выполняются с одним и тем же са‑ мым высоким в системе приоритетом. Таким образом, для Windows характерна постоянно проявляюща‑ яся инверсия приоритетов. В системе часто возникают непредсказуе‑ мые временные задержки. Особенно это связано с передачей данных по сети, т. к. DPC сетевого драйвера отрабатывает немалый тайм‑аут, что составляет секунды. В это время все остальные процессы забло‑ кированы. Системы Linux, являясь представителями семейства UNIX и, сле‑ довательно, POSIX‑совместимыми, гораздо полнее отвечают требова‑ ниям реального времени [48, 49]. Обработка прерываний в Linux пол‑ ностью соответствует рассмотренной выше процедуре. В Linux присутствуют приоритеты нескольких типов. Базовый при‑ оритет процесса имеет 40 значений. Он принимает значения от –20 — самое высокое, до +19 — самое низкое. Поскольку самым приори‑ тетным является процесс с минимальным значением приоритета, в литературе базовый приоритет иногда называется «любезностью». Подразумевается, что процесс, имеющий высокое значение приори‑ тета, любезно разрешает себя прерывать. Установка базового приоритета процесса осуществляется следую‑ щим образом: int setpriority (int which, int who, int prio); which: • PRIO_PROCESS • PRIO_PGRP • PRIO_USER who: • PID • PGID • UID prio: –20..19 В программном коде указывают, для какого объекта задается при‑ оритет, идентификатор объекта и сам приоритет. Базовый приори‑ 117 5. Системы реального времени тет используется диспетчером для принятия решения о предоставле‑ нии процессу кванта времени и для вычисления начального значения этого кванта. Планирование процессов осуществляется диспетчером в соответ‑ ствии с заданной для процесса или потока дисциплиной диспетчери‑ зации. Выделяют следующие дисциплины диспетчеризации: • очередь — FIFO (First In First Out). Процесс из очереди готовых к выполнению процессов данного приоритета получает управ‑ ление и выполняется до тех пор, пока не завершится; • карусель — Round Robin. Процессу выделяется квант времени, который фиксирован или является заранее вычисляемой вели‑ чиной. По истечении кванта времени процесс теряет управле‑ ние и помещается в конец очереди готовых к выполнению про‑ цессов данного приоритета; • адаптивный алгоритм; • спорадический алгоритм. Адаптивный алгоритм диспетчеризации работает следующим об‑ разом: • из очереди готовых к выполнению процессов выбирается про‑ цесс с максимальным приоритетом, который находится в оче‑ реди самое длительное время. Ему выделяется квант времени фиксированного размера; • по истечении кванта времени приоритет процесса начинает ли‑ нейно уменьшаться; • когда динамический приоритет процесса становится меньше приоритета какого‑либо процесса в очереди, выполняется пе‑ реключение. Процесс помещается в очередь с исходным значе‑ нием приоритета. Эта дисциплина использовалась в классических системах UNIX и в QNX4. В Linux не применяется. Спорадический алгоритм диспетчеризации работает следующим образом. Процесс имеет 2 приоритета: основной N (normal) и фоно‑ вый L (foreground). Когда диспетчер выбирает процесс для выполне‑ ния, происходит следующее: • при запуске поток имеет начальный бюджет времени С (initial budget) и основной приоритет N; • если время обработки, заданное бюджетом времени C, заканчи‑ вается, приоритет потока понижается до фонового значения L; 118 Раздел Б. Разработка информационно-управляющих систем • через период времени T происходит пополнение (replenishment) бюджета времени потока до значения C, и он снова может вы‑ полняться с основным приоритетом N. Этот алгоритм широко используется, например, в QNX6 [46, 47] и в последних ядрах Linux [48, 49]. При планировании выделяют 2 типа процессов: • обычные (интерактивные и пакетные) процессы; • процессы реального времени. Для диспетчеризации обычных процессов используется статиче‑ ский приоритет s, описываемый функцией nice (). Для всех готовых к выполнению процессов вычисляется динамический приоритет d: в зависимости от времени ожидания процессов, диспетчер может из‑ менить приоритет процесса до значения d = s – b + m (m — целое чис‑ ло, например 5 или 20, b = [10·t], где t — среднее время, проведенное процессом в ожидании за 1 с и выраженное в секундах), но ограничен‑ ного максимальным значением, задаваемым системой. Из списка го‑ товых к выполнению процессов выбирается процесс с минимальным значением динамического приоритета. В зависимости от статическо‑ го приоритета, вычисляется величина базового кванта t q как функция s. Процесс выполняется в течение интервала времени, соответствую‑ щего базовому кванту t q Для диспетчеризации процессов реального времени использует‑ ся статический приоритет реального времени. Стандарт POSIX тре‑ бует не менее 32 значений этого приоритета. ОС Linux поддерживает 99 значений. Диапазон значений статического приоритета реального времени может быть получен при помощи функций: int sched_get_priority_min (int policy); int sched_get_priority_max (int policy); где policy — дисциплина диспетчеризации. Дисциплина диспетчеризации для процесса и его статический при‑ оритет реального времени могут быть заданы следующим образом: int sched_setscheduler (pid_t pid, int policy, const struct sched_param *p); struct sched_param {… int sched_priority; …}; где pid — идентификатор процесса, pid=0 означает текущий процесс; policy — дисциплина диспечеризации; sched_priority — статический приоритет реального времени (1 …99). 119 5. Системы реального времени Поддерживаются следующие символические константы для обо‑ значения дисциплин диспечеризации: • SCHED_FIFO — First in‑first out scheduling; • SCHED_RR — Round‑robin scheduling; • SCHED_DEADLINE — Sporadic task model deadline scheduling (since 3.14); • SCHED_OTHER (sched_priority=0); • SHED_BATCH (sched_priority=0); • SHED_IDLE (sched_priority=0). В конкретных системах часть из них может отсутствовать. Обя‑ зательно поддерживаются SCHED_FIFO, SCHED_RR, SCHED_ OTHER. Дисциплины диспетчеризации SCHED_OTHER, SHED_BATCH и SHED_IDLE применяются только для обычных низ‑ коприоритетных процессов, статический приоритет реального вре‑ мени которых всегда равен 0. Процесс может самостоятельно обратиться к диспетчеру и досроч‑ но прервать квант времени int sched_yield (void); Обычно это используется применительно к дисциплине SCHED_ FIFO для реализации невытесняющей многозадачности. В случае использования дисциплины SCHED_RR, диспетчер вы‑ числяет величину кванта времени по значению динамического при‑ оритета процесса. Получить значение этого кванта можно следую‑ щим образом: int sched_rr_get_interval (pid_t pid, struct timespec *tp); struct timespec { time_t tv_sec;/* секунды */ long tv_nsec;/* наносекунды */ }; Приведем пример программы, определяющей величину кванта времени: Листинг kvant.c #include #include #include #include 120 Раздел Б. Разработка информационно-управляющих систем #include #include __uint64_t timespec2nsec (struct timespec *tp) { return (__uint64_t) (tp->tv_sec)*1000000000+/* seconds */ tp->tv_nsec;/* nanoseconds */ } int main () { struct timespec ts; __uint64_t kv; int i; struct sched_param sp; sp.sched_priority=0; sched_setscheduler (0, SCHED_RR,&sp); for (i=-20; i<=20; i++) { errno=0; printf (“ %d\t %d\t %d\t”, i, setpriority (PRIO_PROCESS,0, i), errno); sleep (1); sched_rr_get_interval (0, &ts); kv=timespec2nsec (&ts); printf (“ %lu\n”, (unsigned long)kv/1000); } } Программа позволяет исследовать зависимость кванта времени от базового приоритета, приоритета реального времени и дисципли‑ ны диспетчеризации. Современные операционные системы в обязательном порядке должны позволять создавать многопотоковые процессы. При созда‑ нии потока задается функция, которая будет выполняться в потоке, структура атрибутов и адрес, куда будет помещен результат работы потока: int pthread_create ( pthread_t *thread,//переменная для записи идентификато- ра потока TID const pthread_attr_t *attr,//структура атрибутов потока 121 5. Системы реального времени void * (*start_routine) (void *),// — функция, выполня- емая в потоке void *arg);// — результат работы потока Атрибуты потока задаются в структуре, которая сначала инициа‑ лизируется int pthread_attr_init (pthread_attr_t *attr); а затем для потока может быть задана дисциплина диспетчери‑ зации int pthread_attr_setschedpolicy (thread_attr_t *attr, int policy); и приоритет int pthread_attr_setschedparam (pthread_attr_t *attr, const struct sched_param *param); struct sched_param {… int sched_priority; …}; По умолчанию поток создается как присоединенный, при этом воз‑ можна его синхронизации с родительским потоком. Ожидание завершения присоединенного потока имеет вид: int pthread_join (pthread_t thread, void **value_ptr); value_ptr — указатель результата работы потока. Отсоединение потока — операция безвозвратная и синхронизация невозможна: int pthread_detach (pthread_t thread); Завершается поток либо путем простого завершения выполнения функции в нем, при этом можно указать числовой результат работы потока return (void *)data; либо путем вызова функции int pthread_exit (void *value_ptr); где value_ptr — указатель области данных, передаваемых родителю (ре‑ зультат выполнения потока). Таким образом может быть построена иерархия потоков: главный поток (с функцией main ()), его дочерние потоки, дочерние потоки дочерних потоков и т. д. Процесс завершается по окончании главно‑ го потока, и для успешного выполнения всех потоков необходима их синхронизация (ожидание завершения дочерних потоков). Приведем пример демонстрационной программы, которая созда‑ ет несколько потоков, задает для них дисциплину диспетчеризации и приоритеты, определяет величину кванта времени. 122 Раздел Б. Разработка информационно-управляющих систем Листинг Pth_kvant.c #include #include #include #include #include #include #include () */ pthread_t thread_id;/* ID returned by pthread_create () */ int thread_num;/* Application-defined thread # */ char *argv_string;/* From command-line argument */ }; __uint64_t timespec2nsec (struct timespec *tp) { return (__uint64_t) (tp->tv_sec)*1000000000+/* seconds */ tp->tv_nsec;/* nanoseconds */ } struct timespec ts; /* Thread start function */ static void *thread_start (void *arg) { struct thread_info *tinfo = arg; char *uargv, *p; __uint64_t kv; sched_rr_get_interval (0, &ts); kv=timespec2nsec (&ts); printf (“Thread %d: argv_string= %s; Time kvant: %lu\n”, tinfo->thread_num, tinfo->argv_string, (unsigned long) kv/1000); uargv = strdup (tinfo->argv_string); if (uargv == NULL) perror (“strdup”); return uargv; } int main (int argc, char *argv []) { 123 5. Системы реального времени int s, tnum, opt, num_threads; struct thread_info *tinfo; pthread_attr_t attr; int stack_size; void *res; struct sched_param param; int policy=SCHED_RR; num_threads = argc; /* Initialize thread creation attributes */ s = pthread_attr_init (&attr); if (s!= 0) perror (“pthread_attr_init”); /* Allocate memory for pthread_create () arguments */ tinfo = calloc (num_threads, sizeof (struct thread_ info)); if (tinfo == NULL) perror (“calloc”); /* Create one thread for each command-line argument */ for (tnum = 1; tnum < num_threads; tnum++) { tinfo [tnum].thread_num = tnum; tinfo [tnum].argv_string = argv [tnum]; s=pthread_attr_setschedpolicy (&attr, policy); if (s!= 0) perror (“pthread_attr_setschedpolicy”); if (tnum==1) param.sched_priority=sched_get_priority_ max (policy); else param.sched_priority= (tnum+1)*10; s=pthread_attr_setschedparam (&attr, ¶m); if (s!= 0) perror (“pthread_attr_setschedparam”); //The pthread_create () call stores the thread ID into corresponding element of tinfo [] s = pthread_create (&tinfo [tnum].thread_id, &attr, &thread_start, &tinfo [tnum]); if (s!= 0) perror (“pthread_create”); } //Destroy the thread attributes object, since it is no longer needed s = pthread_attr_destroy (&attr); if (s!= 0) perror (“pthread_attr_destroy”); //Now join with each thread, and display its returned value 124 Раздел Б. Разработка информационно-управляющих систем for (tnum = 1; tnum < num_threads; tnum++) { s = pthread_join (tinfo [tnum].thread_id, &res); if (s!= 0) perror (“pthread_join”); printf (“Joined with thread %d; returned value was %s\n”, tinfo [tnum].thread_num, (char *) res); free (res);/* Free memory allocated by thread */ } free (tinfo); exit (EXIT_SUCCESS); } В демонстрационной программе создаются потоки, количество которых равно числу аргументов командной строки, им передают‑ ся эти аргументы, и они же возвращаются как результирующие зна‑ чения. Для каждого потока задается дисциплина диспетчеризации и приоритет реального времени и определяется величина кванта времени. Взаимодействие процессов и потоков в ОС РВ может осуществлять‑ ся стандартным образом [47, 48]. В то же время существуют механизмы, предназначенные для применения именно в ОС РВ. С 1996 г. в рам‑ ках POSIX одно из таких средств было стандартизировано, как сигна- лы реального времени. Сигналы реального времени, как и обычные сигналы, пред‑ ставляют собой простейшие быстро работающие асинхронные сообщения, передаваемые между процессами. Выделим их от‑ личительные особенности: • реализуется очередь сигналов, в которых экземпляры одноимен‑ ных сигналов не объединяются; • порядок доставки сигналов определяется по их номерам; сигна‑ лы с большими номерами более приоритетны (однако стандарт POSIX утверждает обратное); • номера сигналов от SIGRTMIN до SIGRTMAX (стандартно не менее 8, в Linux их 31); • с сигналом передается 32 бит данных; • для работы с сигналами добавлено несколько дополнительных функций. Действия процесса по обработке сигнала задаются, как обычно — системным вызовом sigaction: |