clockid_t (первый аргумент clock_gettime) переносимость

Большинство POSIX-совместимых систем предоставляют функцию для получения или установки одного из таймеров с высоким разрешением:

int clock_gettime(clockid_t clock_id, struct timespec *tp);

Документация для каждой системы обычно перечисляет несколько символических имен в качестве возможных clock_id значений, но фактические числовые значения никогда не упоминаются. Оказывается, в разных системах различаются не только числовые значения, но и символические имена для одного и того же значения. Более того, не все часы, реально поддерживаемые системой, определены в time.h (bits/time.h) — некоторые определены только, скажем, в linux/time.h.


То есть в системе Linux у нас может быть:

#define CLOCK_REALTIME                  0
#define CLOCK_MONOTONIC                 1
#define CLOCK_PROCESS_CPUTIME_ID        2
#define CLOCK_THREAD_CPUTIME_ID         3
#define CLOCK_MONOTONIC_RAW             4
#define CLOCK_REALTIME_COARSE           5
#define CLOCK_MONOTONIC_COARSE          6
#define CLOCK_BOOTTIME                  7
#define CLOCK_REALTIME_ALARM            8
#define CLOCK_BOOTTIME_ALARM            9
#define CLOCK_SGI_CYCLE                10      // In linux/time.h only.
#define CLOCK_TAI                      11      // In linux/time.h only.

В среде Cygwin (не дословная выдержка):

#define CLOCK_REALTIME                  1       // Means CLOCK_MONOTONIC?
#define CLOCK_MONOTONIC                 4       // Means CLOCK_MONOTONIC_RAW?
#define CLOCK_PROCESS_CPUTIME_ID        2
#define CLOCK_THREAD_CPUTIME_ID         3

Во FreeBSD:

#define CLOCK_REALTIME                  0
#define CLOCK_VIRTUAL                   1
#define CLOCK_PROF                      2
#define CLOCK_MONOTONIC                 4
#define CLOCK_UPTIME                    5       // Synonymous to CLOCK_BOOTTIME?
#define CLOCK_UPTIME_PRECISE            7
#define CLOCK_UPTIME_FAST               8
#define CLOCK_REALTIME_PRECISE          9       // Same as CLOCK_REALTIME?
#define CLOCK_REALTIME_FAST            10       // Synonymous to CLOCK_REALTIME_COARSE?
#define CLOCK_MONOTONIC_PRECISE        11       // Same as CLOCK_MONOTONIC?
#define CLOCK_MONOTONIC_FAST           12       // Synonymous to CLOCK_MONOTONIC_COARSE?
#define CLOCK_SECOND                   13
#define CLOCK_THREAD_CPUTIME_ID        14
#define CLOCK_PROCESS_CPUTIME_ID       15

В AIX:

#define CLOCK_REALTIME                ...
#define CLOCK_MONOTONIC               ...
#define CLOCK_PROCESS_CPUTIME_ID      ...
#define CLOCK_THREAD_CPUTIME_ID       ...

В СанОС:

#define CLOCK_REALTIME                ...
#define CLOCK_HIGHRES                 ...       // Synonymous to CLOCK_MONOTONIC_RAW?

В QNX:

#define CLOCK_REALTIME                ...
#define CLOCK_SOFTTIME                ...
#define CLOCK_MONOTONIC               ...

И так далее.


Это заставляет меня задаться вопросом, как использовать clock_gettime() с первым аргументом, отличным от CLOCK_REALTIME переносимым способом. Например, если я хочу использовать CLOCK_MONOTONIC_COARSE или CLOCK_BOOTTIME, как я узнаю, что BSD вместо этого называет их CLOCK_MONOTONIC_FAST и CLOCK_UPTIME соответственно?

Разумно ли делать предположения на основе числовых значений первых 4 символических имен? Такие как:

#define POSIX_CLOCK_REALTIME            0
#define POSIX_CLOCK_MONOTONIC           1
#define POSIX_CLOCK_PROCESS_CPUTIME_ID  2
#define POSIX_CLOCK_THREAD_CPUTIME_ID   3
#define POSIX_CLOCK_MONOTONIC_RAW       4

