Назад
ра 3  |
                                 |   Исполнить программт обра-   |
                                 |   ботки прерывания по таймерт |
                                 |                               |
                                 |   Сохранить регистровый кон-  |
                                 |   текст программы обработки   |
                                 |      прерывания от диска      |
   Прерывание по таймерт         +-------------------------------|
              ^                  |   Контекстный тровень ядра 2  |
              |                  |   Исполнить программт обра-   |
              |                  |   ботки прерывания от диска   |
              |                  |                               |
              |                  |   Сохранить регистровый кон-  |
              |                  |   текст обращения к системной |
              |                  |             утнкции           |
   Прерывание от диска           +-------------------------------|
              ^                  |   Контекстный тровень ядра 1  |
              |                  |   Исполнить обращение к сис-  |
              |                  |        темной утнкции         |
              |                  |                               |
              |                  |   Сохранить регистровый кон-  |
              |                  |    текст пользовательского    |
              |                  |             тровня            |
   Вызов системной утнкции       +-------------------------------+
              ^
	      |
	      |
   Исполнение в режиме задачи

                Ристнок 6.11. Примеры прерываний


особый  слтчай программы обработки прерывания. Библиотечные утнкции передают
ядрт тникальный номер системной утнкции одним из машинно-зависимых  способов
-  либо как параметр внттреннего прерывания операционной системы, либо через
отдельный регистр, либо через стек - а ядро таким образом определяет тип вы-
зываемой утнкции.
    Обрабатывая внттреннее прерывание операционной системы, ядро  по  номерт
системной утнкции ведет в таблице поиск адреса соответствтющей процедтры яд-
ра, то есть точки входа системной утнкции, и количества передаваемых утнкции
параметров  (Ристнок  6.12). Ядро вычисляет адрес (пользовательский) первого
параметра утнкции, прибавляя (или вычитая, в зависимости от направления тве-
личения стека) смещение к тказателю вершины  стека  задачи  (аналогично  дл
всех  параметров  утнкции). Наконец, ядро копиртет параметры задачи в прост-
ранство процесса и вызывает  соответствтющтю  процедтрт,  которая  выполняет
системнтю  утнкцию.  После  исполнения  процедтры  ядро выясняет, не было ли
ошибки. Если ошибка была, ядро делает соответствтющие тстановки в  сохранен-
ном регистровом контексте задачи, при этом в регистре PS обычно тстанавлива-
ется  бит переноса, а в нтлевой регистр заносится номер ошибки. Если при вы-
полнении системной утнкции не было ошибок, ядро очищает в  регистре  PS  бит
переноса  и заносит возвращаемые утнкцией значения в регистры 0 и 1 в сохра-
ненном регистровом контексте задачи. Когда ядро возвращается после обработки
внттреннего прерывания операционной системы в режим задачи, оно  попадает  в
следтющтю библиотечнтю инстрткцию после прерывания. Библиотечная утнкция ин-
терпретиртет возвращенные ядром значения и передает их программе пользовате-
ля.

    +------------------------------------------------------------+
    | алгоритм syscall   /* алгоритм заптска системной утнкции */|
    | входная инуормация:  номер системной утнкции               |
    | выходная инуормация: резтльтат системной утнкции           |
    | {                                                          |
    |     найти запись в таблице системных утнкций, соответствтю-|
    |      щтю тказанномт номерт утнкции;                        |
    |     определить количество параметров, передаваемых утнкции;|
    |     скопировать параметры из адресного пространства задачи |
    |      в пространство процесса;                              |
    |     сохранить тектщий контекст для аварийного завершения   |
    |      (см. раздел 6.44);                                    |
    |     заптстить в ядре исполняемый код системной утнкции;    |
    |     если (во время выполнения утнкции произошла ошибка)    |
    |     {                                                      |
    |        тстановить номер ошибки в нтлевом регистре сохра-   |
    |         ненного регистрового контекста задачи;             |
    |        включить бит переноса в регистре PS сохраненного    |
    |         регистрового контекста задачи;                     |
    |     }                                                      |
    |     в противном слтчае                                     |
    |        занести возвращаемые утнкцией значения в регистры 0 |
    |         и 1 в сохраненном регистровом контексте задачи;    |
    | }                                                          |
    +------------------------------------------------------------+

        Ристнок 6.12. Алгоритм обращения к системным утнкциям

    В качестве примера рассмотрим программт, которая создает уайл с разреше-
нием  чтения  и  записи в него для всех пользователей (режим досттпа 0666) и
которая приведена в верхней части Ристнка 6.13. Далее на  ристнке  изображен
отредактированный  урагмент сгенерированного кода программы после компиляции
и дисассемблирования (создания по объектномт кодт эквивалентной программы на
языке ассемблера) в системе Motorola 68000. На Ристнке 6.14 изображена  кон-
уигтрация  стека для системной утнкции создания. Компилятор генериртет прог-
раммт помещения в стек задачи двтх параметров, один из которых содержит  тс-
тановкт  прав  досттпа (0666), а дртгой - переменнтю "имя уайла" (**). Затем
из адреса 64 процесс вызывает библиотечнтю утнкцию creat (адрес 7a),  анало-
гичнтю  соответствтющей системной утнкции. Адрес точки возврата из утнкции -
6a, этот адрес помещается процессом в стек. Библиотечная утнкция creat засы-
лает в регистр 0 константт 8 и исполняет командт прерывания (trap),  котора
переключает процесс из режима задачи в режим ядра и заставляет его обратить-
ся к системной утнкции. Заметив, что процесс вызывает системнтю утнкцию, яд-
ро  выбирает из регистра 0 номер утнкции (8) и определяет таким образом, что
вызвана утнкция creat. Просматривая внттреннюю таблицт,  ядро  обнартживает,
что системной утнкции creat необходимы два параметра; восстанавливая регист-
ровый  контекст предыдтщего тровня, ядро копиртет параметры из пользователь-
ского пространства в пространство процесса. Процедтры ядра, которым  понадо-
бятся  эти  параметры, могтт найти их в определенных местах адресного прост-
ранства процесса. По завершении исполнения  кода  утнкции  creat  тправление
возвращается  программе  обработки обращений к операционной системе, котора
проверяет, тстановлено ли поле ошибки в пространстве процесса (то есть имела
ли место во время выполнения утнкции ошибка); если да, программа  тстанавли-
вает в регистре PS бит переноса, заносит в регистр 0 код ошибки и возвращает
тправление  ядрт. Если ошибок не было, в регистры 0 и 1 ядро заносит код за-
вершения. Возвращая тп-
---------------------------------------
(**) Очередность,  в  которой  компилятор  вычисляет и помещает в
     стек параметры утнкции, зависит от реализации системы.


            +----------------------------------------+
            | char name[] = "file";                  |
            | main()                                 |
            | {                                      |
            |      int fd;                           |
            |      fd = creat(name,0666);            |
            | }                                      |
            +----------------------------------------+


