netlib.narod.ru< Назад | Оглавление | Далее >

Запуск новой программы

Если бы можно было выполнять только программу fork (или __clone), можно было бы только снова и снова создавать копии одного и того же процесса — система Linux могла бы запускать копии первого когда-либо созданного в ней процесса пользователя — init. Этот процесс полезен, но не настолько, желательно иметь возможность делать и что-нибудь еще.

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

Таким образом, создание «действительно» нового процесса, который из родительской программы запускает образ другой программы, состоит из двух этапов: одного для fork и второго — для exec, приводя к следующей знакомой структуре кода С:

  /* Возможность возникновения ошибки в следующих
     строках программы игнорируется. */
  if (fork()) {
      /* Я - родительская программа; продолжаю
         обычную работу. */
  } else {
      /* Я - дочерняя программа */
      /* Становлюсь /some/other/program. */
  execl("/some/other/program",
           "/some/other/program")
  }

(execl — одна из нескольких функций семейства exec.)

Основополагающей функцией ядра, реализующей все функции в семействе exec является do_execve, определенная в строках с 10079 по 10141. Функция do_execve выполняет три задачи:

Помня об этих задачах, давайте начнем подробное рассмотрение функции do_execve.

do_execve

prepare_binprm

Попутно отметим здесь возможное осложнение поддержки: в строке 13787 член buf структуры struct linux_binprm был объявлен имеющим длину 128 байт, а в строке 9933 128 байт были считаны. Но в обоих местах используется литеральная константа 128 — никакое выражение #define не утверждает, что оба числа должны быть одинаковыми; следовательно, одно из них могло бы измениться без соответствующего изменения второго, внося путаницу. Не хочется быть педантом, но это упущение трудно оправдать повышением эффективности — как, впрочем, и как-либо иначе.

Здесь читателям предоставляется возможность внести небольшой, но полезный вклад в исходный код: замените 128 новым значением #define (или чем-либо вроде sizeof(bprm->buf)) везде, где оно используется для этой цели; существует всего несколько таких случаев, но я предоставляю читателям самим их найти. Попытавшись сделать это, читатели поймут, почему в этом случае лучше использовать #define, а не sizeof. (А еще лучше было бы выявить и исправить все подобные повторяющиеся магические числа. Но выполнить такое глобальное исправление не так просто, поскольку точное выявление всех совпадений — весьма трудоемкий процесс; начните с малого, постепенно расширяя задачу.)

search_binary_handler

Обработчик двоичных файлов — это механизм ядра Linux, предназначенный для единообразной обработки различных двоичных форматов, потребность в котором связана с тем, что не все программы хранятся в одном и том же файловом формате. Хорошим примером служат файлы .class Java. Java определяет независимый от платформы формат двоичных исполняемых файлов — сами файлы остаются неизменными, независимо от платформы, на которой они выполняются — поэтому ясно, что они не могут быть структурированы так же, как собственные исполняемые файлы Linux. Тем не менее, благодаря использованию соответствующего обработчика двоичных файлов Linux может обрабатывать их, как если бы они были собственными исполняемыми файлами.

Обработчики двоичных файлов будут подробно описаны далее, но теперь читатели знают о них достаточно, чтобы понять, как функция do_execve находит подходящий обработчик. Она делегирует эту задачу функции search_binary_handler (строка 9996).


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz