Red de conocimiento del abogados - Ley de patentes - Windows utiliza secciones críticas, ¿tengo que cambiar al modo kernel?

Windows utiliza secciones críticas, ¿tengo que cambiar al modo kernel?

La sección crítica es un mecanismo liviano que permite que solo un hilo ejecute una sección determinada de código en un momento determinado. Las secciones críticas generalmente se usan cuando varios subprocesos modifican datos globales. Los eventos y semáforos también se utilizan para la sincronización de subprocesos múltiples, pero a diferencia de ellos, las secciones críticas no siempre realizan un cambio al modo kernel, lo cual es costoso. Para obtener una sección crítica desocupada, sólo se requieren algunas modificaciones en la memoria, lo cual es muy rápido. Saltará al modo kernel sólo cuando intente obtener una sección crítica ocupada. La desventaja de esta característica liviana es que las secciones críticas solo se pueden usar para sincronizar subprocesos dentro del mismo proceso.

La sección crítica está representada por la estructura RTL_CRITICAL_SECTION definida en WINNT.H. Después de WINBASE.H, encontrará:

typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;

Las funciones API para operar secciones críticas son:

(1) Inicializar sección crítica InitializeCriticalSection

(2) Entrar en la sección crítica EnterCriticalSection

(3) Salir de la sección críticaLeaveCriticalSection

(4) Eliminar la sección críticaDeleteCriticalSection

En la sección crítica En el caso ideal donde no se usa, la llamada a EnterCriticalSection es muy rápida porque simplemente lee y modifica la ubicación de la memoria en la memoria en modo de usuario. Los subprocesos bloqueados esperan en modo kernel y no se pueden programar hasta que el propietario de la sección crítica los libere. Si se bloquean varios subprocesos en una sección crítica, cuando otro subproceso libera la sección crítica, solo un subproceso obtiene la sección crítica.

Estructura RTL_CRITICAL_SECTION

La sección crítica de un proceso se almacena en una lista vinculada y se puede enumerar. De hecho, WINDBG admite el comando !locks, que enumera todas las secciones críticas del proceso de destino.

RTL_CRITICAL_SECTION tiene la siguiente estructura:

struct RTL_CRITICAL_SECTION

{

PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

LONG LockCount;

p>

LONG RecursionCount

HANDLE OwningThread;

HANDLE LockSemaphore

ULONG_PTR SpinCount; p>};

Los siguientes párrafos describen cada campo.

DebugInfo Este campo contiene un puntero a una estructura complementaria asignada por el sistema de tipo RTL_CRITICAL_SECTION_DEBUG. Esta estructura contiene información más valiosa y también está definida en WINNT.H. LockCount Este es el campo más importante en la sección crítica. Se inicializa al valor -1; cuando este valor es igual o mayor que 0, significa que esta sección crítica está ocupada. Cuando no es igual a -1, el campo OwningThread contiene el ID del subproceso propietario de esta sección crítica. La diferencia entre este campo y el valor (RecursionCount-1) indica cuántos otros subprocesos están esperando para obtener la sección crítica.

RecursionCount Este campo contiene el número de veces que el hilo propietario ha adquirido esta sección crítica.

Si este valor es cero, el siguiente hilo que intente adquirir la sección crítica tendrá éxito.

OwningThread Este campo contiene el identificador del hilo que ocupa actualmente esta sección crítica. Este ID de subproceso es el mismo ID que devuelven API como GetCurrentThreadId.

LockSemaphore Es un identificador de objetos del kernel que se utiliza para notificar al sistema operativo: la sección crítica ahora está libre. El sistema operativo crea automáticamente dicho identificador cuando un subproceso intenta por primera vez adquirir la sección crítica pero es bloqueado por otro subproceso que ya posee la sección crítica. Se debe llamar a DeleteCriticalSection (emitirá una llamada CloseHandle que invoca el evento y libera la estructura de depuración si es necesario); de lo contrario, se producirá una fuga de recursos.

SpinCount sólo se utiliza en sistemas multiprocesador. La documentación de MSDN explica este campo de la siguiente manera: "En sistemas multiprocesador, si la sección crítica no está disponible, el hilo de llamada girará dwSpinCount veces antes de realizar una operación de espera en la señal asociada con la sección crítica. Si la sección crítica está en se convierte disponible durante la operación de giro, el hilo de llamada evita la operación de espera. "El conteo de giros puede proporcionar un mejor rendimiento en computadoras multiprocesador porque girar en un bucle suele ser más rápido que ingresar a un estado de espera en modo kernel. El valor predeterminado de este campo es cero, pero se puede establecer en un valor diferente mediante la API InitializeCriticalSectionAndSpinCount.

RTL_CRITICAL_SECTION_DEBUG tiene la siguiente estructura:

struct _RTL_CRITICAL_SECTION_DEBUG

{

Tipo de WORD

WORD CreatorBackTraceIndex;

p>

RTL_CRITICAL_SECTION *CriticalSection;

LIST_ENTRY ProcessLocksList

DWORD EntryCount

DWORD ContentionCount;

DWORD Spare[ 2 ];

}

