Comment démarrer «correctement» une application à partir d'un shell

21

J'ai du mal à formuler la question avec précision, mais je ferai de mon mieux. J'utilise dwmcomme gestionnaire de fenêtres par défaut etdmenucomme lanceur d'applications. J'utilise à peine les applications GUI en dehors de mon navigateur. La plupart de mon travail se fait directement à partir de la ligne de commande. De plus, je suis un grand fan du minimalisme concernant les systèmes d'exploitation, les applications, etc. L'un des outils dont je ne me suis jamais débarrassé était un lanceur d'applications. Principalement parce que je ne comprends pas exactement comment fonctionnent les lanceurs d'applications / ce qu'ils font. Même une recherche approfondie sur Internet ne révèle qu'une vague explication. Ce que je veux faire, c'est me débarrasser même de mon lanceur d'applications car en dehors de la création de l'application, je n'ai absolument aucune utilité. Pour ce faire, je voudrais vraiment savoir comment démarrer "correctement" les applications à partir du shell. Ainsi, la signification de "correctement" peut être approximée par "comme le ferait un lanceur d'application".

Je connais les façons suivantes de générer des processus à partir du shell:

  1. exec /path/to/Program remplacer le shell par la commande spécifiée sans créer de nouveau processus
  2. sh -c /path/to/Program lancer un processus dépendant du shell
  3. /path/to/Program lancer un processus dépendant du shell
  4. /path/to/Program 2>&1 & lancer un processus indépendant du shell
  5. nohup /path/to/Program & lancer un processus indépendant du shell et rediriger la sortie vers nohup.out

Mise à jour 1: je peux illustrer ce que fait par exemple sa dmenureconstruction à partir d'appels répétés ps -efldans différentes conditions. Il génère un nouveau shell /bin/bashet en tant qu'enfant de ce shell, l'application /path/to/Program. Tant que l'enfant est là, la coquille restera aussi longtemps. (La façon dont il gère cela me dépasse ...) En revanche, si vous sortez nohup /path/to/Program &d'un shell, /bin/bashle programme deviendra l'enfant de ce shell MAIS si vous quittez ce shell, le parent du programme sera le processus le plus élevé. Donc, si le premier processus était par exemple /sbin/init verboseet qu'il a PPID 1alors, il sera le parent du programme. Voici ce que j'ai essayé d'expliquer à l'aide d'un graphique: a chromiumété lancé via dmenu, a firefoxété lancé en utilisant 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

Mise à jour 2: Je suppose que la question peut également se résumer à: Quel devrait être le parent d'un processus? Doit-il par exemple être un shell ou doit-il être le initprocessus c'est-à-dire le processus avec PID 1?

lord.garbage
la source
3
La réponse laconique à votre question serait «tout ce qui obtient les résultats que vous souhaitez».
Wayne Werner
1
dang, ordures - vous posez de bonnes questions. mais je pense que wayne est sur le nez ici - votre dernier montage demande init- à quoi pourrait être la réponse ... peut-être? cela dépend de comment / si vous prévoyez de lui parler, de ce que initvous utilisez et de l'emplacement des canaux de données. En général, ces trucs auront tendance à s'arranger - c'est à ça que initsert. Dans tous les cas, généralement lorsque vous démonifiez un processus init. Ou si vous voulez contrôler le travail, le shell actuel.
mikeserv
Hahaha, bravo @mikeserv; 4h37 du matin ici et déjà le premier rire de la journée. Certes, ce genre de choses fonctionne toujours d'une manière ou d'une autre. Je vais supprimer dmenuet voir comment je m'entends avec ce que j'ai appris. Je trouve exec /path/to/Program & exitou /bin/bash -c /path/to/Program & exitêtre tout à fait utilisable. Mais ils font tous, 1c'est-à init- dire le parent de ce Programqui me convient tant que cela a du sens et ne viole aucun *nixprincipe de base .
lord.garbage
@ lord.garbage - c'est parce que vous exec &, je pense. Habituellement, je fais juste mes affaires depuis le terminal ... peut-être que vous pourriez utiliser la question de Ben Crowell ici . J'ai une réponse là-bas, mais elles sont toutes très bonnes. de toute façon, quand vous fondez un processus et son parent meurt comme: sh -c 'cat & kill $$'vous l'orphelin, et il finit par être récolté par la suite. c'est le travail d'init - c'est pourquoi ils y tombent tous.
mikeserv
Peut-être un pour est maintenant question simple: Comment est - il possible d'obtenir l'arborescence des processus ci - dessus de la coquille: systemd--bash--chromium. Toutes les méthodes que j'essaie mèneront finalement à un arbre de processus de la forme suivante systemd--chromiumlorsque j'apparaît firefox à partir du shell. Comment l'obus est-il diabolisé ici? Il n'est associé à aucun terminal.
lord.garbage

