Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект_ОС.doc
Скачиваний:
44
Добавлен:
14.02.2015
Размер:
506.88 Кб
Скачать
    1. 2.2. Понятия потока («нити») и многопоточности

Когда говорят о процессах, то тем самым хотят отметить, что операционная система поддерживает их обособленность: у каждого процесса имеется свое виртуальное адресное пространство, каждому процессу назначаются свои ресурсы – файлы, окна, семафоры и так далее. Такая обособленность нужна для того, чтобы защитить один процесс от другого, поскольку они, совместно используя все ресурсы ВМ, конкурируют друг с другом. В общем случае процессы могут быть никак не связаны между собой и даже могут принадлежать разным пользователям, разделяющим одну вычислительную машину. Другими словами, в случае процессов ОС считает их совершенно несвязанными и независимыми. При этом именно ОС берет на себя роль арбитра в конкуренции между процессами по поводу ресурсов.

Однако желательно иметь еще и возможность задействовать внутренний параллелизм, который может быть в самих процессах. Такой внутренний параллелизм встречается достаточно часто, а его использование позволяет ускорить реализацию процессов. Например, некоторые операции, выполняемые приложением, могут требовать для своего исполнения достаточно длительного использования центрального процессора. В этом случае при интерактивной работе с приложением пользователь вынужден долго ожидать завершения заказанной операции и не может управлять приложением до тех пор, пока операция не выполнится до самого конца. Такие ситуации встречаются достаточно часто, например, при обработке больших изображений в графических редакторах. Если же программные модули, исполняющие такие длительные операции, оформлять в виде самостоятельных «подпроцессов» (так называемых легковесных или облегченных процессов), которые будут выполняться параллельно с другими подобными «подпроцессами», то у пользователя появляется возможность параллельно выполнять несколько операций в рамках одного приложения (процесса). Такие «подпроцессы» принято называть потоками или «нитями» (thread). «Подпроцессы» (потоки) называют легковесными потому, что операционная система не должна для них организовывать полноценную виртуальную машину. Потоки не имеют своих собственных ресурсов, они развиваются в том же виртуальном адресном пространстве, могут пользоваться теми же файлами, виртуальными устройствами и иными ресурсами, что и данный процесс. Единственное, что им необходимо иметь, – это процессорный ресурс. В однопроцессорной машине потоки разделяют между собой процессорное время так же, как это делают обычные процессы, а в многопроцессорной машине могут выполняться одновременно, если не встречают конкуренции из-за обращения к иным ресурсам.

2.2.1. Макросы и подпрограммы

Макросы и подпрограммы служат одной цели: повторяющиеся совокупности программных кодов заменяются именем той или другой структуры, что дает возможность не только оптимизировать написание программы, но и улучшить ее «читабельность». Однако механизмы достижения этой цели совершенно разные. Каждый раз, когда в программе встречается имя макроса, компилятор вставляет его коды. Таким образом, после компиляции длина объектных кодов будет такой же, как если бы мы не применяли макросы, а писали программу обычным способом. Подпрограмма же выполняется в своем окружении, т. е. при ее вызове происходит скачок в другую область памяти. Если не предпринять специальные меры по воз­вращению в вызываемую программу, то в указанном окружении придется находиться до ближайшей дежурной перезагрузки системы. Оператор CALL легко улаживает эту проблему, запоминая при вызове подпрограммы адрес возврата, который записывается в стек. Стек выбран в данном случае средой исполнения как самая быстрая структура, что поддерживает­ся скоростными качествами операций push (заталкивание в стек) и pop (вы­талкивание из стека). Указатель стека при запоминании адреса возврата уменьшается:

  • при ближнем вызове на 2 байта (запоминается только счетчик команд);

  • при дальнем вызове на 4 байта (запоминается счетчик команд и значение регистра CS);

• при дальнем вызове и запоминании регистра флагов на 6 байта. Вызов подпрограммы в чистом виде (т. е. на ассемблере) будет иметь

следующий вид:

Однако CALL не решает проблемы с передачей параметров в подпрограмму и при ее написании на ассемблере об этом должен побеспокоиться автор программы. Напротив, механизм передачи параметров легко вписывается в макросы, и на ассемблере это будет выглядеть следующим образом:

<Имя_подпрограммы пар1,пар2...>.

Говоря другими словами, то, что на языке высокого уровня называется подпрограммой, по виду напоминает макрос. Попытаемся подробнее разобраться в этом вопросе. Ниже приведен текст простейшей программы для 16 -разрядного компилятора. Попутно отметим, что включение системных библиотек осуществлено для солидности, и ни какой практической пользы в данном случае не приносит.

Include <stdlib.h>

#include <conio.h>

#include <dos.h>

void summator(int I, int k);

void main(void)

{ int I, k ; I=k=2; summator(I,k);

}

void summator(int I, int k)

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

_main: void main (void)

(пропущенная строка)

  1. mov ax,0002

  2. mov [bp-04], ax

  3. mov [bp-02], ax

  4. Summator(l,k);

  5. push word ptr [bp-4]

  6. push word ptr [bp-2]

  7. CALL Summator

  8. add sp,OOO4 (пропущенные строки)

(7.1) void Summator(int 1, int k)

  1. push bp

  2. mov bp, sp

  3. mov dx, [bp+04]

  4. mov ax, dx

  5. add ax, [bp+06]

  6. mov dx, ax

  7. pop bp

  8. ret

Прежде чем перейти к рассмотрению особенностей вызова подпро­граммы на ЯВУ, отметим, что, анализируя п. (1) и (2), можно прийти к вы­воду об отсутствии сегмента данных. Данные описаны в кодовом сегменте, так как их местоположение в памяти определяется смещением от указателя базы bр.

В основной программе вызов подпрограммы начинает обрабатываться с п. (4). Однако переключение на коды (7.1)—(7.9), которые выполняются в окружении, отличном от окружения основной программы, происходит только в п. (7). В пунктах между началом обработки вызова и собственно вызовом подпрограммы подготавливается передача в подпрограмму пара­метров 1 и к, которые засылаются в стек. Далее, зная, как воздействует опе­ратор CALL на состояние стека и запоминание в стеке регистра bр (7.3), нетрудно через указатель базы bр добраться до этих параметров (7.4) и (7.6). Разумеется, предварительно следует заслать в bр значение указателя стека sp (7.3). Таким образом, комбинируя свойства макросов и оператора CALL, можно не только осуществить скачок в новое окружение, но и пере­дать туда нужные параметры. Осталось уладить один деликатный момент. По адресу возврата выполнение программы возвращается к оператору, ко­торый следует после команды, содержащей оператор CALL, т. е. в макрос, тогда как согласно жанру, управление должно передаваться оператору, который следует после формального вызова, т. е. после макроса. Есть все основания полагать, что после того, как читатель внимательно ознакомится с представленными кодами, его недоумение по этому поводу отпадет.

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