Опубликован: 22.06.2005 | Уровень: для всех | Доступ: платный | ВУЗ: Компания IBM
Лекция 5:

Доступ процессов к файлам и каталогам

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >
Аннотация: В лекции описываются понятия процесса в Linux, алгоритм порождения новых процессов и одно из средств межпроцессного взаимодействия – сигналы. Рассматриваются три вида доступа к ресурсам файловой системы – чтение, запись и использование, их различия для файлов и каталогов, а также команды изменения доступа.

Процессы

Как уже упоминалось в лекции 1, загрузка Linux завершается тем, что на всех виртуальных консолях (на самом деле – на всех терминалах системы), предназначенных для работы пользователей, запускается программа getty. Программа выводит приглашение и ожидает активности пользователя, который может захотеть работать именно на этом терминале. Введенное входное имя getty передает программе login, которая вводит пароль и определяет, разрешено ли работать в системе с этим входным именем и этим паролем. Если login приходит к выводу, что работать можно, она запускает стартовый командный интерпретатор, посредством которого пользователь и управляет системой.

Выполняющаяся программа называется в Linux процессом . Все процессы система регистрирует в таблице процессов, присваивая каждому уникальный номер – идентификатор процесса ( p rocess id entificator, PID ). Манипулируя процессами, система имеет дело именно с их идентификаторами, другого способа отличить один процесс от другого, по большому счету, нет. Для просмотра своих процессов можно воспользоваться утилитой ps (" p rocess s tatus"):

[methody@localhost methody]$ ps -f
  UID		PID	PPID	C	STIME	TTY	TIME		CMD
  methody	3590	1850	0	13:58	tty3	00:00:00	-bash
  methody	3624	3590	0	14:01	tty3	00:00:00	ps -f
5.1. Просмотр таблицы собственных процессов

Здесь Мефодий вызвал ps с ключом " -f " (" f ull"), чтобы добыть побольше информации. Представлены оба принадлежащих ему процесса: стартовый командный интерпретатор, bash, и выполняющийся ps. Оба процесса запущены с терминала tty3 (третьей системной консоли) и имеют идентификаторы 3590 и 3624 соответственно. В поле PPID (" p arent p rocess id entificator") указан идентификатор родительского процесса, т. е. процесса, породившего данный. Для ps это – bash, а для bash, очевидно, login, так как именно он запускает стартовый shell. В выдаче не оказалось строки для этого login, равно как и для большинства других процессов системы, так как они не принадлежат пользователю methody.

Процесс - выполняющаяся программа в Linux. Каждый процесс имеет уникальный идентификатор процесса, PID. Процессы получают доступ к ресурсам системы (оперативной памяти, файлам, внешним устройствам и т. п.) и могут изменять их содержимое. Доступ регулируется с помощью идентификатора пользователя и идентификатора группы, которые система присваивает каждому процессу.

Запуск дочерних процессов

Запуск одного процесса вместо другого организован в Linux с помощью системного вызова exec(). Старый процесс из памяти удаляется навсегда, вместо него загружается новый, при этом настройка окружения не меняется, даже PID остается прежним. Вернуться к выполнению старого процесса невозможно, разве что запустить его снова с помощью того же exec() (от "execute" – "исполнить"). Кстати, имя файла (программы), из которого запускается процесс, и собственное имя процессатаблице процессов ) могут и не совпадать. Собственное имя процесса – это такой же параметр командной строки, как и те, что передаются ему пользователем: для exec() требуется и путь к файлу, и полная командная строка, нулевой (стартовый) элемент которой – как раз название команды1Нулевой параметр - argv[0] в терминах языка Си и $0 в терминах shell. .

Вот откуда " - " в начале имени стартового командного интерпретатора ( -bash ): его "подсунула" программа login, чтобы была возможность отличать его от других запущенных тем же пользователем оболочек.

Для работы командного интерпретатора одного exec() недостаточно. В самом деле, shell не просто запускает утилиту, а дожидается ее завершения, обрабатывает результаты ее работы и продолжает диалог с пользователем. Для этого в Linux служит системный вызов fork() ("вилка, развилка"), применение которого приводит к возникновению еще одного, дочернего, процесса – точной копии породившего его родительского. Дочерний процесс ничем не отличается от родительского: имеет такое же окружение, те же стандартный ввод и стандартный вывод, одинаковое содержимое памяти и продолжает работу с той же самой точки (возврат из fork() ). Отличий два: во-первых, эти процессы имеют разные PID, под которыми они зарегистрированы в таблице процессов, а во-вторых, различается возвращаемое значение fork(): родительский процесс получает в качестве результата fork() идентификатор процесса-потомка, а процесс-потомок получает " 0 ".

Дальнейшие действия shell при запуске какой-либо программы очевидны. Shell-потомок немедленно вызывает эту программу с помощью exec(), а shell-родитель дожидается завершения работы процесса-потомка ( PID которого ему известен) с помощью еще одного системного вызова, wait(). Дождавшись и проанализировав результат команды, shell продолжает работу:

[methody@localhost methody]$ cat > loop
 while true; do true; done
^D
[methody@localhost methody]$ sh loop
^C
[methody@localhost methody]$
5.2. Создание бесконечно выполняющегося сценария

По совету Гуревича Мефодий создал сценарий для sh (или bash, на таком уровне их команды совпадают), который ничего не делает. Точнее было бы сказать, что этот сценарий делает ничего, бесконечно повторяя в цикле команду, вся работа которой состоит в том, что она завершается без ошибок (в лекции 7 говорится о том, что " > файл " в командной строке просто перенаправляет стандартный вывод команды в файл). Запустив этот сценарий с помощью команды вида sh имя_сценария, Мефодий ничего не увидел, но услышал, как загудел вентилятор охлаждения центрального процессора: машина трудилась! Управляющий символ " ^C ", как обычно, привел к завершению активного процесса, и командный интерпретатор продолжил работу.