Réponses:

7

Eh bien, vous semblez en avoir une assez bonne compréhension. Pour clarifier une partie de ce que vous avez,

  • sh -c /path/to/Program est assez similaire à

    $ sh 
    % / path / to / Program 
    % Ctrl+ D                             (ou vous pouvez taper " exit ") 
    $

    où vous démarrez un nouveau processus de shell, fournissez le chemin de commande d'application au nouveau shell, puis laissez le nouveau shell se terminer. J'ai montré le nouveau shell donnant une invite différente à des fins d'illustration; cela n'arriverait probablement pas dans la vraie vie. La construction est surtout utile pour faire des choses délicates, comme encapsuler plusieurs commandes dans un bundle, de sorte qu'elles ressemblent à une seule commande (sorte de script sans nom à usage unique), ou pour construire des commandes compliquées, éventuellement à partir de variables de shell. Vous ne l'utiliseriez presque jamais uniquement pour exécuter un seul programme avec des arguments simples.sh -c "command"

  • 2>&1signifie rediriger l'erreur standard vers la sortie standard. Cela n'a pas vraiment grand-chose à voir avec &; vous l'utilisez plutôt lorsqu'une commande envoie des messages d'erreur à l'écran même si vous dites et que vous souhaitez capturer les messages d'erreur dans le fichier.command > file
  • La redirection de la sortie vers nohup.outest un effet secondaire trivial de nohup. Le but principal de est de s'exécuter de manière asynchrone (communément appelé «en arrière-plan», ou en tant que «processus indépendant du shell», pour utiliser vos mots) et de le configurer afin qu'il ait de meilleures chances de pouvoir continuer à s'exécuter si vous terminer le shell (par exemple, déconnexion) pendant que la commande est toujours en cours d'exécution.nohup command &command

bash(1)et le Bash Reference Manual sont de bonnes sources d'information.

G-Man dit «Réintègre Monica»
la source
7

Il existe plusieurs façons d'exécuter un programme et de le détacher d'un terminal. La première consiste à l'exécuter en arrière-plan d'un sous-shell , comme ceci (remplacez-le firefoxpar votre programme préféré):

(firefox &)

Une autre consiste à renier le processus:

firefox & disown firefox

Si vous êtes curieux de savoir comment les lanceurs d'applications de travail, dmenufournit 1 binaire et 2 scripts shell: dmenu, dmenu_pathet dmenu_run, respectivement.

dmenu_rundmenu_pathredirige la sortie de dans dmenu, qui à son tour redirige vers la $SHELLvariable définie. S'il est vide, il utilisera /bin/sh.

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

dmenu_path est un peu plus complexe, mais en bref, il fournit une liste de binaires dans votre $PATH variable d'environnement, et utilise un cache si possible.

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

Il n'est pas nécessaire que les programmes s'exécutent dans des shells. Une autre façon d'écrire dmenu_run, sans tuyauterie dans un shell serait:

#!/bin/sh
$(dmenu_path | dmenu "$@") &
svenper
la source
6

J'aime beaucoup la réponse de G-Man. Mais je réponds parce que je pense que vous confondez les préoccupations. Comme Wayne le fait remarquer, la meilleure réponse est «tout ce qui obtient les résultats souhaités».

Dans la gestion des processus Unix, chaque processus a un parent. La seule exception à cela est leinit processus qui est démarré par le système d'exploitation au démarrage. Il est normal qu'un processus parent emporte tous ses processus enfants avec lui lorsqu'il meurt. Cela se fait en envoyant le signal SIGHUP à tous les processus enfants; la gestion par défaut de SIGHUP met fin au processus.

