Mengapa mungkin memindahkan program yang sedang berjalan di Ubuntu?

24

Saya baru menyadari bahwa saya dapat memindahkan program aktif yang sedang berjalan ke direktori yang berbeda. Dalam pengalaman saya itu tidak mungkin di MacO atau Windows. Bagaimana cara kerjanya di Ubuntu?

Sunting: Saya pikir itu tidak mungkin pada Mac tetapi ternyata itu mungkin karena komentar memverifikasi. Itu hanya tidak mungkin pada Windows mungkin. Terima kasih untuk semua jawaban.

n0.ob
sumber
2
Cukup banyak pembohong lintas situs: stackoverflow.com/a/196910/1394393 .
jpmc26
1
Anda tidak dapat rename(2)menjalankan executable di OS X? Apa yang terjadi, apa yang Anda dapatkan EBUSYatau sesuatu? Mengapa itu tidak berhasil? Halaman manual rename (2) tidak mendokumentasikan ETXTBUSYuntuk panggilan sistem itu, dan hanya berbicara tentang EBUSYkemungkinan untuk mengganti nama direktori, jadi saya tidak tahu sistem POSIX bahkan dapat melarang penggantian nama executable.
Peter Cordes
3
aplikasi macOS juga dapat dipindahkan saat dijalankan, tidak dibuang begitu saja. Saya kira beberapa aplikasi mungkin salah setelah itu, misalnya, jika mereka menyimpan URL file ke sumber biner atau paket mereka di suatu tempat sebagai variabel alih-alih menghasilkan URL seperti itu melalui NSBundle et al. Saya menduga itu kepatuhan POSIX macOS '.
Constantino Tsarouhas
1
Ini sebenarnya berfungsi sesuai keinginan Linux, Anda harus tahu apa yang Anda lakukan. : P
userDepth
2
Saya kira cara lain untuk memikirkannya adalah, mengapa itu tidak mungkin? Hanya karena Windows tidak membiarkan Anda, tidak berarti itu pada dasarnya tidak mungkin karena cara kerja proses atau sesuatu.
Thomas

Jawaban:

32

Biarkan saya memecahnya.

Saat Anda menjalankan yang dapat dieksekusi, urutan panggilan sistem dijalankan, terutama fork()dan execve():

  • fork()menciptakan proses turunan dari proses pemanggilan, yang (sebagian besar) merupakan salinan persis dari induknya, keduanya masih menjalankan executable yang sama (menggunakan halaman memori copy-on-write, sehingga efisien). Ini mengembalikan dua kali: Pada induknya, ia mengembalikan PID anak. Pada anak, itu mengembalikan 0. Biasanya, proses panggilan anak mengeksekusi segera:

  • execve()mengambil path lengkap ke executable sebagai argumen dan menggantikan proses pemanggilan dengan executable. Pada titik ini proses yang baru dibuat mendapatkan ruang alamat virtual sendiri yaitu memori virtual, dan eksekusi dimulai pada titik masuknya (dalam keadaan yang ditentukan oleh platform aturan ABI untuk proses baru).

Pada titik ini, ELF loader kernel telah memetakan segmen teks dan data yang dapat dieksekusi ke dalam memori, seolah-olah ia telah menggunakan mmap()panggilan sistem (masing-masing dengan pemetaan read-only dan private read-write). BSS juga dipetakan seolah-olah dengan MAP_ANONYMOUS. (BTW, saya mengabaikan tautan dinamis di sini untuk kesederhanaan: Linker dinamis open()s dan mmap()semua perpustakaan dinamis sebelum melompat ke titik masuk utama yang dapat dieksekusi.)

Hanya beberapa halaman yang benar-benar dimuat ke dalam memori dari disk sebelum yang baru dijalankan () ed mulai menjalankan kodenya sendiri. Halaman selanjutnya adalah halaman permintaan sesuai kebutuhan, jika / ketika proses menyentuh bagian-bagian dari ruang alamat virtualnya. (Pra-memuat halaman kode atau data apa pun sebelum mulai mengeksekusi kode ruang pengguna hanya merupakan pengoptimalan kinerja.)


File yang dapat dieksekusi diidentifikasi oleh inode pada level yang lebih rendah. Setelah file mulai dieksekusi, kernel menjaga konten file tetap utuh dengan referensi inode, bukan dengan nama file, seperti untuk deskriptor file terbuka atau pemetaan memori yang didukung file. Jadi Anda dapat dengan mudah memindahkan executable ke lokasi lain dari sistem file atau bahkan pada sistem file yang berbeda. Sebagai catatan tambahan, untuk memeriksa berbagai stat proses, Anda dapat mengintip ke /proc/PIDdirektori (PID adalah ID Proses dari proses yang diberikan). Anda bahkan dapat membuka file yang dapat dieksekusi sebagai /proc/PID/exe, bahkan sudah dihapus dari disk.