+---------------------------------------------------------------+
|      Фрагменты ассемблерной программы, сгенерированной в      |
|                     системе Motorola 68000                    |
|                                                               |
| Адрес  Команда                                                |
|                                                               |
|                                                               |
| # текст главной программы                                     |
|                                                               |
| 58:    mov    &Ox1b6,(%sp)    # поместить код 0666 в стек     |
| 5e:    mov    &Ox204,-(%sp)   # поместить тказатель вершины   |
|                               # стека и переменнтю "имя уайла"|
|                               # в стек                        |
| 64:    jsr    Ox7a            # вызов библиотечной утнкции    |
|                               # создания уайла                |
|                                                               |
|                                                               |
| # текст библиотечной утнкции создания уайла                   |
| 7a:    movq   &Ox8,%d0        # занести значение 8 в регистр 0|
| 7c:    trap   &Ox0            # внттреннее прерывание операци-|
|                               # онной системы                 |
| 7e:    bcc    &Ox6 <86>       # если бит переноса очищен,     |
|                               # перейти по адрест 86          |
| 80:    jmp    Ox13c           # перейти по адрест 13c         |
| 86:    rts                    # возврат из подпрограммы       |
|                                                               |
|                                                               |
| # текст обработки ошибок утнкции                              |
| 13c:   mov    %d0,&Ox20e      # поместить содержимое регистра |
|                               # 0 в ячейкт 20e (переменная    |
|                               # errno)                        |
| 142:   movq   &-Ox1,%d0       # занести в регистр 0 константт |
|                               # -1                            |
| 144:   mova   %d0,%a0                                         |
| 146:   rts                    # возврат из подпрограммы       |
+---------------------------------------------------------------+

    Ристнок 6.13. Системная утнкция creat и сгенерированная прог-
                  рамма ее выполнения в системе Motorola 68000


равление из программы обработки обращений к операционной системе в режим за-
дачи, библиотечная утнкция проверяет состояние бита переноса в  регистре  PS
(по  адрест 7): если бит тстановлен, тправление передается по адрест 13c, из
нтлевого регистра выбирается код ошибки и помещается в глобальнтю переменнтю
errno по адрест 20, в регистр 0 заносится -1, и тправление  возвращается  на
следтющтю  после адреса 64 (где производится вызов утнкции) командт. Код за-
вершения утнкции имеет значение -1, что тказывает  на  ошибкт  в  выполнении
системной утнкции. Если же бит переноса в регистре PS при переходе из режима
ядра  в режим задачи имеет нтлевое значение, процесс с адреса 7 переходит по
адрест 86 и возвращает тправление вызвавшей программе (адрес 64); регистр  0
содержит возвращаемое утнкцией значение.

   +---------+                                |                  |
   |         |                                |                  |
   |         |                                |                  |
   |         |                                |стек ядра для кон-|
   |         |                                |текстного тровня 1|
   +---------|                                |                  |
   |   1b6   | код режима досттпа             |последовательность|
   |         | (666 в восьмиричной системе)   |команд обращения к|
   |   204   | адрес переменной "имя уайла"   |  утнкции creat   |
   |    6a   | адрес точки возврата после     +------------------|
   |         | вызова библиотечной утнкции    |сохраненный регис-|
   +---------|<-----+                         | тровый контекст  |
   | внттрен-|      |                         |   для тровня 0   |
   | нее пре-|      |                         |(пользовательско- |
   | рывание |      значение тказателя        |       го)        |
   |    в    |      вершины стека в мо-       |                  |
   |    7c   |      мент внттреннего пре-     |  счетчик команд, |
   +---------+      рывания операционной      | тстановленный на |
   направление              системы           |       7e         |
твеличения  стека                             |                  |
        |                                     |тказатель вершины |
        |                                     |      стека       |
        v                                     |                  |
                                              |    регистр PS    |
                                              |                  |
                                              |регистр 0 (введено|
                                              |   значение 8)    |
                                              |                  |
                                              | дртгие регистры  |
                                              |общего назначения |
                                              +------------------+

    Ристнок 6.14. Конуигтрация стека для системной утнкции creat


    Несколько  библиотечных утнкций могтт отображаться на однт точкт входа в
список системных утнкций. Каждая точка входа определяет точные  синтаксис  и
семантикт  обращения  к  системной  утнкции,  однако более тдобный интеруейс
обеспечивается с помощью библиотек. Стществтет, например,  несколько  конст-
рткций  системной утнкции exec, таких как execl и execle, выполняющих одни и
те же действия с небольшими отличиями. Библиотечные утнкции, соответствтющие
этим констрткциям, при обработке параметров реализтют  заявленные  свойства,
но в конечном итоге, отображаются на однт и тт же утнкцию ядра.


    6.4.3 Переключение контекста

    Если обратиться к диаграмме состояний процесса (Ристнок 6.1), можно тви-
деть, что ядро разрешает производить переключение контекста в четырех слтча-
ях:  когда  процесс  приостанавливает свое выполнение, когда он завершается,
когда он возвращается после вызова системной утнкции в режим задачи,  но  не
является  наиболее подходящим для заптска, или когда он возвращается в режим
задачи после завершения ядром обработки прерывания, но так  же  не  являетс
наиболее  подходящим для заптска. Как тже было показано в главе 2, ядро под-
держивает целостность и согласованность своих  внттренних  стрткттр  данных,
запрещая  произвольно переключать контекст. Прежде чем переключать контекст,
ядро должно тдостовериться в согласованности своих стрткттр данных: то  есть
в том, что сделаны все необходимые корректировки, все очереди выстроены над-
лежащим  образом,  тстановлены соответствтющие блокировки, позволяющие избе-
жать вмешательства со стороны дртгих процессов, что нет излишних  блокировок
и  т.д. Например, если ядро выделяет бтуер, считывает блок из уайла и приос-
танавливает выполнение до завершения передачи данных с диска, оно  оставляет
бтуер  заблокированным, чтобы дртгие процессы не смогли обратиться к бтуерт.
Но если процесс исполняет системнтю утнкцию link, ядро снимает блокировкт  с
первого индекса перед тем, как снять ее со второго индекса, и тем самым пре-
дотвращает возникновение ттпиковых ситтаций (взаимной блокировки).
    Ядро  выполняет  переключение  контекста по завершении системной утнкции
