Come "correttamente" avviare un'applicazione da una shell


21

Trovo difficile formulare la domanda con precisione, ma darò il massimo. Uso dwmcome gestore delle finestre predefinito edmenucome il mio lanciatore di applicazioni. Difficilmente uso le applicazioni della GUI oltre al mio browser. Gran parte del mio lavoro viene svolto direttamente dalla riga di comando. Inoltre, sono un grande fan del minimalismo per quanto riguarda i sistemi operativi, le applicazioni, ecc. Uno degli strumenti di cui non mi sono mai liberato era un lanciatore di applicazioni. Principalmente perché non ho una comprensione precisa di come funzionano i lanciatori di applicazioni / cosa fanno. Anche una vasta ricerca su Internet mostra solo vaghe spiegazioni. Quello che voglio fare è sbarazzarmi anche del mio lanciatore di applicazioni perché, oltre a generare effettivamente l'applicazione, non ne ho assolutamente bisogno. Per fare ciò vorrei davvero sapere come "correttamente" avviare le applicazioni dalla shell. Per cui il significato di "correttamente" può essere approssimato da "come farebbe un lanciatore di applicazioni".

Conosco i seguenti modi per generare processi dalla shell:

  1. exec /path/to/Program sostituire la shell con il comando specificato senza creare un nuovo processo
  2. sh -c /path/to/Program avviare il processo dipendente dalla shell
  3. /path/to/Program avviare il processo dipendente dalla shell
  4. /path/to/Program 2>&1 & avviare un processo indipendente dalla shell
  5. nohup /path/to/Program & avvia il processo indipendente dalla shell e reindirizza l'output a nohup.out

Aggiornamento 1: posso illustrare cosa, ad esempio dmenu, ricostruendolo da chiamate ripetute a ps -eflin condizioni diverse. Genera una nuova shell /bin/bashe come figlio di questa shell l'applicazione /path/to/Program. Fintanto che il bambino è in giro così a lungo il guscio sarà in giro. (Il modo in cui gestisce questo è al di là di me ...) Al contrario, se si emette nohup /path/to/Program &da una shell, /bin/bashil programma diventerà figlio di questa shell MA se si esce da questa shell, il genitore del programma sarà il processo più in alto. Quindi, se il primo processo è stato ad es. /sbin/init verboseE lo è, PPID 1allora sarà il genitore del programma. Ecco cosa ho cercato di spiegare usando un grafico: è chromiumstato avviato tramite dmenu, è firefoxstato avviato utilizzando 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

Aggiornamento 2: Immagino che la domanda possa anche essere ridotta a: Quale dovrebbe essere il genitore di un processo? Dovrebbe ad esempio essere una shell o dovrebbe essere il initprocesso, ovvero il processo con PID 1?


3
La risposta concisa alla tua domanda sarebbe "qualunque cosa ottenga i risultati desiderati".
Wayne Werner,

1
dang, spazzatura - fai alcune buone domande. ma penso che Wayne sia al naso qui - chiede la tua ultima modifica init- a quale potrebbe essere la risposta ... forse? dipende da come / se prevedi di parlarne, da cosa initusi e da dove sono i canali di dati. In generale, le cose tenderanno a risolversi da sole, ecco a cosa initservono. In ogni caso, di solito quando demonizzi un processo allora init. O se vuoi il controllo del lavoro, shell corrente.
Mikeserv,

Hahaha, salute @mikeserv; 4:37 del mattino qui e già la prima risata della giornata. È vero, quella roba funziona sempre in qualche modo. Rimuoverò dmenue vedrò come vado d'accordo con quello che ho imparato. Trovo exec /path/to/Program & exito /bin/bash -c /path/to/Program & exitessere abbastanza utilizzabile. Ma tutti hanno 1cioè initil genitore della Programcosa che va bene per me fintanto che questo ha senso e non viola alcun *nixprincipio di base .
lord.garbage,