Sekarang mari kita gali langkahnya:

Saat Anda memindahkan file dalam sistem file yang sama, panggilan sistem yang dijalankan adalah rename(), yang hanya mengganti nama file ke nama lain, inode file tetap sama.

Sedangkan antara dua sistem file yang berbeda, dua hal terjadi:

  • Konten file yang disalin terlebih dahulu ke lokasi baru, oleh read()danwrite()

  • Setelah itu, file tidak terhubung dari direktori sumber menggunakan unlink()dan jelas file tersebut akan mendapatkan inode baru pada sistem file baru.

rmsebenarnya hanya unlink()-ing file yang diberikan dari pohon direktori, jadi memiliki izin menulis pada direktori akan membuat Anda cukup untuk menghapus file dari direktori itu.

Sekarang untuk bersenang-senang, bayangkan apa yang terjadi ketika Anda memindahkan file antara dua file files dan Anda tidak memiliki izin untuk unlink()file dari sumber?

Nah, file akan disalin ke tujuan pada awalnya ( read(), write()) dan kemudian unlink()akan gagal karena izin tidak mencukupi. Jadi, file tersebut akan tetap berada di kedua sistem file !!

heemayl
sumber
5
Memori virtual dan fisik Anda agak membingungkan. Deskripsi Anda tentang cara program dimuat ke memori fisik tidak akurat. Panggilan sistem exec sama sekali tidak menyalin berbagai bagian yang dapat dieksekusi ke memori fisik tetapi hanya memuat yang diperlukan untuk memulai proses. Setelah itu, halaman-halaman yang diperlukan dimuat saat diminta, mungkin lama kemudian. File byte yang dapat dieksekusi adalah bagian dari memori virtual proses dan mungkin dibaca, dan mungkin dibaca lagi selama masa proses.
jlliagre
@ jlliagre Diedit, saya harap ini diklarifikasi sekarang. Terima kasih.
heemayl
6
"Prosesnya tidak menggunakan sistem file lagi" pernyataan masih dipertanyakan.
jlliagre
2
Pemahaman dasar bahwa file yang diberikan dalam sistem file tidak secara langsung diidentifikasi oleh nama file harus jauh lebih jelas.
Thorbjørn Ravn Andersen
2
Masih ada ketidakakuratan dalam pembaruan Anda. The mmapdan unmapsistem panggilan tidak digunakan untuk memuat dan membongkar halaman pada permintaan, halaman dimuat oleh kernel ketika mengakses mereka menghasilkan kesalahan halaman, halaman yang diturunkan dari memori ketika OS terasa RAM akan lebih baik digunakan untuk sesuatu yang lain. Tidak ada panggilan sistem yang terlibat dalam operasi muat / bongkar ini.
jlliagre
14

Ya, itu cukup lurus ke depan. Mari kita ambil executable bernama / usr / local / bin / whoopdeedoo. Itu hanya referensi yang disebut inode (struktur dasar file pada Unix Filesystems). Inode yang ditandai "sedang digunakan".

Sekarang ketika Anda menghapus atau memindahkan file / usr / local / whoopdeedoo, satu-satunya hal yang dipindahkan (atau dihapus) adalah referensi ke inode. Inode itu sendiri tetap tidak berubah. Pada dasarnya itu.

Saya harus memverifikasinya, tetapi saya percaya Anda dapat melakukan ini pada sistem file Mac OS X juga.

Windows mengambil pendekatan yang berbeda. Mengapa? Siapa tahu...? Saya tidak akrab dengan internal NTFS. Secara teoritis, semua sistem file yang menggunakan referensi ke struktur intenal untuk nama file harus dapat melakukan ini.

Saya akui, saya terlalu disederhanakan, tetapi bacalah bagian "Implikasi" di Wikipedia, yang melakukan pekerjaan yang jauh lebih baik daripada saya.

jawtheshark
sumber
1
Nah jika Anda menggunakan jalan pintas di Windows untuk memulai executable, Anda dapat menghapus pintasan juga, jika Anda ingin membandingkannya seperti itu, mungkin? = 3
Ray
2
Tidak, itu seperti menyeka tautan simbolik. Di suatu tempat di komentar lain, dinyatakan bahwa perilaku ini disebabkan oleh dukungan sebelumnya dengan sistem file FAT. Itu terdengar seperti alasan yang mungkin.
jawtheshark
1
Ini tidak ada hubungannya secara khusus dengan inode. NTFS menggunakan catatan MFT untuk melacak status file, dan FAT menggunakan entri direktori untuk ini, tetapi Linux masih bekerja dengan cara yang sama dengan sistem file ini - dari sudut pandang pengguna.
Ruslan
13

