Я столкнулся со странной проблемой, из-за которой ps -o args -p <pid>
команда очень редко не может найти нужный процесс, даже если она определенно выполняется на рассматриваемом сервере. Рассматриваемые процессы - это длительные сценарии-оболочки, используемые для запуска некоторых приложений Java.
Кажется, что «в дикой природе» возникновение проблемы всегда происходит рано утром, поэтому есть некоторые свидетельства того, что это связано с загрузкой диска на рассматриваемом сервере, потому что они довольно сильно загружены, но при запуске ps
in вопрос в узком цикле, я могу в конечном итоге повторить проблему - каждые несколько сотен или около того пробежек я получаю ошибку.
Запустив следующий скрипт bash, мне удалось сгенерировать выходные данные для неудачного и успешного выполнения:
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
, я могу видеть, что getdents
системный вызов при сбое каким-то образом возвращает намного меньшее число, чем фактическое число процессов в системе (порядка ~ 500 по сравнению с ~ 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
... и этот короткий список не включает в себя фактический 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
второй раз, если первый не помог, но мне было бы интересно узнать, есть ли у кого-нибудь какие-либо идеи о том, что здесь происходит.
В рассматриваемой системе выполняется ядро 4.16.13-1.el7.elrepo.x86_64 в CentOS 7 и procps-ng версии 3.3.10-17.el7_5.2.x86_64.
>/dev/null
вызов 'fail' (в цикле), но не вызов 'good', следовательно, ENOTTY на fd 1.Ответы:
Попробуйте прочитать необходимую информацию непосредственно из
/proc
файловой системы, а не с помощью такого инструмента, какps
. Вы найдете информацию, которую вы ищете ("args") внутри файла/proc/$pid/cmdline
, только разделенные байтами NUL вместо пробелов.Вы можете использовать эту
sed
однострочную строку для получения аргументов процесса$pid
:Эта команда эквивалентна:
(Использование
args=
inps
пропустит заголовок.)Команда
sed
сначала ищет последний завершающий байт NUL и заменяет его новой строкой, а после этого заменяет все другие байты NUL (разделяя отдельные аргументы) пробелами, в результате получая тот же формат, который вы видитеps
.Что касается перечисления процессов в системе,
ps
делает это путем перечисления каталогов в/proc
, но для этой процедуры есть неотъемлемые условия гонки, поскольку процессыps
запускаются и завершаются во время работы, так что в действительности вы получаете не снимок, а приблизительное значение. В частности, возможно, чтоps
будут показаны процессы, которые уже завершены к тому времени, когда он показывает свои результаты, или пропущены процессы, которые были запущены во время его работы (но не были возвращены ядром при перечислении содержимого/proc
.)Я всегда предполагал, что если процесс существует перед
ps
запуском и все еще существует после того, как он завершен, то он не будет пропущен им, я предполагал, что ядро будет гарантировать, что они будут всегда включены, даже если есть много других процессов создается и уничтожается. То, что вы описываете, подразумевает, что это не так. Я по-прежнему скептически отношусь к этому, но, учитывая, что в гонке известны условия гонокps
, я думаю, что по крайней мере правдоподобно, что перечисление идентификаторов PID/proc
может пропустить существующий из-за этих гонок.Это можно было бы проверить, проверив источник ядра Linux, но я этого еще не сделал (пока), поэтому не могу точно сказать, существует ли такое состояние гонки, которое пропустит длительный процесс, так как Вы описываете.
Другая часть - это способ
ps
работы. Даже если вы передаете ему один PID с-p
аргументом, он по-прежнему перечисляет все существующие PID, даже если вас интересует только один. В этом случае можно определенно использовать ярлык и пропустить перечисление записей/proc
и перейти непосредственно к/proc/$pid
.Я не могу сказать, почему это было реализовано таким образом. Возможно, потому что большинство
ps
опций являются «фильтрами» процессов, поэтому реализация-p
одного и того же способа была проще , поэтому для быстрого перехода к/proc/$pid
быстрому выбору может потребоваться отдельный путь к коду или дублирование кода ... Другая гипотеза состоит в том, что в некоторых случаях, включая-p
дополнительные опции, в конечном итоге требуется листинг, поэтому, возможно, сложно определить, какие именно случаи позволят использовать ярлык, а какие нет.Это приводит нас к обходному пути, переходя прямо к
/proc/$pid
, не перечисляя полный набор PID системы, избегая всех известных рас и просто получая необходимую информацию прямо из источника.Это немного уродливо, но проблема, которую вы описываете, действительно существует, это должен быть надежный способ получить эту информацию.
источник