Нейл Мэтью - Основы программирования в Linux
10438 ? Ss 0:00 /usr/bin/ssh-agent /bin/bash /etc/X11/xinit/xinitrc
10478 ? S 0:00 start_kdeinit --new-startup +kcminit_startup
10479 ? Ss 0:00 kdeinit Running...
10500 ? S 0:53 kdesktop [kdeinit]
10502 ? S 1:54 kicker [kdeinit]
10524 ? Sl 0:47 beagled /usr/lib/beagle/BeagleDaemon.exe --bg
10530 ? S 0:02 opensuseupdater
10539 ? S 0:02 kpowersave [kdeinit]
10541 ? S 0:03 klipper [kdeinit]
10555 ? S 0:01 kio_uiserver [kdeinit]
10688 ? S 0:53 konsole [kdeinit]
10689 pts/1 Ss+ 0:07 /bin/bash
10784 ? S 0:00 /opt/kde3/bin/kdesud
11052 ? S 0:01 [pdflush]
19996 ? SN1 0:20 beagled-helper /usr/lib/beagle/IndexHelper.exe
20254 ? S 0:00 qmgr -1 -t fifo -u
21192 ? Ss 0:00 /usr/sbin/ntpd -p /var/run/ntp/ntpd.pid -u ntp -i /v
21198 ? S 0:00 pickup -1 -t fifo -u
21475 pts/2 R+ 0:00 ps ax
Здесь вы видите на самом деле очень важный процесс
1 ? Ss 0:03 init [5]
В основном каждый процесс запускается другим процессом, называемым родительским или порождающим процессом. Подобным образом запущенный процесс называют дочерним или порожденным. Когда стартует ОС Linux, она выполняет единственную программу, первого предка и процесс с номером 1, init. Это, если хотите, диспетчер процессов операционной системы и прародитель всех процессов. Другие системные процессы, с которыми вы вскоре встретитесь, запускаются процессом init или другим процессом, запущенным процессом init.
Один из таких примеров — процедура регистрации. Процесс init запускает программу getty для каждого последовательного терминала или модема коммутируемой линии передачи, которые можно применять для регистрации. Эти процессы отображены в следующем выводе команды ps:
9619 tty2 Ss+ 0:00 /sbin/mingetty tty2
Процессы getty ждут работы на терминале, приглашая пользователя зарегистрироваться хорошо всем знакомой строкой, и затем передают управление программе регистрации, которая устанавливает окружение пользователя и в конце запускает сеанс командной оболочки. Когда пользовательский сеанс командной оболочки завершается, процесс init запускает новый процесс getty.
Как видите, способность запускать новые процессы и ждать их окончания — одна из основных характеристик системы. Позже в этой главе вы узнаете, как выполнять аналогичные задачи в ваших собственных программах с помощью системных вызовов fork, exec и wait.
Планирование процессов
В следующем примере вывода команды ps приведен элемент списка для самой команды ps.
21475 pts/2 R+ 0:00 ps ax
Эта строка означает, что процесс 21475 находится в состоянии выполнения (R) и выполняет он команду ps ах. Таким образом, процесс описан в своем собственном выводе! Индикатор состояния показывает только то, что программа готова к выполнению, а не то, что она обязательно выполняется в данный момент. На однопроцессорном компьютере в каждый момент времени может выполняться только один процесс, в то время как другие процессы ждут своего рабочего периода. Эти периоды, называемые квантами времени, очень короткие и создают впечатление одновременного выполнения программ. Опция R+ просто показывает, что данная программа — фоновая задача, не ждущая завершения других процессов или окончания ввода или вывода данных. Именно поэтому можно увидеть два таких процесса, приведенные в списке вывода команды ps. (Другой, часто встречающийся процесс, помечаемый как выполняющийся, — дисплейный сервер системы X.)
Ядро Linux применяет планировщик процессов для того, чтобы решить, какой процесс получит следующий квант времени. Решение принимается исходя из приоритета процесса (мы обсуждали приоритеты процессов в главе 4). Процессы с высоким приоритетом выполняются чаще, а другие, такие как низкоприоритетные фоновые задачи, — реже. В ОС Linux процессы не могут превысить выделенный им квант времени. Они преимущественно относятся к разным задачам, поэтому приостанавливаются и возобновляются без взаимодействия друг с другом. В более старых системах, например Windows 3.х, как правило, для возобновления других процессов требовалось явное согласие процесса.
В многозадачных системах, таких как Linux, несколько программ могут претендовать на один и тот же ресурс, поэтому программы с короткими рабочими циклами, прерывающиеся для ввода, считаются лучше ведущими себя, чем программы, прибирающие к рукам процессор для продолжительного вычисления какого-либо значения или непрерывных запросов к системе, касающихся готовности ввода данных. Хорошо ведущие себя программы называют nice-программами (привлекательными программами) и в известном смысле эту "привлекательность" можно измерить. Операционная система определяет приоритет процесса на основе значения "nice", по умолчанию равного 0, и поведения программы. Программы, выполняющиеся без пауз в течение долгих периодов, как правило, получают более низкие приоритеты. Программы, делающие паузы время от времени, например в ожидании ввода, получают награду. Это помогает сохранить отзывчивость программы, взаимодействующей с пользователем; пока она ждет какого-либо ввода от пользователя, система увеличивает ее приоритет, чтобы, когда программа будет готова возобновить выполнение, у нее был высокий приоритет. Задать значение nice для процесса можно с помощью команды nice, а изменить его — с помощью команды renice. Команда nice увеличивает на 10 значение nice процесса, присваивая ему более низкий приоритет. Просмотреть значения nice активных процессов можно с помощью опций -l или -f (для полного вывода) команды ps. Интересующие вас значения представлены в столбце NI (nice).
$ ps -l
F S UID PID PPID С PRI NI ADDR SZ WCHAN TTY TIME CMD
000 S 500 1259 1254 0 75 0 - 710 wait4 pts/2 00:00:00 bash
000 S 500 1262 1251 0 75 0 - 714 wait4 pts/1 00:00:00 bash
000 S 500 1313 1262 0 75 0 - 2762 schedu pts/1 00:00:00 emacs
000 S 500 1362 1262 2 80 0 - 789 schedu pts/1 00:00:00 oclook
000 R 500 1363 1262 0 81 0 - 782 - pts/1 00:00:00 ps
Как видно из списка, программа oclock выполняется (как процесс 1362) со значением nice по умолчанию. Если бы она была запущена командой
$ nice oclock &
то получила бы значение nice +10. Если вы откорректируете это значение командой
$ renice 10 1362
1362: old priority 0, new priority 10
программа oclock будет выполняться реже. Увидеть измененное значение nice можно снова с помощью команды ps:
$ ps -l
F S UID PID PPID С PRI NI ADDR SZ WCHAN TTY TIME CMD
000 S 500 1259 1254 0 75 0 - 710 wait4 pts/2 00:00:00 bash
000 S 500 1262 1251 0 75 0 - 714 wait4 pts/1 00:00:00 bash
000 S 500 1313 1262 0 75 0 - 2762 schedu pts/1 00:00:00 emacs
000 S 500 1362 1262 0 90 10 - 789 schedu pts/1 00:00:00 oclock
000 R 500 1365 1262 0 81 0 - 782 - pts/1 00:00:00 ps
Столбец состояния теперь также содержит N, указывая на то, что значение nice было изменено по сравнению с принятым по умолчанию:
$ ps х
PID TTY STAT TIME COMMAND
1362 pts/1 SN 0:00 oclock
Поле PPID в выводе команды ps содержит ID родительского процесса (PID), либо процесса, запустившего данный процесс, либо, если этот процесс уже не выполняется, процесса init (PID, равный 1).
Планировщик процессов ОС Linux решает, какому процессу разрешить выполнение, на основе приоритета. Конкретные реализации конечно отличаются, но высокоприоритетные процессы выполняются чаще. В некоторых случаях низкоприоритетные процессы не выполняются совсем, если высокоприоритетные процессы готовы к выполнению.
Запуск новых процессов
Применив библиотечную функцию system, вы можете заставить программу выполняться из другой программы и тем самым создать новый процесс:
#include <stdlib.h>
int system(const char *string);
Функция system выполняет команду, переданную ей как строку, и ждет ее завершения. Команда выполняется, как если бы командной оболочке была передана следующая команда:
$ sh -с string
Функция system возвращает код 127, если командная оболочка не может быть запущена для выполнения команды, и -1 в случае другой ошибки. Иначе system вернет код завершения команды.
Выполните упражнение 11.1.
Упражнение 11.1. Функция systemВы можете использовать system для написания программы, выполняющей команду ps. Хотя нельзя сказать, что она необычайно полезна, вы увидите, как применять этот метод в последующих примерах. (Для простоты примера мы не проверяем, работает ли на самом деле системный вызов.)