Satu hal yang kelihatannya hilang dari semua jawaban lain adalah: setelah file dibuka dan program menyimpan deskriptor file terbuka, file tidak akan dihapus dari sistem hingga deskriptor file ditutup.

Upaya untuk menghapus inode yang dirujuk akan ditunda hingga file ditutup: pengubahan nama dalam sistem file yang sama atau berbeda tidak dapat memengaruhi file yang terbuka, terlepas dari perilaku penggantian nama, atau secara eksplisit menghapus atau menimpa file dengan yang baru. Satu-satunya cara di mana Anda dapat mengacaukan file adalah dengan secara eksplisit membuka inode dan mengacaukan isinya, bukan dengan operasi pada direktori seperti mengganti nama / menghapus file.

Terlebih lagi ketika kernel mengeksekusi file, ia menyimpan referensi ke file yang dapat dieksekusi dan ini akan mencegah modifikasi apa pun selama eksekusi.

Jadi pada akhirnya bahkan jika sepertinya Anda dapat menghapus / memindahkan file-file yang membentuk program yang sedang berjalan, sebenarnya isi dari file-file itu disimpan dalam memori sampai program berakhir.

Bakuriu
sumber
1
Ini tidak benar. execve()tidak mengembalikan FD, itu hanya menjalankan program. Jadi misalnya, jika Anda menjalankan tail -f /foo.logmaka mereka adalah FD ( /proc/PID/fd/<fd_num>) yang terkait dengan tailuntuk foo.logtetapi tidak untuk executable itu sendiri tail, tidak pada induknya juga. Ini berlaku untuk executable tunggal juga.
heemayl
@ heemayl saya tidak menyebutkan execvejadi saya tidak melihat bagaimana ini relevan. Setelah kernel mulai mengeksekusi file, mencoba mengganti file tidak akan memodifikasi program, kernel akan memuat rendering point moot. Jika Anda ingin "memperbarui" yang dapat dieksekusi saat sedang berjalan, Anda dapat menghubungi execvebeberapa titik untuk membuat kernel membaca kembali file tersebut, tetapi saya tidak melihat bagaimana ini penting. Intinya adalah: menghapus "running executable" tidak benar-benar memicu penghapusan data apa pun hingga executable berhenti.
Bakuriu
Saya berbicara tentang bagian ini jika program terdiri dari satu file yang dapat dieksekusi setelah Anda memulai eksekusi, program akan berjalan dengan baik secara independen dari setiap perubahan dalam direktori: mengganti nama dalam sistem file yang sama atau berbeda tidak dapat memengaruhi open handler , Anda tentu berbicara tentang execve()dan FD ketika tidak ada FD yang terlibat dalam kasus ini.
heemayl
2
Anda tidak memerlukan pegangan file untuk memiliki referensi ke file - memiliki halaman yang dipetakan juga cukup.
Simon Richter
1
Unix tidak memiliki "pegangan file". open()mengembalikan deskriptor file , yang sedang dibicarakan oleh heemayl di sini execve(). Ya, proses yang berjalan memiliki referensi untuk dieksekusi, tetapi itu bukan deskriptor file. Mungkin bahkan jika ia munmap()memperbaiki semua pemetaan yang dapat dieksekusi, ia masih memiliki referensi (tercermin dalam / proc / self / exe) yang menghentikan inode agar dibebaskan. (Ini akan mungkin tanpa crash jika melakukan ini dari fungsi perpustakaan yang tidak pernah kembali.) BTW, memotong atau memodifikasi executable yang sedang digunakan mungkin memberi Anda ETXTBUSY, tetapi mungkin berhasil.
Peter Cordes
7

Dalam sistem file Linux, ketika Anda memindahkan file, selama itu tidak melewati batas filesystem (baca: tetap pada disk / partisi yang sama) yang Anda ubah hanyalah inode dari ..(direktori induk) ke lokasi baru . Data aktual tidak bergerak sama sekali pada disk, hanya penunjuk sehingga sistem file tahu di mana menemukannya.

Inilah sebabnya mengapa operasi pemindahan sangat cepat dan kemungkinan mengapa tidak ada masalah dalam memindahkan program yang sedang berjalan karena Anda sebenarnya tidak memindahkan program itu sendiri.

I_GNU_it_all_along
sumber
Jawaban Anda tampaknya menyiratkan memindahkan biner yang dapat dieksekusi ke sistem file lain akan berdampak pada proses yang berjalan yang diluncurkan dari biner itu.
jlliagre
6