Если бы в описанной выше ситуации родительский процесс не ждал, пока дочерний завершится, а сразу продолжал работать, получилось бы, что оба процесса выполняются параллельно: пока запущенный процесс что-то делает, пользователь продолжает командовать оболочкой. Для того чтобы запустить процесс параллельно, в shell достаточно добавить " & " в конец командной строки:

[methody@localhost methody]$ sh loop&
 [1] 3634
[methody@localhost methody]$ ps -f
 UID       PID PPID  C STIME TTY        TIME CMD
 methody  3590 1850  0 13:58 tty3    00:00:00 -bash
 methody  3634 3590 99 14:03 tty3    00:00:02 sh loop
 methody  3635 3590  0 14:03 tty3    00:00:00 ps -f
5.3. Запуск фонового процесса

В результате стартовый командный интерпретатор ( PID 3590) оказался родителем сразу двух процессов: sh, выполняющего сценарий loop, и ps.

Процесс, запускаемый параллельно, называется фоновым (background). Фоновые процессы не имеют возможности вводить данные с того же терминала, что и породивший их shell (только из файла), зато выводить данные на этот терминал могут (правда, когда на одном и том же терминале вперемешку появляются сообщения от нескольких фоновых процессов, начинается неразбериха). При каждом терминале в каждый момент времени может быть не больше одного активного (foreground) процесса, которому разрешено вводить данные с этого терминала. На время, пока команда (например, cat ) работает в активном режиме, породивший ее командный интерпретатор "уходит в фон", и там, в фоне, выполняет свой wait().

активный процесс, foreground process - процесс, предоставляющий возможность вводить данные с терминала..

Фоновый процесс, background process - процесс, не имеющий возможности вводить данные с терминала. Пользователь может запустить любое, но не превосходящее заранее заданное в системе, число фоновых процессов.

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

В выдаче ps, которую получил Мефодий, можно заметить, что PID стартовой оболочки равен 3590, а PID запущенных из-под него команд (одной фоновой и одной активной) – 3634 и 3635. Это значит, что за время, прошедшее с момента входа Мефодия в систему до момента запуска sh loop&, в системе было запущено еще 3634-3590=44 процесса. Что ж, в Linux могут одновременно работать несколько пользователей, да и самой системе иногда случается запустить какую-нибудь утилиту (например, выполняя действия по расписанию). А вот sh и ps получили соседние PID, значит, пока Мефодий нажимал Enter и набирал ps -f, никаких других процессов не запускалось.

В действительности далеко не всем процессам, зарегистрированным в системе, на самом деле необходимо давать поработать наравне с другими. Большинству процессов работать прямо сейчас не нужно: они ожидают какого-нибудь события, которое им необходимо обработать. Чаще всего процессы ждут завершения операции ввода-вывода. Чтобы посмотреть, как потребляются ресурсы системы, можно использовать утилиту top. Но сначала Мефодий решил запустить еще один бесконечный сценарий (ему было интересно, как два процесса конкурируют за ресурсы):

[methody@localhost methody]$ bash loop&
 [2] 3639
[methody@localhost methody]$ top
 14:06:50 up 3:41, 5 users, load average: 1,31, 0,76, 0,42
 4 processes: 1 sleeping, 3 running, 0 zombie, 0 stopped
 CPU states: 99,4% user, 0,5% system, 0,0% nice, 0,0% iowait, 0,0% idle
 Mem:  514604k av, 310620k used, 203984k free,  0k shrd, 47996k buff
       117560k active, 148388k inactive
 Swap: 1048280k av,     0k used, 1048280k free        184340k cached
PID  USER   PRI NI SIZE RSS SHARE STAT %CPU %MEM TIME COMMAND
3639 methody 20 0  1260 1260 1044  R   50,3 0,2  0:12  bash
3634 methody 18 0  980  980  844   R   49,1 0,1  3:06  sh
3641 methody 9  0  1060 1060 872   R   0,1  0,2  0:00  top
3590 methody 9  0  1652 1652 1264  S   0,0  0,3  0:00  bash
5.4. Разделение времени между процессами

Оказалось, что дерутся даже не два процесса, а три: sh (первый из запущенных интерпретаторов loop ), bash (второй) и сам top. Правда, по сведениям из поля %CPU, львиную долю процессорного времени отобрали sh и bash (они без устали вычисляют!), а top довольствуется десятой долей процента (а то и меньшей: ошибки округления). Стартовый bash вообще не хочет работать, он спит (значение " S ", S leep, поля STAT, status): ждет завершения активного процесса, top.

Увидев такое разнообразие информации, Мефодий кинулся читать руководство по top, однако скоро понял, что без знания архитектуры Linux большая его часть не имеет смысла. Впрочем, некоторая часть все же понятна: объем оперативной памяти (всей, используемой и свободной), время работы машины, объем памяти, занимаемой процессами и т. п.

Последний процесс, запущенный из оболочки в фоне, можно из этой оболочки сделать активным при помощи команды fg (" f ore g round" – "передний план"):

[methody@localhost methody]$ fg
 bash loop
^C
5.5. Перевод фонового процесса в активное состояние с помощью команды fg (foreground)

Услужливый bash даже написал командную строку, которой был запущен этот процесс: " bash loop ". Мефодий решил "убить" его с помощью управляющего символа " ^C ". Теперь последним запущенным в фоне процессом стал sh, выполняющий сценарий loop.

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >
Аягоз Имансакипова
Аягоз Имансакипова
Тимур Булатов
Тимур Булатов

С момента выхода курса прошло достаточно много времени, и хотелось бы понимать, насколько курс является актуальным на сегодняшний день.