Quantum en Windows

 

Un quantum es la cantidad de tiempo de ejecución que le otorga el planificador de procesos a un proceso. En Windows un proceso no se ejecuta, se administra los que se ejecutan son los hilos.

Windows ejecuta los hilos mediante intervalos de reloj, para versiones de clientes tiene 2 tipos de intervalos y servidores tienes 12 intervalos de reloj (Explicaremos más adelante) la cantidad de intervalos de reloj varía dependiendo de cada plataforma de hardware, por ejemplo, en arquitecturas x86 de un solo procesador el intervalo es 10 milisegundos; en x86(más de un procesador) y x64 son de 15 milisegundos esté valor es almacenado en una variable en el Kernel llamada “KeMaximumIncrement” y es almacenada como milésimas de nanosegundos. Podemos obtener el valor almacenado mediante tres formas, con WinDbg,  clockers (herramienta de SystemInternal: https://docs.microsoft.com/en-us/sysinternals/downloads/clockres)  o la función GetSystemTimeAdjustment  de la Api de windows.

Experimento.

Nota: Si tu no conoces Windbg, no te preocupes, mostrare varias formas.

Extraer el valor del intervalo de reloj con Windbg

Esté post no está enfocado en como instalar y configurar el depurador en modo kernel puedes buscar en el siguiente enlace: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-kernel-mode-debugging-in-windbg--cdb--or-ntsd.

Solo queda decir que Windbg lo tengo conectado a una computadora externa mediante red.

1.- Inicializamos en depuración en modo kernel (debe mostrar una ventana muy similar a la de abajo)



2.- En el apartado de la línea de comandos escribimos: dd nt!KeMaximumIncrement L1


Muestra dos valores el primero es la dirección de memoria y el otro es el valor que nos interesa (está subrayado) como pueden observar esté valor aparece el hexadecimal. Escribes: ? x donde x es el valor subrayado(pero el que te aparezca a ti).



Convierte el valor hexadecimal a decimal; como les comentaba el valor guarda el intervalo de reloj en la variable KeMaximumIncrement en milésimas de segundos.

La otra forma de extraer esté dato es con Clockres (herramienta de la suite de System-internals, el link está arriba).

Ya que lo descargaste. Te ubicas en la carpeta donde está el archivo ejecutable y escribes clockres te desplegará una ventana similar y te mostrará 3 intervalos el qué nos interesa es “Maximum timer Interval” que representa la misma cantidad, pero ahora representado en  ms. 


3.- Otra forma es mediante la función GetSystemTimeAdjustment de la API de windows que recibe 3 parámetros (el segundo es el que nos interesa) ambos son 3 punteros.

https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeadjustment

Vamos a obtener ese valor.


Muestra el intervalo de reloj





Como se mostró anteriormente se obtiene el intervalo de reloj, pero, aunque Windows corre en unidades de intervalos de reloj. El sistema no usa los pulsos de reloj como medidor para la cantidad de tiempo que un hilo se ha ejecutado o si su quantum ha terminado. La cantidad de tiempo de ejecución de un hilo es basada en ciclos del procesador, para determinar ese valor, cuando el sistema inicia, multiplica la velocidad del procesador (intervalos de reloj por segundo en MHz) por el número de segundos que un pulso de reloj tarda en cargar ese valor es basado en KeMaximumIncrement y el resultado es guardado en otra variable en el kernel: KiCyclesPerSecondQuantum.

Como se mencionó al inicio Windows(cliente) cuenta con 2 intervalos de reloj. Cada proceso tiene su estructura KPROCESS un valor de reseteo. Esté valor es usado cuando un proceso crea nuevos hilos y es duplicado en la estructura del hilo KTHREAD y al final forma un “Quantum Target”.

El valor de reseteo se guarda en “quantum units” este se puede obtener con windbg.

1.- Debes estar en modo kernel

2.- Escribes !process 0 0 te desplegaran todos los procesos, esperemos que termine de cargar.



3.- Eligen el proceso que quiera. 
Yo elijo “cmd”. Elige la dirección de memoria del proceso y escribe:
 dt nt!_eprocess ##ladirección del proceso en el caso de la mía es
 ffffde09de4c6080

Te desplegara algo similar.


4.- En los datos que se mostraron en la captura anterior algunos aparecen subrayados y en color azul. En el inicio de la estructura EPROCESS, aparece otra estructura llamada PCB en esa le damos clic y desplegara una imagen similar.





Como mencionaba antes, windows mantiene por cada proceso un valor QuantumReset en su estructura KPROCESS, observas qué ese valor es igual a 6 ¿Por qué 6?  Ese valor también conocido como Quantum Unit cada unidad es una tercera parte de un pulso de reloj, entonces un pulso de reloj equivale a 3 quantums por eso Windows mantiene 2 intervalos de reloj(cliente) y su valor de QuantumReset 6 (3*2) y un servidor  contiene un valor de reseteo de 36(12 *3).

 

Ya podemos calcular el consumo de un Quantum en Windows

 

1.- Obtenemos el número de Mhz tomaremos un ejemplo: 1896

2.- Ahora lo convertirmos ese número en ciclos por segundo: (1896 *  10^6 ) = 1,896,000,000.

3.- El valor obtenido con clockres se divide entré 1000 (un segundo equivale a 1000ms) mi valor obtenido con clockres es de 15.625 ms y resultado de dividirlo es  0.015625

4.- El valor obtenido del paso 3 se multiplica por los ciclos de reloj (obtenidos en el paso 2)   = 1,896,000,000. * 0.015625 = 29,625,000

5.- Por último, ese valor se divide entre 3.    29,625, 000 / 3 = 9,875,000

Ese es el valor que el sistema obtiene para correr.

Podemos comprobar.

Vamos a Windbg y escribimos dd nt!KiCyclesPerClockQuantum L1
Obtuvimos 9,875,000 y Windbg nos muestra los siguiente.



Este valor puede tener un margen de error, pero debe ser casi cercano.
Puedes obtener también el valor a través de funciones del api de Windows.
 

#include <iostream>

#include <Windows.h>

#include <math.h>

using namespace std;

ULONG GetProcessorSpeed()

{

    DWORD mhz = MAX_PATH;

    DWORD bufferSize = MAX_PATH;

    HKEY hKey;

    LSTATUS statusKey = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &hKey);

    if (statusKey != ERROR_SUCCESS)

        return ERROR_SUCCESS;

    statusKey = ::RegQueryValueEx(hKey, L"~MHz", NULL, NULL, (LPBYTE)&mhz, &bufferSize);

    return (ULONG)(mhz * 1000000);

}

