Мегаобучалка Главная | О нас | Обратная связь


Механизмы синхронизации



2019-12-29 236 Обсуждений (0)
Механизмы синхронизации 0.00 из 5.00 0 оценок




Потокам так же необходимо разделять ресурсы, например переменные в памяти. Для защиты от искажения, вызванного одновременным редактированием одних и тех же данных разными потоками, используются переменные, которые называются объектами синхронизации. Это мютексы, семафоры, события. В задачах реального времени к этим объектам предъявляется специфические требования, так как на них возможны задержки выполнения потоков, поскольку их назначение — блокирование доступа к некоторому разделяемому ресурсу. Проблема, возникающая при блокировании ресурса, - инверсия приоритетов. Это когда поток высокоприоритетный и низкоприоритетный разделяют общий ресурс, и есть еще один поток, имеющий средний приоритет среди них. Когда высокоприоритетный поток в состоянии готовности, а поток с низким приоритетом активен, и он заблокировал ресурс, то поток с более высоким приоритетом вытеснит с более низким, а ресурс останется заблокирован. И когда высокоприритетному потоку понадобится ресурс, то он сам перейдет в заблокированное состояние. Если в состоянии готовности находится только низкоприоритетный поток, то ничего страшного не произойдет, низкоприориттеный поток освободит заблокированный ресурс и будет вытеснен потоком с более высоким приоритетом. А если в момент блокирования потока с высоким приоритетом в состоянии готовности находится поток со средним приоритетом, то активным станет он, а с низким приоритетом опять будет вытеснен. И получит он управления после выполнения потока со средним приоритетом. И критическое время обслуживания потока с высоким приоритетом будет пропущено. Если это поток жесткого реального времени, то это недопустимо. Защита от нее заключается в наследовании приоритетов . Суть его в наследование низкоприоритетным потоком, захватившим ресурс, приоритета от высокоприоритетного потока, которому ресурс нужен. Есть еще один метод — Протокол Предельного Приоритета. Он заключается в добавлении к стандартным свойствам объектов синхронизации параметра, определяемого максимальным приоритетом потока, которые к объекту обращается. Если параметр установлен, то приоритет потока, который обращается к данном объекту синхронизации, будет увеличен до указанного уровня, и не сможет быть вытеснен никаким потоком, который сможет нуждаться в заблокированном ресурсе. После разблокирования ресурса, приоритет потока понижается до начального уровня, и так получается что-то вроде наследования приоритетов.

Микроядро

Микроядро — визитная карточка QNX. Защита памяти процессов и связь между ними на основе синхронного обмена сообщениями. Базовые функции ОС вынесены в отдельный модуль, включающий микроядро и менеджер процессов. Микроядро — коммутирующий элемент по сути, шина, обеспечивающая интеграцию других изолированных программных компонентов в единую систему.


Практическая часть

Установка QNX

QNX стоит сразу устанавливать на виртуальную машину. Версия для некоммерческого использования доступна для скачивания на веб-сайте разработчика www.qnx.com. При установке на VirtualBox у меня возникли проблемы, QNX не устанавливался скорее всего по причине отсутствия поддержки процессором виртуализации. Поэтому следующая попытка была поставить QNX на VMware Workstation. Там почти все заработало, но были проблемы с сетью. Установить QNX очень просто, установка заключается практически лишь только в выборе раздела для установки. Устанавливать QNX на жесткий диск в качестве полноценной ОС для работы не имеет смысла. Для разработки программного обеспечения, которое нуждается в реальном времени ( будет работать под управлением QNX) можно использовать модификацию IDE Eclipse — QNX Momentics, или же работать с QNX посредством, используя виртуализацию( Parallels, VMware, VirtualBox XEN). Следует с осторожностью  подходить к выбору аппаратного обеспечения для работы проектов под управлением QNX, так для многого оборудования отсутствуют драйвера.

Тест на инверсию приоритетов

Код программы, написанном на С/С++:

#include <stdlib.h>

#include <stdio.h>

#include <iostream.h>

#include <string.h>

#include <unistd.h>

#include <pthread.h>

#include <semaphore.h>

 

const int THRNUM = 3, NCKL = 10, LINOUT = 5, BUFLEN = THRNUM * NCKL + 1;

 

inline void workfun( int n ) {

for( long i = 0; i < n; i++ ) double f = sqrt( 1. + sqrt( (double) rand() ) );

};

 

char buf[ BUFLEN ];

