Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
202
Добавлен:
02.05.2014
Размер:
2.83 Mб
Скачать

Листинг 2. Окончание процедуры инициализации ядра Linux

if (execute_command)

execve(execute_command,argv_init, envp_init);

execve(”/sbin/init”,argv_init,envp_init);

execve(”/etc/init”,argv_init,envp_init);

execve(”/bin/init”,argv_init,envp_init);

execve(”/bin/sh”,argv_init,envp_init);

panic(”No init found. Try passing init= option to kernel.”);}

Первый процесс в системе запускается при инициализации ядра. Пожалуй, даже человеку, не умеющему программировать, достаточно будет взглянуть на конец процедуры инициализации ядра Linux (см. листинг 2), чтобы понять, как определяется выполняемая в этом процессе программа: вначале делается попытка «переключить» процесс на файл, указанный в командной строке ядра (есть и такая...), потом на файлы /sbin/init, /etc/init, /bin/init и напоследок на /bin/sh.

Смерть процесса

Рассмотрев рождение процесса, логично будет обсудить и его смерть. Когда процесс закончит работу (нормально или аварийно), он уничтожается, освобождая все использовавшиеся им ресурсы компьютера.

Обратимся еще раз к примеру, рассмотренному выше. Когда мы нажатием <Ctrl>+C принудительно завершили выполнение программ dd и wc, соответствующие процессы были уничтожены, и на экране появилось приглашение командного интерпретатора. Пока программы работали, приглашения не было: интерпретатор находился в состоянии ожидания, в которое перешел, послав специальный системный вызов (в действительности таких вызовов существует несколько: wait, waitpid, wait3, wait4). После окончания работы программ вызов вернул управление интерпретатору, и тот выдал на терминал приглашение.

Если родительский процесс по какой-то причине завершится раньше дочернего, последний становится «сиротой» (orphaned process). «Сироты» автоматически «усыновляются» программой init, выполняющейся в процессе с номером 1, которая и принимает сигнал об их завершении.

Если же потомок уже завершил работу, а предок не готов принять от системы сигнал об этом событии, то потомок не исчезает полностью, а превращается в «зомби» (zombie); в поле Stat такие процессы помечаются буквой Z. Зомби не занимает процессорного времени, но строка в таблице процессов остается, и соответствующие структуры ядра не освобождаются. После завершения родительского процесса «осиротевший» зомби на короткое время также становится потомком init, после чего уже «окончательно умирает».

Наконец, процесс может надолго впасть в «сон», который не удается прервать: в поле Stat это обозначается буквой D. Процесс, находящийся в таком состоянии, не реагирует на системные запросы и может быть уничтожен только перезагрузкой системы.

О сигналах

Постойте, но ведь приглашение командного интерпретатора появилось и тогда, когда мы нажали <Ctrl>+Z, хотя программы не заканчивали работу, и, следовательно, вызов wait* не мог вернуть управление! Выдача сообщения Stopped (процесс остановлен) и затем приглашения к вводу была реакцией на сигнал CHLD, который ядро посылает при нажатии <Ctrl>+Z предкам — в данном случае одному предку — процессов, работающих с терминалом (сами процессы получают свой сигнал).

Сигналы посылаются одними процессами другим с помощью команды, которая носит устрашающее название kill, хотя в общем случае никого не убивает. Все зависит от конкретного сигнала, и практически любой сигнал при необходимости может быть процессом проигнорирован. Исключение составляют KILL, который «без разговоров» уничтожает процесс, и STOP, который его аналогичным образом останавливает.

Правила о том, какой процесс какому имеет право послать сигнал, достаточно сложны. Суперпользователь, очевидно, может посылать сигналы любым процессам, а обычный пользователь — только своим, но здесь есть масса тонкостей: например, нельзя послать сигнал CONT (продолжить выполнение остановленного процесса) своему же процессу, запущенному в другой сессии.

