Mengapa ps * sangat * kadang-kadang gagal menemukan proses yang valid?


9

Saya telah mengalami masalah aneh di mana suatu ps -o args -p <pid>perintah kadang-kadang gagal menemukan proses yang dimaksud, meskipun itu pasti berjalan pada server yang dimaksud. Proses yang dimaksud adalah skrip pembungkus yang sudah berjalan lama yang digunakan untuk meluncurkan beberapa aplikasi Java.

The "di alam liar" kejadian dari masalah ini tampaknya selalu terjadi pagi, sehingga ada beberapa bukti bahwa hal itu terkait dengan beban disk pada server yang bersangkutan, karena mereka cukup berat dimuat kemudian, tetapi dengan menjalankan psdi pertanyaan dalam lingkaran yang ketat, saya akhirnya bisa meniru masalah - sekali setiap beberapa ratus berjalan saya mendapatkan kesalahan.

Dengan menjalankan skrip bash berikut, saya berhasil menghasilkan output strace untuk menjalankan yang gagal dan berhasil:

while [ $? == 0 ] ; do strace -o fail.out ps -o args -p <pid> >/dev/null ; done ; strace -o good.out ps -o args -p <pid>

Membandingkan output dari fail.outdan good.out, saya dapat melihat bahwa getdentssystem call pada proses yang gagal, entah bagaimana, mengembalikan jumlah yang jauh lebih kecil daripada jumlah aktual proses pada sistem (pada urutan ~ 500 dibandingkan dengan ~ 1100)

grep getdents good.out
  getdents(5, /* 1174 entries */, 32768)  = 32760
  getdents(5, /* 31 entries */, 32768)    = 992
  getdents(5, /* 0 entries */, 32768)     = 0

grep getdents fail.out
  getdents(5, /* 673 entries */, 32768)   = 16728
  getdents(5, /* 0 entries */, 32768)     = 0

... dan daftar yang lebih pendek itu tidak termasuk pid yang sebenarnya dalam pertanyaan, jadi tidak ditemukan.

Anda dapat mengabaikan bagian ini, kesalahan ENOTTY dijelaskan oleh komentar dave_thompson di bawah ini, dan tidak terkait

Selain itu, proses yang gagal mendapatkan beberapa ENOTTYkesalahan yang tidak muncul dalam proses yang berhasil. Dekat awal output yang saya lihat

ioctl (1, TIOCGWINSZ, 0x7fffe19db310) = -1 ENOTTY (ioctl yang tidak sesuai untuk perangkat) ioctl (1, TCGETS, 0x7fffe19db280) = -1 ENOTTY (ioctl tidak sesuai untuk perangkat)

Dan pada akhirnya saya melihat satu pun

ioctl (1, TCGETS, 0x7fffe19db0d0) = -1 ENOTTY (ioctl yang tidak sesuai untuk perangkat)

Gagal ioctlpada akhirnya terjadi tepat sebelum pspengembalian, tetapi terjadi setelah pstelah mencetak hasil kosong yang ditetapkan, jadi saya tidak yakin apakah itu terkait. Saya tahu bahwa mereka konsisten dalam semua output strace gagal yang saya miliki, tetapi tidak muncul di yang sukses.

Saya sama sekali tidak tahu mengapa getdentskadang-kadang tidak menemukan daftar lengkap proses, dan saya sekarang telah mencapai titik di mana saya hanya akan menampar seluruh bantuan band dengan mengubah skrip kontrol yang memeriksa skrip pembungkus dalam pertanyaan untuk memanggil pskedua kalinya jika yang pertama gagal, tetapi saya akan tertarik untuk mengetahui apakah ada yang tahu apa yang terjadi di sini.

Sistem yang dimaksud menjalankan Kernel 4.16.13-1.el7.elrepo.x86_64 pada CentOS 7 dan procps-ng versi 3.3.10-17.el7_5.2.x86_64


1
FYI, ioctl harus dilakukan dengan mendapatkan pengaturan terminal (misalnya, yang pertama adalah menemukan jumlah baris dan kolom) - jadi aneh mereka gagal, tetapi kemungkinan bukan penyebab langsung. Ini kedengarannya seperti bug kernel ...
derobert

2
penelitian terkait dari OpenBSD: https.www.google.com.tedunangst.com/flak/post/…
thrig

2
Anda memiliki >/dev/nulldoa 'gagal' (dalam loop) tetapi bukan doa 'baik', maka ENOTTY pada fd 1.
dave_thompson_085

Oh sial Terima kasih telah menangkap yang Dave, yang jelas menjelaskan ENOTTY.
James

