Jak „poprawnie” uruchomić aplikację z powłoki

21

Trudno mi precyzyjnie sformułować pytanie, ale dam z siebie wszystko. Używam dwmjako domyślnego menedżera okien idmenujako mój program uruchamiający aplikacje. Prawie nie używam aplikacji GUI poza przeglądarką. Większość mojej pracy jest wykonywana bezpośrednio z wiersza poleceń. Ponadto jestem wielkim fanem minimalizmu w odniesieniu do systemów operacyjnych, aplikacji itp. Jednym z narzędzi, którego nigdy się nie pozbyłem, było uruchamianie aplikacji. Głównie dlatego, że brakuje mi dokładnego zrozumienia działania programów uruchamiających aplikacje / ich działania. Nawet obszerne wyszukiwanie w Internecie pokazuje tylko niejasne wyjaśnienia. To, co chcę zrobić, to pozbyć się nawet mojego programu uruchamiającego aplikacje, ponieważ poza faktycznym spawaniem aplikacji, nie mam z niej absolutnie żadnego pożytku. W tym celu naprawdę chciałbym wiedzieć, jak „poprawnie” uruchamiać aplikacje z powłoki. Przy czym znaczenie „poprawnie” może być przybliżone przez „tak jak zrobiłby to program uruchamiający aplikacje”.

Wiem o następujących sposobach odradzania procesów z powłoki:

  1. exec /path/to/Program zamień powłokę na określone polecenie bez tworzenia nowego procesu
  2. sh -c /path/to/Program uruchom proces zależny od powłoki
  3. /path/to/Program uruchom proces zależny od powłoki
  4. /path/to/Program 2>&1 & uruchom niezależny proces powłoki
  5. nohup /path/to/Program & uruchom niezależny od powłoki proces i przekieruj wyjście do nohup.out

Aktualizacja 1: Mogę zilustrować, na przykład, co to dmenujest rekonstrukcja z powtarzających się wywołań ps -eflw różnych warunkach. Odradza nową powłokę /bin/bashi jako dziecko tej powłoki aplikacja /path/to/Program. Tak długo, jak dziecko będzie w pobliżu, tak długo będzie w pobliżu. (Jak sobie z tym radzi, jest to poza mną ...) W przeciwieństwie do tego, jeśli wydajesz nohup /path/to/Program &z powłoki, /bin/bashprogram stanie się dzieckiem tej powłoki, ALE jeśli wyjdziesz z tej powłoki, rodzic programu będzie najwyższym procesem. Więc jeśli pierwszym procesem był np. /sbin/init verboseI ma, PPID 1to będzie rodzicem programu. Oto, co próbowałem wyjaśnić za pomocą wykresu: chromiumzostało uruchomione przez dmenu, firefoxzostało uruchomione za pomocą exec firefox & exit:

