Основы применения операционной системы реального времени QNX
Основные механизмы взаимодействия процессов на примере средств, предоставляемых операционной системой QNX. Системные процессы, микроядро и драйверы устройств. Теоретические сведения, текст программы, последовательность действий. Сервер, создание канала.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | лабораторная работа |
Язык | русский |
Дата добавления | 08.10.2014 |
Размер файла | 300,8 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
Размещено на http://www.allbest.ru/
РЕФЕРАТ
Комплекс лабораторных работ содержит 33 с., 8 рис., 7 источников.
Ключевые слова: ОСРВ, МИКРОЯДРО, ОБМЕН СООБЩЕНИЯМИ, СЕРВЕР, КЛИЕНТ, ПК, ПРОЦЕСС, ПОТОК, СИНХРОНИЗАЦИЯ.
Объектом рассмотрения является программное обеспечение, которое используется в системах жесткого реального времени.
Цель работы - ознакомление студентов с основными механизмами взаимодействия процессов на примере средств, предоставляемых операционной системой QNX.
СОДЕРЖАНИЕ
ВВЕДЕНИЕ
1. СТУКТУРНАЯ ОРГАНИЗАЦИЯ ОСРВ QNX
1.1 Архитектура микроядра системы QNX
1.1.1 Настоящее ядро
1.1.2 Системные процессы
1.2 Микроядро
1.3 Системные и пользовательские процессы
1.4 Драйверы устройств
1.5 Связь между процессами (IPC)
1.6 QNX как сеть
2. КОМПЛЕКС ЛАБОРАТОРНЫХ РАБОТ
2.1 Лабораторная работа №1 «Простейший пример»
2.1.1 Теоретические сведения
2.1.2 Текст программы
2.1.3 Последовательность действий
2.1.4 Результаты
2.2 Лабораторная работа №2 «Процессы и потоки»
2.2.1 Теоретические сведения
2.2.2 Текст программы
2.2.3 Последовательность действий
2.2.4 Результаты
2.3 Лабораторная работа №3 «Обмен сообщениями»
2.3.1 Теоретические сведения
2.3.2 Текст программы
2.3.3 Последовательность действий
2.3.4 Результаты
2.4 Лабораторная работа №4 «Тайм-ауты»
2.4.1 Теоретические сведения
2.4.2 Текст программы
2.4.3 Последовательность действий
2.4.4 Результаты
2.5 Лабораторная работа №5 «Барьеры»
2.5.1 Теоретические сведения
2.5.2 Текст программы
2.5.3 Последовательность действий
2.5.4 Результаты
2.6 Лабораторная работа №6 «Условные переменные»
2.6.1 Теоретические сведения
2.6.2 Текст программы
2.6.3 Последовательность действий
2.6.4 Результаты
ЗАКЛЮЧЕНИЕ
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
ОПРЕДЕЛЕНИЯ, ОБОЗНАЧЕНИЯ И СОКРАЩЕНИЯ
ВВЕДЕНИЕ
Главная обязанность операционной системы состоит в управлении ресурсами компьютера. Все действия в системе - диспетчеризация прикладных программ, запись файлов на диск, пересылка данных по сети и т.п. - должны выполняться совместно настолько слитно и прозрачно, насколько это возможно.
Некоторые области применения предъявляют более жесткие требования к управлению ресурсами и диспетчеризации программ, чем другие [1]. Приложения реального времени, например, полагаются на способность операционной системы обрабатывать многочисленные события в пределах ограниченного интервала времени. Чем быстрее реагирует операционная система, тем большее пространство для маневра имеет приложение реального времени в пределах жестких временных рамок.
Операционная система QNX идеальна для приложений реального времени. Она обеспечивает все неотъемлемые составляющие системы реального времени: многозадачность, диспетчеризацию программ на основе приоритетов и быстрое переключение контекста [2].
QNX - удивительно гибкая система. Разработчики легко могут настроить операционную систему таким образом, чтобы она отвечала требованиям конкретных приложений. QNX позволяет вам создать систему, использующую только необходимые для решения вашей задачи ресурсы. Конфигурация системы может изменяться в широком диапазоне - от ядра с несколькими небольшими модулями до полноценной сетевой системы, обслуживающей сотни пользователей.
QNX достигает своего уникального уровня производительности, модульности и простоты благодаря двум фундаментальным принципам:
· архитектура на основе микроядра;
· связь между процессами на основе сообщений.
Рассматриваемый комплекс лабораторных работ по применению ОСРВ QNX ориентирован на использование в курсах «Системы реального времени», «Программное обеспечение систем реального времени».
1. Стуктурная организациЯ ОСРВ QNX
1.1 Архитектура микроядра системы QNX
QNX состоит из небольшого ядра, координирующего работу взаимодействующих процессов [3]. Как показано на рисунке, структура больше напоминает не иерархию, а команду, в которой несколько игроков одного уровня взаимодействуют между собой и со своим "защитником" - ядром.
Рис.1 Микроядро системы QNX координирует работу системных менеджеров.
1.1.1 Настоящее ядро
Ядро - это "сердце" любой операционной системы. В некоторых операционных системах на него возлагается так много функций, что ядро, по сути, заменяет всю операционную систему!
В QNX же Микроядро - это настоящее ядро. Во-первых, как и следует ядру реального времени, ядро QNX имеет очень маленький размер. Во-вторых, оно выполняет две важнейшие функции:
· передача сообщений - Микроядро обеспечивает маршрутизацию всех сообщений между всеми процессами в системе;
· диспетчеризация - планировщик - это часть Микроядра, и он получает управление всякий раз, когда процесс изменяет свое состояние в результате получения сообщения или прерывания.
В отличие от всех остальных процессов, ядро никогда не получает управления в результате диспетчеризации. Входящий в состав ядра код выполняется только в результате прямых вызовов из процесса или аппаратного прерывания.
1.1.2 Системные процессы
Все услуги операционной системы, за исключением тех, которые выполняются ядром, в QNX предоставляются через стандартные процессы. Типичная конфигурация QNX имеет следующие системные процессы:
· Менеджер процессов (Proc);
· Менеджер файловой системы (Fsys);
· Менеджер устройств (Dev);
· Менеджер сети (Net).
1.2 Микроядро
Микроядро QNX отвечает за выполнение следующих функций:
· связь между процессами - Микроядро управляет маршрутизацией сообщений; оно также поддерживает и другие формы IPC сигналы, флаги;
· сетевой интерфейс низкого уровня - Микроядро осуществляет доставку всех сообщений, предназначенных для процессов на других узлах сети;
· диспетчеризация процессов - входящий в состав Ядра планировщик решает, какому из запущенных процессов должно быть передано управление;
· первичная обработка прерываний - все аппаратные прерывания и исключения сначала проходят через Микроядро, а затем передаются соответствующему драйверу или системному менеджеру.
Рис. 2. Внутри микроядра QNX
1.3 Системные и пользовательские процессы
Системные процессы практически ничем не отличаются от любых написанных пользователем программ - они не имеют какого-либо скрытого или особого интерфейса, недоступного пользовательским процессам.
Именно за счет такой системной архитектуры QNX обладает уникальной наращиваемостью. Так как большинство услуг операционной системы предоставляются стандартными процессами QNX, то расширение операционной системы требует всего лишь написания новой программы, обеспечивающей новую услугу!
Фактически, граница между операционной системой и прикладной программой может быть очень размыта. Единственный критерий, по которому мы можем отличить прикладные процессы и системные сервисные процессы, состоит в том, что процесс операционной системы управляет каким-либо ресурсом в интересах прикладного процесса.
Предположим, что вы написали сервер базы данных. Как же должен быть классифицирован этот процесс?
Точно так же, как сервер файловой системы принимает запросы (в QNX реализованные через механизм сообщений) на открытие файлов и запись или чтение данных, это будет делать и сервер базы данных. Хотя запросы к серверу базы данных могут быть и более сложными, сходство обоих серверов заключается в том, что оба они обеспечивают доступ к ресурсу посредством запросов. Оба они являются независимыми процессами, которые могут быть написаны пользователем и запускаться по мере необходимости.
Сервер базы данных может рассматриваться как процесс в одном случае и как приложение в другом [4]. Это действительно не имеет значения! Важно то, что создание и выполнение таких процессов в QNX не требует абсолютно никаких изменений в стандартных компонентах операционной системы.
1.4 Драйверы устройств
Драйверы устройств - это процессы, которые являются посредниками между операционной системой и устройствами и избавляют операционную систему от необходимости иметь дело с особенностями конкретных устройств.
Так как драйверы запускаются как обычные процессы, добавление нового драйвера в QNX не влияет на другие части операционной системы. Таким образом, добавление нового драйвера в QNX не требует ничего, кроме непосредственно запуска этого драйвера.
После запуска и завершения процедуры инициализации, драйвер может выбрать один из двух вариантов поведения:
· стать расширением определенного системного процесса;
· продолжать выполнение как независимый процесс.
1.5 Связь между процессами (IPC)
В типичной для многозадачной системы реального времени ситуации, когда несколько процессов выполняются одновременно, операционная система должна предоставить механизмы, позволяющие им общаться друг с другом.
Связь между процессами (Interprocess communication, сокращенно IPC) является ключом к разработке приложений как совокупности процессов, в которых каждый процесс выполняет отведенную ему часть общей задачи.
QNX предоставляет простой, но мощный набор возможностей IPC, которые существенно облегчают разработку приложений, состоящих из взаимодействующих процессов.
Передача сообщений
QNX была первой коммерческой операционной системой своего класса, которая использовала передачу сообщений в качестве основного способа IPC. Именно последовательное воплощение метода передачи сообщения в масштабах всей операционной системы обусловливает мощность, простоту и элегантность QNX.
Сообщения в QNX - это последовательность байт, передаваемых от одного процесса другому. Операционная система не пытается анализировать содержание сообщения - передаваемые данные имеют смысл только для отправителя и получателя, и ни для кого более.
Передача сообщения позволяет не только обмениваться данными, но и является способом синхронизации выполнения нескольких процессов. Когда они посылают, получают или отвечают на сообщения, процессы претерпевают различные "изменения состояния", которые влияют на то, когда и как долго они могут выполняться. Зная состояния и приоритеты процессов, ядро организует их диспетчеризацию таким образом, чтобы максимально эффективно использовать ресурсы центрального процессора (ЦП).
Приложение реального времени и другие ответственные приложения по праву нуждаются в надежном механизме передачи сообщений, т.к. входящие в состав этих приложений процессы тесно взаимосвязаны. Реализованный в QNX механизм передачи сообщений способствует упорядочению и повышению надежности программ.
1.6 QNX как сеть
В простейшем случае локальная сеть обеспечивает разделяемый доступ к файлам и периферийным устройствам для нескольких соединенных между собой компьютеров. QNX идет гораздо дальше этого простейшего представления и объединяет всю сеть в единый, однородный набор ресурсов.
Любой процесс на любом компьютере в составе сети может непосредственно использовать любой ресурс на любом другом компьютере. С точки зрения приложений, не существует никакой разницы между местным или удаленным ресурсом, и использование удаленных ресурсов не требует каких-либо специальных средств. Более того, чтобы определить, находится ли такой ресурс как файл или устройство на локальном компьютере или на другом узле сети, в программу не потребуется включить специальный дополнительный код!
Пользователи могут иметь доступ к файлам по всей сети, использовать любое периферийное устройство, запускать программы на любом компьютере сети (при условии, что они имеют надлежащие полномочия). Связь между процессами осуществляется единообразно, независимо от их местоположения в сети. В основе такой прозрачной поддержки сети в QNX лежит всеобъемлющая концепция IPC на основе передачи сообщений.
2. КОМПЛЕКС ЛАБОРАТОРНЫХ РАБОТ
2.1 Лабораторная работа №1 «Простейший пример»
2.1.1 Теоретические сведения
Минимальный набор действий, необходимый для демонстрации примера программы для QNX: Набрать текст программы. Откомпилировать программу. Запустить программу на исполнение.
Текст программы можно набрать во встроенном редакторе, или взять готовый текстовый файл. Для дальнейших действий, желательно сделать текущим каталог, где находится текст программы. Для этого можно воспользоваться командами # cd <имя дериктории> (- сменить текущую директорию на указанную) или # cd. (- подняться на уровень выше). Чтобы просмотреть содержимое директории, можно воспользоваться командой # ls.
Чтобы откомпилировать программу, можно воспользоваться встроенным компилятором - GCC. Для этого в командной строке необходимо написать # gcc <имя_файла>. Если в тексте программы есть ошибки, то они будут выведены на экран. Если ошибок нет, буден создан файл a.out - это и есть исполняемый файл программы. Чтобы его запустить на исполнение, в командной строке необходимо написать # `pwd`/a.out.
2.1.2 Текст программы
#include <stdio.h>
int main(void)
{printf("Hello World \n");
return(1);
}
2.1.3 Последовательность действий
Создаём текстовый файл программы.
Компилируем его и запускаем на исполнение.
2.1.4 Результаты
# cd..
# ls
. .lastlogin .ph a.out lab2 lab4
.. .profile lab1 lab3 lab5
# cd lab1
# ls
. .. myfirst.c
# gcc myfirst.c
# ls
. .. a.out myfirst.c
# `pwd`/a.out
Hello World
#
2.2 Лабораторная работа №2 «Процессы и потоки»
2.2.1 Теоретические сведения
Процессы и потоки
На самом высоком уровне абстракции система состоит из множества процессов. Каждый процесс ответственен за обеспечение служебных функций определенного характера, независимо от того, является ли он элементом файловой системы, драйвером дисплея, модулем сбора данных, модулем управления или чем-либо еще.
В пределах каждого процесса может быть множество потоков. Число потоков варьируется. Один разработчик ПО, используя только единственный поток, может реализовать те же самые функциональные возможности, что и другой, использующий пять потоков. Некоторые задачи сами по себе приводят к многопоточности и дают относительно простые решения, другие в силу своей природы, являются однопоточными, и свести их к многопоточной реализации достаточно трудно.
Почему процессы?
Почему же не взять просто один процесс с множеством потоков? В то время как некоторые операционные системы вынуждают вас программировать только в таком варианте, возникает ряд преимуществ при разделении объектов на множество процессов:
· возможность декомпозиции задачи и модульной организации решения;
· удобство сопровождения;
· надежность.
Концепция разделения задачи на части, т. е., на несколько независимых задач, является очень мощной. И именно такая концепция лежит в основе QNX. Операционная система QNX состоит из множества независимых модулей, каждый из которых наделен некоторой зоной ответственности. Эти модули независимы и реализованы в отдельных процессах. Единственная возможность установить зависимость этих модулей друг от друга -- наладить между ними информационную связь с помощью небольшого количества строго определенных интерфейсов.
Это естественно ведет к упрощению сопровождения программных продуктов, благодаря незначительному числу взаимосвязей. Поскольку каждый модуль четко определен, и устранять неисправности в одном таком модуле будет гораздо проще - тем более, что он не связан с другими.
Запуск процесса
Теперь обратим внимание на функции, предназначенные для работы с потоками и процессами. Любой поток может осуществить запуск процесса; единственные налагаемые здесь ограничения вытекают из основных принципов защиты (правила доступа к файлу, ограничения на привилегии и т. д.).
Запуск процесса из командной строки
Например, при запуске процесса из командного интерпретатора вы можете ввести командную строку:
$ program1
Это указание предписывает командному интерпретатору запустить программу program1 и ждать завершения ее работы. Или, вы могли набрать:
$ program2 &
Это указание предписывает командному интерпретатору запустить программу program2 без ожидания ее завершения. В таком случае говорят, что программа program2 работает в фоновом режиме.
Если вы пожелаете скорректировать приоритет программы до ее запуска, вы можете применить команду nice -- точно так же, как в Unix:
$ nice program3
Запуск процесса из программы
Нас обычно не заботит тот факт, что командный интерпретатор создает процессы -- это просто подразумевается. В некоторых прикладных задачах можно положиться на сценарии командного интерпретатора (пакеты команд, записанные в файл), которые сделают эту работу за вас, но в ряде других случаев вы пожелаете создавать процессы самостоятельно.
Например, в большой мультипроцессорной системе вы можете пожелать, чтобы одна главная программа выполнила запуск всех других процессов вашего приложения на основании некоторого конфигурационного файла. Другим примером может служить необходимость запуска процессов по некоторому событию.
Рассмотрим некоторые из функций, которые обеспечивает для запуска других процессов (или подмены одного процесса другим):
· system();
· семейство функций ехес();
· семейство функций spawn();
· fork();
· vfork().
Какую из этих функций применять, зависит от двух требований: переносимости и функциональности. Как обычно, между этими двумя требованиями возможен компромисс.
Обычно при всех запросах на создание нового процесса происходит следующее. Поток в первоначальном процессе вызывает одну из вышеприведенных функций. В конечном итоге функция заставит администратор процессов создать адресное пространство для нового процесса. Затем ядро выполнит запуск потока в новом процессе. Этот поток выполнит несколько инструкций и вызовет функцию main().
Запуск потока
Теперь, когда мы знаем, как запустить другой процесс, давайте рассмотрим, как осуществить запуск другого потока.
Любой поток может создать другой поток в том же самом процессе; на это не налагается никаких ограничений (за исключением объема памяти, конечно!) Наиболее общий путь реализации этого -- использование вызова функций pthread_create():
#include <pthread.h>
int int
pthread_create (pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
Функция pthread_create() имеет четыре аргумента:
· thread - указатель на pthread_t, где хранится идентификатор потока;
· attr - атрибутная запись;
· start_routine - подпрограмма, с которой начинается поток;
· arg - параметр, который передается подпрограмме start_routine.
Отметим, что указатель thread и атрибутная запись (attr) -- необязательные элементы, вы можете передавать вместо них NULL.
Параметр thread может использоваться для хранения идентификатора вновь создаваемого потока. Обратите внимание, что в примерах, приведенных ниже, мы передадим NULL, обозначив этим, что мы не заботимся о том, какой идентификатор будет иметь вновь создаваемый поток.
Если бы нам было до этого дело, мы бы сделали так:
pthread_t tid;
pthread_create (&tid,...
printf («Новый поток имеет идентификатор %d\n», tid);
Такое применение совершенно типично, потому что вам часто может потребоваться знать, какой поток выполняет какой участок кода.
Небольшой тонкий момент. Новый поток может начать работать еще до присвоения значения параметру tid. Это означает, что вы должны внимательно относиться к использованию tid в качестве глобальной переменной. В примере, приведенном выше, все будет корректно, потому что вызов pthread_create() отработал до использования tid, что означает, что на момент использования tid имел корректное значение.
Новый поток начинает выполнение с функции start_routine (), с параметром arg.
Атрибутная запись потока
Когда вы осуществляете запуск нового потока, он может следовать ряду четко определенных установок по умолчанию, или же вы можете явно задать его характеристики.
Прежде, чем мы перейдем к обсуждению задания атрибутов потока, рассмотрим тип данных
Синхронизация
Самый простой метод синхронизации -- это «присоединение» (joining) потоков. Реально это действие означает ожидание завершения.
Присоединение выполняется одним потоком, ждущим завершения другого потока. Ждущий поток вызывает pthreadjoin():
#include <pthread.h>
int
Функции pthreadjoin() передается идентификатор потока, к которому вы желаете присоединиться, а также необязательный аргумент value_ptr, который может быть использован для сохранения возвращаемого присоединяемым потоком значения. (Вы можете передать вместо этого параметра NULL).
Где нам брать идентификатор потока?
В функции pthread_create() в качестве первого аргумента указатель на pthread_t. Там и будет сохранен идентификатор вновь созданного потока.
2.2.2 Текст программы
#include <stdio.h>
#include <pthread.h>
#include <sys/neutrino.h>
pthread_t thread_id1;
pthread_t thread_id2;
void * long_thread1(void *notused)
{
int n;
for(n=0;n<5;n++)
{
printf("Eto pervii potok, TID %d - N povtora %d \n", thread_id1, n);
sleep(2);
}
}
void * long_thread2(void *notused)
{
int m;
for(m=0; m<5; m++)
{
printf("Eto vtoroi potok, TID %d - N povtora %d \n", thread_id2, m);
sleep(1);
}
}
int main(void)
{
printf("Prog threads PID %d \n",getpid());
pthread_create(&thread_id1, NULL, long_thread1, NULL);
pthread_create(&thread_id2, NULL, long_thread2, NULL);
sleep(40);
return(1);
}
2.2.3 Последовательность действий
В программе создаются и запускаются на исполнение два потока. Когда один поток приостанавливается, сразу начинает работу другой. Приостановка реализована функцией sleep(n), которая останавливает процесс на n секунд. На экране можно наблюдать, как по очереди работают два процесса.
2.2.4 Результаты
# gcc pthread.c
# `pwd`a.out
Prog threads PID 852000
Eto pervii potok, TID 0 - N povtora 0
Eto vtoroi potok, TID 0 - N povtora 0
Eto vtoroi potok, TID 3 - N povtora 1
Eto pervii potok, TID 2 - N povtora 1
Eto vtoroi potok, TID 3 - N povtora 2
Eto vtoroi potok, TID 3 - N povtora 3
Eto pervii potok, TID 2 - N povtora 2
Eto vtoroi potok, TID 3 - N povtora 4
Eto pervii potok, TID 2 - N povtora 3
Eto pervii potok, TID 2 - N povtora 4
#
2.3 Лабораторная работа №3 «Обмен сообщениями»
2.3.1 Теоретические сведения
Архитектура и структура обмена сообщениями
Три ключевые выражения:
· «Клиент посылает (sends) сообщение серверу»;
· «Сервер принимает (receives) сообщение от клиента»;
· «Сервер отвечает (replies) клиенту».
Эти выражения в точности соответствуют действительным именам функций, которые используются для передачи сообщений в QNX/Neutrino.
Минимальный полезный набор функций включает в себя функции ChannelCreate(), ConnectAttach(), MsgReply(), MsgSend() и MsgRecieve().
Разобьем обсуждение на две части: отдельно обсудим функции, которые применяются на стороне клиента, и отдельно -- те, что применяются на стороне сервера.
Клиент
Клиент, который желает послать запрос серверу, блокируется до тех пор, пока сервер не завершит обработку запроса. Затем, после завершения сервером обработки, запроса клиент разблокируется, чтобы принять «ответ».
Это подразумевает обеспечение двух условий: клиент должен «уметь» сначала установить соединение с сервером, а потом обмениваться с ним данными с помощью сообщений -- как в одну сторону (запрос -- «send»), так и в другую (ответ -- «reply»).
Установление соединения
Первое, что должны сделать клиент -- это установить соединение с помощью функции ConnectAttach(), описанной следующим образом:
#include <sys/neutrino.h>
int ConnectAttach
(int nd,pid_t pid, int chid, unsigned index, int flags);
Функции ConnectAttach() передаются три идентификатора;
· nd-- дескриптор узла (Node Descriptor),
· pid-- идентификатор процесса (process ID)
· chid -- идентификатор канала (channel ID).
Вместе эти три идентификатора, которые обычно записываются в виде «ND/PID/CHID», однозначно идентифицируют сервер, с которым клиент желает соединиться. Аргументы index и flags мы здесь просто проигнорируем (установим их в ноль).
Итак, предположим, что мы хотим подсоединиться к процессу, находящемуся на нашем узле и имеющему идентификатор 77, по каналу с идентификатором 1. Ниже приведен пример программы для выполнения этого:
int coid;
coid = ConnectAttach (0, 77, 1, 0, 0);
Можно видеть, что присвоением идентификатору узла (nd) нулевого значения мы сообщаем ядру о том, что мы желаем установить соединение на локальном узле.
Соединиться надо с процессом 77 и по каналу 1.
С этого момента есть идентификатор соединения -- небольшое целое число, которое однозначно идентифицирует соединение моего клиента с конкретным сервером по заданному каналу.
Теперь можно применять этот идентификатор для отправки запросов серверу сколько угодно раз. Выполнив все, для чего предназначалось соединение, его можно уничтожить с помощью функции:
ConnectDetach (coid);
Передача сообщений (sending)
Передача сообщения со стороны клиента осуществляется применением функции MsgSend(). Мы рассмотрим это на примере:
#include <sys/neutrino.h>
int MsgSend (int coid,
const void *smsg, int sbytes, void *rmsg, int rbytes);
Аргументами функции MsgSend() являются:
· идентификатор соединения с целевым сервером (coid);
· указатель на передаваемое сообщение (smsg);
· размер передаваемого сообщения (sbytes);
· указатель на буфер для ответного сообщения (rmsg);
· размер ответного сообщения (rbytes).
Передадим сообщение процессу с идентификатором 77 по каналу 1:
#include <sys/neutrino.h>
char *smsg = «Это буфер вывода»; char rmsg [200]; int coid;
// Установить соединение
coid = ConnectAttach (0, 77, 1, 0, 0);
if (coid == -1) {
fprintf (stderr, «Ошибка ConnectAttach к 0/77/1!\n»);
perror (NULL);
exit (EXIT_FAILURE);
// Послать сообщение
if(MsgSend(coid, smsg,strlen (smsg) + 1,rmsg,sizeof(rmsg)) == -1)
{ fprintf (stderr, «Ошибка MsgSendXn»);
perror (NULL);
exit (EXIT_FAILURE);
if (strlen (rmsg) > 0)
{
printf («Процесс с ID 77 возвратил \«%s\»\n», rmsg);
}
Предположим, что процесс с идентификатором 77 был действительно активным сервером, ожидающим сообщение именно такого формата по каналу с идентификатором 1. После приема сообщения сервер обрабатывает его и в некоторый момент времени выдает ответ с результатами обработки. В этот момент функция MsgSend() должна возвратить ноль (0), указывая этим, что все прошло успешно. Если бы сервер послал нам в ответ какие-то данные, мы смогли бы вывести их на экран с помощью последней строки в программе. программа драйвер сервер
Сервер. Создание канала
Сервер должен создать канал -- то, к чему присоединялся клиент, когда вызывал функцию ConnectAttach(). Обычно сервер, однажды создав канал, приберегает его «впрок». Канал создается с помощью функции ChannelCreate() и уничтожается с помощью функции ChannelDestroy():
#include <sys/neutrino.h>
int ChannelCreate (unsigned flags);
int ChannelDestroy (int chid);
Таким образом, для создания канала сервер должен сделать так:
int chid;
chid = ChannelCreate (0);
Теперь у нас есть канал. В этом пункте клиенты могут подсоединиться (с помощью функции ConnectAttach()) к этому каналу и начать передачу сообщений:
Рис. 3 Связь между каналом сервера и клиентским соединением
Обработка сообщений
В терминах обмена сообщениями, сервер отрабатывает схему обмена в два этапа:
· этап «приема» (receive);
· этап «ответа» (reply).
Рис.4 Взаимосвязь функций клиента и сервера при обмене сообщениями
Обсудим два простейших варианта соответствующих функций, MsgReceive() и MsgReply().
#include <sys/neutrino.h>
int MsgReceive (int chid,void *rmsg, int rbytes, struct _msg_info *info);
int MsgReply (int rcvid, int status, const void *msg, int nbytes);
Четыре основных элемента:
1 Клиент вызывает функцию MsgSend() и указывает ей на буфер передачи (указателем smsg и длиной sbytes). Данные передаются в буфер функции MsgReceive() на стороне сервера, по адресу rmsg и длиной rbytes. Клиент блокируется.
2 Функция MsgReceive() сервера разблокируется и возвращает идентификатор отправителя rcvid, который будет впоследствии использован для ответа. Теперь сервер может использовать полученные от клиента данные.
3 Сервер завершил обработку сообщения и теперь использует идентификатор отправителя rcvid, полученный от функции MsgReceive(), передавая его функции MsgReply(). Заметьте, что местоположение данных для передачи функции MsgReply() задается как указатель на буфер (smsg) определенного размера (sbytes). Ядро передает данные клиенту.
4 Наконец, ядро передает параметр sts, который используется функцией MsgSend() клиента как возвращаемое значение. После этого клиент разблокируется.
Для каждой буферной передачи указываются два размера (в случае запроса от клиента это sbytes на стороне клиента и rbytes на стороне сервера; в случае ответа сервера это sbytes на стороне сервера и rbytes на стороне клиента). Это сделано для того, чтобы разработчики каждого компонента смогли определить размеры своих буферов -- из соображений дополнительной безопасности.
2.3.2 Текст программы
// server.c
#include <stdio.h>
#include <pthread.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/neutrino.h>
void server(void)
{
int rcvid; //Ykazivaet komy nado otvechat
int chid; //Identifikator kanala
char message[512]; //
printf("Server start working \n");
chid=ChannelCreate(0); //Sozdanie Kanala
printf("Chanel id: %d \n", chid);
printf("Pid: %d \n", getpid());
// vipolniaetsa vechno- dlia servera eto normalno
while(1)
{
// Polychit i vivesti soobshenie
rcvid=MsgReceive(chid,message,sizeof(message), NULL);
printf("Polychili soobshenie, rcvid %X \n",rcvid );
printf("Soobshenie takoe: \"%s\". \n", message );
// Podgotovit otvet
strcpy(message,"Eto otvet");
MsgReply(rcvid, EOK, message, sizeof(message));
printf("\"%s\". \n", message );
}
}
int main(void)
{
printf("Prog server \n");
server();
sleep(5);
return(1);
}
//client.c
#include <stdio.h>
#include <pthread.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/neutrino.h>
int main(void)
{
char smsg[20];
char rmsg[200];
int coid;
long serv_pid;
printf("Prog client, Vvedite PID servera \n");
scanf ("%ld",&serv_pid);
printf("Vveli %ld \n", serv_pid);
coid=ConnectAttach(0,serv_pid,1,0,0);
printf("Connect res %d \n, vvedite soobshenie ", coid);
scanf("%s",&smsg);
printf("Vveli %s \n", smsg);
if(MsgSend(coid,smsg,strlen(smsg)+1,rmsg, sizeof(rmsg))==-1)
{
printf("Error MsgSend \n");
}
return(1);
}
2.3.3 Последовательность действий
После компиляции программ сервера и клиента у нас будет 2 исполняемых файла. Назовём их server и client соответственно.
Программу server необходимо запустить в фоновом режиме # server &. Она начнёт работать: создаст канал, выведет номер канала и идентификатор процесса сервера, будет ждать сообщения от клиента.
Потом необходимо запустить клиента. Он попросит ввести идентификатор процесса сервера, для установления соединения с ним, и само сообщение (20 символов). Далее можно наблюдать, как сервер получит сообщение, выведет его и пошлёт ответ.
На этом клиент закончит свою работу, но его можно запустить ещё раз и послать другое сообщение.
Остановить работу сервера можно функцией kill < идентификатор процесса сервера >.
2.3.4 Результаты
# `pwd`/server &
[3] 2019364
# Prog server
Server start working
Chanel id: 1
Pid: 2019364
# `pwd`/client
Prog client, Vvedite PID servera
2019364
Vveli 2019364
Connect res 3
, vvedite soobshenie Hello_server
Vveli Hello_server
Polychili soobshenie, rcvid 2
Soobshenie takoe: "Hello_server".
"Eto otvet".
# `pwd`/client
Prog client, Vvedite PID servera
2019364
Vveli 2019364
Connect res 3
, vvedite soobshenie I_snova_Privet
Vveli I_snova_Privet
Polychili soobshenie, rcvid 2
Soobshenie takoe: "I_snova_Privet".
"Eto otvet".
# kill 2019364
#
2.4 Лабораторная работа №4 «Тайм-ауты»
2.4.1 Теоретические сведения
Тайм-ауты ядра
QNX/Neutrino позволяет вам получать тайм-ауты по всем блокированным состояниям. Наиболее часто у вас может возникнуть потребность в этом при обмене сообщениями: клиент, посылая сообщение серверу, не желает ждать ответа «вечно». В этом случае было бы удобно использовать тайм-аут ядра. Тайм-ауты ядра также полезны в сочетании с функцией pthreadjoin(): завершения потока тоже не всегда хочется долго ждать.
Ниже приводится декларация для функции TimerTimeout(), которая является системным вызовом, ответственным за формирование тайм-аутов ядра.
#include <sys/neutrino.h>
int
TimerTimeout (clockid_t id,
int flags,
const struct sigevent *notify,
const uint64_t *ntime,
uint64 t *otime);
Видно, что функция TimerTimeout() возвращает целое число (индикатор удачи/неудачи; 0 означает, что все в порядке, -1 -- что произошла ошибка, и ее код записан в errno). Источник синхроимпульсов (CLOCK_REALTIME, и т.п.) указывается в id, параметр flags задает соответствующее состояние (или состояния). Параметр notify всегда должен быть событием уведомления типа SIGEV_UNBLOCK; параметр ntime указывает относительное время, спустя которое ядро должно сгенерировать тайм-аут. Параметр otime показывает предыдущее значение тайм-аута и в большинстве случаев не используется (вы можете передать вместо него NULL).
Важно отметить, что тайм-ауты «взводятся» функцией TimerTimeout(), а запускаются по входу в одно из состояний, указанных в параметре flags. Сбрасывается тайм-аут при возврате; из любого системного вызова. Это означает, что вы должны заново «взводить» тайм-аут перед каждым системным вызовом которому вы хотите его применить. Сбрасывать тайм-аут после системного вызова не надо -- это выполняется автоматически.
Тайм-ауты ядра и функция pthreadjoin()
Самый простой пример для рассмотрения -- это использование тайм-аута с функцией pthreadjoin().
Применим макроопределение SIGEV_UNBLOCK_INIT() для инициализации структуры события, но можно было установить sigev_notify в SIGEV_UNBLOCK и «вручную». Можно было даже сделать еще более изящно, передав NULL вместо struct sigevent -- функция TimerTimeout() понимает это как знак того, что нужно использовать SIGEV_UNBLOCK.
Если поток (заданный, в thread_id) остается работающим более 10 секунд, то системный вызов завершится по тайм-ауту -- функция pthreadjoin () возвратится с ошибкой, установив errno в ETIMEDOUT.
Вы можете использовать и другую «стенографию», указав NULL в качестве значения тайм-аута (параметр ntime в декларации выше), что предпишет ядру не блокироваться в данном состоянии. Этот прием можно использовать для организации программного опроса. (Хоть программный опрос и считается дурным тоном, его можно весьма эффективно использовать в случае с pthreadjoin(), периодически проверяя, завершился ли нужный поток. Если нет, можно пока сделать что-нибудь другое.)
Ниже представлен пример программы, в которой демонстрируется неблокирующий вызов pthreadJoin():
int
pthread_join_nb (int tid, void **rval)
{
TimerTimeout (CLOCK_REALTIME, _NTO_TIMEOUT_JOIN, NULL, NULL, NULL);
return (pthread_join (tid, rval));)
Тайм-ауты ядра при обмене сообщениями
Все становятся несколько сложнее, когда вы используете тайм-ауты ядра при обмене сообщениями. На момент отправки клиентом сообщения сервер может как ожидать его, так и нет. Это означает, что клиент может заблокироваться как по передаче (если сервер еще не принял сообщение), так и по ответу (если сервер принял сообщение, но еще не ответил). Основной смысл здесь в том, что вы должны предусмотреть оба блокирующих состояния в параметре flags функции TimerTimeout(), потому что клиент может оказаться в любом из них.
Чтобы задать несколько состояний, сложите их операцией ИЛИ (OR): TimerTimeout(_NTO_TIMEOUT_SEND | _NTO_TIMEOUT_REPLY).
Это вызовет тайм-аут всякий раз, когда ядро переведет клиента в состояние блокировки по передаче (SEND) или по ответу (REPLY). В тайм-ауте SEND-блокировки нет ничего особенного -- сервер еще не принял сообщение, значит, ничего для этого клиента он не делает. Это значит, что если ядро генерирует тайм-аут для SEND-блокированного клиента, сервер об этом информировать не обязательно. Функция MsgSend() клиента возвратит признак ETIMEDOUT и обработка тайм-аута завершится.
Однако, если сервер уже принял сообщение клиента и клиент желает разблокироваться, для сервера существует два варианта реакции. Если сервер не указал флаг _NTO_CHF_UNBLOCK на канале, по которому было принято сообщение, клиент будет разблокирован немедленно, и сервер не получит об этом никакого оповещения. У большинства серверов, флаг _NTO_CHF_UNBLIOCK всегда установлен. В этом случае ядро посылает серверу импульс, а клиент остается заблокированным до тех пор, пока сервер ему не ответит! Это сделано для того, чтобы сервер мог узнать о запросе клиента на разблокирование и выполнить по этому поводу какие-то действия.
2.4.2 Текст программы
#include <stdio.h>
#include <pthread.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/neutrino.h>
#define SEC_NSEC 1000000000LL // 1 sekynda billion nanosekynd
void * long_thread(void *notused)
{
printf("Etot potok vipolnaetsa bolee 10 sekynd \n");
sleep(20);
}
int main(void)
{
uint64_t timeout;
struct sigevent event;
int rval;
pthread_t thread_id;
printf("Prog timer \n");
event.sigev_notify = SIGEV_UNBLOCK;
//SIGEV_UNBLOCK_INIT(&event);
pthread_create(&thread_id, NULL, long_thread, NULL);
timeout = 10LL*SEC_NSEC;
TimerTimeout(CLOCK_REALTIME, _NTO_TIMEOUT_JOIN,&event, &timeout, NULL);
rval = pthread_join(thread_id, NULL);
if (rval == ETIMEDOUT)
{
printf ("istekli 10 sekynd, potok %d vipolniaetsia!\n", thread_id);
}
sleep(5);
TimerTimeout (CLOCK_REALTIME, _NTO_TIMEOUT_JOIN, &event, & timeout, NULL);
rval = pthread_join(thread_id, NULL);
if(rval == ETIMEDOUT)
{
printf("potok %d >25 sek!", rthread_id);
}
else
{
printf ("Potok %d zavershon kak nado \n", thread_id);
}
return(1);
2.4.3 Последовательность действий
Запустить программу на исполнение и сопоставлять то, что она выводит на экран с текстом программы.
2.4.4 Результаты
# cd..
# cd lab2
# ls
. .. timer.c timer.exe
# `pwd`/timer.exe
Prog timer
Etot potok vipolnaetsa bolee 10 sekynd
istekli 10 sekynd, potok 2 vipolniaetsia!
Potok 2 zavershon kak nado
#
2.5 Лабораторная работа №5 «Барьеры»
2.5.1 Теоретические сведения
Применение барьера
Два метода синхронизации: один метод с применением функции pthreadJoin(), который мы только что рассмотрели, и метод с применением барьера. Основной поток должен дождаться того момента, когда все рабочие потоки завершат работу, и только затем можно начинать следующую часть программы. Однако, с применением функции pthreadJoin() мы ожидаем завершения потоков. Это означает, что на момент ее разблокирования потоков нет больше с нами; они закончили работу и завершились.
В случае с барьером, мы ждем «встречи» определенного числа потоков у барьера. Когда заданное число потоков достигнуто, мы их все разблокируем (заметьте, что потоки при этом продолжат выполнять свою работу). Сначала барьер следует создать при помощи функции barrier_init0:
#include <sync.h>
int
barrier_init (barrier t *barrier,
const barrier attr t *attr,
int count);
Эта функция создает объект типа «барьер» по переданному ей адресу (указатель на барьер хранится в параметре barrier) и назначает ему атрибуты, которые определены в attr (мы будем использовать NULL, чтобы установить значения по умолчанию). Число потоков, которые должны вызывать функцию barrier_wait(), передается в параметре count.
После того как барьер создан, каждый из потоков должен будет вызвать функцию barrier_wait(), чтобы сообщить, что он отработал:
#include <sync.h>
int barrier_wait (barrier t *barrier);
После того как поток вызвал barrier_wait(), он будет блокирован до тех пор, пока число потоков, указанное первоначально в параметре count функции barrier_wait(), не вызовет функцию barrier_wait() (они также будут блокированы). После того как нужное число потоков выполнит вызов функции barrier_wait(), все эти потоки будут разблокированы «одновременно».
2.5.2 Текст программы
#include <stdio.h>
#include <time.h>
#include <sync.h>
#include <sys/neutrino.h>
barrier_t barrier;
//int data_ready = 0;
//int inf = 0;
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;
void *thread1 (void * not_used)
{
time_t now;
char buf[27];
time(&now);
printf("Potok 1, vremia starta %s \n", ctime_r(&now,buf));
sleep(3);
barrier_wait(&barrier);
time(&now);
printf("barier v potoke 1, vremia srabativania %s \n", ctime_r(&now,buf));
}
void *thread2 (void * not_used)
{
time_t now;
char buf[27];
time(&now);
printf("Potok 2, vremia starta %s \n", ctime_r(&now,buf));
sleep(6);
barrier_wait(&barrier);
time(&now);
printf("barier v potoke 2, vremia srabativania %s \n", ctime_r(&now,buf));
}
main()
{
time_t now;
char buf[27];
barrier_init(&barrier, NULL, 3);
printf("Start \n");
pthread_create(NULL,NULL, thread1,NULL);
pthread_create(NULL,NULL, thread2,NULL);
time(&now);
printf(" Main(): oshidanie y bariera, vremia %s \n", ctime_r(&now,buf));
barrier_wait(&barrier);
time(&now);
printf("barier v main(), vremia srabativania %s \n", ctime_r(&now,buf));
sleep(5);
}
2.5.3 Последовательность действий
Основной поток создал объект типа «барьер» и инициализировал его значением счетчика, равным числу потоков (включая себя!), которые должны «встретиться» у барьера, прежде чем он «прорвется». В нашем примере этот индекс был равен 3 -- один для потока main(), один для потока thread1() и один для потока thread2(). Затем, как и прежде, стартуют потоки вычисления графики (в нашем случае это потоки thread1() и thread2()). Для примера вместо приведения реальных алгоритмов графических вычислений мы просто временно «усыпили» потоки, указав в них sleep (20) и sleep (40), чтобы имитировать вычисления. Для осуществления синхронизации основной поток (таin()) просто блокирует сам себя на барьере, зная, что барьер будет разблокирован только после того, как рабочие потоки аналогично присоединятся к нему.
Как упоминалось ранее, с функцией pthreadJoin() рабочие потоки для синхронизации главного потока с ними должны умереть. В случае же с барьером потоки живут и чувствуют себя вполне хорошо. Фактически, отработав, они просто разблокируются по функции barrier_wait(). Тонкость здесь в том, что вы обязаны предусмотреть, что эти потоки должны делать дальше! В нашем примере с графикой мы не дали им никакого задания для них -- просто потому что мы так придумали алгоритм. В реальной жизни вы могли бы захотеть, например, продолжить вычисления.
2.5.4 Результаты
# root/a.out
Start
Potok 1, vremia starta Tue Oct 21 00:29:01 2003
Potok 2, vremia starta Tue Oct 21 00:29:01 2003
Main(): oshidanie y bariera, vremia Tue Oct 21 00:29:01 2003
barier v potoke 2, vremia srabativania Tue Oct 21 00:29:07 2003
barier v main(), vremia srabativania Tue Oct 21 00:29:07 2003
barier v potoke 1, vremia srabativania Tue Oct 21 00:29:07 2003
#/**
2.6 Лабораторная работа №6 «Условные переменные»
2.6.1 Теоретические сведения
Условные переменные
Условные переменные (или «condvars») очень похожи на ждущие блокировки, которые мы рассматривали выше. В действительности, ждущие блокировки -- это надстройка над механизмом условных переменных, и именно поэтому в таблице, иллюстрировавшей использование ждущих блокировок, у нас встречалось состояние CONDVAR. Функция pthread_cond_wait() точно так же освобождает мутекс, ждет, а затем повторно блокирует мутекс, аналогично функции pthread_sleepon_wait().
2.6.2 Текст программы
#include <stdio.h>
#include <pthread.h>
int data_ready = 0;
int inf = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;
void *consumer (void * notused)
{
printf("Eto potrebitel \n");
while(1)
{
pthread_mutex_lock (&mutex);
printf("W1 \n");
while (!data_ready)
{
printf("W2 \n");
pthread_cond_wait (&condvar, & mutex);
printf("W3 \n");
}
printf("dannie ot proizv = %d \n",inf);
data_ready=0;
pthread_cond_signal(&condvar);
pthread_mutex_unlock(&mutex);
}
}
void *producer (void * notused)
{
printf("Eto proizvoditel \n");
while(1)
{
sleep(2);
printf("proizvoditel polychil dannie ot h/w = %d \n",inf);
pthread_mutex_lock (&mutex);
printf("Wp1 \n");
while (data_ready)
{
printf("Wp2 \n");
pthread_cond_wait (&condvar, & mutex);
}
data_ready=1;
inf++;
printf("Wp3 \n");
pthread_cond_signal(&condvar);
pthread_mutex_unlock(&mutex);
}
}
main()
{
printf("Start \n");
pthread_create(NULL,NULL, consumer,NULL);
pthread_create(NULL,NULL, producer,NULL);
sleep(10);
}
2.6.3 Последовательность действий
Этот пример в значительной степени похож на программу с применением ждущей блокировки, с небольшими отличиями. Первое отличие, которое бросается в глаза, -- здесь использован новый тип данных, pthread_cond__t. Это просто декларация для условной переменной; мы назвали нашу условную переменную condvar.
Следующее, что видно из примера, -- это то, что структура «потребителя» идентична таковой в предыдущем примере со ждущей блокировкой.
Основное различие здесь состоит в том, что библиотека ждущих блокировок имеет скрытый внутренний мутекс, а при использовании условных переменных мутекс передается явно. Последний способ дает нам больше гибкости.
И, наконец, обратите внимание на то, что мы использовали функцию pthread_cond_signal() (опять же, с явной передачей мутекса).
2.6.4 Результаты
# root/a.out
Start
Eto potrebitel
W1
W2
Eto proizvoditel
proizvoditel polychil dannie ot h/w = 0
Wp1
Wp3
W3
dannie ot proizv = 1
W1
W2
proizvoditel polychil dannie ot h/w = 1
Wp1
Wp3
W3
dannie ot proizv = 2
W1
W2
proizvoditel polychil dannie ot h/w = 2
Wp1
Wp3
W3
dannie ot proizv = 3
W1
W2
proizvoditel polychil dannie ot h/w = 3
Wp1
Wp3
W3
dannie ot proizv = 4
W1
W2
ЗАКЛЮЧЕНИЕ
Операционная система QNX является мощной сетевой операционной системой, позволяющей проектировать сложные программные комплексы, работающие в реальном времени как на локальном компьютере, так и в рамках распределенной вычислительной системы.
Встроенные средства операционной системы QNX обеспечивают как поддержку многозадачного режима на одном компьютере, в том числе с мультипоточным или мультиядерным процессором, так и взаимодействие параллельно выполняемых задач на разных компьютерах, работающих в среде локальных вычислительных сетей.
Основным языком программирования в системе является язык Си, широко используемый в настоящее время на большинстве персональных компьютеров. Это позволяет с небольшими доработками перенести необходимое накопленное программное обеспечение в среду операционной системы QNX для организации его работы в среде распределенной обработки.
Более подробно ознакомиться с операционной системой реального времени QNX, с особенностями функционирования ее составных частей, таких как, например, графическая оболочка Photon и встроенная ОС РВ Neutrino, а также познакомиться с многочисленными примерами приложений, использующих QNX, Вы можете непосредственно на WEB-сервере фирмы QSSL[5], а также используя отечественные Интернет ресурсы [6,7].
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
1. Алексеев Д., Ведревич Е., Волков А. и др. Практика работы с QNX.- М.: Издательский Дом «КомБук», 2004. - 432 с.
2. Зыль С.Н. Операционная система реального времени QNX: от теории к практике.- СПБ.: БХВ-Петербург, 2004. -192 с.
3. Операционная система реального времени QNX Neutrino 6.3. Системная архитектура: Пер. с англ. - СПБ.: БХВ-Петербург, 2005. -336 с.
4. Р. Кертен. Введение в QNX/Neutrino 2. - СПБ.: ООО «Издательство «Петрополис»», 2001. - 479 с.
5. http://www.swd.ru/qnx/support/literature/sysarch/.
6. QNX Realtime Platform: Русский Портал http:/qnx.org.ru.
7. WEB-сервер фирмы QSSL: http:/www.qnx.com/.
ОПРЕДЕЛЕНИЯ, ОБОЗНАЧЕНИЯ И СОКРАЩЕНИЯ
ПК - персональный компьютер;
ОСРВ - операционная система реального времени;
IPC - связь между процессами (Interprocess communication).
Размещено на Allbest.ru
...Подобные документы
Разграничение прав пользователя в операционной системе. Предварительная настройка операционной системы с последующей установкой драйверов для периферийных устройств и системных комплектующих. Классификация операционных систем и периферийных устройств.
реферат [2,1 M], добавлен 26.10.2022Функции для работы с протоколом TCP/IP, Socket, Bind, listen и accept. Файловый дескриптор. Коммуникационные процессы. Прием данных. Чтение из сокета. Запись в сокет. Закрытие сокета. Текст программы, создающей Web-сервер в операционной системе QNX.
лабораторная работа [174,0 K], добавлен 29.09.2008Три группы компонентов в составе современной операционной системы: ядро (планировщик и драйверы устройств), системные библиотеки, оболочка с утилитами. Типы архитектур ядер операционных систем: монолитное, модульное, гибридное, микро-, экзо-, наноядро.
курсовая работа [22,1 K], добавлен 27.05.2014Методы и приемы работы в операционной системе Windows XP, часто используемой при работе с персональным компьютером. Средства по настройке и конфигурации операционной системы. Соответствие используемых аппаратных средств потребностям пользователя.
курсовая работа [4,7 M], добавлен 15.07.2009Установка платформы виртуализации VirtualBox. Создание и настройка виртуальной машины VirtualBox с операционной системой Ubuntu. Ознакомление с операционной системой Ubuntu-desktop x32. Компиляция программ на С/С++ в терминале Ubuntu-desktop x32.
лабораторная работа [2,6 M], добавлен 08.11.2022Взаимодействие процессов и потоков в операционной системе, основные алгоритмы и механизмы синхронизации. Разработка школьного курса по изучению процессов в операционной системе Windows для 10-11 классов. Методические рекомендации по курсу для учителей.
дипломная работа [3,2 M], добавлен 29.06.2012Мониторинг операционной системы в современном мире. Программа для операционной системы Windows как средство для его проведения. Особенности разработки программы в Delphi 7.0. Описание работы программы, порядок выполняемых действий, и программная часть.
курсовая работа [2,7 M], добавлен 02.12.2009Знакомство с операционной системой Windows. Исследование её устройства, истории, возможностей, особенностей работы с ней для получения новых знаний. Описание наиболее использующихся и важных функций этой операционной системы, их практическое освоение.
контрольная работа [2,9 M], добавлен 14.12.2009Правовые основы защиты информации на предприятии. Анализ среды пользователей. Автоматизированная система предприятия. Краткие сведения об операционной системе Windows XP. Классификация троянских программ. Способы защиты операционной системы Windows XP.
дипломная работа [187,3 K], добавлен 14.07.2013Общая характеристика и возможности сетевой операционной системы Windows 95. Объекты ОС, их классификация, разновидности. Правила работы с Word, создание и сохранение документов. Формирование и ввод данных в таблицах Excel. Форматирование дисков.
контрольная работа [835,0 K], добавлен 25.02.2009Планирование задач в операционной системе реального времени. Основные виды планирования применительно к задачам реального времени. Выбор приемлемого алгоритма планирования при проектировании RTS. Статическое прогнозирование с использованием таблиц.
контрольная работа [40,7 K], добавлен 28.05.2014Unix - полноценная, изначально многопользовательская, многозадачная и многотерминальная операционная система. Особенности архитектуры Unix, ее два кита - файлы и процессы. Ядро операционной системы, ее файловая система, работа устройств, драйверы.
реферат [1,0 M], добавлен 22.03.2016Создание виртуальной машины для гостевой операционной системы Microsoft Windows Server 2003. Первоначальная настройка установленной операционной системы. Создание DHCP-сервера с диапазоном рабочих адресов. Настройка доменного имени для IP-адреса сервера.
лабораторная работа [3,2 M], добавлен 20.12.2012Разработка клиент-серверного игрового приложения на примере игры в шашки для мобильных устройств на базе операционной системы Android. Обзор мобильных платформ. Экраны приложения и их взаимодействие. Графический интерфейс, руководство пользователя.
курсовая работа [2,6 M], добавлен 15.06.2013Конфигурация серверной машины на примере Hyperion RS230 G4. Выбор серверной операционной системы. Аппаратная составляющая сервера. Правило замены блоков. Сборка и установка в стойку. Оптимизация работы оборудования. Обновление операционной системы Unix.
курсовая работа [3,7 M], добавлен 11.09.2014Основные компоненты системы X-Com. Иерархия узлов и серверов. Методы разбиения исходной задачи на блоки. Структуры данных сервера для хранения информации об узлах. Точки взаимодействия прикладной программы с системой X-Com. Фоновые процессы на сервере.
лекция [217,2 K], добавлен 28.06.2009Серверные операционные системы, их особенности и сферы применения. Функции и ресурсы операционной системы Windows Server 2003. Сервер как программный компонент вычислительной системы. Аппаратные и серверные решения. Минимальные системные требования.
презентация [1005,9 K], добавлен 05.12.2013Концепция операционных систем: главное назначение, основные функции и типы. Характеристика и оценка возможностей Microsoft Windows и Linux. Подбор операционной системы для рабочих персональных компьютеров и для сервера на предприятии ООО "Газ-сервес".
дипломная работа [272,3 K], добавлен 16.06.2012Изучение операционной системы Linux: элементов файлов, структуры каталогов и прав доступа к ним. Получение практических навыков по работе с некоторыми командами данной ОС. Теоретические сведения и практические навыки по работе с процессами Linux.
лабораторная работа [847,5 K], добавлен 16.06.2011Использование стандартных библиотек Windows. Установка и настройка дополнительных устройств ввода/вывода. Использование камеры, динамиков, сканера, дисков и портов ввода/вывода. Драйверы внешних устройств. Безопасность данных в операционных системах.
контрольная работа [1,8 M], добавлен 13.10.2022