volatile unsigned ind = 0, nfin = 0;

long nrep = 1000;

bool bmutex = false, bsemaphore = false;

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

static sem_t semaphore, semfinal;

 

void *thrfunc( void *p ) {

int t = (int)p;

if( t != 1 ) {

if( bmutex ) pthread_mutex_lock( &mutex );  

if( bsemaphore ) sem_wait( &semaphore );

};  

struct timespec tv;

tv.tv_sec = 0;

tv.tv_nsec =(long)( 1e+6 );

sem_trywait( &semfinal );  

nanosleep( &tv, &tv );

for( int i = 0; i < NCKL; i++ ) {

buf[ ind++ ] = t + '0';

workfun( nrep );     

};

if( t != 1 ) {

if( bmutex ) pthread_mutex_unlock( &mutex );  

if( bsemaphore ) sem_post( &semaphore );

};

if( ++nfin == THRNUM ) sem_post( &semfinal );

};

 

int main( int argc, char *argv[] ) {

cout << "inverse test, QNX API, vers.1.05L" << endl;

int c = 0;

while( ( c = getopt( argc, argv, "hmst:" ) ) != -1 )

switch( c ) {

    case 'h':

       cout << "\t" << argv[ 0 ] << " [ h | { m | s } | t = value ]" << endl;

       exit( EXIT_SUCCESS );           

    case 'm': bmutex = true; break;

    case 's': bsemaphore = true; break;        

    case 't':

       nrep = 1;

       for( int i = 0; i < atoi( optarg ); i++ ) nrep *= 10;

       break;

    default: exit( EXIT_FAILURE );        

};

sem_init( &semaphore, 0, 1 );

cout << "repeating number = " << nrep; // << ", debug level = " << ndebug;

 if( bmutex ) cout << ", lock on mutex";

if( bsemaphore ) cout << ", lock on semaphore";  

cout << endl;  

struct sched_param param;

int polisy;

pthread_getschedparam( pthread_self(), &polisy, &param );

pthread_attr_t attr;

for( int i = 0; i < THRNUM; i++ ) {

pthread_attr_init( &attr );

pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );

pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED );

pthread_attr_setschedpolicy( &attr, SCHED_RR );

attr.param.sched_priority = param.sched_priority + i + 1;

pthread_create( NULL, &attr, &thrfunc, (void*)i );

};

sem_init( &semfinal, 0, 0 );  

sem_wait( &semfinal );

buf[ ind ] = '\0';

cout << buf << endl;

exit( EXIT_SUCCESS );

 

};

Скомпилируем и запустим его на QNX. Вот вывод программы:

repeating number = 100000, debug level = 1

2[13:13] 2[13:13] 2[13:13] 2[13:13] 2[13:13]

2[13:13] 2[13:13] 2[13:13] 2[13:13] 2[13:13]

1[12:12] 1[12:12] 1[12:12] 1[12:12] 1[12:12]

1[12:12] 1[12:12] 1[12:12] 1[12:12] 1[12:12]

0[11:11] 0[11:11] 0[11:11] 0[11:11] 0[11:11]

0[11:11] 0[11:11] 0[11:11] 0[11:11] 0[11:11]

 

repeating number = 100000, debug level = 1, lock on mutex

0[11:13] 0[11:13] 0[11:13] 0[11:13] 0[11:13]

0[11:13] 0[11:13] 0[11:13] 0[11:13] 0[11:13]

1[12:12] 2[13:13] 2[13:13] 2[13:13] 2[13:13]

2[13:13] 2[13:13] 2[13:13] 2[13:13] 2[13:13]

2[13:13] 1[12:12] 1[12:12] 1[12:12] 1[12:12]

1[12:12] 1[12:12] 1[12:12] 1[12:12] 1[12:12]

 

repeating number = 100000, debug level = 1, lock on semaphore

1[12:12] 1[12:12] 1[12:12] 1[12:12] 1[12:12]

1[12:12] 1[12:12] 1[12:12] 1[12:12] 1[12:12]

0[11:11] 0[11:11] 0[11:11] 0[11:11] 0[11:11]

0[11:11] 0[11:11] 0[11:11] 0[11:11] 0[11:11]

2[13:13] 2[13:13] 2[13:13] 2[13:13] 2[13:13]

2[13:13] 2[13:13] 2[13:13] 2[13:13] 2[13:13]

 

Тестовые потоки запускаются с приоритетами 11, 12, 13, а запускающий поток имеет приоритет 10.

