c — PHP Internals: как работает TSRMLS_FETCH?

Как работает PHP Internals TSRMLS_FETCH макрос делает свою работу?

В соответствии с Руководство по PHP

При разработке расширений ошибки сборки, содержащие «tsrm_ls не определен», или ошибки на этот счет проистекают из того факта, что TSRMLS не определен в текущей области видимости, чтобы исправить это, объявите функцию, принимающую TSRMLS с соответствующим макросом, если прототип рассматриваемая функция не может быть изменена, вы должны вызвать TSRMLS_FETCH в теле функции.

Я это понимаю объявив функцию для принятия TSRMLS с соответствующими макросами означает использование TSRMLS_C, TSRMLS_D, TSRMLS_CC и TSRMLS_DC либо определить вызов функции с дополнительными параметрами / аргументами.

Тем не мение, если прототип рассматриваемой функции не может быть изменен, вы должны вызвать TSRMLS_FETCH в теле функции немного смущает меня Если я посмотрю на php-src оба Вот а также Вот TSRMLS_FETCH кажется пустой макрос.

Так что возникает вопрос: как TSRMLS_FETCH даже работа? Что-то еще заполняет этот макрос во время компиляции?

3

Решение

Во-первых, я бы не стал обращать слишком много внимания на то, что говорится в руководстве по внутренним компонентам PHP. Он очень устарел, и есть большая вероятность, что он будет удален из руководства в ближайшем будущем. В настоящее время существует два веб-сайта, посвященных внутренним компонентам PHP: PHPInternalsBook.com а также PHPInternals.net (Я автор контента для последнего). Есть также несколько хороших блогов, чтобы следовать, в том числе Никита а также Жюльена.

TSRM в серии PHP 5.x был довольно агрессивным. При желании получить доступ к любому Zend Globals из функции выбор был либо между извлечением указателя памяти TLS из вызова функции (например, pthread_getspecific, что было относительно дорого) или распространение указателя памяти TLS через параметры функции (грязный и подверженный ошибкам процесс, но более быстрый способ). TSRMLS_FETCH упомянутый вами макрос использовался для первого подхода.

В PHP 7.x распространение указателя памяти TLS (через TSRMLS_[D|C]C? макросы) был удален полностью (хотя их макросы все еще определены для обратной совместимости — они просто ничего не будут делать). Предпочтительный способ получить доступ к TLS TSRM — через статический кеш. Это в основном просто локальная глобальная переменная потока, используемая для хранения текущего указателя памяти TLS.

Вот соответствующие макросы:

#define TSRMLS_CACHE _tsrm_ls_cache // the TLS global variable
#define TSRMLS_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE = NULL; // define it
#define TSRMLS_CACHE_UPDATE() TSRMLS_CACHE = tsrm_get_ls_cache() // update it - i.e. calls pthread_getspecific()
#define TSRMLS_CACHE_RESET()  TSRMLS_CACHE = NULL // reset it

Использование вышеупомянутых макросов требует особого внимания для надлежащего обновления статического кэша (обычно во время GINIT, и иногда RINIT, фазы расширения). Тем не менее, это более чистый способ предоставления доступа к указателю памяти TLS без беспорядочной передачи его через параметры функции или снижения производительности при его постоянной загрузке (через pthread_getspecific и тому подобное).

Дополнительное чтение:

  • Родной TLS (RFC, который представил это изменение в PHP 7.0)
  • Потоки и PHP (Сообщение в блоге Жюльена на TSRM)
1

Другие решения

Взгляни на старые версии этого файла:

#define TSRMLS_FETCH()            void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
#define TSRMG(id, type, element)  (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#define TSRMLS_D  void ***tsrm_ls
#define TSRMLS_DC , TSRMLS_D
#define TSRMLS_C  tsrm_ls
#define TSRMLS_CC , TSRMLS_C

Кажется, в какой-то момент PHP удалил поддержку этих макросов, но оставил их пустыми, чтобы избежать необходимости разделять внешний код на две версии: одну для нового PHP и одну для старого PHP.

2

Этот кусок кода

#if ZEND_DEBUG
...
#else
#define TSRMLS_FETCH()
...
#endif

Делает следующее:

Если вы не находитесь в режиме отладки, меняйте каждый вызов на макрос TSRMLS_FETCH() ни с чем.

В этом примере:

#if 0
#define TSRMLS_FETCH() printf("Bla");
#else
#define TSRMLS_FETCH()
#endif

int main(void)
{
TSRMLS_FETCH()
return 0;
}

cpp demo.c (вывод препроцессора) возвращает:

int main(void)
{
return 0;
}
1
По вопросам рекламы [email protected]