Отправляет email-рассылки с помощью сервиса Sendsay

Программирование и др.

  Все выпуски  

Программирование в Linux с нуля - Выпуск 10


Информационный Канал Subscribe.Ru

Рассылка проекта Lindevel.Ru

Программирование в Linux с нуля. Выпуск 10

1. Предисловие

Здравствуйте, уважаемые подписчики. Мы продолжаем изучать многозадачность Linux. В этом выпуске я расскажу вам, как можно одну программу подменить другой.

На сайте Lindevel.Ru поменялся форум. Новый форум требует отдельной регистрации, но это небольшое неудобство окупается многими новыми возможностями.

2. Программа

2.1. Описание

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

execxy.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>

extern char** environ; /* Current environment */

void show_usage ()
{ 
 fprintf (stderr, "Usage: execxy X[Y]\n"
   "X = { l, v }\n"
   "Y = { e, p }\n");
 exit (1);
}

int main (int argc, char** argv)
{ 
 /* *argv[] for new program */
 char* arglist[] = { "Unix-ls", "-l", NULL }; 
 char sprogram[3] = "ls"; /* Program short name (without path) */
 const char lprogram[8] = "/bin/ls"; /* Program full name */

 if (argc != 2) { show_usage (); }
 
 if (!strcmp (argv[1], "l")) {
  /* LONGNAME + LIST */
  execl (lprogram, arglist[0], arglist[1], arglist[2]);
  abort ();
 }
 else if (!strcmp (argv[1], "v")) {
  /* LONGNAME + ARGV */
  execv (lprogram, arglist);
  abort ();
 }
 else if (!strcmp (argv[1], "le")) {
  /* LONGNAME + LIST + ENVIRONMENT*/
  execle (lprogram, arglist[0], arglist[1], arglist[2], environ);
  abort ();
 }
 else if (!strcmp (argv[1], "lp")) {
  /* SHORTNAME + LIST */
  execlp (sprogram, arglist[0], arglist[1], arglist[2]);
  abort ();
 }
 else if (!strcmp (argv[1], "ve")) {
  /* LONGNAME + ARGV + ENVIRONMENT */
  execve (lprogram, arglist, environ);
  abort ();
 }
 else if (!strcmp (argv[1], "vp")) {
  /* SHORTNAME + ARGV */
  execvp (sprogram, arglist);
  abort ();
 }
 else { show_usage (); }
 
 return 0;
}


2.3. Компиляция и запуск

Компиляция и сборка осуществляются обычным образом, а вот при запуске требуется указать один из параметров: l, e, le, lp, ve или vp. О том, что делают эти параметры, читайте теорию и разбор полетов.

3. Теория

3.1. Метод кукушки

В Linux можно одной программе подсунуть код другой программы, так, что первая ничего и не заметит. При этом, после завершения подсунутой программы никакого возврата в исходную программу не происходит. Это делается при помощи семейства функций exec(). Функция exec() (говоря "функция exec()" будем понимать любую из функций семейства) не вызвает другую программу, а передает ей управление. exec() действует по аналогии с ассемблерной инструкцией jmp far (или ljmp), только намного сложнее.

3.2. Семейство функций exec()

Семейство функций exec() включает в себя шесть основных функций: execl(), execv(), execle(), execlp(), execve() и execvp(). В некоторых Unix-системах встречаются и другие представители этого семейства, но нам важно знать лишь эти шесть функций. Вообще говоря, основная функция семейства exec() - это execve(), а все остальные - лишь реализации, но нас это сейчас не интересует.

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

execX[Y]
Теперь составим небольшую табличку для обязательного X и необязательного Y:
+---+
|X|Y|
+===+
|l|e|
+---+
|v|p|
+---+
Из этой простой таблицы всегда можно вывести следующее:
  • суффиксы l и v не используются вместе (иначе был бы шаблон execXX)
  • суффиксы e и p не используются вместе (иниче был бы шаблон execYY)
  • первым суффиксом должен быть или l или v
  • вторым суффиксом может быть или e или p
  • и т. д.