Работа с нитями требует особой техники, поскольку одни сигналы должны «доводиться до сведения» всех нитей, а другие — посылаться индивидуально. В Linux 2.2 это делалось путем довольно хитрых манипуляций со специальной нитью, единственным назначением которой было управление другими нитями. В версии 2.4 ядро может следить за нитями за счет нового флага CLONE_PARENT (таким образом, если одна нить породит другую и закончит работу, то порожденная нить не останется «сиротой») и нескольких специальных правил доставки сигналов, так что надобность в специальной нити отпала.

Компьютерная демонология

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

На многих машинах демоны, обслуживающие процессы других компьютеров, нужны достаточно редко, так что держать их в памяти постоянно загруженными и транжирить на это ресурсы системы нерационально. Для управления их работой был создан супердемон, которого зовут вовсе не Вельзевулом (в компьютерных демонах вообще мало «демонического» — они ближе демонам Максвелла), а куда скромнее — inetd (что, как вы догадались, является сокращением от Internet daemon).

В конфигурационном файле inetd (/etc/inetd.conf) записано, какой демон обслуживает обращения к какому сервису Internet. Обычно с помощью inetd вызываются программы pop3d, imap4d, ftpd, telnetd (предоставляю читателю определить, какие именно сервисы они обслуживают) и некоторые другие. Эти программы не являются постоянно активными, а значит, не могут считаться демонами в строгом смысле слова, но поскольку они порождаются «полноценным» демоном, их все равно так называют.

Продвинутые средства общения

Процессы посылают друг другу сигналы, передают данные через неименованные и именованные каналы, а также «гнезда». Все это замечательно, но как быть, если один процесс должен передавать другому огромные объемы информации и притом быстро (это нужно, например, при воспроизведении видео)? Могут ли процессы, адресные пространства которых строго разделены, каким-либо образом получить в совместное пользование часть памяти? Да, с помощью временных файлов.

Для передачи обширных массивов данных между процессами служит системный вызов mmap, представляющий собой довольно неожиданное применение страничной виртуальной памяти. Он позволяет, грубо говоря, сказать: «я хочу обращаться к такому-то участку такого-то файла как к оперативной памяти». Данные, которые процесс читает из указанной области памяти, по мере надобности считываются из файла, а те, которые он туда пишет, когда-нибудь попадут на диск. Но процесс сам не работает с диском, этим занимается ядро.

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

Вызов mmap применяется также для «загрузки в память» исполняемых файлов и библиотек, так что если программа использует 25 библиотек общим объемом во много десятков мегабайт, это вовсе не значит, что она и в памяти будет занимать такое же количество мегабайт.

С помощью временных файлов можно, кроме того, синхронизировать работу процессов, используя возможности системы, предназначенные для работы с рекомендательными (advisory) блокировками файлов. Это позволяют сделать системные вызовы fcntl и его более быстрый и простой вариант flock.

Иногда создавать временные файлы нежелательно, поэтому в Linux включены также функции для общения процессов из Unix SVR4 (Unix System V Release 4). Это shmget — создание области памяти для общения процессов, semget — создание семафора, msgget — создание очереди сообщений. В версии 2.4 к ним добавились еще более мощные функции mq_open, shm_open из SUS2 (Single Unix Specification Version 2).

Получение информации о процессах

Для работы с информацией о процессах, которую выводят на терминал программы ps и top, в Linux используется достаточно необычный механизм: особая файловая система procfs. В большинстве дистрибутивов она монтируется при запуске системы как каталог /proc. Данные о процессе с номером 1 (обычно это /sbin/init) содержатся в подкаталоге /proc/1, о процессе с номером 364 — в /proc/364, и т. д. Все файлы, открытые процессом, представлены в виде символических ссылок в каталоге /proc/<pid>/fd, а ссылка на корневой каталог процесса хранится как /proc/<pid>/root.

Со временем у файловой системы procfs появились и другие функции. Например, командой

echo 100000 > /proc/sys/fs/file-max

суперпользователь может определить, что в системе разрешается открыть до 100 000 файлов, а команда

echo 0 > /proc/sys/kernel/cap-bound

отнимет у всех процессов в системе все дополнительные права, т. е. фактически лишит систему понятия «суперпользователь».

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

В следующей статье мы поговорим о командном интерпретаторе, его роли в системе и вообще о том, как из отдельных процессов и файлов складывается нечто единое.