UNIX: вызов exec для родительского процесса после разветвления - PullRequest
1 голос
/ 25 сентября 2010

Я пишу в своей грамматике, в 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, то путь фактически неверный , он будет включать этот каталог.

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

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

Jary

1 Ответ

0 голосов
/ 25 сентября 2010

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

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

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

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

Я перефразирую [вопрос], если вы не возражаете: прямо сейчас, execl() выполняет двоичный файл, но только если я загружаю родителя непосредственно из его папки. Если я зайду в подпапку, назовем ее fd, тогда путь будет «правильный путь / оболочка / оболочка», а не «правильный путь / оболочка». Кажется, проблема в том, что вызов 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) будет содержать это имя пути. Если он выполняется с использованием «shell», полагаясь на $ PATH для определения местоположения программы, то argv[0] будет содержать либо просто «shell», либо абсолютный путь к «shell» (реже).

Если программа вызывается из подкаталога 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(), во-первых, чтобы спасти себя? боль.

...