Dlaczego musimy dwukrotnie podać nazwę pliku w funkcjach exec?

12

Czytałem Zaawansowane Programowanie w środowisku systemu UNIX przez Stevensa, 8 th rozdziału. Czytam i rozumiem wszystkie sześć funkcji exec.

Zauważam, że we wszystkich funkcjach exec:

  • pierwszy argument to nazwa pliku / nazwa ścieżki (zależy od funkcji exec).
  • drugim argumentem jest argv [0], który otrzymujemy main(), czyli sama nazwa pliku.

Więc tutaj musimy dwukrotnie przekazać nazwę pliku w funkcji.

Czy jest jakiś powód (na przykład, że nie możemy uzyskać nazwy pliku z nazwy ścieżki z pierwszego argumentu)?

munjal007
źródło

Odpowiedzi:

15

Więc tutaj musimy dwukrotnie przekazać nazwę pliku w funkcji.

Nie są to dokładnie to samo, co zauważysz, obserwując, że jeden z nich jest używany jako argv[0]wartość. To nie musi być taki sam jak basename pliku wykonywalnego; wiele / większość rzeczy ignoruje to i możesz włożyć tam, co chcesz.

Pierwszy to rzeczywista ścieżka do pliku wykonywalnego, dla którego istnieje oczywista konieczność. Drugi jest przekazywany do procesu pozornie tak, jak nazwa go wywołuje, ale np .:

execl("/bin/ls", "banana", "-l", NULL);

Będzie działać dobrze, zakładając, że /bin/lsjest to właściwa ścieżka.

Jednak niektóre aplikacje wykorzystują argv[0]. Zwykle mają one jeden lub więcej dowiązań symbolicznych $PATH; jest to powszechne w przypadku narzędzi do kompresji (czasami używają zamiast tego owijarek powłok). Jeśli masz xzzainstalowany, stat $(which xzcat)pokazuje, że jest to link do xzi man xzcatjest taki sam, jak ten, man xzktóry wyjaśnia „xzcat jest równoważne z xz --decompress --stdout”. Sposób, w jaki xz może stwierdzić, w jaki sposób został wywołany, polega na sprawdzeniu argv[0], czyniąc je równoważnymi:

execl("/bin/xz", "xzcat", "somefile.xz", NULL);
execl("/bin/xz", "xz", "--decompress", "--stdout", "somefile.xz", NULL);
Złotowłosa
źródło
5
Ach, więc to wyjaśnia, jak busyboxmoże wyglądać to, jak chcesz, w zależności od tego, jak to nazywasz?
terdon
4
@terdon właśnie tak pojedynczy plik binarny dla busybox spełnia tak wiele różnych poleceń.
mah
7
Co oznaczałoby, że gdyby /bin/lsbył zajęty, nie wiedziałby, jak wykonać banana!
Riking
6

Nie musisz podawać nazwy pliku dwa razy.

Pierwszy to plik, który jest faktycznie wykonywany.

Drugi argument dotyczy tego, co powinno być argv[0]procesem, tj. Tego , co proces powinien postrzegać jako swoją nazwę. Np. Jeśli uruchamiasz lsz powłoki, pierwszym argumentem jest /bin/ls, drugi jest po prostu ls.

Możesz wykonać określony plik i nazwać go czymś innym za pomocą drugiego argumentu; program może sprawdzić swoją nazwę i zachowywać się inaczej w zależności od nazwy. Można to również zrobić za pomocą twardych łączy (lub dowiązań symbolicznych), ale w ten sposób zapewnia większą elastyczność.

wurtel
źródło
W rzeczywistości łącza są tą samą metodą, ponieważ ustawia argv[0]się nazwę łącza.
goldilocks
W ostatnim akapicie „Możesz wykonać określony plik i nazwać go czymś innym za pomocą drugiego argumentu; program może sprawdzić jego nazwę i zachowywać się„ inaczej ”w zależności od nazwy”. czy możesz proszę opracować lub dać mi kilka odczytów, jestem nowy w tym środowisku.
munjal007
Wyjaśnia to ostatnia część odpowiedzi goldilocks.
wurtel
1

Na wynos argv[0]można ustawić wszystko (w tym NULL). Umownie , argv[0]zostanie ustawiony na ścieżce wykonywalny został uruchomiony jako (w procesie powłoki kiedy to robi execve()).

Jeśli ./fooi dir/barsą dwa różne łącza (twarde lub symboliczne) do tego samego pliku wykonywalnego, wówczas uruchomienie programu z powłoki przy użyciu dwóch ścieżek zostanie ustawione odpowiednio argv[0]na ./fooi dir/bar.

Fakt, że argv[0]może być, NULLjest często pomijany. Poniższy kod może NULL argv[0]na przykład ulec awarii (chociaż glibc wypisuje coś takiego jak <null> zamiast argv[0]):

if (argc != 3) {
    fprintf(stderr, "%s: expected 2 arguments\n", argv[0]);
    exit(EXIT_FAILURE);
}

Alternatywą w Linuksie jest zastosowanie /proc/self/exew takich przypadkach.

Ulfalizer
źródło
jak ustawić argv [0] na ./foo i dir / bar
munjal007
@ munjal007 Przepraszam, jeśli byłem niejasny. Miałem na myśli uruchomienie programu dwa razy: raz tak ./fooi raz jak dir/bar. argv[0]będzie różny dla tych dwóch przypadków (w każdym przypadku będzie taki sam, jak użyta ścieżka).
Ulfalizer
@ munjal007 Zakłada się, że uruchamiasz go oczywiście z powłoki. Chodzi o to, że możesz ustawić argv[0]wszystko, kiedy sam exec*()program. Jest to konwencja powłoki ustawiana argv[0]na ścieżkę użytą do uruchomienia programu (i rozsądnie jest zrobić to samo, gdy exec*()program, ponieważ wiele programów sprawdza argv[0]i oczekuje, że utrzyma ścieżkę).
Ulfalizer