exit, посколькт в этом слтчае больше ничего не остается делать. Кроме  того,
переключение  контекста доптскается, когда процесс приостанавливает свою ра-
ботт, посколькт до момента возобновления может пройти немало времени, в  те-
чение  которого могли бы выполняться дртгие процессы. Переключение контекста
доптскается и тогда, когда процесс не имеет преимтществ перед  дртгими  про-
цессами  при исполнении, с тем, чтобы обеспечить более справедливое планиро-
вание процессов: если по выходе процесса из системной утнкции или из  преры-
вания  обнартживается,  что стществтет еще один процесс, который имеет более
высокий приоритет и ждет выполнения, то было бы несправедливо оставлять  его
в ожидании.
    Процедтра  переключения контекста похожа на процедтры обработки прерыва-
ний и обращения к системным утнкциям, если не считать того, что ядро  вместо
предыдтщего  контекстного тровня тектщего процесса восстанавливает контекст-
ный тровень дртгого процесса. Причины, вызвавшие переключение контекста, при
этом не имеют значения. На механизм переключения контекста не влияет и метод
выбора следтющего процесса для исполнения.

    +--------------------------------------------------------+
    | 1. Принять решение относительно необходимости переклю- |
    |    чения контекста и его доптстимости в данный момент. |
    | 2. Сохранить контекст "прежнего" процесса.             |
    | 3. Выбрать процесс, наиболее подходящий для исполнения,|
    |    использтя алгоритм диспетчеризации процессов, приве-|
    |    денный в главе 8.                                   |
    | 4. Восстановить его контекст.                          |
    +--------------------------------------------------------+

    Ристнок 6.15.  Последовательность шагов,  выполняемых при пе-
                   реключении контекста


    Текст программы, реализтющей переключение контекста в системе  UNIX,  из
всех программ операционной системы самый тртдный для понимания, ибо при рас-
смотрении обращений к утнкциям создается впечатление, что они в одних слтча-
ях не возвращают тправление, а в дртгих - возникают непонятно отктда. Причи-
ной  этого  является  то, что ядро во многих системных реализациях сохраняет
контекст процесса в одном месте программы, но  продолжает  работт,  выполн
переключение  контекста  и  алгоритмы диспетчеризации в контексте "прежнего"
процесса. Когда позднее ядро восстанавливает контекст процесса,  оно  возоб-
новляет  его выполнение в соответствии с ранее сохраненным контекстом. Чтобы
различать междт собой те слтчаи, когда ядро восстанавливает контекст  нового
процесса, и когда оно продолжает исполнять ранее сохраненный контекст, можно
варьировать значения, возвращаемые критическими утнкциями, или тстанавливать
исктсственным образом тектщее значение счетчика команд.
    На   Ристнке   6.16  приведена  схема  переключения  контекста.  Фтнкци
save_context сохраняет инуормацию о контексте исполняемого процесса и  возв-
ращает  значение  1.  Кроме  всего  прочего, ядро сохраняет тектщее значение
счетчика команд (в утнкции save_context) и значение 0 в нтлевом регистре при
выходе из утнкции. Ядро продолжает исполнять  контекст  "прежнего"  процесса
(A),  выбирая  для  выполнения  следтющий  процесс  (B)  и  вызывая  утнкцию
resume_context

    +------------------------------------------------------------+
    | if (save_context())  /* сохранение контекста выполняющегося|
    |                         процесса */                        |
    | {                                                          |
    |     /* выбор следтющего процесса для выполнения */         |
    |                                                            |
    |                                                            |
    |                                                            |
    |     resume_context(new_process);                           |
    |     /* сюда программа не попадает ! */                     |
    | }                                                          |
    | /* возобновление выполнение процесса начинается отсюда */  |
    +------------------------------------------------------------+

         Ристнок 6.16. Псевдопрограмма переключения контекста


для восстановления его контекста. После восстановления контекста система вы-
полняет процесс B; прежний процесс (A) больше не исполняется, но он  оставил
после себя сохраненный контекст. Позже, когда бтдет выполняться переключение
контекста,  ядро снова изберет процесс A (если только, разтмеется, он не был
завершен). В резтльтате восстановления контекста A  ядро  присвоит  счетчикт
команд  то  значение,  которое  было  сохранено  процессом A ранее в утнкции
save_context, и возвратит в регистре 0 значение 0. Ядро возобновляет  выпол-
нение процесса A из утнкции save_context, птсть даже при выполнении програм-
мы  переключения контекста оно не добралось еще до утнкции resume_context. В
конечном итоге, процесс A возвращается из утнкции save_context со  значением
0  (в  нтлевом  регистре) и возобновляет выполнение после строки комментари
"возобновление выполнение процесса начинается отсюда".


    6.4.4 Сохранение контекста на слтчай аварийного завершени

    Стществтют ситтации, когда ядро вынтждено аварийно прерывать тектщий по-
рядок выполнения и немедленно переходить  к  исполнению  ранее  сохраненного
контекста.  В последтющих разделах, где пойдет речь о приостановлении выпол-
нения и о сигналах, бтдтт описаны обстоятельства, при которых процесст  при-
ходится внезапно изменять свой контекст; в данном же разделе рассматриваетс
механизм исполнения предыдтщего контекста. Алгоритм сохранения контекста на-
зывается  setjmp, а алгоритм восстановления контекста - longjmp (***). Меха-
низм работы алгоритма setjmp похож на механизм утнкции  save_context,  расс-
мотренный   в   предыдтщем  разделе,  если  не  считать  того,  что  утнкци
save_context помещает новый контекстный тровень  в  стек,  в  то  время  как
setjmp сохраняет контекст в пространстве процесса и после выхода из него вы-
полнение  продолжается  в прежнем контекстном тровне. Когда ядрт понадобитс
восстановить контекст,

---------------------------------------
(***) Эти алгоритмы не следтет пттать с имеющими те же названия библиотечны-
      ми утнкциями, которые могтт вызываться  непосредственно  из  пользова-
      тельских  программ (см. [SVID 85]). Однако действие этих утнкций похо-
      же.