@ lord.garbage - è perché exec &, credo. Di solito faccio solo le mie cose dal terminal ... forse potresti trarre qualche utilità dalla domanda di Ben Crowell qui . Ho una risposta lì, ma sono tutti molto bravi. comunque, quando fai da sfondo a un processo e il suo genitore muore come: l' sh -c 'cat & kill $$'hai lasciato orfano, e alla fine finisce per essere raccolto. questo è il lavoro di init - ecco perché ci cadono tutti.
Mikeserv,

Forse una domanda più semplice per ora è: come è possibile ottenere l'albero processo di cui sopra dalla shell: systemd--bash--chromium. Tutti i metodi che provo alla fine porteranno ad un albero di processo nella forma seguente systemd--chromiumquando genererò firefox dalla shell. Come viene demonizzata la shell qui? Non è associato ad alcun terminale.
lord.garbage,

Risposte:


7

Bene, sembra che tu ne abbia una buona comprensione. Per chiarire un po 'di quello che hai,

  • sh -c /path/to/Program è abbastanza simile a

    $ sh 
    % / path / to / Program 
    % Ctrl+ D                             (oppure puoi digitare " exit ") 
    $

    dove si avvia un nuovo processo di shell, fornire il percorso del comando dell'applicazione alla nuova shell e quindi terminare la nuova shell. Ho mostrato la nuova shell dando un prompt diverso a scopo illustrativo; questo probabilmente non accadrà nella vita reale. Il costrutto è principalmente utile per fare cose complicate, come avvolgere più comandi in un bundle, quindi sembrano un singolo comando (una specie di script senza nome monouso) o costruire comandi complicati, possibilmente da variabili di shell. Non lo useresti quasi mai solo per eseguire un singolo programma con argomenti semplici.sh -c "command"

  • 2>&1significa reindirizzare l'errore standard sull'output standard. Questo non ha molto a che fare con &; piuttosto, lo usi quando un comando invia messaggi di errore allo schermo anche se dici e vuoi catturare i messaggi di errore nel file.command > file
  • Reindirizzare l'output a nohup.outè un banale effetto collaterale di nohup. Lo scopo principale di è eseguire in modo asincrono (comunemente noto come "in background" o come "processo indipendente dalla shell", per usare le tue parole) e configurarlo in modo che abbia maggiori possibilità di poter continuare a funzionare se tu termina la shell (es. logout) mentre il comando è ancora in esecuzione.nohup command &command

bash(1)e il Manuale di riferimento di Bash sono buone fonti di informazioni.


7

Esistono alcuni modi per eseguire un programma e staccarlo da un terminale. Uno è quello di eseguirlo sullo sfondo di una subshell , in questo modo (sostituirlo firefoxcon il tuo programma preferito):

(firefox &)

Un altro è rinnegare il processo:

firefox & disown firefox

Se siete curiosi di sapere come lanciatori app di lavoro, dmenuoffre 1 binari e script di shell 2: dmenu, dmenu_pathe dmenu_run, rispettivamente.

dmenu_runconvoglia l'output di dmenu_pathin dmenu, che a sua volta convoglia in qualunque sia la $SHELLvariabile impostata. Se è vuoto, utilizzerà /bin/sh.

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

dmenu_pathè un po 'più complesso, ma in breve fornisce un elenco di file binari nella $PATHvariabile di ambiente e, se possibile, utilizza una cache.

#!/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

Non è necessario avere programmi in esecuzione in shell. Un altro modo di scrivere dmenu_run, senza eseguire il piping in una shell sarebbe:

#!/bin/sh
$(dmenu_path | dmenu "$@") &

6

Mi piace molto la risposta di G-Man. Ma sto rispondendo perché penso che tu abbia preoccupazioni confuse. Come sottolinea Wayne, la risposta migliore è "qualunque cosa ottenga i risultati desiderati".

Nella gestione dei processi Unix, ogni processo ha un genitore. L'unica eccezione a questo è il initprocesso che viene avviato dal sistema operativo all'avvio. È normale che un processo genitore porti con sé tutti i suoi processi figlio quando muore. Questo viene fatto inviando il segnale SIGHUP a tutti i processi figlio; la gestione predefinita di SIGHUP termina il processo.

