Почему ABI ‘pthread_rwlock_t’ сильно отличается в разных версиях?

#c #pthreads #glibc

#c #pthreads #glibc

Вопрос:

Я изучаю разные версии реализации pthread_rwlock_t .

  1. GLIBC2.30
     typedef union
    {
      struct __pthread_rwlock_arch_t __data;
      char __size[__SIZEOF_PTHREAD_RWLOCK_T];
      long int __align;
    } pthread_rwlock_t;
    
    struct __pthread_rwlock_arch_t
    {
      unsigned int __readers;
      unsigned int __writers;
      unsigned int __wrphase_futex;
      unsigned int __writers_futex;
      unsigned int __pad3;
      unsigned int __pad4;
      ...
     
  2. GLIBC2.17
     typedef union
    {
    # ifdef __x86_64__
      struct
      {
        int __lock;
        unsigned int __nr_readers;
        unsigned int __readers_wakeup;
        unsigned int __writer_wakeup;
        unsigned int __nr_readers_queued;
        unsigned int __nr_writers_queued;
        int __writer;
        int __shared;
    ...
    
    } pthread_rwlock_t;
     

Их реализации кажутся разными и вызывают у меня некоторые трудности. Потому что у меня есть программа a , которая связана с GLIBC2.30, и программа b , которая связана с GLIBC2.17. Обе программы будут работать с тем же pthread_rwlock_t , что и в shm. Я добился этого, статически связав GLIBC.

Однако GLIBC2.30 и GLIBC2.17 будут интерпретировать pthread_rwlock_t объект в shm по-разному, потому что их определения различаются. Например, если __readers для GLIBC2.30 установлено значение 2, а затем к нему обращается GLIBC2.17, он будет думать pthread_rwlock_t , что он заблокирован и переходит в режим сна. Однако pthread_rwlock_t он не заблокирован.

Мои вопросы:

  1. Почему pthread_rwlock_t так сильно меняется в разных версиях? Это потому, что он хочет поддерживать больше функций или повысить производительность?
  2. Есть ли какие-либо способы, которыми я могу GLIBC2.30 и GLIBC2.17 использовать один и тот же rwlock?

Обновить

Я просматриваю больше исходных кодов и нахожу:

  1. GLIBC2.17 использует lll_lock для реализации
  2. GLIBC2.30 использует атомарную операцию для реализации

Тогда у меня есть еще один вопрос: разные версии GLIBC не могут быть сжаты друг с другом? Значит, я не могу использовать shm между разными версиями GLIBC?

Комментарии:

1. I achieved this by staticly linking GLIBC Так почему вас это волнует? For example, if __readers is set to 2 Вы получаете доступ к внутренней структуре pthread_rwlock_t типа или используете только pthread_rwlock_* средства доступа? (Даже если вы используете, вы статически связали glibc. Так .. почему вас это волнует?)

2. 1. Разные программы, связанные с GLIBC, будут работать на одном и том же shm, это вызывает проблемы, поэтому мне не все равно. 2. При вызове pthread_rwlock_wrlock он установит __readers или __lock

3. Обмен данными между двумя программами, связанными с glibc, по-прежнему осуществляется ядром, а не glibc.

4. Я добился этого, статически связав GLIBC. И этот вопрос дает прекрасный пример ТОГО, ПОЧЕМУ ВЫ НЕ СВЯЗЫВАЕТЕ GLIBC СТАТИЧЕСКИ .

5. Это полностью проблема GLIBC и не имеет ничего общего с ядром Нет, это ваша проблема. Вы решили статически связать две программы, которые должны взаимодействовать на двоичном уровне, с двумя разными версиями GLIBC. Вы: «Доктор, мне больно, когда я это делаю». Доктор: «Не делайте этого».

Ответ №1:

  1. Почему pthread_rwlock_t так сильно меняется в разных версиях? Это потому, что он хочет поддерживать больше функций или повысить производительность?

Потому что разработчики glibc решили, что при его изменении можно получить преимущество.

Как и в случае с большинством структур, определяемых стандартными заголовками, расположение struct pthread_rwlock_t не стандартизировано. Как и в некоторых подобных структурах, даже ни одно из имен членов не стандартизировано. Я предполагаю, что причина, по которой структура не является полностью непрозрачной, заключается в том, что экземпляры могут быть объявлены напрямую, а не требовать их создания с помощью какой-либо функции конструктора.

Вы выходите за рамки, если создаете программы, которые зависят от конкретной компоновки этой структуры, за исключением того, что предоставляет версия pthreads, которую вы создаете. Это определение предоставляется версией pthreads.h , используемой во время компиляции, и оно должно быть сопоставлено с соответствующей библиотекой.

  1. Есть ли какие-либо способы, которыми я могу GLIBC2.30 и GLIBC2.17 использовать один и тот же rwlock?

Я думаю, вы уже знаете, что ответ «нет». Каждая реализация библиотеки имеет прямые зависимости от конкретного макета этой структуры, и макеты не совпадают вообще. Если вы хотите совместно использовать блокировку pthreads среди процессов, тогда, в дополнение к настройке его pshared атрибута on, вы должны создавать сотрудничающие программы для той же версии libpthread и ее заголовков (или любой другой библиотеки, предоставляющей вашу реализацию pthreads). Некоторый перекос версии может быть приемлемым на практике, но если вы хотите рискнуть, тогда вам придется тестировать и проверять конкретные комбинации.

являются ли разные версии GLIBC несовместимыми друг с другом?

Очевидно, что две версии, связанные с вашей конкретной проблемой, несовместимы так, как вы надеялись. Glibc довольно хорош в отношении совместимости ссылок:

  • программа, динамически связанная с одной версией, с большой вероятностью будет корректно взаимодействовать с разделяемыми библиотеками более поздних версий; и
  • программа, которая правильно строит и статически связывает с одной версией, почти наверняка будет корректно строить и статически связывать с более поздними версиями, вплоть до использования функций, удаленных из стандарта языка Си (я смотрю на вас, gets() ).

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

Значит, я не могу использовать shm между разными версиями GLIBC?

Совместное использование памяти между программами, созданными на основе разных версий Glibc, вероятно, может быть успешно выполнено с типами данных, которые полностью определены вами на основе встроенных в язык C типов данных ( int double , и т.д.) и со стандартизированными представлениями (и т.д. int32_t ). В принципе, однако, даже представления встроенных типов данных могут меняться между версиями.

Я полагаю, что могут существовать библиотеки C, предназначенные для этой конкретной цели (хотя я не знаю ни одной), но в целом реализации C дают очень мало гарантий совместимости представления данных в памяти. В общем случае вы не можете полагаться на разные версии библиотеки для обеспечения совместимых представлений в памяти большинства других типов данных.