Apa itu proses yang tidak terputus?

156

Kadang-kadang setiap kali saya menulis sebuah program di Linux dan crash karena semacam bug, itu akan menjadi proses yang tidak pernah terputus dan terus berjalan selamanya sampai saya me-restart komputer saya (bahkan jika saya logout). Pertanyaan saya adalah:

  • Apa yang menyebabkan proses menjadi tidak terputus?
  • Bagaimana saya menghentikan hal itu terjadi?
  • Ini mungkin pertanyaan bodoh, tetapi apakah ada cara untuk menghentikannya tanpa me-restart komputer saya?
Jason Baker
sumber
Mungkinkah suatu program dapat ditulis untuk memulai suatu proses yang masuk ke TASK_UNINTERUPTIBLEkeadaan kapan pun sistem tidak dalam keadaan diam, sehingga dengan paksa mengumpulkan data, menunggu untuk mengirimkan begitu pengguna super keluar? Ini akan menjadi tambang emas bagi peretas untuk mengambil informasi, kembali ke keadaan zombie, dan mengirimkan informasi melalui jaringan saat idle. Beberapa dapat berpendapat bahwa ini adalah salah satu cara untuk menciptakan Blackdoorkekuatan untuk itu, untuk masuk dan keluar dari sistem apa pun yang diinginkan. Saya sangat percaya celah ini bisa disegel untuk selamanya, dengan menghilangkan `TASK_UNINTERUPTIB
Nuuwski
2
tolong bagikan kodenya?
lagi

Jawaban:

198

Proses yang tidak terputus adalah proses yang terjadi pada panggilan sistem (fungsi kernel) yang tidak dapat diganggu oleh sinyal.

Untuk memahami apa artinya itu, Anda perlu memahami konsep panggilan sistem yang interruptible. Contoh klasiknya adalah read(). Ini adalah panggilan sistem yang dapat memakan waktu lama (detik) karena berpotensi melibatkan pemintalan hard drive, atau menggerakkan kepala. Selama sebagian besar waktu ini, proses akan tidur, menghalangi perangkat keras.

Sementara proses sedang tidur di system call, ia dapat menerima sinyal asinkron Unix (katakanlah, SIGTERM), kemudian terjadi hal berikut:

  • Panggilan sistem keluar sebelum waktunya, dan diatur untuk mengembalikan -EINTR ke userspace.
  • Penangan sinyal dieksekusi.
  • Jika proses ini masih berjalan, itu mendapatkan nilai balik dari panggilan sistem, dan itu dapat membuat panggilan yang sama lagi.

Kembali lebih awal dari panggilan sistem memungkinkan kode ruang pengguna untuk segera mengubah perilakunya sebagai respons terhadap sinyal. Misalnya, mengakhiri dengan bersih sebagai reaksi terhadap SIGINT atau SIGTERM.

Di sisi lain, beberapa panggilan sistem tidak diizinkan terganggu dengan cara ini. Jika sistem memanggil warung untuk beberapa alasan, prosesnya dapat tetap tanpa batas dalam kondisi yang tidak dapat diselesaikan ini.

LWN memuat artikel bagus yang menyentuh topik ini pada bulan Juli.

Untuk menjawab pertanyaan awal:

  • Cara mencegah hal ini terjadi: cari tahu driver mana yang menyebabkan masalah Anda, dan apakah berhenti menggunakan, atau menjadi hacker kernel dan memperbaikinya.

  • Cara membunuh proses tanpa gangguan tanpa me-reboot: entah bagaimana membuat panggilan sistem berakhir. Seringkali cara paling efektif untuk melakukan ini tanpa menekan saklar daya adalah dengan menarik kabel listrik. Anda juga bisa menjadi peretas kernel dan membuat driver menggunakan TASK_KILLABLE, seperti yang dijelaskan dalam artikel LWN.

ddaa
sumber
31
Saya menarik kabel daya pada laptop saya dan itu tidak berfungsi, sayangnya. ;-)
thecarpy
1
Bukankah itu EINTR bukan EAGAIN? Baca juga () mengembalikan -1 dan errno diatur ke kesalahan.
lethalman
2
@Dexter: Anda memang kehilangan intinya. Baca artikel LWN: lwn.net/Articles/288056 . Masalah-masalah tersebut disebabkan oleh pemrogram driver perangkat yang malas, dan mereka harus diperbaiki dalam kode driver perangkat.
ddaa
4
@dada "Tradisi Unix (dan dengan demikian hampir semua aplikasi) percaya bahwa penyimpanan file menulis bukan interupsi sinyal. Tidaklah aman atau praktis untuk mengubah jaminan itu." -> Ini persis bagian yang paling salah dari semua IMO ini. Hanya mengganggu permintaan baca / tulis driver, dan ketika perangkat aktual (hard disk / kartu jaringan / dll) mengirimkan data, abaikan saja. Kernel OS harus dibuat sedemikian rupa sehingga TIDAK pengembang dapat mengacaukannya.
Dexter
2
@dada saya tahu Linux bukan microkernel, meskipun saya tidak yakin bagian mana dari komentar saya yang berhubungan dengan itu ... Dan kemudian, apakah komentar Anda berarti bahwa OS microkernel tidak memiliki masalah dengan proses-proses yang "tidak pernah terputus" itu? Karena jika tidak, mungkin sudah waktunya bagi saya untuk menjadi penggemar microkernel ...: D
Dexter
49

Ketika suatu proses pada mode pengguna, itu dapat terganggu kapan saja (beralih ke mode kernel). Ketika kernel kembali ke mode pengguna, ia memeriksa apakah ada sinyal yang tertunda (termasuk yang digunakan untuk mematikan proses, seperti SIGTERMdan SIGKILL). Ini berarti suatu proses dapat dibunuh hanya setelah kembali ke mode pengguna.

Alasan suatu proses tidak dapat dimatikan dalam mode kernel adalah karena berpotensi merusak struktur kernel yang digunakan oleh semua proses lain di mesin yang sama (cara yang sama membunuh thread dapat berpotensi merusak struktur data yang digunakan oleh utas lain dalam proses yang sama) .

Ketika kernel perlu melakukan sesuatu yang bisa memakan waktu lama (menunggu pada pipa yang ditulis oleh proses lain atau menunggu perangkat keras untuk melakukan sesuatu, misalnya), ia tidur dengan menandai dirinya sebagai tidur dan memanggil penjadwal untuk beralih ke yang lain. proses (jika tidak ada proses non-tidur, itu beralih ke proses "dummy" yang memberitahu CPU untuk sedikit memperlambat dan duduk dalam satu lingkaran - loop menganggur).

Jika sinyal dikirim ke proses tidur, itu harus dibangunkan sebelum akan kembali ke ruang pengguna dan dengan demikian memproses sinyal yang tertunda. Di sini kita memiliki perbedaan antara dua jenis tidur utama:

  • TASK_INTERRUPTIBLE, tidur yang terputus. Jika suatu tugas ditandai dengan bendera ini, ia sedang tidur, tetapi dapat dibangunkan oleh sinyal. Ini berarti kode yang menandai tugas sebagai tidur mengharapkan sinyal yang mungkin, dan setelah bangun akan memeriksanya dan kembali dari panggilan sistem. Setelah sinyal ditangani, panggilan sistem berpotensi dapat dimulai kembali secara otomatis (dan saya tidak akan menjelaskan lebih lanjut tentang cara kerjanya).
  • TASK_UNINTERRUPTIBLE, tidur tanpa gangguan. Jika tugas ditandai dengan bendera ini, ia tidak diharapkan dibangunkan oleh apa pun selain apa pun yang ditunggu, baik karena tidak dapat dengan mudah dimulai kembali, atau karena program mengharapkan panggilan sistem menjadi atom. Ini juga dapat digunakan untuk tidur yang dikenal sangat singkat.

TASK_KILLABLE (disebutkan dalam artikel LWN yang ditautkan oleh jawaban ddaa) adalah varian baru.

Ini menjawab pertanyaan pertama Anda. Mengenai pertanyaan kedua Anda: Anda tidak dapat menghindari tidur tanpa gangguan, mereka adalah hal yang normal (itu terjadi, misalnya, setiap kali proses membaca / menulis dari / ke disk); Namun, mereka harus bertahan hanya sepersekian detik. Jika mereka bertahan lebih lama, biasanya itu berarti masalah perangkat keras (atau masalah driver perangkat, yang terlihat sama dengan kernel), di mana driver perangkat sedang menunggu perangkat keras untuk melakukan sesuatu yang tidak akan pernah terjadi. Ini juga bisa berarti Anda menggunakan NFS dan server NFS sedang down (menunggu server untuk pulih; Anda juga dapat menggunakan opsi "intr" untuk menghindari masalah).

Akhirnya, alasan Anda tidak dapat memulihkan adalah alasan yang sama dengan kernel menunggu sampai kembali ke mode pengguna untuk mengirimkan sinyal atau mematikan proses: itu berpotensi merusak struktur data kernel (kode menunggu pada tidur interruptible dapat menerima kesalahan yang memberitahu itu untuk kembali ke ruang pengguna, tempat proses dapat dimatikan; kode yang menunggu pada waktu tidur tanpa gangguan tidak mengharapkan kesalahan).

CesarB
sumber
1
Bug penguncian filesystem juga kemungkinan penyebabnya, IME.
Tobu
3
Saya tidak mengerti semua ini. "Anda tidak dapat menghindari tidur tanpa gangguan" - tidak bisakah OS dibuat sedemikian rupa sehingga tidur tanpa gangguan sama sekali tidak ADA sebagai suatu keadaan? Kemudian bagian tentang korupsi - tidak bisakah bagian mode-kernel dari proses itu sendiri (atau apa pun yang BISA menyebabkan korupsi) dihentikan atau hanya kodenya yang dimodifikasi tepat di memori untuk kembali? Tolong jelaskan mengapa ini sangat sulit / tidak mungkin untuk dilakukan bahkan Linux belum melakukannya. (Saya pikir masalah ini hanya ada di Windows)
Dexter
Satu-satunya kasus yang dapat saya pikirkan yang akan membuat (dengan aman) membunuh proses-proses itu benar-benar mustahil (dan bukan hanya, katakanlah, sangat sulit) adalah jika perangkat keras itu sendiri dapat menyebabkan korupsi. Perangkat keras tidak dapat dikontrol; kernel bisa . Tetapi itu adalah kernel yang mendapatkan data dari perangkat keras dan memodifikasi memori (itu sebabnya tidak boleh dibebaskan sebelum proses kembali ke mode pengguna dan mengapa korupsi bisa terjadi) ... ubah kode kernel dalam memori dan tidak ada lagi masalah.
Dexter
@Dexter menganggap kernel seolah-olah itu adalah proses multi-utas tunggal, di mana bagian mode-kernel dari setiap proses adalah utas dalam kernel. Saran Anda akan sama buruknya dengan membunuh satu utas dalam program multi-utas: ia dapat meninggalkan kunci yang menggantung, struktur data yang dimodifikasi sementara atau di tengah-tengah sedang dimodifikasi, dan sebagainya.
CesarB
@CesarB yah Anda benar tentang membunuh sebuah utas ... Tetapi tidak bisakah utas "utama" (yang akan menjadi kernel OS dan utas lainnya akan menjadi driver misalnya) entah bagaimana menanganinya? Meskipun struktur-struktur itu "di tengah-tengah dimodifikasi" tampaknya menjadi salah satu masalah yang sangat sulit ... mungkin kita benar-benar tidak akan pernah melihat OS di mana proses yang tidak terputus tidak mungkin :(
Dexter
23

Proses tanpa gangguan BIASANYA menunggu I / O mengikuti kesalahan halaman.

Pertimbangkan ini:

  • Utas mencoba mengakses halaman yang tidak di dalam inti (baik yang dapat dieksekusi yang memuat permintaan, halaman memori anonim yang telah ditukar, atau file mmap () yang diminta oleh beban, yang merupakan hal yang sama)
  • Kernel sekarang (mencoba) memuatnya
  • Proses tidak dapat dilanjutkan sampai halaman tersedia.

Proses / tugas tidak dapat diganggu dalam kondisi ini, karena tidak dapat menangani sinyal apa pun; jika itu terjadi, kesalahan halaman lain akan terjadi dan itu akan kembali ke tempat semula.

Ketika saya mengatakan "proses", saya benar-benar berarti "tugas", yang di Linux (2.6) secara kasar diterjemahkan menjadi "utas" yang mungkin atau mungkin tidak memiliki entri "grup grup" individual di / proc

Dalam beberapa kasus, mungkin menunggu lama. Contoh khas dari ini adalah ketika file executable atau mmap'd pada sistem file jaringan di mana server telah gagal. Jika I / O akhirnya berhasil, tugas akan berlanjut. Jika akhirnya gagal, tugas umumnya akan mendapatkan SIGBUS atau sesuatu.

MarkR
sumber
1
Jika akhirnya gagal, tugas umumnya akan mendapatkan SIGBUS atau sesuatu. Tunggu, tidak bisakah kernel dibuat sehingga, ketika membunuh proses-proses "tidak terputus" itu, itu hanya MENGATAKAN kepada mereka bahwa operasi I / O gagal? Maka proses akan kembali ke mode pengguna dan pergi? Harus ada cara untuk membunuh proses status 'D' dengan aman. Saya kira itu tidak mudah dan itulah mengapa Windows atau Linux belum memiliki kemungkinan itu. Di sisi lain, saya ingin dapat membunuh proses-proses itu setidaknya secara tidak aman. Saya tidak peduli dengan kemungkinan sistem crash atau apa pun ...
Dexter
@Dexter hmm, saya tidak pernah mengalami masalah ini dengan Windows. Apa cara mereproduksi di sana? Setidaknya sesuai dengan posting ini , semua permintaan I / O dapat terganggu di Windows.
Ruslan
1

Untuk pertanyaan ke-3 Anda: Saya pikir Anda dapat membunuh proses yang tidak terputus dengan menjalankan sudo kill -HUP 1. Ini akan restart init tanpa mengakhiri proses yang berjalan dan setelah menjalankannya, proses saya yang tidak pernah terputus hilang.

Ron Granger
sumber
-3

Jika Anda berbicara tentang proses "zombie" (yang ditetapkan sebagai "zombie" dalam output ps), maka ini adalah catatan yang tidak berbahaya dalam daftar proses menunggu seseorang untuk mengumpulkan kode pengembaliannya dan itu bisa diabaikan dengan aman.

Bisakah Anda jelaskan apa dan "proses tanpa gangguan" untuk Anda? Apakah itu selamat dari "kill -9" dan bahagia chugs bersama? Jika demikian, maka macet di beberapa syscall, yang macet di beberapa driver, dan Anda terjebak dengan proses ini sampai reboot (dan kadang-kadang lebih baik untuk reboot segera) atau membongkar driver yang relevan (yang tidak mungkin terjadi) . Anda bisa mencoba menggunakan "strace" untuk mencari tahu di mana proses Anda macet dan menghindarinya di masa depan.

Mahir
sumber
Tidak bisakah driver diturunkan secara paksa dengan cara yang sama dengan proses yang dapat dimatikan? Saya tahu mode kernel memiliki akses lebih istimewa daripada mode pengguna, tetapi tidak pernah bisa lebih istimewa daripada sistem operasi itu sendiri. Apa pun yang dieksekusi dalam mode kernel selalu dapat merusak apa pun yang dieksekusi dalam mode kernel - sama sekali tidak ada kontrol.
Dexter