DWORD GetMaximunIncrement()

{

    DWORD lpTimeAdjustment;

    DWORD lpTimeIncrement;

    BOOL lpTimeAdjustmentDisabledM;

    BOOL successMaximumIncrement =::GetSystemTimeAdjustment(&lpTimeAdjustment,&lpTimeIncrement, &lpTimeAdjustmentDisabledM);

    if (successMaximumIncrement)

        return lpTimeIncrement;

    return ERROR_SUCCESS;

}

DWORD GetCyclesPerClockQuantum()

{

    DWORD maximumIncrement = GetMaximunIncrement();

    ULONG processorSpeed = GetProcessorSpeed();

    if (processorSpeed == ERROR_INVALID_ACCESS)

        return ERROR_INVALID_DATA;

    if (maximumIncrement == ERROR_SUCCESS)

        return ERROR_INVALID_DATA;

    FLOAT clockIntervalTimerFires = (FLOAT)maximumIncrement / 1000000;

    return (clockIntervalTimerFires * processorSpeed);

}

void  ShowQuantumUnit()

{

    ULONG cyclesPerClockQuantum = GetCyclesPerClockQuantum();

    if (cyclesPerClockQuantum == ERROR_INVALID_DATA)

        cout << "No se pudo calcular el quantum" << endl;

    else

        cout << "Los ciclos del quantum son:" << cyclesPerClockQuantum / 3;

}

int main()

{

  ShowQuantumUnit();

}




¡Muchas gracias por leer! 

Fuentes

D. (2020a,
junio 2). Getting Started with WinDbg (Kernel-Mode) - Windows drivers.
Microsoft Docs. https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg--kernel-mode-

K. (2018a,
diciembre 5). GetSystemTimeAdjustment function (sysinfoapi.h) - Win32 apps.
Microsoft Docs. https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeadjustment

M. (2018b,
diciembre 5). RegOpenKeyExA function (winreg.h) - Win32 apps. Microsoft
Docs.
https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexa

M. (2020b,
septiembre 17). ClockRes - Windows Sysinternals. Microsoft Docs.
https://docs.microsoft.com/en-us/sysinternals/downloads/clockres

Yosifovich,
P., Lonescu, A., Russinovich, M. E., & Solomon, D. A. (2017). Windows
Internals Part 1 System architecture, processes, threads, memory management,
and more (7.a ed., Vol. 1). Microsoft Press.

 

 









Comentarios