Чтобы не запоминать этот список правил, достаточно знать последовательность "levp". Построив из этих символов таблицу и зная, за что отвечает каждый из них, можно составить нужную функцию семейства exec(). Овладев этим подходом за 5 минут вы на всю жизнь избавите себя от запоминания лишних имен.

3.3. Аргументы exec()

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

execX[Y](программа, { argv, ... }, [окружение]);
Аргументы тесно связаны с суффиксами l, e, v и p следующим образом:
  • "программа" - это строка - полный путь к исполняемому файлу подменяемой программы, если отсутствует суффикс p. Если суффикс p (созвучное слово "Path") присутствует, то здесь можно указать только имя исполняемого файла, который должен находиться в каталоге, обозначенном в специальной переменной PATH. Про переменную PATH будет рассказано в последующих выпусках рассылки.
  • "argv" - это массив строк, который передается в функцию main(int argc, char** argv) подменяемой программы. Если указан суффикс v, то argv передается в execvY() в виде единого массива, если же используется суффикс l, то argv передается в функцию execlY() не единым массивом, а в виде отдельных аргументов (argv[0], argv[1], ...). Здесь следует помнить, что argv[0] - это произвольная строка, передающаяся в программу (как правило это имя запускаемой программы), т. е. первым аргументом будет не argv[0], а argv[1]. Последней строкой в argv должна быть нулевая строка.
  • "окружение" - это массив строк. Этот аргумент передается в функции exec() с суффиксом e, т. е в функции execXe(). Об окружении будет рассказано в других выпусках рассылки. Сейчас необходимо знать лишь то, что этот массив, равно как и argv должен заканчиваться нулевой строкой.

Рассмотрим несколько примеров.

Пример 1. Нужна функция exec() для запуска программы ls с аргументом "-a", без указания окружения. Смотрим таблицу. Список аргументов небольшой, поэтому нет большого смысла формировать отдельный массив argv, когда можно указать опцию "-a" как отдельный аргумент функции exec(), т. е. в качестве X используем значение l. Так как программа ls есть в любой нормальной Unix-системе, то для ее запуска можно не указывать полный путь (/bin/ls) и достаточно указать лишь имя исполняемого файла (ls), т. е. в качестве Y используем значение p. Тогда нужным нам вариантом exec() будет execlp ("ls", "List", "-a", NULL);

Пример 2. Нужна функция exec() для сборки программы следующей командой

gcc -o main main.c -lmylib
Для такого большого списка параметров есть смысл воспользоваться отдельным массивом строк
char* arguments[] = { "gnu-cc", "-o", "main", "main.c", "-lmylib", NULL };
В таком случае в качестве X используем v. Команда gcc запускается без указания пути к исполняемому файлу, т. е. в качестве Y будем использовать значение p. Тогда нужным нам вариантом функции exec() будет execvp ("gcc", arguments);

4.1. Разбор полетов

Приведенная программа использует шесть разных функций семейства exec() для запуска в текущем процессе одной и той же команды:

$ ls -l
Так как exec() полностью передает управление другой программе и возврата не происходит (если только не произошло ошибки), то мы не можем выполнить подряд несколько вызовов exec(). Чтобы продемонстрировать все рассмотренные функции семейства, пример следует запускать с параметром XY, где X - это обязательный первый суффикс exec(), а Y - второй необязательный суффикс.

О массиве environ (окружение) будет рассказано в других выпусках рассылки. Сейчас лишь необходимо знать, что это и есть то самое загадочное окружение для функций execXe().

Функция show_usage() вызывается, если программа была запущена с неподходящими параметрами.

Обратите внимание, что после каждого вызова exec() используется функция abort(). Если вызов exec() запустил программу, то назад уже дороги нет, даже если вызванная программа аварийно завершилась.

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

Новости сайта Lindevel.Ru

Новые возможности:

  • Вместо форума Simpleboard теперь используется более профессиональный форум PhpBB, который, однако, требует отдельной регистрации

Новые материалы:

  • Исходный код Linux-1.0 - первая стабильная версия ядра Linux.

Рассылки Subscribe.Ru
*nix project - новости из мира unix-систем! (ежедневная)

http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: comp.soft.prog.linuxp
Отписаться

В избранное