UNIX: вызов exec для родительского процесса после разветвления

Я пишу в своей грамматике, в LEX, некоторый код для fork() моего процесса и запуска дочернего процесса. Дочерний элемент на самом деле получает некоторый ввод от родителя, а затем возвращает результат.

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

У меня есть следующий код в дочерних элементах после fork():

char *path = strdup(getenv("PWD"));
size_t size = strlen(path) + strlen("/shell") + 1;
path = (char *) realloc(path, sizeof(char) * size);
path = strcat(path, "/shell");

// call exec
execl(path, NULL);

Проблема с этим кодом заключается в том, что он работает, если процесс запускается из того же каталога, но если я попытаюсь загрузить из папки в этом каталоге, например, ../shell, тогда путь на самом деле неправильный, он будет включить этот каталог.

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

Спасибо большое,

Джари


person Jary    schedule 25.09.2010    source источник
comment
Обратите внимание, что вызов execl(), который вы показываете, не дает вызываемой программе argv[0] (или, точнее, он дает нулевой указатель как argv[0] - если NULL определен как (void *)0; в противном случае он дает нулевое целое число, которое может быть недостаточно большим. на 64-битной машине). Вы должны использовать execl(path, path, (char *)NULL);, чтобы быть в безопасности (r).   -  person Jonathan Leffler    schedule 25.09.2010


Ответы (1)


Цель, насколько я понимаю, состоит в том, чтобы дочерний процесс повторно выполнял ту же программу, которая представляет родительский процесс.

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

Обычно вы полагаетесь на то, что значения argv[0] достаточно для поиска программы, и используете execvp() для поиска программы через $PATH.

Попытка 2 - может быть, менее запутанная

Я перефразирую [вопрос], если вы не возражаете: прямо сейчас execl() выполняет двоичный файл, но только если я загружаю родителя непосредственно из его папки. Если я зайду во вложенную папку, назовем ее fd, тогда путь будет "correctPath/fd/shell", а не "correctPath/shell". Похоже, проблема в том, что вызов getenv("PWD") для поиска пути не всегда правильный.

Таким образом, цель состоит в том, чтобы загрузить один и тот же процесс или двоичный файл; давайте предположим, что двоичный файл называется «оболочка». Проблема в том, чтобы найти путь. Код, который я показываю, работает в том случае, если оболочка загружается (родительская) из папки, в которой она находится сама по себе, иначе она не работает. Я подозреваю, что вызывать getenv("PWD") неправильно, но я не уверен, что еще вызывать.

Вы правы в том, что использование getenv("PWD") или getcwd() обычно неверно.

char *arg0 = 0;

int main(int argc, char **argv)
{
    ...declarations...

    arg0 = argv[0];

    ...actions...
}

Поэтому основная программа сохраняет значение своего argv[0] в глобальной переменной arg0, чтобы сделать его доступным для других частей процесса — в частности, для кода, который собирается (повторно) запустить команду.

Если программа вызывается в том же каталоге, где находится исполняемый файл, с использованием «./shell», то argv[0] (и, следовательно, arg0) будет содержать этот путь. Если он выполняется с использованием «оболочки», полагаясь на $PATH для поиска программы, то argv[0] будет содержать либо просто «оболочку», либо абсолютный путь «оболочки» (реже).

Если программа вызывается из подкаталога fd, она может вызываться как «../shell», или как «/absolute/path/to/shell», или как «shell», полагаясь на $ PATH, чтобы найти исполняемый файл. Опять же, в любом из этих случаев значение в arg0 является именем, под которым программа была первоначально вызвана, или эквивалентно ему.

Единственный случай, когда это не удается, - это если кто-то намеренно пытается запутать исполняемый файл, что (к счастью) бывает редко.

Итак, в дочернем коде вы можете использовать:

char *args[] = { arg0, 0 };
execvp(arg0, args);

для повторного выполнения команды в том виде, в каком она была первоначально выполнена (за исключением любых вспомогательных аргументов, которые были переданы изначально).

Попытка 1 - несколько запутанная

Предполагая, что значение argv[0] доступно через переменную char *arg0, все, что необходимо, это:

char *args[] = { arg0, 0 };
execvp(arg0, args);

Если вы должны использовать execl(), вам нужно:

execl(arg0, arg0, (char *)0);

Актерский состав необходим; execl() — это функция списка переменных аргументов, и если вы напишете 0, она будет преобразована в int, которая не будет работать в 64-битной системе, где int — 32-битная, а указатель — 64-битный. Однако это не удастся, если arg0 не представляет путь (относительный или абсолютный) к исполняемому файлу. Затем вам нужно будет решить, что делать, если execl() вернется — вы можете либо сдаться, либо искать программу через $PATH, но в этом случае, почему бы не использовать execvp() в первую очередь, чтобы избавить себя от боли.

person Jonathan Leffler    schedule 25.09.2010
comment
Извините, я смущен вашим ответом. Я перефразирую, если вы не возражаете: прямо сейчас execl выполняет двоичный файл, но только если я загружаю родителя непосредственно из его папки. Если я зайду во вложенную папку, назовем ее fd, тогда путь будет правильныйPath/fd/shell, а не правильныйPath/shell. Похоже, проблема в том, что вызов getenv(PWD) для поиска пути не всегда правильный. - person Jary; 25.09.2010
comment
Нет, это нормально, я могу пересказать, пока ты не поймешь, я могу понять, как это может быть непонятно. Таким образом, цель состоит в том, чтобы загрузить тот же процесс или двоичный файл, давайте предположим, что двоичный файл называется оболочкой. Проблема в том, чтобы найти путь. Код, который я показываю, работает в том случае, если оболочка загружается (родительская) из папки, в которой она находится сама по себе, иначе она не работает. Я подозреваю, что вызывать getenv(PWD) неправильно, но я не уверен, что еще можно вызвать. - person Jary; 25.09.2010
comment
Ах, кажется, теперь я понимаю, что вы имеете в виду, спасибо! Я попробую и скажу, получилось или нет. - person Jary; 25.09.2010
comment
Большое спасибо! Вы решили мою проблему! Теперь я получил ваше решение, оно намного проще, чем то, что у меня есть, и работает все время. Идеально! Спасибо. - person Jary; 25.09.2010
comment
Спасибо, что нашли время переформулировать свой ответ! - person Jary; 25.09.2010