База полезных знаний

Выбор процессора

При вставке таймера нужно принять одно важное решение, касающееся выбора подходящей таблицы, — иными словами, нужно выбрать наиболее оптимальный процессор.

Если у таймера нет связанного с ним DPC-вызова, ядро сканирует все не запаркованные процессоры в текущей группе процессоров. Если текущий процессор запаркован, выбирается следующий процессор группы, в противном случае используется текущий процессор. С другой стороны, если с таймером связан какой-нибудь DPC-вызов, код вставки просто останавливает выбор на целевом процессоре, который связан с DPC, и выбирает таблицу таймера этого процессора.

В том случае, если разработчик драйвера не указал целевой процессор для DPC, ядро должно сделать свой выбор. Поскольку разработчики драйверов обычно ожидают, что DPC-вызов будет выполнен на том же самом процессоре, на котором в момент вставки был запущен код драйвера, ядро обычно выбирает центральный процессор 0. Именно он является процессором, следящим за временем, который всегда будет активен для обработки прерываний от часов. Но на серверных системах ядро выбирает процессор так, как будто DPC-вызова не существует, используя только что рассмотренные принципы выбора.

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

По мере накопления системных таймеров, из-за того, что большинство драйверов не создают родственных связей между своими DPC-вызовами, центральный процессор 0 становится все более и более загруженным выполнением кода истечения времени таймера.

Это повышает время отклика и может даже стать причиной длительных задержек выполнения DPC-вызовов или их утраты. Кроме того, истечение времени таймера может начать конкурировать с DPC-таймером, обычно связанным с обработкой прерывания, инициированного драйвером, например кодом сетевого пакета, что приводит к замедлению работы всей системы. Этот процесс усугубляется в сценарии Hyper-V, где центральный процессор 0 должен обрабатывать таймеры и DPC-вызовы, связанные с потенциальным множеством виртуальных машин, у каждой из которых есть свои таймеры и связанные с ними устройства.

Путем распределения таймеров между процессорами, как показано на рисунке, загрузка каждого процессора, связанная с истечением времени таймера, полностью распределяется между незапаркованными логическими процессорами.

На 32-разрядных системах объект таймера сохраняет номер связанного с ним процессора в заголовке диспетчера, а на 64-разрядныйх системах этот номер сохраняется в самом объекте.

ПРИМЕЧАНИЕ. Это поведение управляется переменной ядра KiDistributeTimers, которая инициализируется на основе параметра реестра, имеющего разное значение для установки сервера и клиента. Это поведение может быть настроено по-разному путем изменения или создания значения DistributeTimers, отличного от его исходного, основанного на SKU значения в параметре HKLM\SYSTEM\CurrentControlSet\Control\SessionManager\kernel.

Вывод списка системных таймеров.

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

В качестве примера посмотрите на следующий вывод.