Itu dimungkinkan karena memindahkan program tidak memengaruhi proses yang berjalan dimulai dengan meluncurkannya.

Setelah sebuah program diluncurkan, bit-bit on-disk-nya dilindungi agar tidak ditimpa tetapi tidak perlu melindungi file yang akan diganti namanya, dipindahkan ke lokasi berbeda pada sistem file yang sama, yang setara dengan mengganti nama file, atau dipindahkan ke sistem file yang berbeda, yang setara dengan menyalin file di tempat lain kemudian hapus.

Menghapus file yang sedang digunakan, baik karena suatu proses memiliki deskriptor file terbuka di atasnya, atau karena proses mengeksekusi itu, tidak menghapus data file, yang tetap dirujuk oleh inode file tetapi hanya menghapus entri direktori, yaitu jalur dari mana inode dapat dijangkau.

Perhatikan bahwa meluncurkan program tidak memuat semuanya sekaligus dalam memori (fisik). Sebaliknya, hanya minimum ketat yang diperlukan untuk memulai proses dimuat. Kemudian, halaman-halaman yang diperlukan dimuat saat diminta selama proses berlangsung. ini disebut paging permintaan. Jika ada kekurangan RAM, OS bebas untuk melepaskan RAM yang menahan halaman-halaman ini sehingga sangat mungkin bagi suatu proses untuk memuat beberapa kali halaman yang sama dari inode yang dapat dieksekusi.

Alasan mengapa itu tidak mungkin dengan Windows pada awalnya kemungkinan karena fakta bahwa sistem file yang mendasari (FAT) tidak mendukung konsep split dari entri direktori vs inode. Keterbatasan ini tidak ada lagi dengan NTFS tetapi desain OS telah disimpan untuk waktu yang lama, yang mengarah ke kendala menjengkelkan harus me-reboot ketika menginstal versi baru dari biner, yang tidak lagi halnya dengan Windows versi terbaru.

Jlliagre
sumber
1
Saya percaya versi Windows yang lebih baru dapat menggantikan binari yang digunakan tanpa me-reboot.
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen Saya bertanya-tanya mengapa semua pembaruan masih memerlukan restart :(
Braiam
1
@Braiam Mereka tidak. Lihat lebih dekat. Meskipun binari dapat diperbarui kernel tidak dapat (setahu saya) dan membutuhkan reboot untuk diganti dengan versi yang lebih baru. Ini berlaku untuk sebagian besar kernel sistem operasi. Orang yang lebih pintar daripada yang saya tulis kpatch untuk Linux yang dapat menambal kernel Linux saat menjalankan - lihat en.wikipedia.org/wiki/Kpatch
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen yang saya maksud adalah "semua pembaruan Windows"
Braiam
@Braiam ya - saya juga. Silakan melihat lebih dekat.
Thorbjørn Ravn Andersen
4

Pada dasarnya, di Unix dan sejenisnya, nama file (termasuk jalur direktori yang mengarah ke sana) digunakan untuk mengaitkan / menemukan file ketika membukanya (mengeksekusi file adalah salah satu cara membukanya dengan cara). Setelah momen itu, identitas file (melalui "inode") dibuat dan tidak lagi dipertanyakan. Anda dapat menghapus file, mengubah nama file, mengubah izinnya. Selama setiap proses atau path file memiliki pegangan pada file / inode itu, itu akan bertahan, seperti pipa antara proses tidak (sebenarnya, dalam UNIX bersejarah pipa adalah inode tanpa nama dengan ukuran yang baru saja dipasang di referensi penyimpanan disk "blok langsung" dalam inode, sekitar 10 blok).

Jika Anda memiliki penampil PDF terbuka pada file PDF, Anda dapat menghapus file itu dan membuka yang baru dengan nama yang sama, dan selama penampil lama terbuka, masih akan baik-baik saja mengakses file lama (kecuali jika aktif menonton sistem file untuk mengetahui kapan file hilang dengan nama aslinya).

Program yang membutuhkan file sementara hanya dapat membuka file seperti itu di bawah beberapa nama dan kemudian segera menghapusnya (atau lebih tepatnya entri direktori) saat masih terbuka. Setelah itu file tidak lagi dapat diakses dengan nama, tetapi setiap proses yang memiliki deskriptor file terbuka untuk file masih dapat mengaksesnya, dan jika ada keluar yang tidak terduga dari program setelah itu, file akan dihapus dan penyimpanan direklamasi secara otomatis.

Jadi path ke suatu file bukanlah properti dari file itu sendiri (pada kenyataannya, hard link dapat menyediakan beberapa path yang berbeda) dan diperlukan hanya untuk membukanya, bukan untuk akses lanjutan oleh proses yang sudah dibuka.

pengguna584745
sumber