Запуск программы с использованием execv и передача аргументов без поднятия argc - PullRequest
0 голосов
/ 17 января 2019

Мне дали этот код в классе:

    int main(int argc, char **argv)
    {
        if(argc)
        {
            return 1;
        }

        puts(argv[3]);
        return 0;
    }

Теперь я должен написать вторую программу, которая выполняет это и заставляет печатать «Hello World!». Поэтому я должен найти способ передать аргументы (я полагаю, с execv), пока argc остается в 0.

Это то, что я имею до сих пор:

    int main()
    {
        pid_t pid = fork();

        if(pid == 0)
        {
            char *argv[] = { "filepath", "placeholder",
                             "placeholder, "Hello World!" };
            execv("filepath", argv);
            exit(0);
        }
        else
        {
            waitpid(pid, 0, 0);
        }

        return 0;
    }

Это будет работать в Linux.

Я попробовал первоначальное предложение mbj передать 0 в массиве arguments, что, к сожалению, не сработало. Однако если в качестве первого аргумента указать 0, argc станет 0, но затем я получу вывод "LC_MEASUREMENT = de_DE.UTF-8" при попытке напечатать аргументы. Гугл это мне тоже не очень помогло.

Я в полной растерянности, поэтому любая помощь приветствуется.

1 Ответ

0 голосов
/ 17 января 2019

Хорошо, я понял это сейчас, после своего собственного эксперимента.

Поскольку передача массива, начинающегося с NULL (0), в execv приводит к распечатке программы LC_MEASUREMENT=de_DE.UTF-8, я понял, что это должно означать, что argv[3] относится к элементу в процессе environment (LC_MEASUREMENT - одна из переменных среды, используемая для настройки параметров локали в Linux).

Решение с execv

Поскольку execv скопирует текущую среду в новую программу, нам просто нужно изменить среду и поместить строку "Hello World!" в правильное место передзвонит execv.Оказывается, что строка, которая печатается, является той, на которую указывает индекс 2 среды.

Чтобы получить доступ к текущей среде, нам нужно объявить переменную environ вне main:

external char **environ;

int main() 
{
    ...

, а затем сделать это перед вызовом execv:

        char *argv[] = { NULL };
        environ[2] = "Hello world!";
        execv("filepath", argv);

Более простое решение с execve

Вместо объявления external char **environ и изменения текущей среды перед вызовом execv, мы можем использовать функцию execve, которая позволяет нам передавать новуюмассив строк для использования в качестве среды для программы:

        char *argv[] = { NULL };
        char *envp[] = { "foo", "bar", "Hello World!", NULL };
        execve("filepath", argv, envp);

Как показывает приведенный выше фрагмент кода, "Hello World!" должен находиться в индексе 2 среды, независимо от того, какой метод мы используем.

Как это работает

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

Поскольку эти массивы завершаются пустой строкой, это будет выглядеть так:

+---------+---------------------------------------+ 
| Args    | Environment                           |
+---------+---------+---------+---------+---------+
|  NULL   | envp[0] | envp[1] | envp[2] |  NULL   | 
+---------+---------+---------+---------+---------+
    ^         ^                   ^                     
    |         |                   |
 argv[0]    argv[1]     ...     argv[3]

Надеюсь, искусство ASCII поможет вам понять, почему argv[3] означает то же самое, что и environ[2] когда мы выполняем программу следующим образом.

...