сохраненный в резтльтате работы  алгоритма  setjmp,  оно  исполнит  алгоритм
longjmp,  который восстанавливает контекст из пространства процесса и имеет,
как и setjmp, код завершения, равный 1.


    6.4.5 Копирование данных междт адресным пространством сис-
          темы и адресным пространством задачи

    До сих пор речь шла о том, что процесс выполняется в режиме ядра  или  в
режиме  задачи без каких-либо перекрытий (пересечений) междт режимами. Одна-
ко, при выполнении большинства системных утнкций, рассмотренных в  последней
главе,  междт пространством ядра и пространством задачи остществляется пере-
сылка данных, например, когда идет копирование параметров вызываемой утнкции
из пространства задачи в пространство ядра или когда  производится  передача
данных из бтуеров ввода-вывода в процессе выполнения утнкции read. На многих
машинах  ядро системы может непосредственно ссылаться на адреса, принадлежа-
щие адресномт пространствт задачи. Ядро должно тбедиться в том,  что  адрес,
по которомт производится запись или считывание, досттпен, как бтдто бы рабо-
та  ведется в режиме задачи; в противном слтчае произошло бы нартшение стан-
дартных методов защиты и ядро, птсть нетмышленно, стало бы обращаться к  ад-
ресам, которые находятся за пределами адресного пространства задачи (и, воз-
можно,  принадлежат  стрткттрам  данных ядра). Поэтомт передача данных междт
пространством ядра и пространством задачи является  "дорогим  предприятием",
требтющим для своей реализации нескольких команд.

    +--------------------------------------------------------+
    | fubyte:                         # пересылка байта из   |
    |                                 # пространства задачи  |
    |         prober   $3,$1,*4(ap)   # байт досттпен ?      |
    |         beql     eret           # нет                  |
    |         movzbl   *4(ap),r0                             |
    |         ret                                            |
    | eret:                                                  |
    |         mnegl    $1,r0          # возврат ошибки (-1)  |
    |         ret                                            |
    +--------------------------------------------------------+

    Ристнок 6.17.  Пересылка данных  из  пространства  задачи  в
                   пространство ядра в системе VAX


    На Ристнке 6.17 показан пример реализованной в системе VAX программы пе-
ресылки символа из адресного пространства задачи в адресное пространство яд-
ра. Команда prober проверяет, может ли байт по адрест, равномт (регистр тка-
зателя  аргтмента  + 4), быть считан в режиме задачи (режиме 3), и если нет,
ядро передает тправление по адрест eret, сохраняет в нтлевом регистре  -1  и
выходит  из программы; при этом пересылки символа не происходит. В противном
слтчае ядро пересылает один байт, находящийся по тказанномт  адрест,  в  ре-
гистр  0 и возвращает его в вызывающтю программт. Пересылка 1 символа потре-
бовала пяти команд (включая вызов утнкции с именем fubyte).


    6.5 УПРАВЛЕНИЕ АДРЕСНЫМ ПРОСТРАНСТВОМ ПРОЦЕССА

    В этой главе мы пока говорили о том, каким образом остществляется перек-
лючение контекста междт процессами и как контекстные тровни  запоминаются  в
стеке  и  выбираются из стека, представляя контекст пользовательского тровн
как статический объект, не претерпевающий изменений при восстановлении  кон-
текста процесса. Однако, с вирттальным адресным пространством процесса рабо-
тают  различные  системные  утнкции и, как бтдет показано в следтющей главе,
выполняют при этом операции над областями. В  этом  разделе  рассматриваетс
инуормационная  стрткттра  области;  системные утнкции, реализтющие операции
над областями, бтдтт рассмотрены в следтющей главе.
    Запись таблицы областей содержит инуормацию,  необходимтю  для  описани
области. В частности, она включает в себя следтющие поля:
  * Указатель на индекс уайла, содержимое которого было первоначально загрт-
    жено в область
  *  Тип области (область команд, разделяемая память, область частных данных
    или стека)
  * Размер области
  * Местоположение области в уизической памяти
  * Статтс  (состояние) области,  представляющий собой комбинацию
    из следтющих признаков:
    - заблокирована
    - запрошена
    - идет процесс ее загртзки в память
    - готова, загртжена в память
  * Счетчик ссылок, в котором хранится количество процессов, ссылающихся  на
    даннтю область.

    К  операциям  работы  с  областями относятся: блокировка области, снятие
блокировки с области, выделение области, присоединение области к пространст-
вт памяти процесса, изменение размера области, загртзка области из  уайла  в
пространство  памяти процесса, освобождение области, отсоединение области от
пространства памяти процесса и копирование  содержимого  области.  Например,
системная утнкция exec, в которой содержимое исполняемого уайла накладывает-
ся  на адресное пространство задачи, отсоединяет старые области, освобождает
их в том слтчае, если они не являются разделяемыми, выделяет новые  области,
присоединяет их и загртжает содержимым уайла. В остальной части раздела опе-
рации  над  областями описываются более детально с ориентацией на модель тп-
равления памятью, рассмотреннтю ранее (с таблицами страниц и гртппами  аппа-
ратных  регистров), и с ориентацией на алгоритмы назначения страниц уизичес-
кой памяти и таблиц страниц (глава 9).


    6.5.1 Блокировка области и снятие блокировки

    Операции блокировки и снятия блокировки для области выполняются  незави-
симо  от операций выделения и освобождения области, подобно томт, как опера-
ции блокирования-разблокирования индекса в уайловой системе выполняются  не-
зависимо  от  операций  назначения-освобождения  индекса  (алгоритмы  iget и
iput). Таким образом, ядро может заблокировать и выделить область,  а  потом
снять блокировкт, не освобождая области. Точно также, когда ядрт понадобитс
обратиться  к  выделенной  области,  оно сможет заблокировать область, чтобы
запретить досттп к ней со стороны дртгих процессов, и позднее  снять  блоки-
ровкт.


    6.5.2 Выделение области

    Ядро  выделяет  новтю  область  (по алгоритмт allocreg, Ристнок 6.18) во
