Рис. 3.14. Организация таблицы отложенных вызовов
Алармы
Процесс может запросить ядро отправить сигнал по прошествии определенного интервала времени. Существуют три типа алармов — реального времени (real-time), профилирования (profiling) и виртуального времени (virtual time). С каждым из этих типов связан таймер интервала (interval timer, или itimer). Значение itimer уменьшается на единицу при каждом тике. Когда значение itimer достигает нуля, процессу отправляется соответствующий сигнал.
Указанные таймеры обладают следующими характеристиками:
ITIMER_REAL |
Этот таймер используется для отсчета реального времени. Когда значение таймера становится равным нулю, процессу отправляется сигнал SIGALRM. |
ITIMER_PROF |
Этот таймер уменьшается только когда процесс выполняется в режиме ядра или задачи. Когда значение таймера становится равным нулю, процессу отправляется сигнал SIGPROF. |
ITIMER_VIRT |
Этот таймер уменьшается только когда процесс выполняется в режиме задачи. Когда значение таймера становится равным нулю, процессу отправляется сигнал SIGVTALRM. |
В версиях BSD UNIX для установки таймеров всех трех типов используется системный вызов settimer(2), для которого значение таймера устанавливается в микросекундах[34]. Ядро системы преобразует это значение в тики, на основании которых и производится уменьшение таймера. Напомним, что тик является максимальным временным разрешением, которое может обеспечить система. В версиях System V для установки таймера реального времени используется вызов alarm(2), позволяющий указать интервал в секундах. UNIX SVR4 позволяет установить таймеры высокого разрешения с помощью системного вызова hrtsys(2), для которого время указывается в микросекундах. С помощью этого вызова также достигается совместимость с BSD, которая обеспечивается библиотечной функцией settimer(3). Аналогично, в BSD UNIX вызов alarm(3) реализован в виде библиотечной функции.
Не следует, однако, заблуждаться насчет высокого разрешения таймеров реального времени. На самом деле их точность может быть довольно низкой. Допустим, что значение таймера реального времени, установленного каким-либо процессом, достигло нуля. При этом ядро отправит этому процессу сигнал SIGALRM. Однако процесс сможет получить и обработать этот сигнал, только когда он будет выбран планировщиком и поставлен на выполнение. В зависимости от приоритета процесса и текущей загрузки системы это может привести к существенным задержкам и, как следствие, к неточностям определения временного интервала. Таймеры реального времени высокого разрешения обладают достаточной точностью лишь для больших интервалов времени или для высокоприоритетных процессов. Тем не менее и для таких процессов получение сигнала может быть задержано, если в текущий момент процесс выполняется в режиме ядра и не может быть приостановлен.
Два других типа таймера обладают более высокой точностью, поскольку не имеют отношения к реальному течению времени. Однако их точность для малых временных интервалов может определяться следующим фактором.
При обработке таймера процессу засчитывается тик целиком, даже если, предположим, процесс выполнялся лишь часть тика. Для временных интервалов порядка тика это может внести значительную погрешность.
Контекст процесса
Каждый процесс UNIX имеет контекст, под которым понимается вся информация, требуемая для описания процесса. Эта информация сохраняется, когда выполнение процесса приостанавливается, и восстанавливается, когда планировщик предоставляет процессу вычислительные ресурсы. Контекст процесса состоит из нескольких частей:
□ Адресное пространство процесса в режиме задачи. Сюда входят код, данные и стек процесса, а также другие области, например, разделяемая память или код и данные динамических библиотек.
□ Управляющая информация. Ядро использует две основные структуры данных для управления процессом — proc и user. Сюда же входят данные, необходимые для отображения виртуального адресного пространства процесса в физическое.
□ Окружение процесса. Переменные окружения процесса представляют собой строки пар вида:
переменная=значение
которые наследуются дочерним процессом от родительского и обычно хранятся в нижней части стека. Окружение процесса упоминалось в предыдущих главах, там же были показаны функции, позволяющие получить или изменить переменные окружения.
□ Аппаратный контекст. Сюда входят значения общих и ряда системных регистров процессора. К системным регистрам, в частности, относятся:
• указатель инструкций, содержащий адрес следующей инструкции, которую необходимо выполнить;
• указатель стека, содержащий адрес последнего элемента стека;
• регистры плавающей точки;
• регистры управления памятью, отвечающие за трансляцию виртуального адреса процесса в физический.
Переключение между процессами, необходимое для справедливого распределения вычислительного ресурса, по существу выражается в переключении контекста, когда контекст выполнявшегося процесса запоминается, и восстанавливается контекст процесса, выбранного планировщиком. Переключение контекста является достаточно ресурсоемкой операцией. Помимо сохранения состояния регистров процесса, ядро вынуждено выполнить множество других действий. Например, для некоторых систем ядру необходимо очистить кэш данных, инструкций или адресных трансляций, чтобы предотвратить некорректные обращения нового процесса. Поэтому запущенный процесс сначала вынужден работать по существу без кэша, что также сказывается на производительности.
Существуют четыре ситуации, при которых производится переключение контекста:
1. Текущий процесс переходит в состояние сна, ожидая недоступного ресурса.
2. Текущий процесс завершает свое выполнение.
3. После пересчета приоритетов в очереди на выполнение находится более высокоприоритетный процесс.
4. Происходит пробуждение более высокоприоритетного процесса.
Первые два случая соответствуют добровольному переключению контекста и действия ядра в этом случае достаточно просты. Ядро вызывает процедуру переключения контекста из функций sleep() или exit(). Третий и четвертый случаи переключения контекста происходят не по воле процесса, который в это время выполняется в режиме ядра и поэтому не может быть немедленно приостановлен. В этой ситуации ядро устанавливает специальный флаг runrun, который указывает, что в очереди находится более высокоприоритетный процесс, требующий предоставления вычислительных ресурсов. Перед переходом процесса из режима ядра в режим задачи ядро проверяет этот флаг и, если он установлен, вызывает функцию переключения контекста.
Принципы планирования процессов
Традиционные алгоритмы планирования UNIX обеспечивают возможность одновременного выполнения интерактивных и фоновых приложений. Таким образом, они хорошо подходят для систем общего назначения с несколькими подключенными пользователями, работающими с текстовыми и графическими редакторами, компилирующими программы и выполняющими вычислительные задачи. Эти алгоритмы обеспечивают малое время реакции для интерактивных приложений, следя в то же время, чтобы фоновым громоздким задачам справедливо предоставлялись ресурсы системы. Современные системы поддерживают выполнение задач реального времени, однако в данном разделе мы остановимся на планировании системы разделения времени.
34
Некоторые системы System V, например SCO UNIX, также имеют в своем распоряжении этот системный вызов.