Столкнулся с интересной задачей по сетям и полез разбираться, как же генерируется TCP Timestamp в Linux. Испытания было решено провести на Linux 3.16 и моем любимом Debian Jessie.
Итак, для начала поднимем любой http сервер на локалхосте и дернем его curl'ом с той же машины, глядя в этот момент на показания tcpdump.
Как же оно сгенерировано?
После часа поисков по коду Линукс Ядра я наткнулся на вот такой код в файле include/net/tcp.h:
Но что же такое jiffie и как его посчитать? Вопрос сложный и мерзкий. Если кратко, то jiffies - это число срабатываний прерываний таймера в Linux с момента загрузки системы (ключевое слово uptime).
Интерес заключается в том, что для каждой системы частота этого таймера индивидуальна (и зовется общепринято - HZ) и я потратил очень много времени прежде чем узнал, как часто срабатывает этот таймер на моей машине (на вашей показания могут быть совершенно иные!).
Итак, чтобы прекратить растрату времени я решил написать простейший модуль для своего ядра и посмотреть, что он выдает. Код можете найти вот здесь.
Ровно в момент когда я дергал curl я также загрузил этот модуль ядра и получил в dmesg следующие показания:
Опа! Вы уже заметили. что странное число в поле "current jiffie converted to timestamp" очень похоже на TCP timestamp? Поздравляю! Это он и есть. Осталось понять, как он все-таки получается и как связан с аптаймом машины.
Итак, мы узнали, что таймер работает с частотой 250 герц, то есть срабатывает 1/250 раз в секунду. Чтобы перевести jiffies в секунды нужно jiffies поделить на HZ, получится следующее:
Итак, мы теперь можем имея время аптайма в секундах сами сгенерировать TCP timestamp просто умножив на показания из /proc/uptime на 250 и отбросив дробные значения :)
Итак, для начала поднимем любой http сервер на локалхосте и дернем его curl'ом с той же машины, глядя в этот момент на показания tcpdump.
18:25:40.137056 IP 127.0.0.1.33673 > 127.0.0.1.80: Flags [S], seq 4291943858, win 43690, options [mss 65495,sackOK,TS val 170139796 ecr 0,nop,wscale 7], length 0Ок, что мы тут видим? Мы видим число 170139796, которое и является TCP timestamp.
18:25:40.137079 IP 127.0.0.1.80 > 127.0.0.1.33673: Flags [S.], seq 3577086755, ack 4291943859, win 43690, options [mss 65495,sackOK,TS val 170139796 ecr 170139796,nop,wscale 7], length 0
Как же оно сгенерировано?
После часа поисков по коду Линукс Ядра я наткнулся на вот такой код в файле include/net/tcp.h:
/* TCP timestamps are only 32-bits, this causes a slightСтало быть, некий jiffie (который сам по себе 64 битный) преобразуется в 32 битное значение, путем отсечения старших битов.
* complication on 64-bit systems since we store a snapshot
* of jiffies in the buffer control blocks below. We decided
* to use only the low 32-bits of jiffies and hide the ugly
* casts with the following macro.
*/
#define tcp_time_stamp ((__u32)(jiffies))
Но что же такое jiffie и как его посчитать? Вопрос сложный и мерзкий. Если кратко, то jiffies - это число срабатываний прерываний таймера в Linux с момента загрузки системы (ключевое слово uptime).
Интерес заключается в том, что для каждой системы частота этого таймера индивидуальна (и зовется общепринято - HZ) и я потратил очень много времени прежде чем узнал, как часто срабатывает этот таймер на моей машине (на вашей показания могут быть совершенно иные!).
Итак, чтобы прекратить растрату времени я решил написать простейший модуль для своего ядра и посмотреть, что он выдает. Код можете найти вот здесь.
Ровно в момент когда я дергал curl я также загрузил этот модуль ядра и получил в dmesg следующие показания:
[680865.248893] Current jiffie: 4465108609В то время, как все возможные статьи в интернете говорили о том, что HZ либо 100 либо 1000, но уж никак не 250.
[680865.249188] Current jiffie converted to timestamp: 170141313
[680865.249480] Current HZ: 250
Опа! Вы уже заметили. что странное число в поле "current jiffie converted to timestamp" очень похоже на TCP timestamp? Поздравляю! Это он и есть. Осталось понять, как он все-таки получается и как связан с аптаймом машины.
Итак, мы узнали, что таймер работает с частотой 250 герц, то есть срабатывает 1/250 раз в секунду. Чтобы перевести jiffies в секунды нужно jiffies поделить на HZ, получится следующее:
perl -e 'print 170141313/250'А в свою очередь это время в секундах показывающее, как давно был загружен сервер, его можно также взять в переменной /proc/uptime (первое число):
680565.252
cat /proc/uptimeТакже uptimе можно получить с помощью соответствующей команды:
681437.68 5443271.52
uptime
18:39:48 up 7 days, 21:21, 3 users, load average: 0.11, 0.06, 0.06
Итак, мы теперь можем имея время аптайма в секундах сами сгенерировать TCP timestamp просто умножив на показания из /proc/uptime на 250 и отбросив дробные значения :)
Собственно частота таймера указывается при сборке ядра и для Debian она поумолчанию 250Гц.
ReplyDelete"работает с частотой 250 герц, то есть срабатывает 1/250 раз в секунду" - тут "1/" немного лишнее :)
ReplyDelete