Jadi, saya pikir saya memiliki pemahaman yang baik tentang ini, tetapi hanya menjalankan tes (sebagai tanggapan atas percakapan di mana saya tidak setuju dengan seseorang) dan menemukan bahwa pemahaman saya salah…
Sedetail mungkin apa yang sebenarnya terjadi ketika saya menjalankan file di shell saya? Yang saya maksud adalah, jika saya mengetik:./somefile some arguments
ke dalam shell saya dan tekan kembali (dan somefile
ada di cwd, dan saya telah membaca+mengeksekusi izin pada somefile
) lalu apa yang terjadi di balik tenda?
Saya pikir jawabannya adalah:
- Shell membuat panggilan sys ke
exec
, meneruskan jalur kesomefile
- Kernel memeriksa
somefile
dan melihat angka ajaib dari file untuk menentukan apakah itu format yang dapat ditangani oleh prosesor - Jika angka ajaib menunjukkan bahwa file tersebut dalam format yang dapat dieksekusi oleh prosesor, maka
- proses baru dibuat (dengan entri di tabel proses)
somefile
dibaca/dipetakan ke memori. Tumpukan dibuat dan eksekusi melompat ke titik masuk kodesomefile
, denganARGV
diinisialisasi ke array parameter (char**
,["some","arguments"]
)
- Jika angka ajaib adalah shebang maka
exec()
memunculkan proses baru seperti di atas, tetapi eksekusi yang digunakan adalah penerjemah yang dirujuk oleh Shebang (mis./bin/bash
atau/bin/perl
) dansomefile
diteruskan keSTDIN
- Jika file tidak memiliki angka ajaib yang valid, maka kesalahan seperti “file tidak valid (angka ajaib buruk):Kesalahan format Exec” terjadi
Namun seseorang memberi tahu saya bahwa jika file tersebut adalah teks biasa, maka Shell mencoba menjalankan perintah (seolah-olah saya telah mengetik bash somefile
). Saya tidak percaya ini, tetapi saya baru saja mencobanya, dan itu benar. Jadi saya jelas memiliki beberapa kesalahpahaman tentang apa yang sebenarnya terjadi di sini, dan ingin memahami mekanismenya.
Apa sebenarnya yang terjadi ketika saya menjalankan file di shell saya? (sedetail mungkin…)
Jawaban yang Diterima:
Jawaban pasti untuk "bagaimana program dijalankan" di Linux adalah sepasang artikel di LWN.net berjudul, cukup mengejutkan, Bagaimana program dijalankan dan Bagaimana program dijalankan:ELF biner. Artikel pertama membahas skrip secara singkat. (Tepatnya jawaban pasti ada di kode sumber, tetapi artikel ini lebih mudah dibaca dan menyediakan tautan ke kode sumber.)
Sebuah eksperimen kecil menunjukkan bahwa Anda cukup banyak melakukannya dengan benar, dan bahwa eksekusi file yang berisi daftar perintah sederhana, tanpa shebang, perlu ditangani oleh shell. Halaman manual execve(2) berisi kode sumber untuk program pengujian, execve; kita akan menggunakannya untuk melihat apa yang terjadi tanpa cangkang. Pertama, tulis skrip tes, testscr1
, berisi
#!/bin/sh
pstree
dan satu lagi, testscr2
, hanya berisi
pstree
Jadikan keduanya dapat dieksekusi, dan verifikasi bahwa keduanya dijalankan dari shell:
chmod u+x testscr[12]
./testscr1 | less
./testscr2 | less
Sekarang coba lagi, menggunakan execve
(dengan asumsi Anda membuatnya di direktori saat ini):
./execve ./testscr1
./execve ./testscr2
testscr1
masih berjalan, tetapi testscr2
menghasilkan
execve: Exec format error
Ini menunjukkan bahwa shell menangani testscr2
berbeda. Itu tidak memproses skrip itu sendiri, masih menggunakan /bin/sh
untuk melakukannya; ini dapat diverifikasi dengan mem-piping testscr2
menjadi less
:
./testscr2 | less -ppstree
Di sistem saya, saya mendapatkan
|-gnome-terminal--+-4*[zsh]
| |-zsh-+-less
| | `-sh---pstree
Seperti yang Anda lihat, ada shell yang saya gunakan, zsh
, yang dimulai dengan less
, dan cangkang kedua, sh
. biasa (dash
di sistem saya), untuk menjalankan skrip, yang menjalankan pstree
. Dalam zsh
ini ditangani oleh zexecve
di Src/exec.c
:shell menggunakan execve(2)
untuk mencoba menjalankan perintah, dan jika gagal, ia membaca file untuk melihat apakah ia memiliki Shebang, memprosesnya sesuai (yang juga akan dilakukan oleh kernel), dan jika gagal ia mencoba menjalankan file dengan sh
, selama tidak membaca nol byte dari file:
for (t0 = 0; t0 != ct; t0++)
if (!execvebuf[t0])
break;
if (t0 == ct) {
argv[-1] = "sh";
winch_unblock();
execve("/bin/sh", argv - 1, newenvp);
}
bash
memiliki perilaku yang sama, diimplementasikan di execute_cmd.c
dengan komentar yang membantu (seperti yang ditunjukkan oleh taliezin):
Jalankan perintah sederhana yang mudah-mudahan didefinisikan dalam file disk
di suatu tempat.
fork ()
- menghubungkan pipa
- cari perintahnya
- lakukan pengalihan
execve ()
- Jika
execve
gagal, lihat apakah file memiliki mode yang dapat dieksekusi yang disetel.
Jika demikian, dan itu bukan direktori, maka jalankan isinya sebagai
skrip shell.
POSIX mendefinisikan satu set fungsi, yang dikenal sebagai exec(3)
fungsi, yang membungkus execve(2)
dan menyediakan fungsi ini juga; lihat jawaban muru untuk detailnya. Di Linux setidaknya fungsi-fungsi ini diimplementasikan oleh pustaka C, bukan oleh kernel.