Роб Кёртен - Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
- Название:Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
- Автор:
- Жанр:
- Издательство:Петрополис
- Год:2001
- Город:Санкт-Петербург
- ISBN:5-94656-025-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Роб Кёртен - Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform краткое содержание
Книга "Введение в QNX/Neutrino 2» откроет перед вами в мельчайших подробностях все секреты ОСРВ нового поколения от компании QNX Software Systems Ltd (QSSL) — QNX/Neutrino 2. Книга написана в непринужденной манере, легким для чтения и понимания стилем, и поможет любому, от начинающих программистов до опытных системотехников, получить необходимые начальные знания для проектирования надежных систем реального времени, от встраиваемых управляющих приложений до распределенных сетевых вычислительных систем
В книге подробно описаны основные составляющие ОС QNX/Neutrino и их взаимосвязи. В частности, уделено особое внимание следующим темам:
• обмен сообщениями: принципы функционирования и основы применения;
• процессы и потоки: базовые концепции, предостережения и рекомендации;
• таймеры: организация периодических событий в программах;
• администраторы ресурсов: все, что относится к программированию драйверов устройств;
• прерывания: рекомендации по эффективной обработке.
В книге представлено множество проверенных примеров кода, подробных разъяснений и рисунков, которые помогут вам детально вникнуть в и излагаемый материал. Примеры кода и обновления к ним также можно найти на веб-сайте автора данной книги, www.parse.com.
Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Тут есть один интересный момент, который вы, может быть, для себя уже отметили. Прототип клиентской функции chown() имеет вид:
int chown(const char * path , uid_t owner , gid_t group );
Вспомните: сообщение об установлении соединения всегда содержит имя пути и является либо однократным, либо устанавливает контекст для дальнейших сообщений ввода/ вывода.
Так почему же сообщение, соответствующее клиентской функции chown() , не является сообщением установления соединения? К чему здесь сообщение ввода/вывода, когда в прототипе даже дескриптора файла нет?!
Ответ простой — чтобы облегчить вам жизнь.
Представьте себе, что было бы, если бы функции типа chown() , chmod() , stat() и им подобные требовали от администратора ресурсов, чтобы он сначала анализировал имя пути, а затем уже выполнял нужные действия. (Именно так, кстати, все реализовано в QNX4.) Типичные проблемы этого подхода:
• Каждой функции приходится вызывать процедуру поиска.
• Для функций, у которых есть также версия, ориентированная на файловый дескриптор, драйвер должен обеспечить две отдельные точки входа: одну для версии с именем пути, и еще одну — версии с дескриптором файла.
В QNX/Neutrino же происходит следующее. Клиент создает составное сообщение — реально это одно сообщение, но оно включает в себя несколько сообщений администратору ресурсов. Без составных сообщений мы могли бы смоделировать функцию chown() чем-то таким:
int chown(const char *path, uid_t owner, gid_t group) {
int fd, sts;
if ((fd = open(path, O_RDWR)) == -1) {
return (-1);
}
sts = fchown(fd, owner, group);
close(fd);
return (sts);
}
где функция fchown() — это версия функции chown() , ориентированная на файловые дескрипторы. Проблема здесь в том, что мы в этом случае используем три вызова функций (а значит, и три отдельных транзакции передачи сообщений) и привносим дополнительные накладные расходы применением функций open() и close() на стороне клиента.
При использовании составных сообщений в QNX/Neutrino непосредственно клиентским вызовом chown() создается одиночное сообщение, выглядящее примерно так:

