Dalam terminologi POSIX, lingkungan subkulit terkait dengan gagasan Lingkungan Eksekusi Shell .
Lingkungan subkulit adalah lingkungan eksekusi shell terpisah yang dibuat sebagai duplikat dari lingkungan induk. Lingkungan eksekusi itu mencakup hal-hal seperti file yang dibuka, umask, direktori kerja, variabel shell / fungsi / alias ...
Perubahan pada lingkungan subkulit itu tidak memengaruhi lingkungan induk.
Secara tradisional di shell Bourne atau ksh88 yang menjadi dasar spesifikasi POSIX, yang dilakukan dengan forking proses anak.
Area-area di mana POSIX membutuhkan atau mengizinkan perintah untuk dijalankan dalam lingkungan subshell adalah area-area di mana secara tradisional ksh88 memotong proses shell anak.
Namun itu tidak memaksa implementasi untuk menggunakan proses anak untuk itu.
Shell dapat memilih untuk mengimplementasikan lingkungan eksekusi yang terpisah dengan cara apa pun yang mereka suka.
Sebagai contoh, ksh93 melakukannya dengan menyimpan atribut dari lingkungan eksekusi induk dan mengembalikannya setelah penghentian lingkungan subkulit dalam konteks di mana forking dapat dihindari (sebagai optimasi karena forking cukup mahal pada kebanyakan sistem).
Misalnya, di:
cd /foo; pwd
(cd /bar; pwd)
pwd
POSIX memang mengharuskan cd /foo
untuk berjalan di lingkungan yang terpisah dan itu untuk menampilkan sesuatu seperti:
/foo
/bar
/foo
Itu tidak mengharuskannya untuk dijalankan dalam proses terpisah. Sebagai contoh, jika stdout menjadi pipa yang rusak, pwd
jalankan di lingkungan subshell bisa saja SIGPIPE dikirim ke satu-satunya proses shell.
Sebagian besar shell termasuk bash
akan mengimplementasikannya dengan mengevaluasi kode di (...)
dalam proses anak (sementara proses induk menunggu penghentiannya), tetapi ksh93 sebaliknya akan menjalankan kode di dalam (...)
, semua dalam proses yang sama:
- ingat itu dalam lingkungan subkulit.
- setelah itu
cd
, simpan direktori kerja sebelumnya (biasanya pada deskriptor file dibuka dengan O_CLOEXEC), simpan nilai OLDPWD, variabel PWD dan apa pun yang cd
mungkin memodifikasi dan kemudian lakukanchdir("/bar")
- sekembalinya dari subkulit, direktori kerja saat ini dipulihkan (dengan
fchdir()
pada yang disimpan fd), dan segala sesuatu yang subkulit mungkin telah dimodifikasi.
Ada konteks di mana proses anak tidak dapat dihindari. ksh93 tidak bercabang di:
var=$(subshell)
(subshell)
Tetapi apakah dalam
{ subshell; } &
{ subshell; } | other command
Artinya, kasus-kasus di mana hal-hal harus berjalan dalam proses terpisah sehingga mereka dapat berjalan secara bersamaan.
optimisasi ksh93 melangkah lebih jauh dari itu. Misalnya, saat dalam
var=$(pwd)
kebanyakan shell akan melakukan proses, meminta anak menjalankan pwd
perintah dengan stdout diarahkan ke pipa, pwd
menulis direktori kerja saat ini ke pipa itu, dan proses induk membaca hasilnya di ujung lain pipa, ksh93
virtualisasi semua itu dengan tidak membutuhkan garpu atau pipa. Garpu dan pipa hanya akan digunakan untuk perintah non-builtin.
Perhatikan bahwa ada konteks lain yang mensub kulit shell proses anak. Misalnya, untuk menjalankan perintah yang disimpan dalam executable yang terpisah (dan itu bukan skrip yang dimaksudkan untuk penerjemah shell yang sama), sebuah shell harus melakukan proses untuk menjalankan perintah itu di dalamnya karena jika tidak maka tidak akan dapat menjalankan lebih banyak perintah setelah perintah itu kembali.
Di:
/bin/echo "$((n += 1))"
Itu bukan subkulit, perintah akan dievaluasi dalam lingkungan eksekusi shell saat ini, n
variabel lingkungan eksekusi shell saat ini akan bertambah, tetapi shell akan memotong proses anak untuk mengeksekusi /bin/echo
perintah itu di dalamnya dengan perluasan $((n += 1))
sebagai argumen .
Banyak shell mengimplementasikan optimasi karena mereka tidak melakukan proses anak untuk menjalankan perintah eksternal jika itu perintah terakhir dari skrip atau subkulit (untuk subkulit yang diimplementasikan sebagai proses anak). ( bash
Namun hanya melakukannya jika perintah itu adalah satu-satunya perintah dari subkulit).
Apa artinya itu adalah, dengan cangkang tersebut, jika perintah terakhir dalam subkulit adalah perintah eksternal, subkulit tersebut tidak menyebabkan proses tambahan untuk dimunculkan. Jika Anda membandingkan:
a=1; /bin/echo "$a"; a=2; /bin/echo "$a"
dengan
a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")
akan ada jumlah proses yang sama dibuat, hanya dalam kasus kedua, garpu kedua dilakukan sebelumnya sehingga a=2
dijalankan di lingkungan subkulit.