systemd-+-acpid
        |-bash---chromium-+-chrome-sandbox---chromium-+-chrome-sandbox---nacl_helper
        |                 |                           `-chromium---5*[chromium-+-{Chrome_ChildIOT}]
        |                 |                                                    |-{Compositor}]
        |                 |                                                    |-{HTMLParserThrea}]
        |                 |                                                    |-{OptimizingCompi}]
        |                 |                                                    `-3*[{v8:SweeperThrea}]]
        |                 |-chromium
        |                 |-chromium-+-chromium
        |                 |          |-{Chrome_ChildIOT}
        |                 |          `-{Watchdog}
        |                 |-{AudioThread}
        |                 |-3*[{BrowserBlocking}]
        |                 |-{BrowserWatchdog}
        |                 |-5*[{CachePoolWorker}]
        |                 |-{Chrome_CacheThr}
        |                 |-{Chrome_DBThread}
        |                 |-{Chrome_FileThre}
        |                 |-{Chrome_FileUser}
        |                 |-{Chrome_HistoryT}
        |                 |-{Chrome_IOThread}
        |                 |-{Chrome_ProcessL}
        |                 |-{Chrome_SafeBrow}
        |                 |-{CrShutdownDetec}
        |                 |-{IndexedDB}
        |                 |-{LevelDBEnv}
        |                 |-{NSS SSL ThreadW}
        |                 |-{NetworkChangeNo}
        |                 |-2*[{Proxy resolver}]
        |                 |-{WorkerPool/1201}
        |                 |-{WorkerPool/2059}
        |                 |-{WorkerPool/2579}
        |                 |-{WorkerPool/2590}
        |                 |-{WorkerPool/2592}
        |                 |-{WorkerPool/2608}
        |                 |-{WorkerPool/2973}
        |                 |-{WorkerPool/2974}
        |                 |-{chromium}
        |                 |-{extension_crash}
        |                 |-{gpu-process_cra}
        |                 |-{handle-watcher-}
        |                 |-{inotify_reader}
        |                 |-{ppapi_crash_upl}
        |                 `-{renderer_crash_}
        |-2*[dbus-daemon]
        |-dbus-launch
        |-dhcpcd
        |-firefox-+-4*[{Analysis Helper}]
        |         |-{Cache I/O}
        |         |-{Cache2 I/O}
        |         |-{Cert Verify}
        |         |-3*[{DOM Worker}]
        |         |-{Gecko_IOThread}
        |         |-{HTML5 Parser}
        |         |-{Hang Monitor}
        |         |-{Image Scaler}
        |         |-{JS GC Helper}
        |         |-{JS Watchdog}
        |         |-{Proxy R~olution}
        |         |-{Socket Thread}
        |         |-{Timer}
        |         |-{URL Classifier}
        |         |-{gmain}
        |         |-{localStorage DB}
        |         |-{mozStorage #1}
        |         |-{mozStorage #2}
        |         |-{mozStorage #3}
        |         |-{mozStorage #4}
        |         `-{mozStorage #5}
        |-gpg-agent
        |-login---bash---startx---xinit-+-Xorg.bin-+-xf86-video-inte
        |                               |          `-{Xorg.bin}
        |                               `-dwm-+-dwmstatus
        |                                     `-xterm---bash-+-bash
        |                                                    `-pstree
        |-systemd---(sd-pam)
        |-systemd-journal
        |-systemd-logind
        |-systemd-udevd
        |-wpa_actiond
        `-wpa_supplicant

Aktualizacja 2: Wydaje mi się, że pytanie można sprowadzić również do: Co powinno być rodzicem procesu? Czy powinien to być np. Powłoka, czy powinien to być initproces, tj. Proces z PID 1?

lord.garbage
źródło
3
Krótka odpowiedź na twoje pytanie brzmi: „cokolwiek osiągnie pożądane rezultaty”.
Wayne Werner
1
cholera, śmieci - zadajesz dobre pytania. ale myślę, że Wayne jest tutaj na nosie - pyta o to twój najnowszy artykuł init- na co odpowiedź może być ... może? zależy to od tego, jak / jeśli planujesz z nim rozmawiać, czego initużywasz i gdzie znajdują się kanały danych. Ogólnie rzecz biorąc, rzeczy będą się same układać - po to initjest. W każdym razie, zwykle wtedy, gdy demonizujesz proces init. Lub jeśli chcesz kontrolować zadania, bieżąca powłoka.
mikeserv
Hahaha, pozdrawiam @mikeserv; 4:37 rano tutaj i już pierwszy śmiech tego dnia. To prawda, że ​​te rzeczy zawsze jakoś się sprawdzają. Usunę dmenui zobaczę, jak sobie radzę z tym, czego się nauczyłem. Uważam exec /path/to/Program & exitlub /bin/bash -c /path/to/Program & exitbyć całkiem użyteczne. Ale wszystkie czynią, 1tj init. Rodzicem, Programco jest dla mnie w porządku, o ile ma to sens i nie narusza żadnych podstawowych *nixzasad.
lord.garbage
@ lord.garbage - myślę, że to dlatego exec &, że ty. Zwykle robię swoje rzeczy z terminala ... może skorzystasz z pytania Ben Crowella . Mam tam odpowiedź, ale wszystkie są bardzo dobre. tak czy inaczej, kiedy tło procesu, a jego rodzic umiera, to: sh -c 'cat & kill $$'osierocacie go, a w końcu zostanie skończony. taka jest praca init - dlatego wszyscy do niej należą.
mikeserv
Może teraz prostsze pytanie brzmi: Jak to jest możliwe, aby uzyskać powyższy drzewo procesów z powłoki: systemd--bash--chromium. Wszystkie metody, które wypróbuję, ostatecznie doprowadzą do drzewa procesu o następującej formie, systemd--chromiumgdy spawnuję firefoxa z powłoki. Jak demonizowana jest tu skorupa? Nie jest powiązany z żadnym terminalem.
lord.garbage

Odpowiedzi:

7

Wygląda na to, że dobrze to rozumiesz. Aby wyjaśnić, co masz,

  • sh -c /path/to/Program jest dość podobny do

    $ sh 
    % / path / to / Program 
    % Ctrl+ D                             (lub możesz wpisać „ exit ”) 
    $

    tam, gdzie uruchamiasz nowy proces powłoki, podaj ścieżkę polecenia aplikacji do nowej powłoki, a następnie pozwól nowej powłoce zakończyć. Pokazałem nową powłokę podając inny monit w celach ilustracyjnych; prawdopodobnie tak się nie stanie w prawdziwym życiu. Konstrukt jest głównie przydatna robi trudne rzeczy, jak zawijania wielu poleceń w wiązce, tak że wyglądają jak jedno polecenie (rodzaj jednorazowego użytku bezimiennego skryptu), lub budynku skomplikowane polecenia, ewentualnie ze zmiennych powłoki. Prawie nigdy nie używałbyś go tylko do uruchamiania jednego programu z prostymi argumentami.sh -c "command"

  • 2>&1oznacza przekierowanie standardowego błędu na standardowe wyjście. To naprawdę nie ma wiele wspólnego z &; raczej używasz go, gdy polecenie wysyła komunikaty o błędach na ekran, nawet jeśli mówisz i chcesz przechwycić komunikaty o błędach w pliku.command > file
  • Przekierowanie wyjścia do nohup.outjest trywialnym efektem ubocznym nohup. Podstawowym celem jest uruchomienie asynchroniczne (powszechnie znane jako „w tle” lub jako „proces niezależny od powłoki”, aby użyć twoich słów) i skonfigurowanie go, aby miał większą szansę na kontynuację, jeśli: zakończ powłokę (np. wyloguj się), gdy polecenie jest nadal uruchomione.nohup command &command

bash(1)a Podręcznik referencyjny Bash jest dobrym źródłem informacji.

G-Man mówi „Przywróć Monikę”
źródło
7

Istnieje kilka sposobów wykonania programu i odłączenia go od terminala. Jednym z nich jest uruchomienie go w tle podpowłoki , takiej jak ta (zamień na firefoxswój ulubiony program):

(firefox &)

Innym jest odrzucenie procesu:

firefox & disown firefox

Jeśli jesteś ciekaw, jak wyrzutnie aplikacji do pracy, dmenuzapewnia 1 binarny i 2 skrypty powłoki: dmenu, dmenu_pathi dmenu_run, odpowiednio.

dmenu_rundmenu_pathpotokuje wyjście z dmenu, które z kolei potokuje do dowolnej $SHELLzmiennej. Jeśli jest pusty, użyje /bin/sh.

#!/bin/sh
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &

dmenu_pathjest nieco bardziej skomplikowany, ale w skrócie zawiera listę plików binarnych w $PATHzmiennej środowiskowej i w miarę możliwości używa pamięci podręcznej.

#!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
if [ -d "$cachedir" ]; then
        cache=$cachedir/dmenu_run
else
        cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
fi
IFS=:
if stest -dqr -n "$cache" $PATH; then
        stest -flx $PATH | sort -u | tee "$cache"
else
        cat "$cache"
fi

Nie jest konieczne, aby programy działały w powłokach. Innym sposobem pisania dmenu_runbez instalowania w powłoce byłoby:

#!/bin/sh
$(dmenu_path | dmenu "$@") &
svenper
źródło
6

Bardzo podoba mi się odpowiedź G-Mana. Ale odpowiadam, ponieważ myślę, że mylicie obawy. Jak wskazuje Wayne, najlepszą odpowiedzią jest „cokolwiek osiągnie pożądane rezultaty”.

W zarządzaniu procesami uniksowymi każdy proces ma element nadrzędny. Jedynym wyjątkiem jest initproces uruchamiany przez system operacyjny podczas rozruchu. Proces nadrzędny jest normalnym zachowaniem, gdy zabiera wszystkie procesy potomne. Odbywa się to poprzez wysłanie sygnału SIGHUP do wszystkich procesów potomnych; domyślna obsługa SIGHUP kończy proces.

Odradzanie powłoki procesów użytkownika nie różni się niczym od tego, czy zakodowano wywołania fork (2) / exec (3) w wybranym języku. Powłoka jest twoim rodzicem, a jeśli powłoka się skończy (np. Wylogujesz się), wówczas proces potomny, który ona odradza, idzie z nią w parze. Niuanse, które opisujesz, są tylko sposobami modyfikacji tego zachowania.

exec /path/to/programjest jak wywołanie exec (3) . Tak, zastąpi twoją powłokę program, zachowując dowolnego rodzica, który uruchomił powłokę.

sh -c /path/to/programrodzaj bezcelowo tworzy proces powłoki potomnej, który utworzy proces potomny program. Jest to cenne tylko wtedy, gdy w /path/to/programrzeczywistości jest sekwencją instrukcji skryptu, a nie plikiem wykonywalnym. ( sh /path/to/script.shmoże być użyty do uruchomienia skryptu powłoki, który nie ma uprawnień do wykonywania w gorszej powłoce)

/path/to/programtworzy proces „pierwszego planu”, co oznacza, że ​​powłoka czeka na zakończenie procesu przed podjęciem jakichkolwiek innych działań. W kontekście wywołań systemowych przypomina to fork (2) / exec (3) / waitpid (2) . Zauważ, że dziecko dziedziczy stdin / stdout / stderr od rodzica.

/path/to/program &(ignorowanie przekierowania) tworzy „proces w tle”. Proces jest nadal potomkiem powłoki, ale rodzic nie czeka na jej zakończenie.

nohup /path/to/programwywołuje nohup (1), aby zapobiec wysłaniu SIGHUP, programjeśli terminal kontrolny jest zamknięty. To, czy jest to na pierwszym planie, czy w tle, jest wyborem (chociaż najczęściej proces jest w tle). Zauważ, że nohup.outjest to wynik tylko wtedy, gdy nie przekierujesz stdout w inny sposób.

Gdy umieścisz proces w tle, jeśli proces nadrzędny umiera, dzieje się jedna z dwóch rzeczy. Jeśli rodzic jest terminalem kontrolnym , wówczas SIGHUP zostanie wysłany do dzieci. Jeśli tak nie jest, proces może zostać „osierocony” i odziedziczony przez initproces.

Przekierowując dane wejściowe / wyjściowe / błędy, po prostu podłączasz deskryptory plików, które każdy proces ma do innych plików niż te, które dziedziczy od swojego rodzica. Nic z tego nie wpływa na własność procesu ani głębokość drzewa (ale zawsze sensowne jest przekierowanie wszystkich 3 z dala od terminala dla procesów w tle).

Biorąc to wszystko pod uwagę, nie sądzę, że powinieneś zajmować się tworzeniem procesów przez powłokę, podpowłoki lub podprocesy, chyba że istnieje konkretny problem, który rozwiązujesz związany z zarządzaniem procesami.

jwm
źródło
nitpick: sh -c /path/to/programnie uruchomi programu jako skryptu powłoki, jeśli brakuje mu bitów wykonywalnych, sh /path/to/programzrobi to. sh -c /path/to/programpo prostu otworzy powłokę i uruchomi się w niej /path/to/programjako polecenie, co zakończy się niepowodzeniem, jeśli nie będzie wykonywalna.
filbranden
Cóż, jeśli robimy dupki, oboje się mylimy. sh -c /path/to/programodczytuje polecenia /path/to/programjako dane wejściowe do powłoki. Plik nie wymaga uprawnień do wykonywania, ale powinien to być skrypt powłoki
jwm
Hmmm, sh /path/to/programrobi to. Po prostu spróbowałem tego sam:, echo echo hello world >abc.sha następnie sh ./abc.shdrukuje hello world, podczas gdy sh -c ./abc.shmówi sh: ./abc.sh: Permission denied(co jest tak samo, jakbyś uruchomił ./abc.shbezpośrednio bieżącą powłokę). Czy coś przeoczyłem? (A może nie
wyrażałem
Moja wina. sh -c _something_to to samo, co pisanie _something_w wierszu polecenia, z wyjątkiem spawnowania gorszej powłoki. Więc masz rację, że nie powiedzie się, jeśli przegapisz bit wykonawczy (jako plik). Z drugiej strony możesz podać serię poleceń powłoki, takich jak sh -c "echo hello world"i będzie działać dobrze. Nie wymaga to, aby wpisywane przez Ciebie bity były wykonywane (a nawet być plikiem), tylko interpretator powłoki może coś z tym zrobić.
jwm
Wierzę, że OP odnosiło się do niektórych skompilowanych lub systemowych plików wykonywalnych, więc przyjęto pozwolenie na wykonanie.
jwm