В первом случае нет взаимного блокирования потоков. Все потоки выполняются в такой последовательности 2-1-0 в соответствии со своими статическими приоритетами, указанными перед выполнением. А динамиеский приоритет равен статическому.

Во втором случае — взаимное блокирование на мютексе. Поток 0 начинает выполняться, «почувствовав» блокирование со стороны 2, под приоритетом ожидающего потока 2 ([11:13]), вытесняет поток 1. Затем, освободив мютекс, передает управлению потоку 2, после его завершения потоку 1. это наследование приоритетов, препятствующее возникновению инверсии приоритетов.

В третьем случае - блокирование на семафоре. Здесь потоки выполняются в таком порядке: 1-0-3 — это инверсия приоритетов.

А вот краткий вывод того же самого:

repeating number = 100000, debug level = 1

222222222211111111110000000000

 

repeating number = 100000, debug level = 1, lock on mutex

000000000012222222222111111111

 

repeating number = 100000, debug level = 1, lock on semaphore

111111111100000000002222222222

Теперь если запустить немного измененный код этой программы для Windows, то получим следующее:

 

repeating number = 500000, no priority

210021021021012211020021220101

 

repeating number = 500000

011111111110002222222222000000

 

repeating number = 500000, lock on mutex

000000000022222222221111111111

Как ни странно в Windows, не должно быть принято никаких мр по защите от инверсии приоритетов, так как она не ориентирована на realtime поведение, но в третьем случае мы видим отчетливое наследование приоритетов при блокирование на мютексе. А в первом случае наблюдаются перебои ( там потоки равного приоритета) в отличие от QNX.

 

Особенности написания кода под QNX

Драйверы

В случае ОС реального времени для встраиваемого оборудования необходимость написания драйверов — обычное дело. Поэтому разработчики QNX (фирма QSSL) создали беспрецедентную по своей простоте — технологию создания драйверов. Это если сравнивать с техникой драйверов в системах: OS-360 — IBM/360; MS-DOS; Windows 95/98; Linux x86. Почему такую технологию удалось создать в QNX? Ответ — все дело в микроядре. ОС QNX построена на редко реализуемой в ОС концепции — коммутации сообщений. Ядро ОС при этом выступает в качестве компактного коммутатора сообщений между взаимодействующими программными компонентами. Все запросы, обслуживаемые драйвером, предусматриваемые POSIX ( open(), read()...), реально же посылаются драйверу (менеджеру ресурса) в виде сообщений уровня микроядра. Код сообщения определяет тип операции, а последующее тело сообщения — конкретные параметры запроса, зависящие от типа операции. ( в этой технике могут обрабатываться и сообщения произвольных, не специфицированных системой типов, несущих в себе любой пользовательский каприз). Вот порядок взаимодействия:


В этой ОС все запросы API пакуются в тело сообщений, а микроядро системы обеспечивает их синхронизацию и доставку от отправителя к получателю. Для любых взаимодействий в ОС имеется единый механизм передачи, приема и обработки запросов, однако в других семействах ОС такого нет.

Из этого понятно, что:

· любой драйвер (менеджер ресурса) в этой логической модели рассматривается, как сервер некоторой услуги (ресурса), предоставляющий сервис клиентам — пользовательским приложениям;

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

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

· возможность динамически выгружать и загружать драйвера — путь к построению систем высокой живучести;

· драйвер может быть написан на С/С++;

· обеспечивается полная переносимость исходного кода;

· единообразно и автоматически обеспечиваются механизмы синхронизации в ОС на низшем уровне синхронизации сообщений микроядром;

· огромный плюс: драйвер-сервер совершенно одинаково обслуживает запросы клиентов как со своего компьютера, так и с любого другого по сети QNET (сетевая система QNX)



2019-12-29 236 Обсуждений (0)
Механизмы синхронизации 0.00 из 5.00 0 оценок









Обсуждение в статье: Механизмы синхронизации

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Как вы ведете себя при стрессе?: Вы можете самостоятельно управлять стрессом! Каждый из нас имеет право и возможность уменьшить его воздействие на нас...
Модели организации как закрытой, открытой, частично открытой системы: Закрытая система имеет жесткие фиксированные границы, ее действия относительно независимы...
Как распознать напряжение: Говоря о мышечном напряжении, мы в первую очередь имеем в виду мускулы, прикрепленные к костям ...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (236)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.007 сек.)