Составное сообщение.
Сообщение состоит из двух частей. Первая часть посвящена установлению соединения (подобно сообщению, которое сгенерировала бы функция open() ), вторая — вводу/выводу (эквивалент сообщения, генерируемого функцией fchown() ). Никакого эквивалента функции close() здесь нет, поскольку мы выбрали сообщение типа _IO_CONNECT_COMBINE_CLOSE, которое гласит: «Открой указанное имя пути, используй полученный дескриптор файла для обработки остальной части сообщения, а когда закончишь дела или столкнешься с ошибкой, закрой дескриптор».
Написанный вами администратор ресурса даже не заметит, вызвал ли клиент функцию chown() или сначала сделал open() , а потом вызвал fchown() и далее close() . Все это скрыто базовым уровнем библиотеки.
Как выясняется, концепция составных сообщений полезна не только для экономии ресурсов вследствие уменьшения числа сообщений (как в случае с chown() , см. выше). Она также критически важна для обеспечения атомарности операций.
Предположим, что в клиентском процессе есть два или более потоков, работающих с одним дескриптором файла. Один из потоков в клиенте вызывает функцию lseek() , за которой следует read() . Все так, как мы и предполагаем. А вот если другой клиента попробует выполнить ту же самую последовательность операций с тем же самым дескриптором файла, вот тут у нас начнутся проблемы. Поскольку функции lseek() и read() друг о друге ничего не знают, то возможно, например, что первый поток выполнит lseek() , а затем будет вытеснен вторым потоком. Второй поток выполнит свои lseek() и read() , после чего освободит процессор. Проблема здесь состоит в том, что поскольку эти два потока разделяют один и тот же дескриптор файла, у первого потока теперь получается неправильное смещение lseek() , поскольку оно было изменено функциями lseek() и read() второго потока! Эта проблема проявляется также с дескрипторами файлов, которые дублируются ( dup() ) между процессами, не говоря уже о сети.
Очевидным решением здесь является заключение lseek() и read() в пределы действия мутекса — когда первый поток захватит мутекс, мы будем знать, что он имеет эксклюзивный доступ к дескриптору. Второй поток должен будет ждать освобождения мутекса, прежде чем он сможет творить свое безобразие с позиционированием дескриптора.
К сожалению, если кто-то вдруг забудет захватить мутекс перед каждой и любой операцией с дескриптором файла , всегда остается возможность того, что такой «незащищенный» доступ может привести поток к чтению или записи данных не туда, куда надо.
Давайте взглянем на библиотечный вызов readblock() (из ):
int readblock(int fd , size_t blksize , unsigned block ,
int numblks , void * buff );
(Функция writeblock() описывается аналогично.)
Вы можете вообразить для функции readblock() довольно «простенькую» реализацию:
int readblock(int fd, size_t blksize, unsigned block,
int numblks, void *buff) {
lseek(fd, blksize * block, SEEK_SET); // Идем к блоку
read(fd, buff, blksize * numblks);
}
Очевидно, что от такой реализации в многопоточной среде толку мало. Нам нужно будет как минимум добавить использование мутекса:
int readblock(int fd, size_t blksize, unsigned block,
int numblks, void *buff) {
pthread_mutex_lock(&block_mutex);
lseek(fd, blksize * block, SEEK_SET); // Идем к блоку
read(fd, buff, blksize * numblks);
pthread_mutex_unlock(&block_mutex);
}
(Мы здесь предполагаем, что мутекс уже инициализирован.)
Этот код по-прежнему уязвим для «незащищенного» доступа — если некий поток вызовет lseek() на этом файловом дескрипторе без предварительной попытки захвата мутекса, вот у нас уже и ошибка.
Решение проблемы заключается в использовании составного сообщения, аналогично вышеописанному случаю с функцией chown() . В данном случае библиотечная реализация функции readblock() помещает обе операции — lseek() и read() — в единое сообщение и посылает это сообщение администратору ресурсов:

Составное сообщение для функции readblock() .
Это работает, потому что передача сообщения является атомарной операцией. С точки зрения клиента, сообщение уходит либо целиком, либо не уходит вообще. Поэтому, вмешательство «незащищенной» функции lseek() становится несущественным — когда администратор ресурсов принимает сообщение с запросом readblock() , он делает это за один прием. (Очевидно, что в этом случае пострадает сама «незащищенная» lseek() , поскольку после отработки readblock() смещение на этом дескрипторе файла будет отличаться от того, которое она хотела установить.)
Читать дальшеИнтервал:
Закладка: