Код: Выделить всё
#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, приведенное выше выведет три строки с двумя разными значениями.
Код: Выделить всё
>=2.25
- Однако с musl приведенное выше выдаст три одинаковые строки.
На странице руководства 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;
}
Подробнее здесь: https://stackoverflow.com/questions/784 ... id-caching