-
Notifications
You must be signed in to change notification settings - Fork 1
1. ОС с монолитным ядром. Переключение в режим ядра. Диаграмма состояний процесса и переход из одного состояние в другое — причины каждого перехода. Диаграмма состояний процесса в UNIX. Переключение контекста. Система прерываний.
Монолитное ядро - такая структура ядра ОС, при которой все его компоненты являются модулем одной программы. Все компоненты ОС используют общие СД и взаимодействуют друг с другом путем вызова процедур.
Монолитное ядро - один процесс, который работает в одном адресном пространстве и в привилегированном режиме. Представляет собой статический двоичный файл. Взаимодействие приложений с ядром происходит с помощью системных вызовов.
До недавнего времени в системах с монолитным ядром расширение функциональности достигалось полной перекомпиляцией ядра (недопустимо). Так появилась технология динамической реконфигурации (это создает проблемы безопасности и устойчивости). В Linux - загружаемые модули ядра, в Windows - многоуровневые драйвера и службы.
Процесс - программа в стадии выполнения.
В UNIX:
- Процесс выполняет собственный код - выполняется в режиме задачи.
- Процесс выполняет реентерабельный код (код чистой процедуры) ОС - выполняется в режиме ядра.
Системные вызовы - запрос процесса у ОС системных ресурсов (ввод-вывод). ОС не позволяет процессу обращаться к устройствам ввода-вывода напрямую (такая система была бы незащищенная). Для обслуживания системного вызова, процесс переходит в режим ядра и выполняет реентеберальный код ОС. Коды ядра работают с системными таблицами. После того как системный вызов обслужен, процесс переключается обратно в режим задачи.
В режиме ядра процесс переносится в очередь готовых процессов. Когда процесс получает квант, он начинает выполнять свой собственный код. Если процесс запросил дополнительный ресурс, то он будет переключен в режим ядра и будет заблокирован до того момента, пока не получит нужный ему ресурс, после чего система поместит процесс в очередь готовых процессов и после получения кванта процесс сможет продолжить выполнение своего кода.
События, переводящие систему в систему ядра:
- Системные вызовы
- Исключения
- Аппаратные прерывания


Понятие контекста является важнейшим в системе.
Переключение аппартного контекста - сохранение содержимого регистров процессора. Такое переключение поддерижвается аппаратно, для этого существует команда pusha. При переходе из режима ядра в режим задачи, переключается аппаратный контекст.
Переключение полного контекста - переключение аппаратного контекста и сохранение информации о выделенных процессу ресурсах, например, выделенная ему память, которая описывается соответствующими таблицами. Такое переключение происходит когда процесс исчерпал свой квант или происходит системный вызов, потому что системе необходимо знать информацию о тех ресурсах, которые были выделены процессу. Переключение полного контекста - затратная операция, т.к. не поддерживается аппаратно, теряется актуальность кэшей.
Важнейшей для работы систем с монолитным ядром является система прерываний, состоящая из:
- системные вызовы (часто используется термин API - те функции, которые может запросить пользователь у системы).
- исключения - аварийные ситуации в системе (исправимые -
page fault, неисправимые - деление на 0). - аппаратные прерывания - ассинхронное событие, пред. 2 - синхронные. Предназначены для информирования процессора об окончании операции ввода-вывода.
1-2 - синхронные события, 3 - асинхронное событие.
Реализована идея распараллеливания функций - внешними устройствами управляет не процессор. Канальные, шинные внешние устройства - контроллеры. Команды от процессора переводят контроллер в нужный режим. В результате нажатия клавиши она поступает в буфер клавиатуры, для этого необходимо проинформировать процессор и он запустит нужный обработчик прерывания.
Приложение никогда не должно уметь обращаться напрямую к внешним устройствам, иначе оно бы смогло изменять код ОС и такую ОС невозможно было бы защитить. В операционной системе read/write главные системные вызовы.
2. Задача: читатели-писатели — монитор Хоара, решение с использованием семафоров Unix и разделяемой памяти, пример реализаци из лабораторной работы.
Для этой задачи характерно наличие двух типов процессов:
- «читатели», которые могут только читать данные. Могут работать параллельно, поскольку они друг другу не мешают
- «писатели», которые могут только изменять данные. Работают только в режиме монопольного доступа: только один писатель может получить доступ к разделяемой переменной, причем, когда работает писатель, то другие писатели и читатели не могут получить доступ к этой переменной.
В мониторе Хоара "Читатели-писатели" характерно наличие 4 процедур:
start_read(), stop_read(), start_write() и stop_write().
#define ACTIVE_READER 0
#define ACTIVE_WRITER 1
#define WRITE_QUEUE 2
#define READ_QUEUE 3
struct sembuf READER_LOCK[] = {
{ READ_QUEUE, 1, 0 },
{ ACTIVE_WRITER, 0, 0 },
{ WRITE_QUEUE, 0, 0 },
{ ACTIVE_READER, 1, 0 },
{ READ_QUEUE, -1, 0 },
};
struct sembuf READER_RELEASE[] = {
{ ACTIVE_READER, -1, 0 },
};
struct sembuf WRITER_LOCK[] = {
{ WRITE_QUEUE, 1, 0 },
{ ACTIVE_READER, 0, 0 },
{ ACTIVE_WRITER, 0, 0 },
{ ACTIVE_WRITER, 1, 0 },
{ WRITE_QUEUE, -1, 0 },
};
struct sembuf WRITER_RELEASE[] = {
{ ACTIVE_WRITER, -1, 0 },
};
int start_read(int sid) {
return semop(sid, READER_LOCK, 5) != -1;
}
int stop_read(int sid) {
return semop(sid, READER_RELEASE, 1) != -1;
}
int start_write(int sid) {
return semop(sid, WRITER_LOCK, 5) != -1;
}
int stop_write(int sid) {
return semop(sid, WRITER_RELEASE, 1) != -1;
}
int reader_run(int *const shared_mem, const int sid, const int rid) {
srand(time(NULL) + rid);
if (!shared_mem) {
return 1;
}
for (size_t i = 0; i < 8; i++) {
int sleep_time = rand() % TIME_RANGE + TIME_START;
sleep(sleep_time);
if (!start_read(sid)) {
return 2;
}
int readed = *shared_mem;
fprintf(stdout, "Reader %d read: %d\n", rid + 1, readed);
if (!stop_read(sid)) {
return 3;
}
}
return 0;
}
int writer_run(int *const shared_mem, const int sid, const int wid) {
if (!shared_mem) {
return 1;
}
for (size_t i = 0; i < ITER_CNT; i++) {
int sleep_time = rand() % TIME_RANGE + TIME_START;
sleep(sleep_time);
if (!start_write(sid)) {
return 4;
}
int updated = ++(*shared_mem);
fprintf(stdout, "Writer %d write: %d\n", wid + 1, updated);
if (!stop_write(sid)) {
return 5;
}
}
return 0;
}