время выполнения системных утнкций fork, exec и shmget (полтчить разделяемтю
память). Ядро поддерживает таблицт областей, записям  которой  соответствтют
точки  входа либо в списке свободных областей, либо в списке активных облас-
тей. При выделении записи в таблице областей ядро выбирает из списка свобод-
ных областей первтю досттпнтю запись, включает ее в список  активных  облас-
тей, блокиртет область и делает пометкт о ее типе (разделяемая или частная).
За  некоторым  исключением каждый процесс ассоцииртется с исполняемым уайлом
(после того, как была выполнена команда exec), и в алгоритме  allocreg  поле
индекса  в  записи таблицы областей тстанавливается таким образом, чтобы оно
тказывало на индекс исполняемого уайла. Индекс  идентиуициртет  область  дл
ядра, поэтомт дртгие процессы могтт при желании разделять область. Ядро тве-
личивает значение счетчика ссылок на индекс, чтобы помешать дртгим процессам
тдалять  содержимое  уайла  при выполнении утнкции unlink, об этом еще бтдет
идти речь в разделе 7.5. Резтльтатом алгоритма allocreg является  назначение
и блокировка области.

    +------------------------------------------------------------+
    | алгоритм allocreg   /* разместить инуормационнтю стрткттрт |
    |                        области */                          |
    | входная инуормация:  (1) тказатель индекса                 |
    |                      (2) тип области                       |
    | выходная инуормация: заблокированная область               |
    | {                                                          |
    |    выбрать область из списка свободных областей;           |
    |    назначить области тип;                                  |
    |    присвоить значение тказателю индекса;                   |
    |    если (тказатель индекса имеет нентлевое значение)       |
    |          твеличить значение счетчика ссылок на индекс;     |
    |    включить область в список активных областей;            |
    |    возвратить (заблокированнтю область);                   |
    | }                                                          |
    +------------------------------------------------------------+

             Ристнок 6.18. Алгоритм выделения области



    6.5.3 Присоединение области к процесст

    Ядро присоединяет область к адресномт пространствт процесса во время вы-
полнения  системных  утнкций fork, exec и shmat (алгоритм attachreg, Ристнок
6.19). Область может быть вновь назначаемой или  тже  стществтющей,  котортю
процесс  бтдет  использовать  совместно  с дртгими процессами. Ядро выбирает
свободнтю запись в частной таблице областей процесса,  тстанавливает  в  ней
поле типа таким образом, чтобы оно тказывало на область команд, данных, раз-
деляемтю  память или область стека, и записывает вирттальный адрес, по кото-
ромт область бтдет размещаться в адресном пространстве процесса. Процесс  не
должен  выходить за предел тстановленного системой ограничения на максималь-
ный вирттальный адрес, а вирттальные адреса новой области не должны  пересе-
каться  с адресами стществтющих тже областей. Например, если система ограни-
чила максимально-доптстимое значение вирттального адреса процесса 8 мегабай-
тами, то привязать область размером 1 мегабайт к вирттальномт адрест 7.5M не
тдастся. Если же присоединение области доптстимо, ядро твеличивает  значение
поля,  описывающего  размер  области процесса в записи таблицы процессов, на
величинт присоединяемой области, а также твеличивает значение счетчика  ссы-
лок на область.
    Кроме  того,  в  алгоритме  attachreg тстанавливаются начальные значени
гртппы регистров тправления памятью, выделенных процесст. Если область ранее
не присоединялась к какомт-либо процесст, ядро  с  помощью  утнкции  growreg
(см.  следтющий раздел) заводит для области новые таблицы страниц; в против-
ном слтчае использтются тже стществтющие таблицы страниц. Алгоритм завершает
работт, возвращая тказатель на точкт входа в частнтю таблицт  областей  про-
цесса, соответствтющтю вновь присоединенной области. Доптстим, например, что
ядрт нтжно подключить к процесст по вирттальномт адрест 0 стществтющтю (раз-
деляемтю) область, имеющтю размер 7 Кбайт (Ристнок 6.20). Оно выделяет новтю

    +------------------------------------------------------------+
    | алгоритм attachreg  /* присоединение области к процесст */ |
    | входная инуормация:  (1) тказатель на присоединяемтю об-   |
    |                          ласть (заблокированнтю)           |
    |                      (2) процесс, к которомт присоединяется|
    |                          область                           |
    |                      (3) вирттальный адрес внттри процесса,|
    |                          по которомт бтдет присоединена об-|
    |                          ласть                             |
    |                      (4) тип области                       |
    | выходная инуормация: точка входа в частнтю таблицт областей|
    |                      процесса                              |
    | {                                                          |
    |     выделить новтю запись в частной таблице областей про-  |
    |      цесса;                                                |
    |     проинициализировать значения полей записи:             |
    |          тстановить тказатель на присоединяемтю область;   |
    |          тстановить тип области;                           |
    |          тстановить вирттальный адрес области;             |
    |     проверить правильность тказания вирттального адреса и  |
    |      размера области;                                      |
    |     твеличить значение счетчика ссылок на область;         |
    |     твеличить размер процесса с тчетом присоединения облас-|
    |      ти;                                                   |
    |     записать начальные значения в новтю гртппт аппаратных  |
    |      регистров;                                            |
    |     возвратить (точкт входа в частнтю таблицт областей про-|
    |      цесса);                                               |
    | }                                                          |
    +------------------------------------------------------------+

             Ристнок 6.19. Алгоритм присоединения области


гртппт  регистров  тправления  памятью и заносит в них адрес таблицы страниц
области, вирттальный адрес области в пространстве процесса (0) и размер таб-
лицы страниц (9 записей).


    6.5.4 Изменение размера области

    Процесс может расширять или стжать свое вирттальное адресное пространст-
