ps *very*가 때때로 유효한 프로세스를 찾지 못하는 이유는 무엇입니까?

ps *very*가 때때로 유효한 프로세스를 찾지 못하는 이유는 무엇입니까?

ps -o args -p <pid>명령 중 하나가 이상한 문제에 부딪혔습니다.아주 가끔해당 프로세스가 해당 서버에서 확실히 실행되고 있음에도 불구하고 해당 프로세스를 찾을 수 없습니다. 이러한 프로세스는 특정 Java 애플리케이션을 실행하는 데 사용되는 장기 실행 래퍼 스크립트입니다.

"야생"에서 발생하는 문제는 항상 이른 아침에 발생하는 것으로 보이므로 해당 서버의 로드가 상당히 과중하기 때문에 해당 서버의 디스크 로드와 관련이 있다는 증거가 있습니다 ps. 문제는 반복될 수 있습니다. 몇 백 번 정도마다 오류가 발생하므로 실행합니다.

다음 bash 스크립트를 실행하여 실패한 실행과 성공한 실행에 대한 strace 출력을 성공적으로 생성했습니다.

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

fail.out및 의 출력을 비교하면 런타임 시 실패한 시스템 호출이 시스템의 실제 프로세스 수보다 훨씬 작은 숫자를 반환한다는 good.out것을 알 수 있습니다 (~1100에 비해 약 ~500).getdents

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

...그리고 그 짧은 목록에는 문제의 실제 pid가 포함되어 있지 않으므로 찾을 수 없습니다.

이 부분은 무시해도 됩니다. ENOTTY 오류는 아래 dave_thompson의 설명에 설명되어 있으며 관련이 없습니다.

또한 실패한 실행에는 ENOTTY성공적인 실행에서는 발생하지 않는 일부 오류가 표시됩니다. 출력 시작 부분 근처에 표시됩니다.

ioctl(1, TIOCGWINSZ, 0x7fffe19db310) = -1 ENOTTY(ioctl은 장치에 적합하지 않음) ioctl(1, TCGETS, 0x7fffe19db280) = -1 ENOTTY(ioctl은 장치에 적합하지 않음)

마침내 나는 하나를 보았다

ioctl(1, TCGETS, 0x7fffe19db0d0) = -1 ENOTTY(ioctl은 장치에 적합하지 않음)

ioctl최종 실패는 반환하기 전에 발생 하지만, 빈 결과 집합이 인쇄된 ps후에 발생하므로 관련이 있는지 확실하지 않습니다. ps실패한 모든 strace 출력에서는 일관성이 있지만 성공적인 출력에서는 그렇지 않다는 것을 알고 있습니다.

가끔 프로세스의 전체 목록을 찾을 수 없는 이유를 전혀 모르겠고 getdents이제 래퍼를 확인하는 제어 스크립트를 변경하여 전체에 반창고를 붙이려는 지점에 도달했습니다. 처음에 실패하면 두 번째 호출이 의심되지만 ps여기서 무슨 일이 일어나고 있는지 아는 사람이 있는지 알고 싶습니다.

문제의 시스템은 CentOS 7 및 procps-ng 버전 3.3.10-17.el7_5.2.x86_64에서 커널 4.16.13-1.el7.elrepo.x86_64를 실행하고 있습니다.

답변1

/proc를 통하지 않고 파일 시스템에서 직접 필요한 정보를 읽는 것을 고려하십시오 . 공백 대신 NUL 바이트로만 구분된 psfile 에서 찾고 있는 정보("args")를 찾을 수 있습니다 ./proc/$pid/cmdline

sed줄을 사용하여 프로세스 매개변수를 가져올 수 있습니다 $pid.

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

이 명령은 다음과 동일합니다.

ps -o args= -p "$pid"

( args=in을 사용하면 ps헤더가 생략됩니다.)

sed명령은 먼저 마지막 후행 NUL 바이트를 찾아서 줄 바꿈으로 바꾼 다음 다른 모든 NUL 바이트를 공백(개별 매개변수 구분)으로 대체하여 궁극적으로 ps.


시스템의 프로세스 목록을 나열하는 방법은 ps시스템의 디렉터리 목록을 나열하는 것입니다 /proc. 그러나 프로세스가 실행되는 동안 시작 및 종료되기 때문에 이 프로세스에는 고유한 경쟁 조건이 있으므로 ps얻을 수 있는 것은 실제 스냅샷이 아니라 근사. 특히, ps결과를 표시할 때 종료된 프로세스를 표시하거나 실행 중에 시작된 프로세스를 무시할 수 있습니다(그러나 나열된 경우 커널은 반환하지 않습니다 /proc).

난 항상추정프로세스가 시작되기 전에 존재했고 프로세스 ps가 완료된 후에도 여전히 존재하는 경우아니요그리웠어, 난추정커널은 많은 수의 다른 프로세스가 생성되고 삭제되더라도 이러한 프로세스가 항상 포함되도록 보장합니다. 당신이 설명하는 상황은 그렇지 않다는 것을 암시합니다. 나는 여전히 이것에 대해 회의적이지만 이것이 어떻게 작동하는지에 대해 알려진 경쟁 조건이 있다는 점을 고려할 때 ps이러한 경쟁 조건으로 인해 PID 목록이 기존 PID를 놓칠 수 있다는 것이 적어도 합리적이라고 생각합니다./proc

이는 Linux 커널의 소스 코드를 검사하여 확인할 수 있지만 아직 수행하지 않았으므로 설명하는 것처럼 장기 실행 프로세스를 놓칠 수 있는 경쟁 조건이 있는지 실제로 알 수 없습니다.


다른 부분은 ps작동 방식입니다. 매개변수를 사용하여 단일 PID를 전달하더라도 -p단일 PID에만 관심이 있는 경우에도 기존의 모든 PID가 계속 나열됩니다. 이 경우 바로가기를 사용하여 항목 나열을 건너 /proc뛰고 바로 로 이동할 수 있습니다 /proc/$pid.

왜 이런 식으로 구현되었는지 말할 수 없습니다. 어쩌면 대부분의 ps옵션이 프로세스의 "필터" 이기 때문에 -p동일한 방식으로 구현하는 것이 더 쉬울 수도 있습니다. 바로가기를 직접 사용하면 별도의 코드 경로나 코드 중복이 포함될 수 있습니다... 또 다른 가설은 다음과 같습니다. 플러스를 /proc/$pid포함한 일부 상황 -p궁극적으로 추가 옵션을 나열해야 하므로 어떤 특정 상황에서 지름길을 허용하고 어떤 상황을 허용하지 않는지 결정하는 것은 복잡할 수 있습니다.


/proc/$pid이를 통해 시스템의 전체 PID 세트를 나열하지 않고 바로 시작하고, 알려진 모든 경합을 피하고, 소스에서 직접 필요한 정보를 얻는 해결 방법을 찾을 수 있습니다 .

약간 추악하지만 설명하는 문제가 존재하며 해당 정보를 얻는 신뢰할 수 있는 방법이어야 합니다.

관련 정보