Глава из книги Сага о FreeBSD
Алексей Федорчук
2008-11-19
Итак, повторюсь: процесс — это все, что происходит в системе. А все происходящее в системе — либо порождено ее ядром (то есть как бы самой системой), либо — одним из ее пользователей. Соответственно этому, процессы бывают системные и пользовательские (или прикладные). На первых мы здесь задерживаться не будем. Скажу только, что запуск системных процессов осуществляется ядром ОС, и их задача — управление ресурсами машины (доступом к процессору, памяти, своппингом и т.д.). И это тот самый случай, когда с системными процессами не коррелируют никакие программы в обычном понимании этого слова (кроме ядра, конечно, которое само по себе — программа; но порождает она далеко не один процесс). То есть: в системе не существует отдельного исполняемого файла, ответственного за запуск программы, исполняемой системным процессом — все они запускаются ядром, которое тоже являет собой просто исполняемый файл, хотя и не совсем обычный по своим функциям.
Из сказанного выше существует единственное исключение, и это — процесс init, прародитель всех прочих процессов, почему его относят к системным процессам. Хотя он и имеет ассоциированный с ним исполнимый файл, имя которого — /sbin/init. Он вызывается по окончании загрузки системы и порождает все остальные пользовательские процессы. В том числе — процессы getty, открывающие виртуальные терминалы, и login, ответственный за авторизацию пользователя на одном из них.
Процессы getty и login относятся уже к пользовательским процессам. Которые, в отличие от системных, всегда ассоциированы с неким исполняемым файлом. Для процесса getty это будет /usr/libexec/getty, а для процесса login — /usr/bin/login.
Процессы типа getty и login относятся к категории неинтерактивных, то есть тех, которые не привязаны к какому-либо терминалу, и на которые пользователь не может влиять непосредственно (общение с ними возможно только посредством т.н. сигналов, о которых будет говориться несколько позже). Неинтерактивные процессы часто именуются демонами (daemon). Термин этот не несет в себе мистического смысла, и обычно трактуется как аббревиатура от Disk And Execution MONitor (что можно приблизительно перевести как мониторинг дисков и исполняемых программ), хотя есть мнение, что расшифровка эта появилась задним числом.
Демоны инициируются самой системой при ее загрузке обычным образом (запуском соответствующих программ) и функционируют в фоновом режиме, активизируясь при необходимости совершения некоего действия — печати, доставки почты и т.д. Или, как в случае с login — авторизации пользователя.
Большинство же пользовательских процессов относятся к категории интерактивных. Они отличаются тем, что привязаны к некоторому терминалу, через который пользователь может с ними взаимодействовать. Так, авторизовавшись в системе, пользователь (реальный — о виртуальных далее речи не будет) запускает тем самым свой первый процесс — регистрационную командную оболочку (login shell, во FreeBSD, скорее всего, /bin/sh или /bin/tcsh). Это — процесс интерактивный, он привязан к определенному терминалу, на котором авторизовался пользователь. И последний может с ним взаимодействовать определенным образом (вводить команды, например).
Как же процесс соотносится с запустившим его пользователем? Для ответа нужно ознакомиться с атрибутами процесса. Сразу следует отметить, что любой процесс, системный ли, пользовательский, интерактивный или неинтерактивный, имеет следующие атрибуты:
Идентификатор процесса — это просто номер его в порядке запуска, от нуля до максимального значения, возможного в данной системе (количество одновременно запущенных процессов — величина конечная). Минимальный (нулевой) номер обычно получает процесс init — первопредок всех остальных процессов в системе: именно так дело обстоит в Linux. Однако в BSD-системах PID, равный нулю, обнаруживается у процесса активизации виртуальной памяти (swapper), а init имеет идентификатор 1.
Идентификатор родительского процесса — это номер процесса, от которого произошел (посредством системного вызова fork) процесс данный, о чем будет сказано в следующем разделе.
Для рассмотрения смысла остальных атрибутов нам придется обратиться к команде ps, которая и выводит информацию о процесса в виде, доступном пониманию пользователя.
Команда ps имеет множество опций, из которых нам сейчас потребуется одна: -u (впрочем, символ дефиса в этой команде можно опустить), она выведет весьма подробную информацию о процессах, запущенных данным пользователем на текущей виртуальной консоли (с помощью иных опций можно получить сведения о чужих процессах и о процессах на иных консолях, но для нас пока это не важно). Опять же, далеко не все из этих сведений нам сейчас интересны, поэтому задержим внимание только на существенных в контексте данной главы.
Итак, сразу после авторизации пользователя в ответ на команду ps -u будут выведены две строки, состоящие из нескольких колонок:
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND alv 1730 0.0 0.0 9072 2628 v1 S 11:56AM 0:00.01 -tcsh (tcsh) alv 1734 0.0 0.0 6788 1236 v1 R+ 11:56AM 0:00.00 ps u
Для нас в данный момент важны только три из них — USER, PID и COMMAND.
Первая колонка указывает на имя хозяина процесса — как правило (хотя и не обязательно — и позднее можно будет видеть, что это важно), таковым выступает пользователь, этот процесс запустивший. В колонке PID можно видеть уникальный идентификатор процесса, отличающий его от всех остальных (это обычно просто его номер в порядке запуска). Наконец, в колонке COMMAND фигурирует имя программы, породившей данный процесс; очевидно, что в момент сразу после авторизации это будет только регистрационная командная оболочка — tcsh; символ дефиса перед её именем показывает, что это — не просто некая программа, а именно login shell: завершение ее эквивалентно окончанию сеанса работы данного пользователя. Ну и, конечно, в списке будет присутствовать и сама команда ps — ей ведь тоже соответствует свой процесс, хотя и завершившийся. Так вот, именем хозяина определяются отношения процесса к файлам и иным процессам. То есть процесс получает те же привилегии, которые имеет его хозяин.
Сказанное о принадлежности процессов относится не только к интерактивным пользовательским процессам, но и к демонам. Хотя их, казалось бы, никто не запускает, но и они имеют своих хозяев. Это — некие псевдопользователи, о которых будет сказано в одной из ближайших глав.
Однако не обязательно хозяином процесса является запустивший его пользователь (или псевдопользователь). Это легко проверить, дав от лица обычного пользователя команду passwd для смены его пароля, и посмотрев атрибуты соответствующего процесса:
USER PID ... COMMAND root 5617 ... passwd
в выводе которой мы с удивлением обнаружим, что значением поля USER будет имя не запустившего команду пользователя, а администратора системы (root-оператора).
Как могло получиться, что обычный, непривилегированный пользователь запустил процесс, хозяином которого оказался root? Это станет понятно из следующей главы, посвященной файлам и их атрибутам. Пока же скажу только, для чего это нужно. Ясно, что любой пользователь должен иметь возможность сменить свой пароль. Однако для этого ему придется внести изменения в базу пользовательских акаунтов, а файл /etc/passwd, как мы помним, открыт для изменения только администратору. И потому пользовательский процесс, соответствующий команде passwd, наделяется правами, в обычной жизни этому пользователю недоступными.
Более строго это можно описать так. Как будет показано в соответствующей главе, система работает не с именем пользователя (по большому счету оно ей до лампочки), а с его идентификатором (UID). Именно UID обычно наследуется процессом в качестве идентификатора своего "хозяина" (и потому называется реальным UID). Однако у процесса есть еще и т.н. эффективный идентификатор "хозяина" (EUID), который собственно определяет привилегии данного процесса. Обычно UID и EUID совпадают — но бывают и исключения из этого правила. Если посмотреть их с помощью команды ps -l (которая обеспечивает вывод идентикатора пользователя вместо его имени), то для процесса tcsh в поле UID мы увидим число 1001 (в данном случае — это UID юзера alv). Однако для процесса passwd поле это будет содержать значение 0, то есть эффективный идентификатор его унаследован не от пользователя, а от root-оператора.
Подчеркнем, что возможность получения процессом привилегий, недоступных в обычной ситуации запустившему его пользователю, имеет смысл только в отношении пользовательских процессов, связанных с некими исполнимыми файлами. И, как мы увидим в следующей главе, зависит не от действий пользователя или атрибутов процесса, а исключительно от атрибутов файла, ответственного за запуск программы. И потому, хотя различение реального и эффективного идентификаторов процесса и может в определенных случаях представлять собой угрозу для безопасности системы, для реализации ее требуется именно некорректное функционирование программы, исполнимому файлу которой приданы соответствующие атрибуты.
Кроме реального и эффективного идентификатора пользователя, для процесса устанавливаются еще два атрибута принадлежности — реальный и эффективный идентификаторы группы (GUID и EGUID, соответственно). Смысл их аналогичен, но они наследуют права доступа процесса для группы, к которой принадлежит запустивший процесс пользователь. Именно это дает возможность пользователю — члену определённых групп (например, wheel или operator) выполнять действия, доступные только root'у, например, выключать машину командой shutdown, получать доступ к устройствам и так далее.
Следующий атрибут процесса — его относительный приоритет (NICE), обозначаемый по-английски словосочетанием nice value (что интерпретируется обычно как степень "дружелюбия" или "тактичности" по отношению к другим процессам). Он варьирует в диапазоне от -20 (минимальное "дружелюбие", то есть высший приоритет) до +20 (максимальное "дружелюбие", соответствующее низшему приоритету). Прикладные процессы в момент своего рождения получают приоритет 0 (то есть некую "среднюю" степень "дружелюбия").
Относительный приоритет процесса лишь косвенно связан собственно с приоритетами выполнения процессов, которые перераспределяются системой динамически, в зависимости от ряда факторов (и относительный приоритет — лишь один из них). Относительный приоритет сам по себе остается неизменным на протяжении всего времени существования процесса, хотя, как будет показано ниже, может быть изменен принудительно.
Наконец, последний из упоминавшихся выше атрибутов процесса — TT, то есть терминал, с которым он связан. Атрибут этот имеет смысл только для прикладных процессов, запущенных пользователем с определенной консоли, реальной или виртуальной, или из окна эмулятора терминала в Иксах, от которых он и наследуется, хотя результаты выполнения процесса могут быть перенаправлены на другую консоль или терминал.