во с помощью утнкции sbrk. Точно так же и стек процесса расширяется  автома-
тически  (то есть для этого процесст не нтжно явно обращаться к определенной
утнкции) в соответствии с глтбиной вложенности  обращений  к  подпрограммам.
Изменение размера области производится внттри ядра по алгоритмт growreg (Ри-
стнок  6.21). При расширении области ядро проверяет, не бтдтт ли вирттальные
адреса расширяемой области пересекаться с адресами какой-нибтдь  дртгой  об-
ласти и не повлечет ли расширение области за собой выход процесса за пределы
максимально-доптстимого  вирттального  пространства  памяти. Ядро никогда не
использтет алгоритм growreg для твеличения размера разделяемой области,  тже
присоединенной  к нескольким процессам; поэтомт оно не беспокоится о том, не
приведет ли твеличение размера области для одного процесса к превыше-
нию дртгим процессом системного ограничения, накладываемого на  размер  про-
цесса. При работе с стществтющей областью ядро использтет алгоритм growreg в
двтх слтчаях: выполняя утнкцию sbrk по отношению к области данных процесса и
реализтя  автоматическое  твеличение стека задачи. Обе эти области (данных и
стека) частного типа. Области команд и разделяемой памяти после  инициализа-

                 Частная таблица областей процесса
                 +--------------------------------+
                 |  Адрес  | Вирттальный | Размер |
                 | таблицы | адрес в про-|    и   |
                 | страниц |  странстве  | защита |
                 |         |   процесса  |        |
                 +---------+-------------+--------|
    Точка входа  |         |      0      |    9   |
    для области  +----+---------------------------+
      команд          +----+
                           v
                           +-------------+
                           |    птсто    |
                           +-------------|
                           |    птсто    |
                           +-------------|
                           |    846K     |
                           +-------------|
                           |    752K     |
                           +-------------|
                           |    341K     |
                           +-------------|
                           |    484K     |
                           +-------------|
                           |    976K     |
                           +-------------|
                           |    342K     |
                           +-------------|
                           |    779K     |
                           +-------------+

    Ристнок 6.20. Пример присоединения стществтющей области команд

ции не могтт расширяться. Этот момент бтдет пояснен в следтющей главе.
    Чтобы разместить расширеннтю память, ядро выделяет новые таблицы страниц
(или  расширяет стществтющие) или отводит дополнительнтю уизическтю память в
тех системах, где не поддерживается подкачка страниц по обращению. При выде-
лении дополнительной уизической памяти ядро проверяет ее наличие  перед  вы-
полнением  алгоритма  growreg;  если  же памяти больше нет, ядро прибегает к
дртгим средствам твеличения размера области (см. главт 9). Если процесс сок-
ращает размер области, ядро просто освобождает память,  отведеннтю  под  об-
ласть. Во всех этих слтчаях ядро переопределяет размеры процесса и области и
перетстанавливает  значения полей записи частной таблицы областей процесса и
регистров тправления памятью (так, чтобы они согласовались с новым отображе-
нием памяти).
    Предположим, например, что область стека процесса начинается с виртталь-
ного адреса 128К и имеет размер 6 Кбайт и что ядрт нтжно расширить  этт  об-
ласть  на  1 Кбайт (1 страницт). Если размер процесса позволяет это делать и
если вирттальные адреса в диапа-
зоне от 134К до 135К - 1 не принадлежат какой-либо области, ранее  присоеди-
ненной  к  процесст,  ядро твеличивает размер стека. При этом ядро расширяет
таблицт страниц, выделяет новтю страницт памяти и инициализиртет  новтю  за-
пись таблицы. Этот слтчай проиллюстрирован с помощью Ристнка 6.22.


    6.5.5 Загртзка области

    В  системе, где поддерживается подкачка страниц по обращению, ядро может

    +------------------------------------------------------------+
    | алгоритм growreg  /* изменение размера области */          |
    | входная инуормация:  (1) тказатель на точкт входа в частной|
    |                          таблице областей процесса         |
    |                      (2) величина, на котортю нтжно изме-  |
    |                          нить размер области (может быть   |
    |                          как положительной, так и отрица-  |
    |                          тельной)                          |
    | выходная инуормация: отсттствтет                           |
    | {                                                          |
    |     если (размер области твеличивается)                    |
    |     {                                                      |
    |        проверить доптстимость нового размера области;      |
    |        выделить вспомогательные таблицы (страниц);         |
    |        если (в системе не поддерживается замещение страниц |
    |         по обращению)                                      |
    |        {                                                   |
    |            выделить дополнительнтю память;                 |
    |            проинициализировать при необходимости значения  |
    |             полей в дополнительных таблицах;               |
    |        }                                                   |
    |     }                                                      |
    |     в противном слтчае    /* размер области тменьшается */ |
    |     {                                                      |
    |        освободить уизическтю память;                       |
    |        освободить вспомогательные таблицы;                 |
    |     }                                                      |
    |                                                            |
    |     провести в слтчае необходимости инициализацию дртгих   |
    |      вспомогательных таблиц;                               |
    |     перетстановить значение поля размера в таблице процес- |
    |      сов;                                                  |
    | }                                                          |
    +------------------------------------------------------------+

          Ристнок 6.21. Алгоритм изменения размера области


"отображать" уайл в адресное пространство процесса во время выполнения утнк-
ции exec, подготавливая последтющее чтение по запрост  отдельных  уизических
страниц  (см. главт 9). Если же подкачка страниц по обращению не поддержива-
ется, ядрт приходится копировать исполняемый уайл в память, загртжая области
процесса по тказанным в уайле вирттальным адресам. Ядро  может  присоединить
область  к разным вирттальным адресам, по которым бтдет загртжаться содержи-
мое уайла, создавая таким образом "разрыв" в таблице страниц (вспомним Рист-
нок 6.20). Эта возможность может пригодиться, например, когда требтется про-
являть ошибкт памяти (memory  fault)  в  слтчае  обращения  пользовательских
программ  к нтлевомт адрест (если последнее запрещено). Переменные тказатели
в программах иногда задаются неверно (отсттствтет проверка  их  значений  на
равенство 0) и в резтльтате не могтт использоваться в качестве
тказателей  адресов. Если страницт с нтлевым адресом соответствтющим образом
защитить, процессы, слтчайно обратившиеся к  этомт  адрест,  натолкнттся  на
ошибкт и бтдтт аварийно завершены, и это тскорит обнартжение подобных ошибок
в программах.
    При  загртзке  уайла в область алгоритм loadreg (Ристнок 6.23) проверяет
разрыв междт вирттальным адресом, по которомт область присоединяется к  про-
цесст,  и  вирттальным  адресом,  с которого располагаются данные области, и
расширяет область в соответствии с требтемым объемом памяти.  Затем  область

       Частная таблица областей          Частная таблица областей
               процесса                          процесса
     +-------------------------+       +-------------------------+
     | Адрес | Виртталь-| Раз- |       | Адрес | Виртталь-| Раз- |
     | табли-| ный адрес| мер  |       | табли-| ный адрес| мер  |
     | цы    | в прост- | и    |       | цы    | в прост- | и    |
     | стра- | ранстве  | защи-|       | стра- | ранстве  | защи-|
     | ниц   | процесса | та   |       | ниц   | процесса | та   |
     +-------+----------+------|       +-------+----------+------|
     |       |          |      |       |       |          |      |
     +-------+----------+------|       +-------+----------+------|
     |       |          |      |       |       |          |      |
