Настраивал операционную систему. Выбирал таймеры для реализации виртуальной сети.
Нашел несколько полезных вещей, стал применять.
Таймеры на Windows. Необходимым оказалось подкручивать параметр мультимедиа таймера
Использую функцию timeBeginPeriod(wTimerRes);
Я заметил, что на нескольких разных компьютерах под управлением Windows 7 по разному ведет себя процесс отладки. Он то бежит как угорелый, символы быстро пробегают по экрану, то тормозит. Даже на одном компьютере я получил результат, что после перезагрузки, он стал медленнее обрабатывать протокол. Замерил параметр задержки, оказалось, что вместо usleep(1000)=1мс, квант времени равняется 15мс. А у меня протокол должен работать с разрешением времени минимум 5мс. При 15мс, виртуальная сеть тормозит и вызывает таймауты в работе виртуальных устройств.
Таймеры pthread. Моя сеть должна работать на Windows, Linux и на моей операционке. Основа виртуальной сети - очередь таймерных объектов -- блоков памяти, которые доставляются в строго определенное время.
Исследовал разрешение таймеров.
clock_getres(CLOCK_MONOTONIC) возвращает 370 микросекунд. На разных процессорах эта цифра разная, но меньше 1мс.
clock_getres(CLOCK_REALTIME) возвращает 15.6мс.
Измерять время надо монотонным, иначе цифры округляются до безобразных величин.
Монотонный таймер оказался не очень то монотонным, на этом потерял целый день на отладку работы виртуальной сети и планировщика.
Чтобы сделать из монотонного таймера действительно монотонный применил такой ход:
(uint64_t)(tv_nsec + tv_sec*1000000000); Иногда в tv_nsec встречаются любые числа, неожиданные. Монотонным таймер становится только после такой операции.
timestamp = osKernelSysTick();
while ((uint32_t)(timestamp - tr->wait.timestamp) < tr->wait.timeout) {
interval.tv_nsec = (tr->wait.timeout - (uint32_t)(timestamp - tr->wait.timestamp));
clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, &diff);
timestamp = osKernelSysTick();
}
Применил такой вариант ожидания. Ожидание применяется перед получением пакета данных.
Основа сети - асинхронная очередь, куда любой тред может засунуть пакет данных, но планировщик один - один читатель много писателей. Реализация асинхронной очереди на атомарных операциях, в моем случае делается на операциях атомарного доступа к памяти.
void osAsyncQueuePut(osAsyncQueue_t* queue, void* data)
{
List_t* tr = g_slice_alloc(sizeof(List_t));
tr->data = data;
volatile void** ptr = (volatile void**)&queue->tail;
do {
tr->next = atomic_pointer_get(ptr);
atomic_mb();
} while(!atomic_pointer_compare_and_exchange(ptr, tr->next, tr));
}
Эта операция добавляет элемент в список - вместо верхнего элемента. Список снимается в одно движение со стороны планировщика:
queue->head = atomic_pointer_exchange(&queue->tail, NULL);
Перед разбором список надо перевернуть, чтобы получить нормальный хронологический порядок.
Вот и все искусство.
Нашел несколько полезных вещей, стал применять.
Таймеры на Windows. Необходимым оказалось подкручивать параметр мультимедиа таймера
Использую функцию timeBeginPeriod(wTimerRes);
Я заметил, что на нескольких разных компьютерах под управлением Windows 7 по разному ведет себя процесс отладки. Он то бежит как угорелый, символы быстро пробегают по экрану, то тормозит. Даже на одном компьютере я получил результат, что после перезагрузки, он стал медленнее обрабатывать протокол. Замерил параметр задержки, оказалось, что вместо usleep(1000)=1мс, квант времени равняется 15мс. А у меня протокол должен работать с разрешением времени минимум 5мс. При 15мс, виртуальная сеть тормозит и вызывает таймауты в работе виртуальных устройств.
Таймеры pthread. Моя сеть должна работать на Windows, Linux и на моей операционке. Основа виртуальной сети - очередь таймерных объектов -- блоков памяти, которые доставляются в строго определенное время.
Исследовал разрешение таймеров.
clock_getres(CLOCK_MONOTONIC) возвращает 370 микросекунд. На разных процессорах эта цифра разная, но меньше 1мс.
clock_getres(CLOCK_REALTIME) возвращает 15.6мс.
Измерять время надо монотонным, иначе цифры округляются до безобразных величин.
Монотонный таймер оказался не очень то монотонным, на этом потерял целый день на отладку работы виртуальной сети и планировщика.
Чтобы сделать из монотонного таймера действительно монотонный применил такой ход:
(uint64_t)(tv_nsec + tv_sec*1000000000); Иногда в tv_nsec встречаются любые числа, неожиданные. Монотонным таймер становится только после такой операции.
timestamp = osKernelSysTick();
while ((uint32_t)(timestamp - tr->wait.timestamp) < tr->wait.timeout) {
interval.tv_nsec = (tr->wait.timeout - (uint32_t)(timestamp - tr->wait.timestamp));
clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, &diff);
timestamp = osKernelSysTick();
}
Применил такой вариант ожидания. Ожидание применяется перед получением пакета данных.
Основа сети - асинхронная очередь, куда любой тред может засунуть пакет данных, но планировщик один - один читатель много писателей. Реализация асинхронной очереди на атомарных операциях, в моем случае делается на операциях атомарного доступа к памяти.
void osAsyncQueuePut(osAsyncQueue_t* queue, void* data)
{
List_t* tr = g_slice_alloc(sizeof(List_t));
tr->data = data;
volatile void** ptr = (volatile void**)&queue->tail;
do {
tr->next = atomic_pointer_get(ptr);
atomic_mb();
} while(!atomic_pointer_compare_and_exchange(ptr, tr->next, tr));
}
Эта операция добавляет элемент в список - вместо верхнего элемента. Список снимается в одно движение со стороны планировщика:
queue->head = atomic_pointer_exchange(&queue->tail, NULL);
Перед разбором список надо перевернуть, чтобы получить нормальный хронологический порядок.
Вот и все искусство.