La génération de shell des processus utilisateur n'est pas différente de celle que vous avez codée pour les appels fork (2) / exec (3) dans la langue de votre choix. Le shell est votre parent, et si le shell se termine (par exemple, vous vous déconnectez), le ou les processus enfants qu'il génère vont de pair. Les nuances que vous décrivez ne sont que des moyens de modifier ce comportement.

exec /path/to/programc'est comme appeler exec (3) . Oui, il remplacera votre shell par program, en gardant le parent qui a lancé le shell.

sh -c /path/to/programcrée inutilement un processus shell enfant qui créera un processus enfant de program. Il n'est utile que s'il /path/to/programs'agit en fait d'une séquence d'instructions de script et non d'un fichier exécutable. ( sh /path/to/script.shpeut être utilisé pour exécuter un script shell sans autorisations d'exécution dans un shell inférieur)

/path/to/programcrée un processus de "premier plan", ce qui signifie que le shell attend la fin du processus avant de prendre toute autre action. Dans le contexte d'un appel système, c'est comme fork (2) / exec (3) / waitpid (2) . Notez que l'enfant hérite de stdin / stdout / stderr du parent.

/path/to/program &(ignorer la redirection) crée un "processus d'arrière-plan". Le processus est toujours un enfant du shell, mais le parent n'attend pas qu'il se termine.

nohup /path/to/programinvoque nohup (1) pour empêcher l'envoi de SIGHUP à programsi le terminal de contrôle est fermé. Que ce soit au premier plan ou en arrière-plan est un choix (bien que le plus souvent le processus soit en arrière-plan). Notez que ce nohup.outn'est que la sortie si vous ne redirigez pas autrement stdout.

Lorsque vous mettez un processus en arrière-plan, si le processus parent meurt, l'une des deux choses se produit. Si le parent est un terminal de contrôle , SIGHUP sera envoyé aux enfants. Si ce n'est pas le cas, le processus peut être "orphelin" et hérité par le initprocessus.

Lorsque vous redirigez les entrées / sorties / erreurs, vous connectez simplement les descripteurs de fichiers de chaque processus à des fichiers différents de ceux dont il hérite de son parent. Rien de tout cela n'affecte la propriété du processus ou la profondeur de l'arborescence (mais il est toujours judicieux de rediriger les 3 loin d'un terminal pour les processus d'arrière-plan).

Cela dit, je ne pense pas que vous devriez vous préoccuper de la création de processus par le shell ou les sous-shells ou les sous-processus, à moins qu'il n'y ait un problème spécifique que vous abordez lié à la gestion des processus.

jwm
la source
nitpick: n'exécutera sh -c /path/to/programpas le programme en tant que script shell s'il manque les bits exécutables, le sh /path/to/programfera. sh -c /path/to/programouvrira simplement un shell et s'exécutera /path/to/programcomme une commande dans ce shell, qui échouera s'il n'est pas exécutable.
filbranden
Eh bien, si nous sommes tatillons, nous avons tous deux tort. sh -c /path/to/programlit les commandes depuis /path/to/programcomme entrée dans le shell. Il ne nécessite pas que le fichier dispose d'une autorisation d'exécution, mais il doit s'agir d'un script shell
jwm
Hmmm, sh /path/to/programfait ça. Je viens de l'essayer moi-même :, echo echo hello world >abc.shpuis sh ./abc.shimprime hello world, tandis que sh -c ./abc.shdit sh: ./abc.sh: Permission denied(ce qui est le même que si vous exécutiez ./abc.shdirectement dans le shell actuel.) Ai-je raté quelque chose? (Ou peut-être que je ne me suis pas bien exprimé dans le commentaire précédent ...)
filbranden
Ma faute. sh -c _something_revient à taper simplement _something_à l'invite de commande, à l'exception du frai du shell inférieur. Vous avez donc raison qu'il échouera s'il manque le bit d'exécution (sous forme de fichier). D'un autre côté, vous pouvez fournir une série de commandes shell comme sh -c "echo hello world"et cela fonctionnera très bien. Donc, cela ne nécessite pas que ce que vous tapez ait le bit d'exécution (ou même un fichier), seulement que l'interpréteur de shell puisse faire quelque chose avec.
jwm
Je crois que l'OP faisait référence à un exécutable compilé ou système, donc l'autorisation d'exécution était supposée.
jwm