[lkd> !timer

Dump system timers

Interrupt time: 61876995 000003df [ 4/ 5/2010 18:58:09.189]

List Timer Interrupt Low/High Fire Time DPC/thread

PROCESSOR 0 (nt!_KTIMER_TABLE fffff80001bfd080)

5 fffffa8003099810 627684ac 000003df [ 4/ 5/2010 18:58:10.756]

NDIS!ndisMTimerObjectDpc (DPC @ fffffa8003099850)

13 fffffa8003027278 272dde78 000004cf [ 4/ 6/2010 23:34:30.510] NDIS!ndisMWakeUpDpcX

(DPC @ fffffa80030272b8)

fffffa8003029278 272e0588 000004cf [ 4/ 6/2010 23:34:30.511] NDIS!ndisMWakeUpDpcX

(DPC @ fffffa80030292b8)

fffffa8003025278 272e0588 000004cf [ 4/ 6/2010 23:34:30.511] NDIS!ndisMWakeUpDpcX

(DPC @ fffffa80030252b8)

fffffa8003023278 272e2c99 000004cf [ 4/ 6/2010 23:34:30.512] NDIS!ndisMWakeUpDpcX

(DPC @ fffffa80030232b8)

16 fffffa8006096c20 6c1613a6 000003df [ 4/ 5/2010 18:58:26.901] thread

fffffa8006096b60

19 fffff80001c85c40 64f9aeb5 000003df [ 4/ 5/2010 18:58:14.971]

nt!CmpLazyFlushDpcRoutine (DPC @ fffff80001c85c00)

31 fffffa8002c43660 P dc527b9b 000003e8 [ 4/ 5/2010 20:06:00.673]

intelppm!LongCapTraceDpc (DPC @ fffffa8002c436a0)

40 fffff80001c86f60 62ca1080 000003df [ 4/ 5/2010 18:58:11.304] nt!CcScanDpc (DPC

@ fffff80001c86f20)

fffff88004039710 62ca1080 000003df [ 4/ 5/2010 18:58:11.304]

luafv!ScavengerTimerRoutine (DPC @ fffff88004039750)

...

252 fffffa800458ed50 62619a91 000003df [ 4/ 5/2010 18:58:10.619] netbt!TimerExpiry

(DPC @ fffffa800458ed10)

fffffa8004599b60 fe2fc6ce 000003e0 [ 4/ 5/2010 19:09:41.514] netbt!TimerExpiry

(DPC @ fffffa8004599b20)

PROCESSOR 1 (nt!_KTIMER_TABLE fffff880009ba380)

0 fffffa8004ec9700 626be121 000003df [ 4/ 5/2010 18:58:10.686] thread

fffffa80027f3060

fffff80001c84dd0 P 70b3f446 000003df [ 4/ 5/2010 18:58:34.647]

nt!IopIrpStackProfilerTimer (DPC @ fffff80001c84e10)

11 fffffa8005c26cd0 62859842 000003df [ 4/ 5/2010 18:58:10.855] afd!AfdTimeoutPoll

(DPC @ fffffa8005c26c90)

fffffa8002ce8160 6e6c45f4 000003df [ 4/ 5/2010 18:58:30.822] thread

fffffa80053c2b60

fffffa8004fdb3d0 77f0c2cb 000003df [ 4/ 5/2010 18:58:46.789] thread

fffffa8004f4bb60

13 fffffa8005051c20 60713a93 800003df [ NEVER ] thread

fffffa8005051b60

15 fffffa8005ede120 77f9fb8c 000003df [ 4/ 5/2010 18:58:46.850] thread

fffffa8005ede060

20 fffffa8004f40ef0 629a3748 000003df [ 4/ 5/2010 18:58:10.990] thread

fffffa8004f4bb60

22 fffffa8005195120 6500ec7a 000003df [ 4/ 5/2010 18:58:15.019] thread

fffffa8005195060

28 fffffa8004760e20 62ad4e07 000003df [ 4/ 5/2010 18:58:11.115] btaudio (DPC

@ fffffa8004760e60)+12d10

31 fffffa8002c40660 P dc527b9b 000003e8 [ 4/ 5/2010 20:06:00.673]

intelppm!LongCapTraceDpc (DPC @ fffffa8002c406a0)

...

232 fffff80001c85040 P 62317a00 000003df [ 4/ 5/2010 18:58:10.304] nt!IopTimerDispatch

(DPC @ fffff80001c85080)

fffff80001c26fc0 P 6493d400 000003df [ 4/ 5/2010 18:58:14.304]

nt!EtwpAdjustBuffersDpcRoutine (DPC @ fffff80001c26f80)

235 fffffa80047471a8 6238ba5c 000003df [ 4/ 5/2010 18:58:10.351] stwrt64 (DPC

@ fffffa80047471e8)+67d4

242 fffff880023ae480 11228580 000003e1 [ 4/ 5/2010 19:10:13.304]

dfsc!DfscTimerDispatch

(DPC @ fffff880023ae4c0)

245 fffff800020156b8 P 72fb2569 000003df [ 4/ 5/2010 18:58:38.469]

hal!HalpCmcDeferredRoutine (DPC @ fffff800020156f8)

248 fffffa80029ee460 P 62578455 000003df [ 4/ 5/2010 18:58:10.553]

ataport!IdePortTickHandler (DPC @ fffffa80029ee4a0)

fffffa8002776460 P 62578455 000003df [ 4/ 5/2010 18:58:10.553]

ataport!IdePortTickHandler (DPC @ fffffa80027764a0)

fffff88001678500 fe2f836f 000003e0 [ 4/ 5/2010 19:09:41.512]

cng!seedFileDpcRoutine

(DPC @ fffff880016784c0)

fffff80001c25b80 885e52b3 0064a048 [12/31/2099 23:00:00.008]

nt!ExpCenturyDpcRoutine (DPC @ fffff80001c25bc0)

Total Timers: 254, Maximum List: 8

В данном примере есть несколько драйверных таймеров с коротким сроком истечения времени, которые связаны с драйверами Ndis.sys и Afd.sys (оба этих драйвера имеют отношение к сети), а также с драйверами аудиосистемы, Bluetooth, и ATA/IDE. Есть также фоновые, вспомогательные таймеры с истечением времени, например, связанные с управлением электропитанием, ETW, сбрасыванием на диск системного реестра и с виртуализацией управления учетными записями пользователей (Users Account Control, UAC).

Кроме этого существует около десятка таймеров, не имеющих связанных с ними DPC-вызовов — чаще всего это свидетельствует о том, что эти таймеры пользовательского режима или режима ядра используются для диспетчеризации ожиданий. Чтобы проверить это, можно воспользоваться командой !thread в отношении указателей потоков.

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

Exit mobile version