Tại sao ps * rất * đôi khi không tìm thấy một quy trình hợp lệ?


9

Tôi đã gặp phải một vấn đề kỳ lạ trong đó một ps -o args -p <pid>lệnh rất thỉnh thoảng không tìm thấy quy trình được đề cập, mặc dù nó chắc chắn đang chạy trên máy chủ được đề cập. Các quy trình được đề cập là các tập lệnh trình bao bọc chạy dài được sử dụng để khởi chạy một số ứng dụng Java.

Sự cố "trong tự nhiên" của sự cố dường như luôn xảy ra vào sáng sớm, vì vậy có một số bằng chứng cho thấy nó liên quan đến tải đĩa trên máy chủ, vì chúng đã được tải khá nhiều, nhưng bằng cách chạy psvào câu hỏi trong một vòng lặp chặt chẽ, cuối cùng tôi có thể tái tạo vấn đề - cứ sau vài trăm lần chạy tôi lại gặp một lỗi.

Bằng cách chạy tập lệnh bash sau, tôi đã quản lý để tạo đầu ra strace cho cả lần chạy thất bại và chạy thành công:

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

So sánh đầu ra từ fail.outgood.out, tôi có thể thấy rằng getdentscuộc gọi hệ thống đang chạy mà không bằng cách nào đó trả về một số lượng nhỏ hơn nhiều so với số lượng quá trình thực tế trên hệ thống (theo thứ tự ~ 500 so với ~ 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

... Và danh sách ngắn hơn đó không bao gồm pid thực tế trong câu hỏi, vì vậy nó không được tìm thấy.

Bạn có thể bỏ qua phần này, các lỗi ENOTTY được giải thích bằng nhận xét của dave_thndry bên dưới và không liên quan

Ngoài ra, lần chạy thất bại có một số ENOTTYlỗi không xuất hiện trong lần chạy thành công. Gần đầu ra tôi thấy

ioctl (1, TIOCGWINSZ, 0x7fffe19db 310) = -1 ENOTTY (ioctl không phù hợp cho thiết bị) ioctl (1, TCGETS, 0x7fffe19db280) = -1 ENOTTY (Thiết bị không phù hợp cho thiết bị)

Và cuối cùng, tôi thấy một

ioctl (1, TCGETS, 0x7fffe19db0d0) = -1 ENOTTY (ioctl không phù hợp cho thiết bị)

Thất bại ioctlở cuối xảy ra ngay trước khi pstrả về, nhưng nó xảy ra sau khi psđã in một tập kết quả trống, vì vậy tôi không chắc chúng có liên quan hay không. Tôi biết rằng chúng nhất quán trong tất cả các kết quả đầu ra thất bại mà tôi có, nhưng không xuất hiện trong những sản phẩm thành công.

Tôi hoàn toàn không biết tại sao getdentsđôi khi không tìm thấy danh sách đầy đủ các quy trình và bây giờ tôi đã đạt đến điểm mà tôi sẽ tát một băng hỗ trợ cho toàn bộ bằng cách thay đổi tập lệnh điều khiển kiểm tra tập lệnh bao bọc trong câu hỏi để gọi pslần thứ hai nếu lần đầu tiên thất bại, nhưng tôi muốn biết liệu có ai có ý tưởng gì đang xảy ra ở đây không.

Hệ thống được đề cập đang chạy Kernel 4.16.13-1.el7.elrepo.x86_64 trên CentOS 7 và phiên bản Procps-ng 3.3.10-17.el7_5.2.x86_64


1
FYI, các ioctls phải làm với việc nhận cài đặt thiết bị đầu cuối (ví dụ: đầu tiên là tìm số lượng hàng và cột) - vì vậy, thật kỳ lạ khi chúng bị lỗi, nhưng có thể không phải là nguyên nhân trực tiếp. Điều này nghe có vẻ như một lỗi kernel ...
derobert

2
nghiên cứu liên quan từ OpenBSD: https.www.google.com.vn/tedunangst.com/flak/post/iêng
thrig

2
Bạn có >/dev/nulllệnh gọi 'fail' (trong vòng lặp) nhưng không phải là lời gọi 'tốt', do đó ENOTTY trên fd 1.
dave_thedom_085

Ôi chết tiệt. Cảm ơn vì đã bắt được Dave, điều đó chắc chắn giải thích cho các ENOTTY.
James

Vui mừng khi thấy tôi không phải là người duy nhất gặp vấn đề này. Cách tôi khắc phục điều này là có một lần thử bắt sẽ thử lại nếu lệnh thất bại, mặc dù vẫn khó chịu: /
Josh Correia

Câu trả lời:


7

Cân nhắc đọc thông tin bạn cần trực tiếp từ /prochệ thống tập tin thay vì thông qua một công cụ như ps. Bạn sẽ tìm thấy thông tin bạn đang tìm kiếm ("args") bên trong tệp /proc/$pid/cmdline, chỉ được phân tách bằng byte NUL thay vì khoảng trắng.

Bạn có thể sử dụng sedmột lớp lót này để có được các quy trình $pid:

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

Lệnh này tương đương với:

ps -o args= -p "$pid"

(Sử dụng args=trong pssẽ bỏ qua tiêu đề.)

Đầu sedtiên, lệnh sẽ tìm kiếm byte NUL cuối cùng và thay thế nó bằng một dòng mới và sau đó thay thế tất cả các byte NUL khác (tách các đối số riêng lẻ) bằng khoảng trắng, cuối cùng tạo ra định dạng giống như bạn đang thấy ps.


Về việc liệt kê các quy trình trong hệ thống, psthực hiện bằng cách liệt kê các thư mục /proc, nhưng có các điều kiện chủng tộc vốn có của quy trình đó, vì các quy trình đang bắt đầu và thoát trong khi psđang chạy, vì vậy những gì bạn nhận được không thực sự là ảnh chụp nhanh mà chỉ là xấp xỉ. Cụ thể, có thể nó pssẽ hiển thị các quy trình đã bị chấm dứt vào thời điểm nó hiển thị kết quả hoặc bỏ qua các quy trình đã bắt đầu trong khi nó đang chạy (nhưng không được trả về bởi kernel trong khi liệt kê nội dung của /proc.)

Tôi luôn cho rằng nếu một quá trình ở đó trước khi psbắt đầu và vẫn ở đó sau khi nó được thực hiện, thì nó sẽ không bị bỏ qua, tôi cho rằng hạt nhân sẽ đảm bảo những quy trình đó sẽ luôn được bao gồm, ngay cả khi có rất nhiều quy trình khác được tạo ra và phá hủy. Những gì bạn đang mô tả ngụ ý đó không phải là trường hợp. Tôi vẫn còn nghi ngờ về điều đó, nhưng với những điều kiện chủng tộc đã biết về cách thức pshoạt động, tôi đoán rằng ít nhất có thể hợp lý rằng việc liệt kê các PID từ /proccó thể bỏ lỡ một điều kiện hiện có do những điều kiện cuộc đua đó.

Có thể xác minh rằng bằng cách kiểm tra nguồn của nhân Linux, nhưng tôi chưa thực hiện điều đó (vì vậy) nên không thể thực sự chắc chắn liệu một điều kiện chủng tộc như vậy có tồn tại quá trình lâu dài hay không, như Bạn miêu tả.


Phần khác là cách pslàm việc. Ngay cả khi bạn truyền cho nó một PID với -pđối số, nó vẫn liệt kê tất cả các PID hiện có, mặc dù bạn chỉ quan tâm đến một PID đó. Nó chắc chắn có thể đi một lối tắt trong trường hợp đó và bỏ qua việc liệt kê các mục trong /procvà đi trực tiếp đến /proc/$pid.

Tôi không thể nói tại sao nó được thực hiện theo cách này. Có lẽ vì hầu hết các pstùy chọn là "bộ lọc" trên các quy trình, nên việc thực hiện -ptheo cách tương tự dễ dàng hơn, sử dụng phím tắt để đi thẳng /proc/$pidcó thể liên quan đến một đường dẫn mã hoặc sao chép mã riêng biệt ... Một giả thuyết khác là một số trường hợp bao gồm cả -ptùy chọn bổ sung sẽ cuối cùng yêu cầu liệt kê, vì vậy có lẽ rất phức tạp để xác định trường hợp chính xác nào sẽ cho phép đi đường tắt và trường hợp nào sẽ không.


Điều này đưa chúng ta đến cách giải quyết, đi thẳng vào /proc/$pidmà không liệt kê toàn bộ bộ PID của hệ thống, tránh tất cả các chủng tộc đã biết và chỉ cần lấy thông tin bạn cần trực tiếp từ nguồn.

Hơi xấu một chút, nhưng vấn đề bạn mô tả thực sự tồn tại, nó nên là một cách đáng tin cậy để lấy thông tin đó.


2
Cảm ơn vì Filipe đó, tôi đã nâng cấp vì lệnh sed rất hữu ích (và tôi đã thay đổi tập lệnh của chúng tôi thành chỉ tìm trong / Proc) và vì tôi không nhận ra rằng việc thêm '=' vào ps sẽ làm mất tiêu đề . Tôi đã không chấp nhận câu trả lời vì tôi vẫn thực sự tò mò về lý do tại sao nó không thấy toàn bộ danh sách / Proc và tôi đang giữ hy vọng người khác biết :)
James
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.