Esta estructura es asignada e inicializada por InitializeCriticalSection. Se puede asignar desde una matriz preasignada dentro de NTDLL o desde el montón de procesos. Esta estructura complementaria de RTL_CRITICAL_SECTION contiene un conjunto de campos coincidentes con funciones muy diferentes: dos son difíciles de entender, los dos siguientes proporcionan la clave para comprender la estructura de esta cadena de secciones críticas, dos se configuran repetidamente y los dos últimos no se utilizan. .

La siguiente es una descripción del campo RTL_CRITICAL_SECTION.

Tipo Este campo no se utiliza y se inicializa con el valor 0.

CreatorBackTraceIndex Este campo sólo se utiliza en situaciones de diagnóstico. En la clave de registro HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourProgram se encuentran los valores de campo clave, GlobalFlag y StackTraceDatabaseSizeInMb. Tenga en cuenta que estos valores solo se muestran cuando se ejecuta el comando Gflags que se explica más adelante.

Cuando estos valores de registro se configuran correctamente, el campo CreatorBackTraceIndex se completa con un valor de índice utilizado en el seguimiento de la pila. Puede encontrar más información sobre esto en MSDN buscando en la documentación de GFlags las frases "crear una base de datos de seguimiento de pila en modo de usuario" y "ampliar la base de datos de seguimiento de pila en modo de usuario".

CriticalSection apunta a RTL_CRITICAL_SECTION asociada con esta estructura. La Figura 1 ilustra esta infraestructura y las relaciones entre RTL_CRITICAL_SECTION, RTL_CRITICAL_SECTION_DEBUG y otros actores en la cadena de eventos.

Figura 1 Flujo de procesamiento de la sección crítica

ProcessLocksList LIST_ENTRY es una estructura de datos estándar de Windows utilizada para representar nodos en una lista doblemente enlazada. RTL_CRITICAL_SECTION_DEBUG contiene una parte de la lista vinculada que permite recorrer hacia adelante y hacia atrás la sección crítica. La utilidad que se proporciona más adelante en este artículo muestra cómo utilizar los campos Flink (enlace hacia adelante) y Blink (enlace hacia atrás) para moverse entre miembros en una lista vinculada. Esta estructura de datos resultará muy familiar para cualquiera que haya trabajado con controladores de dispositivos o haya estudiado el kernel de Windows.

EntryCount/ContentionCount Estos campos se incrementan al mismo tiempo y por el mismo motivo. Esta es la cantidad de subprocesos que entran en estado de espera porque no pueden obtener la sección crítica de inmediato. A diferencia de los campos LockCount y RecursionCount, estos campos nunca disminuyen.

Estos dos campos de Repuestos no se utilizan ni se inicializan (aunque estos campos se borran cuando se elimina la estructura de la sección crítica). Como se explicará más adelante, estos campos no utilizados se pueden utilizar para almacenar valores de diagnóstico útiles.

Resumen:

(1) Si el campo LockCount tiene un valor distinto de -1, esta sección crítica está ocupada y el campo OwningThread contiene el identificador del hilo que posee la sección crítica.

(2) Si RecursionCount es un valor mayor que 1, le indica cuántas veces el hilo propietario ha vuelto a adquirir la sección crítica (tal vez innecesario).

(3) Es muy importante que los campos LockCount y RecursionCount contengan sus valores iniciales -1 y 0 respectivamente. De hecho, para un programa de un solo subproceso, no se puede saber si se ha obtenido una sección crítica simplemente marcando estos campos. Sin embargo, los programas multiproceso dejan algunos indicadores que pueden usarse para saber si dos o más subprocesos están intentando poseer la misma sección crítica al mismo tiempo.

(4) Cuando la sección crítica no está ocupada, el campo LockSemaphore todavía contiene un valor distinto de cero. Esto significa: en un momento determinado, esta sección crítica bloqueó uno o más hilos. El identificador de eventos se utiliza para notificar que la sección crítica ha sido liberada y que uno de los subprocesos que espera la sección crítica ahora puede obtener la sección crítica y continuar con la ejecución. Debido a que el sistema operativo asigna automáticamente identificadores de eventos cuando una sección crítica bloquea otro hilo, el campo LockSemaphore puede causar fugas de recursos en su programa si olvida eliminar la sección crítica cuando ya no es necesaria.

(5) Otro estado que se puede encontrar en programas multiproceso es que los campos EntryCount y ContentionCount contienen un valor mayor que cero. Estos dos campos almacenan la cantidad de veces que la sección crítica bloquea un hilo. Estos dos campos se incrementan cada vez que ocurre este evento, pero no disminuyen mientras exista la sección crítica.

Estos campos se pueden utilizar para determinar indirectamente la ruta de ejecución y las características de un programa. Por ejemplo, un EntryCount muy alto significa que la sección crítica está experimentando mucha contención y puede convertirse en un posible cuello de botella en la ejecución del código.

(6) Puede atravesar la sección crítica del proceso a través de LIST_ENTRY en RTL_CRITICAL_SECTION_DEBUG. Flink=NULL es el encabezado y Blink=NULL es la cola.

(7) El campo Repuesto de RTL_CRITICAL_SECTION se puede utilizar para distinguir la sección crítica definida por nosotros y la sección crítica definida por el sistema.