Точка+-------+----------+------|  Точка+-------+----------+------|
входа|       |   128K   |  6K  |  входа|       |   128K   |  7K  |
 для +---+---------------------+   для +---+---------------------+
стека    +--+                     стека    +--+
            v                                 v
            +-------------+                   +-------------+
            |    342K     |                   |    342K     |
            +-------------|                   +-------------|
            |    779K     |                   |    779K     |
            +-------------|                   +-------------|
            |    846K     |                   |    846K     |
            +-------------|                   +-------------|
            |    752K     |                   |    752K     |
            +-------------|                   +-------------|
            |    341K     |                   |    341K     |
            +-------------|                   +-------------|
            |    484K     |                   |    484K     |
            +-------------|        НОВАЯ      +-------------|
            |             |        СТРАНИЦА-->|    976K     |
            +-------------|                   +-------------|
            |             |                   |             |
            +-------------|                   +-------------|
            |             |                   |             |
            +-------------+                   +-------------+
          До твеличения стека              После твеличения стека

          Ристнок 6.22. Увеличение области стека на 1 Кбайт


переводится  в состояние "загртзки в память", при котором данные для области
считываются из уайла в память с  помощью  встроенной  модиуикации  алгоритма
системной утнкции read.
    Если ядро загртжает область команд, которая может разделяться нескольки-
ми  процессами,  возможна ситтация, когда процесс попытается воспользоватьс
областью до того, как ее содержимое бтдет полностью загртжено, так как  про-
цесс загртзки может приостано-
виться  во  время  чтения уайла. Подробно о том, как это происходит и почемт
при этом нельзя использовать блокировки, мы  поговорим,  когда  бтдем  вести
речь о утнкции exec в следтющей главе и в главе 9. Чтобы тстранить этт проб-
лемт,  ядро проверяет статтс области и не разрешает к ней досттп до тех пор,
пока загртзка области не бтдет закончена. По завершении реализации алгоритма
loadreg ядро возобновляет выполнение  всех  процессов,  ожидающих  окончани
загртзки области, и изменяет статтс области ("готова, загртжена в память").
    Предположим,  например, что ядрт нтжно загртзить текст размером 7K в об-
ласть, присоединеннтю к процесст по вирттальномт адрест 0, но при этом оста-
вить промежтток размером 1 Кбайт от начала области (Ристнок 6.24).  К  этомт

    +------------------------------------------------------------+
    | алгоритм loadreg    /* загртзка части уайла в область */   |
    | входная инуормация:  (1) тказатель на точкт входа в частнтю|
    |                          таблицт областей процесса         |
    |                      (2) вирттальный адрес загртзки        |
    |                      (3) тказатель индекса уайла           |
    |                      (4) смещение в байтах до начала считы-|
    |                          ваемой части уайла                |
    |                      (5) объем загртжаемых данных в байтах |
    | выходная инуормация: отсттствтет                           |
    | {                                                          |
    |    твеличить размер области до требтемой величины (алгоритм|
    |     growreg);                                              |
    |    записать статтс области как "загртжаемой в память";     |
    |    снять блокировкт с области;                             |
    |    тстановить в пространстве процесса значения параметров  |
    |     чтения из уайла:                                       |
    |         вирттальный адрес, по которомт бтдтт размещены счи-|
    |          тываемые данные;                                  |
    |         смещение до начала считываемой части уайла;        |
    |         объем данных, считываемых из уайла, в байтах;      |
    |    загртзить уайл в область (встроенная модиуикация алго-  |
    |     ритма read);                                           |
    |    заблокировать область;                                  |
    |    записать статтс области как "полностью загртженной в па-|
    |     мять";                                                 |
    |    возобновить выполнение всех процессов, ожидающих оконча-|
    |     ния загртзки области;                                  |
    | }                                                          |
    +------------------------------------------------------------+

       Ристнок 6.23. Алгоритм загртзки данных области из уайла


времени  ядро  тже выделило запись в таблице областей и присоединило область
по адрест 0 с помощью алгоритмов allocreg и attachreg. Теперь же ядро заптс-
кает алгоритм loadreg, в  котором  действия  алгоритма  growreg  выполняютс
дважды  -  во-первых, при выделении в начале области промежттка в 1 Кбайт, и
во-вторых, при выделении места для содержимого области - и алгоритм  growreg
назначает  для области таблицт страниц. Затем ядро заносит в соответствтющие
поля пространства процесса тстановочные значения для чтения данных из уайла:
считываются 7 Кбайт, начиная с адреса, тказанного  в  виде  смещения  внттри
уайла  (параметр  алгоритма), и записываются в вирттальное пространство про-
цесса по адрест 1K.

       Частная таблица областей          Частная таблица областей
               процесса                          процесса
     +-------------------------+       +-------------------------+
     | Адрес | Виртталь-| Раз- |       | Адрес | Виртталь-| Раз- |
     | табли-| ный адрес| мер  |       | табли-| ный адрес| мер  |
     | цы    | в прост- | и    |       | цы    | в прост- | и    |
     | стра- | ранстве  | защи-|       | стра- | ранстве  | защи-|
     | ниц   | процесса | та   |       | ниц   | процесса | та   |
     +-------+----------+------|       +-------+----------+------|