La generazione di shell dei processi utente non è diversa se si codificassero le chiamate fork (2) / exec (3) nella lingua di propria scelta. La shell è il tuo genitore e se la shell termina (ad es., Ti disconnetti), il processo (i) figlio che viene generato va di pari passo. Le sfumature che descrivi sono solo modi di modificare quel comportamento.

exec /path/to/programè come chiamare exec (3) . Sì, sostituirà la shell con program, mantenendo qualunque genitore abbia avviato la shell.

sh -c /path/to/programin qualche modo crea inutilmente un processo di shell figlio che creerà un processo figlio di program. È utile solo se in /path/to/programrealtà è una sequenza di istruzioni di script e non un file eseguibile. ( sh /path/to/script.shpuò essere utilizzato per eseguire uno script di shell privo di autorizzazioni di esecuzione in una shell inferiore)

/path/to/programcrea un processo "in primo piano", il che significa che la shell attende il completamento del processo prima di intraprendere qualsiasi altra azione. Nel contesto della chiamata di sistema, è come fork (2) / exec (3) / waitpid (2) . Si noti che il figlio eredita stdin / stdout / stderr dal genitore.

/path/to/program &(ignorando il reindirizzamento) crea un "processo in background". Il processo è ancora figlio della shell, ma il genitore non è in attesa che termini.

nohup /path/to/programinvoca nohup (1) per impedire l'invio di SIGHUP programse il terminale di controllo è chiuso. Se è in primo piano o in background è una scelta (anche se più comunemente il processo è in background). Si noti che nohup.outè solo l'output se non si reindirizza altrimenti stdout.

Quando metti un processo in background, se il processo genitore muore, accade una delle due cose. Se il genitore è un terminale di controllo , SIGHUP verrà inviato ai figli. In caso contrario, il processo può essere "orfano" ed è ereditato dal initprocesso.

Quando reindirizzi input / output / error, stai semplicemente collegando i descrittori di file che ogni processo ha a file diversi da quelli che eredita dal suo genitore. Niente di tutto ciò influisce sulla proprietà del processo o sulla profondità dell'albero (ma ha sempre senso reindirizzare tutti e 3 lontano da un terminale per i processi in background).

Detto questo, non credo che dovresti preoccuparti della creazione di processi da parte della shell o delle sub-shell o dei sottoprocessi, a meno che non ci sia un problema specifico che stai affrontando in relazione alla gestione dei processi.


nitpick: sh -c /path/to/programnon eseguirà il programma come script di shell se mancano i bit eseguibili, lo sh /path/to/programfarà. sh -c /path/to/programaprirà semplicemente una shell e verrà eseguito /path/to/programcome comando in quella shell, che fallirà se non è eseguibile.
filbranden,

Bene, se stiamo agitando, sbagliamo entrambi. sh -c /path/to/programlegge i comandi /path/to/programcome input per la shell. Non richiede che il file abbia i permessi di esecuzione ma dovrebbe essere uno script di shell
jwm

Hmmm, lo sh /path/to/programfa. Ci ho appena provato:, echo echo hello world >abc.shpoi sh ./abc.shstampa hello world, mentre sh -c ./abc.shdice sh: ./abc.sh: Permission denied(che è lo stesso che se avessi eseguito direttamente ./abc.shnella shell corrente.) Mi sono perso qualcosa? (O forse non mi sono espresso bene nel commento precedente ...)
filbranden,

Colpa mia. sh -c _something_equivale _something_a digitare al prompt dei comandi, ad eccezione della generazione della shell inferiore. Quindi hai ragione che fallirà se manca il bit di esecuzione (come file). D'altra parte, è possibile fornire una serie di comandi shell simili sh -c "echo hello world"e funzionerà perfettamente. Quindi non richiede che ciò che digiti abbia il bit di esecuzione (o sia addirittura un file), solo che l'interprete della shell può farci qualcosa.
jwm,

Credo che l'OP si riferisse ad alcuni eseguibili compilati o di sistema, quindi è stata assunta l'autorizzazione di esecuzione.
jwm,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.