#if CLOCK_REALTIME == POSIX_CLOCK_MONOTONIC
    #warning This platform has monotonic realtime clock.
#end if
#if CLOCK_MONOTONIC == POSIX_CLOCK_MONOTONIC_RAW
    #warning This platform has undisciplined monotonic clock.
#end if

Если система на самом деле поддерживает CLOCK_TAI, но не определяет его в time.h, как я могу это проверить и использовать, принимая во внимание, что одно и то же числовое значение 11 может обозначать CLOCK_MONOTONIC_PRECISE или что-то еще в других системах?


person Anton Samsonov    schedule 26.06.2015    source источник
comment
Нет, небезопасно делать предположения. Сначала вам нужно изучить все часы в целевых системах, чтобы увидеть, какие из них совпадают с другими. Тогда вам, вероятно, следует использовать условную компиляцию (например, #ifdef __linux__), чтобы выбрать правильные часы для вашего приложения.   -  person Some programmer dude    schedule 26.06.2015
comment
@JoachimPileborg Увидев все эти несоответствия, я теперь боюсь, что даже подход #ifdef __linux__ недостаточно переносим для всех этих систем на базе Linux с различной степенью настройки и частными расширениями (под теми, которые я имею в виду действительно сильно модифицированные системы, а не просто ребрендинг и обновленные дистрибутивы).   -  person Anton Samsonov    schedule 26.06.2015
comment
Как узнать, действительно ли система поддерживает CLOCK_TAI, если она не определяет его в time.h?   -  person Steve Summit    schedule 26.06.2015
comment
@SteveSummit Проверка во время выполнения возможна из-за того, что clock_gettime возвращает ноль, если часы поддерживаются и в настоящее время доступны (имеют допустимое значение), или ненулевое значение в противном случае. Если бы числовые значения были одинаковыми во всех системах, то было бы достаточно определить свои собственные константы, чтобы не зависеть от системных включаемых файлов (которые имеют тенденцию к рассинхронизации) и избежать несоответствий в именах. Но, к сожалению, числовые значения неоднозначны.   -  person Anton Samsonov    schedule 26.06.2015


Ответы (1)


Если бы я пытался написать максимально переносимую программу, я бы ограничился символическими значениями, определенными в соответствующем Стандарте. (Из того, что вы говорите, похоже, что CLOCK_REALTIME — и, возможно, только это значение — является стандартным.)

Если бы некоторые системы реализовывали полезные расширения, которые я хотел использовать, я бы проверил их, сказав

#ifdef CLOCK_MONOTONIC_COARSE
... my code calling clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) ...
#endif

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

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

Кроме того, вы предполагаете, что только потому, что числовое значение 1 используется для CLOCK_MONOTONIC в Linux, но для CLOCK_REALTIME под Cygwin, означает, что эти часы могут каким-то образом быть одинаковыми? Вероятно, это не так. Цифры произвольны, и вам на них наплевать; вот почему существуют символические константы!

person Steve Summit    schedule 26.06.2015
comment
Вопрос о том, означают ли «символические значения совершенно разные вещи» — это отдельный вопрос; Я наивно надеюсь, что поведенческие различия должны быть минимальными. Вместо этого я говорил о противоположной проблеме — о том, что разные системы имеют разные символические имена для одного и того же типа часов. Таким образом, нельзя просто написать #ifdef CLOCK_MONOTONIC_COARSE — нужно знать все псевдонимы этого имени во всех системах, такие как CLOCK_MONOTONIC_FAST или что-то в этом роде. - person Anton Samsonov; 26.06.2015
comment
Ситуация с неопределенными, но реально поддерживаемыми часами не так уж и редка — они у меня сейчас в openSUSE q31056979. Я пытался делать предположения, когда только начал изучать другие системы и увидел, что числовые значения отличаются. Потом я понял, что символические имена тоже различаются. Нет, меня совсем смущает такая «стандартизация». - person Anton Samsonov; 26.06.2015