Есть ли правильный способ кэширования PID/TID?Linux

Ответить Пред. темаСлед. тема
Anonymous
 Есть ли правильный способ кэширования PID/TID?

Сообщение Anonymous »

Рассмотрим следующий код:

Код: Выделить всё

#define _GNU_SOURCE
#include 
#include 
#include 

int main() {
pid_t tid = gettid();
int ret = syscall(SYS_fork, 0);
if (tid == gettid() && ret == 0) {
printf("bad thing happens\n");
}
}
  • С современным () glibc, приведенное выше выведет три строки с двумя разными значениями.
  • Однако с musl приведенное выше выдаст три одинаковые строки.
Разница связана с кешированием. Реализация libc пытается избежать накладных расходов из-за системного вызова, поэтому такие значения кэшируются. Обертки fork/clone отвечают за обновление значений, но пользователь может обойти такие оболочки, что приведет к неожиданным результатам.
На странице руководства getpid есть специальный раздел, посвященный истории:

Начиная с glibc 2.3.4 до glibc 2.24 включительно, функция-обертка glibc
для кэшированных PID getpid() , с целью
избежать дополнительных системных вызовов, когда процесс вызывает getpid()
неоднократно. Обычно это кэширование было невидимым, но его правильная
работа зависела от поддержки функций-оболочек fork(2),
vfork(2) и clone(2): если приложение обходило glibc
оболочки для этих системных вызовов с помощью syscall(2), то вызов
getpid() в дочернем элементе вернет неправильное значение (если быть
точнее: он вернет PID родительского элемента процесс). Кроме того, были случаи, когда getpid() мог возвращать неправильное
значение даже при вызове clone(2) через функцию-оболочку glibc.
(Обсуждение одного такого случая см. ОШИБКИ в clone(2).)
Более того, сложность кода кэширования была
источником нескольких ошибок в glibc на протяжении многих лет.

Интересно, есть ли еще способ правильно кэшировать. Учитывая, что MADV_WIPEONFORK будет инструктировать ядро ​​стереть определенные страницы при разветвлении, можем ли мы создать следующую структуру:

Код: Выделить всё

struct ProcessLocalStorage {
OnceFlag once; /* zero represents uninitialized */
pid_t pid;
pid_t getpid() {
callonce(&this->once, []{ pid = syscall(SYS_getpid); });
return pid;
}
} *pls = mmap(...); /* hinted by MADV_WIPEONFORK */

pid_t gettid() {
static thread_local pid_t pid = 0;
static thread_local pid_t tid = 0;
pid_t real_pid = pls->getpid();
if (pid != real_pid) {
pid = real_pid;
tid = syscall(SYS_gettid);
}
return tid;
}
Одна из проблем, о которой я могу подумать, заключается в том, что пользователь вызывает SYS_clone самостоятельно, не настроив TLS должным образом, но я думаю, мы можем сказать, что пользователи не должны предполагать, что libc работает в таких условиях. сценарии.


Подробнее здесь: https://stackoverflow.com/questions/784 ... id-caching
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Linux»