Как python определяет PID, казалось бы, без обычных системных вызовов в Linux?

При выполнении следующей команды

strace -f python3 -c 'import os; print(os.getpid())'

Я заметил, что strace не перехватывает вызов системного вызова getpid(2). Сначала я подумал, что это произошло из-за того, что glibc кэширует pid, но не должно быть pid для кэширования libc без хотя бы одного реального системного вызова. Затем я подумал, что, возможно, виновником был vdso, но запуск программы на C, которая выполняет этот системный вызов через libc, показывает вызов getpid при трассировке. В конце концов я сдался и посмотрел исходный код модуля Python os.getpid, который, по-видимому, определен в Modules/posixmodule.c. К моему удивлению (и последующему замешательству), он делает обычный вызов getpid!

Итак, мой вопрос: как python определяет результат os.getpid? и если такое значение действительно получено вызовом getpid, как на самом деле выполняется этот вызов?


person Tenders McChiken    schedule 18.09.2018    source источник
comment
strace -e trace=getpid -f python3 -c 'import os; print(os.getpid())' показал два вызова системного вызова getpid, когда я тестировал его.   -  person Shawn    schedule 18.09.2018
comment
@Шон: Очень странно. Я только что протестировал strace с дополнительным trace=getpid на двух разных системах (к сожалению, на обеих одна и та же версия ubuntu, 16.04). Оба печатают PID, но не показывают вызов getpid; только '+++ завершился с 0 +++'.   -  person Tenders McChiken    schedule 18.09.2018
comment
@Shawn: Какую систему ты используешь? vdso включен?   -  person Tenders McChiken    schedule 18.09.2018
comment
Арч и убунту 18.04.   -  person Shawn    schedule 18.09.2018
comment
@jww: Возможно, но с github.com /torvalds/linux/blob/v3.19/include/uapi/linux/ не похоже, чтобы система предоставляла пользователям PID как значение вспомогательного вектора. Значения x86, похоже, также не включают PID ... Это также не объясняет, почему Ubuntu 18.04 делает вызов, а 16.04 - нет: /   -  person Tenders McChiken    schedule 18.09.2018
comment
Python просто вызывает функцию getpid(2) libc. Что бы это ни делало, это то, что делает Python.   -  person Keith    schedule 18.09.2018


Ответы (1)


vdso работает, среди прочего, специфические для процесса переменные в пользовательское пространство, которые функции vdso умеют читать. Одним из них является идентификатор текущего процесса, поэтому gettimeofday не нужно выполнять системный вызов для доступа к этой информации.

Теперь, специально для getpid, на самом деле это не вызов VDSO. В glibc до версии 2.25 вызовы библиотеки кэшировались, а поскольку часть среды выполнения Python вызывает getpid, после первого обращения к ней не будет. Начиная с версии 2.25, библиотека не кэширует идентификатор процесса, поэтому каждый вызов getpid приводит к системному вызову.

person Community    schedule 18.09.2018
comment
Не могли бы вы расширить свой ответ, указав, почему getpid, как сообщается, появляется в 18.04 и других системах? Получил ли ptrace возможность отслеживать вызовы vdso, или python каким-то образом подрывает vdso и делает вызов напрямую? - person Tenders McChiken; 18.09.2018
comment
Интересно. Как оказалось, в 18.04 есть изменение glibc, которое удаляет кеширование, поэтому каждый вызов getpid приводит к системному вызову. Я обновил ответ. Спасибо! - person ; 18.09.2018
comment
Это вызывает больше вопросов, чем ответов (почему моя тестовая программа C показывает getpid при трассировке), но этот ответ кажется наиболее вероятным. Я буду исследовать дальше позже. Спасибо за ответ, мой unix человек! - person Tenders McChiken; 18.09.2018
comment
Я думаю, что это все еще кеш PID, который изменился. Среда выполнения C вообще не вызывает getpid, в отличие от Python, поэтому программа C покажет первый вызов getpid и каждый последующий во всех версиях библиотеки, в то время как Python покажет только первый вызов во всех версиях, а последующие вызовы только в glibc 2.25 и новее. Программа на C с несколькими вызовами getpid тоже может быть интересной. - person ; 18.09.2018