W terminologii POSIX środowisko podpowłoki jest powiązane z pojęciem środowiska wykonawczego powłoki .
Środowisko podpowłoki jest oddzielnym środowiskiem wykonywania powłoki utworzonym jako duplikat środowiska nadrzędnego. To środowisko wykonawcze obejmuje takie rzeczy, jak otwarte pliki, umask, katalog roboczy, zmienne / funkcje / aliasy powłoki ...
Zmiany w tym środowisku podpowłoki nie wpływają na środowisko nadrzędne.
Tradycyjnie w powłoce Bourne'a lub ksh88, na której oparta jest specyfikacja POSIX, zostało to zrobione przez rozwarcie procesu potomnego.
Obszary, w których POSIX wymaga lub pozwala na uruchamianie komend w środowisku podpowłoki, to te, w których tradycyjnie ksh88 rozwidlał proces powłoki potomnej.
Nie zmusza to jednak implementacji do użycia do tego procesu potomnego.
Powłoka może zamiast tego wybrać wdrożenie oddzielnego środowiska wykonawczego w dowolny sposób.
Na przykład, ksh93 robi to poprzez zapisywanie atrybutów nadrzędnego środowiska wykonawczego i przywracanie ich po zakończeniu środowiska podpowłoki w kontekstach, w których można uniknąć rozwidlenia (jako optymalizacja, ponieważ rozwidlanie jest dość kosztowne w większości systemów).
Na przykład w:
cd /foo; pwd
(cd /bar; pwd)
pwd
POSIX wymaga cd /foo
działania w osobnym środowisku i wypisywania czegoś takiego:
/foo
/bar
/foo
Nie wymaga działania w osobnym procesie. Na przykład, jeśli stdout staje się zepsutą rurą, pwd
uruchom w środowisku podpowłoki może bardzo dobrze, że SIGPIPE zostanie wysłany do jedynego procesu powłoki.
Większość powłok, w tym bash
, implementuje go, oceniając kod wewnątrz (...)
procesu potomnego (podczas gdy proces macierzysty czeka na jego zakończenie), ale ksh93 zamiast tego uruchomi kod wewnątrz (...)
, wszystko w tym samym procesie:
- pamiętaj, że jest w środowisku podpowłoki.
- następnie
cd
zapisz poprzedni katalog roboczy (zwykle na deskryptorze pliku otwartym za pomocą O_CLOEXEC), zapisz wartość zmiennych OLDPWD, PWD i wszystko, co cd
może zmodyfikować, a następnie wykonajchdir("/bar")
- po powrocie z podpowłoki przywracany jest bieżący katalog roboczy (
fchdir()
na tym zapisanym fd) i wszystko inne, co podpowłoka mogła zmodyfikować.
Istnieją konteksty, w których nie można uniknąć procesu potomnego. ksh93 nie rozwidla się:
var=$(subshell)
(subshell)
Ale robi to
{ subshell; } &
{ subshell; } | other command
Oznacza to, że przypadki muszą przebiegać w osobnych procesach, aby mogły działać równolegle.
Optymalizacje ksh93 idą dalej. Na przykład podczas pobytu w
var=$(pwd)
większość powłok rozwidliłaby proces, gdyby dziecko uruchomiło pwd
polecenie ze stdout przekierowanym do potoku, pwd
zapisało bieżący katalog roboczy do tego potoku, a proces nadrzędny odczytałby wynik na drugim końcu potoku, ksh93
wirtualizuje to wszystko wymagające widelca ani rury. Widelec i potok byłyby używane tylko dla poleceń niewbudowanych.
Zauważ, że istnieją inne konteksty niż podpowłoki, dla których powłoki rozwidlają proces potomny. Na przykład, aby uruchomić polecenie zapisane w osobnym pliku wykonywalnym (który nie jest skryptem przeznaczonym dla tego samego interpretera powłoki), powłoka musiałaby rozwidlić proces, aby uruchomić w nim to polecenie, ponieważ inaczej nie byłby w stanie uruchomić więcej poleceń po powrocie tego polecenia.
W:
/bin/echo "$((n += 1))"
To nie jest podpowłoka, polecenie zostanie ocenione w bieżącym środowisku wykonywania powłoki, n
zmienna bieżącego środowiska wykonywania powłoki zostanie zwiększona, ale powłoka rozwinie proces potomny w celu wykonania tego /bin/echo
polecenia z rozszerzeniem $((n += 1))
as argument .
Wiele powłok implementuje optymalizację w taki sposób, że nie rozwidla procesu potomnego, aby uruchomić to polecenie zewnętrzne, jeśli jest to ostatnie polecenie skryptu lub podpowłoki (dla tych podpowłok, które są zaimplementowane jako procesy potomne). ( bash
robi to jednak tylko wtedy, gdy to polecenie jest jedynym poleceniem podpowłoki).
Oznacza to, że w przypadku tych powłok, jeśli ostatnie polecenie w podpowłoce jest poleceniem zewnętrznym, podpowłoka nie powoduje odrodzenia dodatkowego procesu. Jeśli porównasz:
a=1; /bin/echo "$a"; a=2; /bin/echo "$a"
z
a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")
powstanie taka sama liczba procesów, tylko w drugim przypadku drugie rozwidlenie jest zrobione wcześniej, aby a=2
było uruchamiane w środowisku podpowłoki.