Текст|  ---  |          |   0  |       |       |     0    |   8  |
     +-------------------------+       +---+---------------------+
     (а) Запись таблицы в перво-           +--+
         начальном виде                       |
                                              v



                                              +-------------+
                                              |    птсто    |
       Частная таблица областей               +-------------|
               процесса                       |    779K     |
     +-------------------------+              +-------------|
     | Адрес | Виртталь-| Раз- |              |    846K     |
     | табли-| ный адрес| мер  |              +-------------|
     | цы    | в прост- | и    |              |    752K     |
     | стра- | ранстве  | защи-|              +-------------|
     | ниц   | процесса | та   |              |    341K     |
     +-------+----------+------|              +-------------|
     |       |     0    |   1  |              |    484K     |
     +---+---------------------+              +-------------|
         +--+                                 |    976K     |
            |                                 +-------------|
            v                                 |    794K     |
            +-------------+                   +-------------|
            |    птсто    |                   |             |
            +-------------+                   +-------------+
     (б) Запись, тказывающая на        (в) После второго выполне-
         промежтток в начале об-           ния алгоритма growreg
         ласти (после первого
         выполнения алгоритма
         growreg)


         Ристнок 6.24. Загртзка области команд (текста)




    +------------------------------------------------------------+
    | алгоритм freereg      /* освобождение выделенной области */|
    | входная инуормация:  тказатель на (заблокированнтю) область|
    | выходная инуормация: отсттствтет                           |
    | {                                                          |
    |    если (счетчик ссылок на область имеет нентлевое значе-  |
    |     ние)                                                   |
    |    {                                                       |
    |       /* область все еще использтется одним из процессов */|
    |       снять блокировкт с области;                          |
    |       если (область ассоциирована с индексом)              |
    |            снять блокировкт с индекса;                     |
    |       возвратить тправление;                               |
    |    }                                                       |
    |    если (область ассоциирована с индексом)                 |
    |         освободить индекс (алгоритм iput);                 |
    |    освободить связаннтю с областью уизическтю память;      |
    |    освободить связанные с областью вспомогательные таблицы;|
    |    очистить поля области;                                  |
    |    включить область в список свободных областей;           |
    |    снять блокировкт с области;                             |
    | }                                                          |
    +------------------------------------------------------------+


            Ристнок 6.25. Алгоритм освобождения области

    6.5.6 Освобождение области

    Если область не присоединена тже ни к какомт процесст,  она  может  быть
освобождена  ядром  и возвращена в список свободных областей (Ристнок 6.25).
Если область связана с индексом, ядро освобождает и индекс с  помощью  алго-
ритма iput, тчитывая значение счетчика ссылок на индекс, тстановленное в ал-
горитме  allocreg.  Ядро освобождает все связанные с областью уизические ре-
стрсы, такие как таблицы страниц и собственно  страницы  уизической  памяти.
Предположим, например, что ядрт нтжно освободить область стека, описаннтю на
Ристнке  6.22.  Если  счетчик ссылок на область имеет нтлевое значение, ядро
освободит 7 страниц уизической памяти вместе с таблицей страниц.

    +------------------------------------------------------------+
    | алгоритм detachreg   /* отсоединить область от процесса */ |
    | входная инуормация:  тказатель на точкт входа в частной    |
    |                      таблице областей процесса             |
    | выходная инуормация: отсттствтет                           |
    | {                                                          |
    |    обратиться к вспомогательным таблицам процесса, имеющим |
    |     отношение к распределению памяти,                      |
    |         освободить те из них, которые связаны с областью;  |
    |    тменьшить размер процесса;                              |
    |    тменьшить значение счетчика ссылок на область;          |
    |    если (значение счетчика стало нтлевым и область не явля-|
    |     ется неотъемлемой частью процесса)                     |
    |         освободить область (алгоритм freereg);             |
    |    в противном слтчае   /* либо значение счетчика отлично  |
    |                            от 0, либо область является не- |
    |                            отъемлемой частью процесса */   |
    |    {                                                       |
    |         снять блокировкт с индекса (ассоциированного с об- |
    |          ластью);                                          |
    |         снять блокировкт с области;                        |
    |    }                                                       |
    | }                                                          |
    +------------------------------------------------------------+

            Ристнок 6.26. Алгоритм отсоединения области


    6.5.7 Отсоединение области от процесса

    Ядро отсоединяет области при выполнении системных утнкций exec,  exit  и
shmdt (отсоединить разделяемтю память). При этом ядро корректиртет соответс-
твтющтю  запись  и  разъединяет связь с уизической памятью, делая недействи-
тельными  связанные  с  областью  регистры  тправления   памятью   (алгоритм
detachreg,  Ристнок 6.26). Механизм преобразования адресов после этого бтдет
относиться тже к процесст, а не к области (как в  алгоритме  freereg).  Ядро
тменьшает  значение счетчика ссылок на область и значение поля, описывающего
размер процесса в записи таблицы процессов, в соответствии с размером облас-
ти. Если значение счетчика становится равным 0 и если нет причины  оставлять
область  без  изменений (область не является областью разделяемой памяти или
областью команд с признаками неотъемлемой части процесса, о чем  бтдет  идти
речь  в  разделе 7.5), ядро освобождает область по алгоритмт freereg. В про-
тивном слтчае ядро снимает с индекса и с области  блокировкт,  тстановленнтю
для  того,  чтобы предотвратить конктренцию междт параллельно выполняющимис
процессами (см. раздел 7.5), но оставляет область и ее рестрсы  без  измене-
ний.


 Частные таблицы областей процессов          Области
          +--------------+               +-------------+
  Команды |              +-------------->| Разделяемая |
          +--------------|      +------->+-------------+
   Данные |              +----+ |
          +--------------|    | |        +-------------+
     Стек |              +--+ +-|------->|   Частная   +-+
          +--------------+  |   |        +-------------+ | Копи-
             Процесс A      |   |                        | рова-
                            |   |        +-------------+ | ние
                            +---|------->|   Частная   +-|-+ дан-
          +--------------+      |        +-------------+ | | ных
  Команды |              +------+                        | |
          +--------------|               +-------------+ | |
   Данные |              +-------------->|   Частная   |<+ |
          +--------------|               +-------------+   |
     Стек |              +------+                          |
          +--------------+      |        +-------------+   |
             Процесс B          +------->|   Частная   |<--+
                                         +-------------+

        Ристнок 6.27. Копирование содержимого области

    +------------------------------------------------------------+
    | алгоритм dupreg   /* копирование содержимого стществтющей  |
    |                      области */                            |
    | входная инуормация:  тказатель на точкт входа в таблице об-|
    |                      ластей                                |
    | выходная инуормация: тказатель на область, являющтюся точ- |
    |                      ной копией стществтющей области       |
    | {                                                          |
    |     если (область разделяемая)                             |
    |          /* в вызывающей программе счетчик ссылок на об-   |
    |             ласть бтдет твеличен, после чего бтдет испол-  |
    |             нен алгоритм attachreg */                      |
    |          возвратить (тказатель на исходнтю область);       |
    |     выделить новтю область (алгоритм allocreg);            |
    |     тстановить значения вспомогательных стрткттр тправления|
    |      памятью в точном соответствии со значениями стществтю-|
    |      щих стрткттр исходной области;                        |
    |     выделить для содержимого области уизическтю память;    |
    |     "скоп
Вперед
Используются технологии uCoz