Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
os2.doc
Скачиваний:
0
Добавлен:
20.06.2023
Размер:
246.78 Кб
Скачать

2.4. Примеры реализации сопрограмм в Си, в защищенном режиме процессора и в Windows

2.4.1. Пример реализации сопрограмм в Си

В языке Си существует предопределенная структура, имеющая следующее описание.

Имя типа jmp_buf. Это по сути дела запись, включающая 10 полей типа word. Имена полей имеют следующий вид:

j_sp, j_cs, j_bp, j_si,

j_ss, j_ip, j_di, j_ds.

j_flag, j_es,

Чтобы сразу провести параллель с сопрограммами, заметим, что структура подобного типа может выступить в качестве дескриптора сопрограммы, причем даже более информативного, чем тот, который мы рассмотрели ранее.

Для работы со структурой jmp_buf существует пара функций, имеющая следующее описание:

  1. int setjmp(jmp_buf jmpb) - пишет состояние текущей задачи в буфер jmpb и возвращает 0;

  2. void longjmp(jmp_buf jmpb, int retval) – восстанавливает состояние задачи из jmpb так, что задача продолжает свое выполнение с той точки, в которую бы она пришла, если бы функция setjmp вернула не 0, а значение, равное retval.

Рассмотрим структуру сопрограмм и функции transfer для данного случая.

void cor1(void) { void cor2(void) {

while (1) { while (1) {

... ...

transfer(jmpc1,jmpc2); transfer(jmpc2,jmpc1);

} }

} }

void transfer(jmp_buf from, jmp_buf to)

{

if (0 == setjmp(from)) {//setjmp пишет такое состояние в буфер

longjmp(to,1); //from, что когда будет вызов longjmp

} //с этим буфером, управление передастся

***** //в точку *****

}

Инициализация буфера (на примере cor1)

jmp_buf jmpc1;

unsigned stack1[1000];

struct SREGS segs;

segread(&segs);

jmpc1[0].j_sp = FP_OFF(stack1) + 1982;

jmpc1[0].j_ss = FP_SEG(stack1);

jmpc1[0].j_flag = 0x200; //прерывания разрешены

jmpc1[0].j_cs = FP_SEG(cor1);

jmpc1[0].j_ip = FP_OFF(cor1);

jmpc1[0].j_bp = jmpc1[0].j_sp;

jmpc1[0].j_di = 0;

jmpc1[0].j_es = segs.es;

jmpc1[0].j_si = 0;

jmpc1[0].j_ds = segs.ds;

Функции setjmp и longjmp вместе со структурой jmp_buf являются чрезвычайно удобным средством реализации сопрограмм. Во-первых, мы не спускаемся на уровень ассемблера, а во-вторых, запись состояния регистров в буфер и восстановление их из буфера происходит в режиме запрета прерываний, что обеспечивает высокую надежность переключения задач.

2.4.2. Пример реализации сопрограмм в защищенном режиме

Защищенный режим процессора архитектурно создан для организации многозадачности. У защищенного режима много аспектов, здесь мы рассмотрим именно вопросы создания и переключения задач, которые по сути дела эквивалентны нашему понятию сопрограмм.

Пример, который реализует переключение задач в защищенном режиме, полностью написан на ассемблере. Я буду приводить здесь только фрагменты примера. Если кому-то нужен полный текст примера, то с ним можно также ознакомиться.

Доступ к памяти в защищенном режиме осуществляется не непосредственно по адресу, а через специальные таблицы - таблицы дескрипторов. В таблице дескрипторов хранятся строки, описывающие отдельные участки памяти - страницы, если размер участка постоянен и известен, или сегменты, размер которых может меняться.

Для описания задач существует структура, которая называется «сегмент состояния задачи» TSS. Она имеет следующий вид:

Селектор LTD

Селектор DS

Селектор SS

Селектор CS

Селектор ES

Регистры AX, BX, CX, DX, SP, BP, SI, DI

Регистр флагов

IP

SS, SP для уровня привилегий 2

SS, SP для уровня привилегий 1

SS, SP для уровня привилегий 0

Указатель на следующий TSS

Как видно, это опять наш дескриптор, только еще более информативный, даже по сравнению со структурой jmp_buf из Си.

Для выполнения программы в защищенном режиме создается глобальная таблица дескрипторов GDT, строки которой включают дескрипторы TSS:

Не используется

Описание самой таблицы дескрипторов

Дескриптор сегмента данных

Дескриптор сегмента стека

Дескриптор кодового сегмента

Дескриптор задачи - main

Дескриптор задачи 1

Дескриптор задачи 2

Строка таблицы дескрипторов содержит следующие данные:

Размер сегмента

Адрес сегмента

Признак сегмента

В программу, реализующую переключение задач в защищенном режиме, вводятся:

  1. Селекторы задач TASK1_SEL, TASK2_SEL, MAIN_TSK – смещения соответствующих дескрипторов в таблице GDT;

  2. Выделяется память под стеки, например, так, tsk1_stack db 1024d (0);

  3. Инициализируется таблица дескрипторов GDT - т.е. корректно заполняются все ее строки;

  4. Инициализируются сегменты состояния задач, например, в поле ip пишется OFFSET Имя процедуры-задачи; в поле sp пишется OFFSET tsk1_stack + Size_Of_Stack;

  5. В регистр GDTR грузится адрес таблицы дескрипторов GDT;

  6. В регистр задач TR грузится селектор задачи - MAIN_TSK.

Таким образом, происходит выполнение задачи Main. Переключение на другую задачу производится инструкцией jmp, например, jmp TASK1_SEL.

По этой инструкции машина видит по признаку сегмента, что селектор указывает на дескриптор задачи. По селектору MAIN_TSK, находящемуся в регистре задач TR, через дескриптор задачи main, находится сегмент состояния задачи main и в него списывается состояние машины в соответствие со структурой сегмента.

Селектор новой задачи TASK1_SEL грузится в регистр задач TR, по селектору также через дескриптор находится сегмент состояния задачи 1, и из этого сегмента устанавливается новое состояние машины. В заключении представим схему переключения задач в защищенном режиме, из схемы видна аналогия с ранее рассмотренными сопрограммами. Если в Си для переключения задач потребовалось написать функцию, то в защищенном режиме переключение задач осуществляется одной инструкцией ассемблера.

Соседние файлы в предмете Операционные системы