Senang melihat saya bukan satu-satunya yang memiliki masalah ini. Cara saya menyiasati ini adalah memiliki try-catch yang akan mencoba lagi jika perintah gagal, masih mengganggu meskipun: /
Josh Correia

Jawaban:


7

Pertimbangkan untuk membaca informasi yang Anda butuhkan langsung dari sistem /procfile daripada melalui alat seperti ps. Anda akan menemukan informasi yang Anda cari ("args") di dalam file /proc/$pid/cmdline, hanya dipisahkan oleh NUL byte alih-alih spasi.

Anda dapat menggunakan sedone-liner ini untuk mendapatkan argumen proses $pid:

sed -e 's/\x00\?$/\n/' -e 's/\x00/ /g' "/proc/$pid/cmdline"

Perintah ini setara dengan:

ps -o args= -p "$pid"

(Menggunakan args=di psakan menghilangkan tajuk.)

The sedPerintah pertama akan mencari trailing terakhir NUL byte dan menggantinya dengan baris baru, dan setelah itu mengganti semua byte NUL lainnya (memisahkan argumen individu) dengan spasi, akhirnya menghasilkan format yang sama yang Anda lihat dari ps.


Mengenai proses daftar dalam sistem, pslakukan dengan mendaftar direktori /proc, tetapi ada kondisi ras yang melekat pada prosedur itu, karena proses mulai dan keluar saat pssedang berjalan, jadi apa yang Anda dapatkan bukanlah snapshot tetapi perkiraan. Secara khusus, ada kemungkinan bahwa psakan menunjukkan proses yang telah dihentikan pada saat itu menunjukkan hasilnya, atau menghilangkan proses yang telah dimulai saat sedang berjalan (tetapi tidak dikembalikan oleh kernel saat daftar isi /proc.)

Saya selalu berasumsi bahwa jika suatu proses ada sebelum psdimulai dan masih ada setelah selesai, maka itu tidak akan terlewatkan olehnya, saya berasumsi kernel akan menjamin mereka akan selalu disertakan, bahkan jika ada banyak churn dari proses lain diciptakan dan dihancurkan. Apa yang Anda gambarkan menunjukkan bahwa bukan itu masalahnya. Saya masih skeptis tentang itu, tetapi mengingat ada kondisi ras yang dikenal dalam cara pskerjanya, saya kira setidaknya masuk akal bahwa mendaftarkan PID dari /procmungkin kehilangan yang sudah ada karena kondisi ras tersebut.

Mungkin untuk memverifikasi bahwa dengan memeriksa sumber kernel Linux, tapi saya belum melakukannya (belum) jadi tidak bisa memastikan apakah kondisi ras seperti itu ada yang akan melewatkan proses yang berjalan lama, seperti Anda jelaskan.


Bagian lainnya adalah cara pskerjanya. Bahkan jika Anda memberikan PID tunggal dengan -pargumen, itu masih mendaftar semua PID yang ada, meskipun Anda hanya tertarik pada satu PID itu. Pasti bisa mengambil jalan pintas dalam kasus itu dan melewati daftar entri /procdan masuk langsung ke /proc/$pid.

Saya tidak bisa mengatakan mengapa ini diterapkan seperti ini. Mungkin karena sebagian besar psopsi adalah "filter" pada proses, jadi menerapkan -pcara yang sama lebih mudah, mengambil jalan pintas untuk langsung /proc/$pidmelibatkan jalur kode yang terpisah atau duplikasi kode ... Hipotesis lain adalah bahwa beberapa kasus termasuk -pbeberapa opsi tambahan akan akhirnya membutuhkan daftar, jadi mungkin rumit untuk menentukan kasus mana yang memungkinkan mengambil jalan pintas dan mana yang tidak.


Yang membawa kita ke solusi, langsung ke /proc/$pid, tanpa daftar set lengkap PID sistem, menghindari semua ras yang diketahui dan hanya mendapatkan informasi yang Anda butuhkan langsung dari sumbernya.

Agak jelek, tetapi masalah yang Anda jelaskan memang ada, itu harus menjadi cara yang andal untuk mengambil informasi itu.


2
Terima kasih untuk Filipe itu, saya telah memutakhirkan karena perintah sed berguna (dan saya telah mengubah skrip kami untuk hanya melihat / proc) dan karena saya tidak menyadari bahwa menambahkan '=' ke ps akan menjatuhkan header . Saya belum menerima jawaban karena saya masih sangat ingin tahu mengapa tidak melihat seluruh daftar / proc dan saya mengulurkan harapan orang lain tahu :)
James
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.