elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.
 
Inicio Ayuda Ingresar Registrarse
13 Octubre 2008, 07:30  



+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General (Moderadores: Hendrix, E0N)
| | |-+  Procesos y Subprocesos en Windows
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Imprimir
Autor Tema: Procesos y Subprocesos en Windows  (Leído 6215 veces)
Slasher-K

Desconectado Desconectado

Mensajes: 1.480


Ver Perfil
Procesos y Subprocesos en Windows
« en: 21 Marzo 2005, 07:18 »


Fuente: MSDN Library
Traducción, formato y edición por Slasher Keeper :P


Procesos y Subprocesos
[/b][/size]



Introducción

  Las aplicaciones basadas en Win32 consisten de uno o más procesos. Un proceso, en términos simples, es un programa ejecutable. Uno o más subprocesos se corren en el contexto del proceso. Un subproceso es la unidad básica para la cual el sistema operativo asigna tiempo del procesador. Un subproceso puede ejecutar cualquier parte del código de un proceso, incluyendo partes que actualmente están siendo ejecutadas por otro subproceso. Un fiber es una unidad de ejecución que debe ser manualmente fijada por la aplicación. Los fibers se corren en el contexto de los subprocesos que los crean.

  Un objeto job permite a grupos de procesos ser manejados como una unidad. Los objetos job son objetos que pueden ser nombrados, asegurados y compartidos y que controlan los atributos de los procesos relacionados con ellos. Las operaciones realizadas en los objetos job afectan a todos los procesos asociados con el objeto job. Estos objetos sólo pueden ser manipulados en Windows NT.


Acerca de los Procesos y los Subprocesos

  Cada proceso proporciona los recursos necesarios para ejecutar un programa. Un proceso tiene un espacio de dirección virtual, código ejecutable, referencias a objetos, variables de entorno, una prioridad base, y un mínimo y un máximo de espacio de trabajo. El espacio de trabajo es la memoria física asignada por el sistema operativo para el proceso. El espacio de trabajo contiene el código y las páginas de datos recientemente usadas por el proceso. Cada proceso es empezado con un subproceso solo, a menudo llamado subproceso primario, pero puede crear subprocesos adicionales desde cualquiera de sus subprocesos.

  Todos los subprocesos de un proceso comparten su espacio de dirección virtual y los recursos del sistema. Además, cada subproceso mantiene control de excepciones, una prioridad de programa, y una serie de estructuras que el sistema usará para guardar el contexto del subproceso hasta que sea fijado. El contexto del subproceso incluye la serie de subprocesos de los registros de la máquina, la pila del núcleo, un bloque de información de subproceso o Thread Information Block (TIB), y la pila del usuario en el espacio de direcciones del proceso del subproceso.

  Windows NT, Windows 95, y Windows 98 soportan preemptive multitasking o multitarea preventiva, que es la habilidad del sistema operativo para fijar el tiempo de ejecución para procesos múltiples y subprocesos que periódicamente suspenden la ejecución del subproceso que se está ejecutando actualmente para cambiar a otro subproceso de mayor prioridad. Esto se usa para crear el efecto de la ejecución simultánea de varios subprocesos desde varios procesos.

  Estos temas se discuten a continuación:

    • Multitarea
    • Programación
    • Subprocesos Múltiples
    • Procesos Hijos
    • Espacio de Trabajo del Proceso (Working Set)
    • Fibers




Multitarea

  Un sistema operativo multitarea divide el tiempo disponible del procesador entre los procesos y los subprocesos que lo necesitan. El sistema es diseñado para multitarea preventiva; este asigna una porción de tiempo del procesador para que se ejecute cada subproceso. El subproceso que se está ejecutando actualmente es suspendido cuando esta porción de tiempo pasa, permitiendo correr otro subproceso. Cuando el sistema cambia de un subproceso a otro, guarda el contexto del subproceso apropiado y restaura el contexto guardado del siguiente subproceso en la cola.

  La longitud de la porción de tiempo depende del sistema operativo y del procesador. Los múltiples subprocesos parecen que se están ejecutando al mismo tiempo porque cada porción de tiempo es pequeña (aproximadamente 20 milisegundos). Este es actualmente el caso de los sistemas de multiprocesador, donde los subprocesos ejecutables son distribuidos entre los procesadores disponibles. Sin embargo, hay que tener cuidado cuando se están usando múltiples subprocesos en una aplicación, porque el rendimiento del sistema puede disminuir si hay demasiados subprocesos.


Ventajas de la multitarea

  Para el usuario, la ventaja de la multitarea es la habilidad para tener varias aplicaciones abiertas y trabajando al mismo tiempo. Por ejemplo, un usuario puede estar editando un archivo de texto mientras otra aplicación puede estar calculando una planilla de cálculo.

  Para el diseñador de las aplicaciones, es la habilidad de crear aplicaciones que usen más que un proceso y crear procesos que usen más que un subproceso de ejecución. Por ejemplo, un proceso puede tener un subproceso de interfaz de usuario que maneje las interacciones con el usuario (entrada del teclado y del mouse), y subprocesos de ejecución que realizan otras tareas mientras el subproceso de interfaz de usuario espera por una entrada del usuario. Si se da a un subproceso de interfaz de usuario una prioridad superior, la aplicación será más sensible para el usuario, mientras los subprocesos de ejecución usan el procesador eficientemente durante los momentos que no hay ninguna entrada del usuario.


Cuando usar la multitarea

  Hay dos maneras para implementar la multitarea: como un proceso simple con múltiples subprocesos o como procesos múltiples, cada uno con uno o más subprocesos. Una aplicación puede meter cada subproceso que necesite espacio de dirección privada y recursos privados en su proceso, para protegerlo de los subprocesos de otros procesos.

  Un proceso multitarea puede manejar tareas exclusivas mutuamente con los subprocesos, tal como proporcionar una interfaz de usuario y realizar cálculos en segundo plano. Crear un subproceso multitarea puede ser también una manera eficiente para estructurar un programa que realice varias tareas similares o iguales concurrentemente. Por ejemplo, un servidor de named pipes o conductos con nombre puede crear un subproceso para cada proceso cliente que se ate al conducto. Este subproceso maneja la comunicación entre el servidor y el cliente. Este proceso podría usar múltiples subprocesos para lograr las siguientes tareas:

    • Manejar las entradas para múltiples ventanas.

    • Manejar las entradas desde varios dispositivos de comunicación.

    • Distinguir tareas de prioridad variante. Por ejemplo, un subproceso de
      prioridad alta maneja tareas de tiempo   crítico, y un subproceso de prioridad
      baja realiza otras tareas.

    • Permitir una interfaz de usuario para permanecer sensible, mientras se asigna
      tiempo para tareas de fondo.

  Es típicamente más eficiente para una aplicación implementar multitarea para crear un proceso de subprocesos múltiples o simples, que crear varios procesos, por las siguientes razones:

    • El sistema puede realizar un cambio de contexto más rápidamente para subprocesos
      que para procesos, porque un proceso está más arriba que un subproceso (el contexto
      de un proceso en más grande que el contexto de un subproceso)

    • Todos los subprocesos de un proceso comparten el mismo espacio de direcciones y
      puede acceder a las variables globales de un proceso, las cuales pueden simplificar
      la comunicación entre subprocesos.

    • Todos los subprocesos de un proceso pueden compartir todos los recursos abiertos,
      tal como archivos y conductos de comunicación.

  La API de Win32 también proporciona métodos alternativos que pueden ser usados en lugar de la multitarea. El más significante de estos métodos las entradas y salidas asíncronas (I/O) y la habilidad para esperar por múltiples eventos.

  Un subproceso simple puede iniciar varias demandas de I/O que pueden ejecutarse concurrentemente usando I/O asíncronas. Las llamadas a I/O asíncronas pueden ser realizadas sobre archivos, conductos, y dispositivos de comunicación en serie.

  Un subproceso simple puede bloquear su ejecución mientras espera que ocurra alguno o todos los eventos. Esto es más eficiente que usar múltiples subprocesos, cada no esperando un evento simple, y más eficiente que usar un subproceso simple, que consume tiempo de procesador para estar continuamente verificando por que ocurra algún evento.


Consideraciones de la multitarea

  La multitarea tiene requisitos de recursos y conflictos potenciales para ser considerados cuando se diseña una aplicación. Los requisitos de recursos son los siguientes:

    • El sistema consume memoria para la información contextual requerida
      por los procesos y subprocesos. Por eso, el número de procesos y subprocesos
      que pueden ser creados está limitado por la memoria disponible.

    • Guardando la información de un gran número de subprocesos se consume
      significantemente tiempo de procesador. Si hay demasiados subprocesos, la
      mayoría de ellos no serán capaces de realizar un progreso significativo. Si
      la mayoría de los subprocesos actuales están en un proceso, los subprocesos
      en otro proceso son ejecutados con menor frecuencia.

  Proporcionando acceso compartido a recursos puede crear conflictos. Para evitarlos, se debe sincronizar el acceso a los recursos compartidos. Esto es verdad para los recursos del sistema (tal como puertos de comunicación), recursos compartidos por múltiples procesos (tal como archivos abiertos), o los recursos de un simple proceso (tal como variables globales) accedidos por múltiples subprocesos. El fracaso para sincronizar el acceso propiamente (en el mismo o en diferentes procesos) puede llevar a problemas tal como bloqueos o condiciones de carrera. La API de Win32 proporciona una serie de objetos y funciones de sincronización que se pueden usar para coordinar recursos compartidos entre múltiples subprocesos.


Programación

  El programador del sistema controla la multitarea determinando cual de los procesos que están compitiendo recibe la siguiente porción de tiempo del procesador. El programador determina qué subproceso es el siguiente en ejecutarse usando su prioridad de programación.

  A continuación se dicuten los siguientes temas:

    • Prioridades de programación
    • Cambios de contexto
    • Aumentos de prioridad
    • Inversión de prioridad



Prioridades de programación

  Cada uno de los subprocesos tiene asignada una prioridad de programación. El rango de los niveles de prioridad es desde cero (la menor prioridad) a 31 (la mayor prioridad). Sólo el subproceso de página cero puede tener una prioridad de cero. El subproceso de página cero es un subproceso del sistema.

  La prioridad de cada subproceso está determinada por los siguientes criterios:

    • La clase de prioridad de su proceso
    • El nivel de prioridad del subproceso dentro de la clase de prioridad de su proceso.

  La clase de prioridad y el nivel de prioridad son combinados para formar la prioridad base de un subproceso.


Clases de prioridad

  Cada proceso pertenece a una de las siguientes clases de prioridad:

Código:
        IDLE_PRORITY_CLASS            (Inactivo)
        NORMAL_PRIORITY_CLASS         (Normal)
        HIGH_PRIORITY_CLASS           (Mayor)
        REALTIME_PRIORITY_CLASS       (Modo Real)

  Predeterminadamente, la clase de prioridad de un proceso es NORMAL_PRIORITY_CLASS. Hay que usar la función CreateProcess para especificar la clase de prioridad de un proceso hijo cuando se crea. Hay que usar la función SetPriorityClass para cambiar la clase de prioridad de un proceso y GetPriorityClass para determinar la clase de prioridad actual de un proceso.

  Los procesos que monitorean el sistema, tales como protectores de pantalla o aplicaciones que periódicamente actualizan la pantalla deberían usar IDLE_PRIORITY_CLASS. Esto previene que los subprocesos de este proceso, los cuales no tienen prioridad alta, interfieran con los subprocesos de prioridad alta.

  Hay que usar HIGH_PRIORITY_CLASS con cuidado. Si un subproceso se corre con el mayor nivel de prioridad por período extenso, otros procesos en el sistema no podrán obtener tiempo del procesador. Si varios procesos son establecidos a prioridad alta al mismo tiempo, los subprocesos pierden su efectividad. La clase de prioridad alta debe ser reservada para subprocesos que deben responder a eventos de tiempo crítico. Si la aplicación realiza una tarea que requiere la clase de prioridad alta mientras el resto de sus tareas son de prioridad normal, hay que usar SetPriorityClass para aumentar la clase de prioridad de la aplicación temporalmente; entonces reducirlo cuando la tarea haya sido completada. Otra estrategia es crear un proceso de prioridad alta que tiene todos sus subprocesos bloqueados la mayoría del tiempo, despertando a los subprocesos sólo cuando las tareas críticas son necesarias. El punto importante es que un subproceso de prioridad alta debería ser ejecutado por un tiempo breve, y sólo cuando deba realizar una tarea crítica.

  Nunca se debería usar REALTIME_PRIORITY_CLASS, porque esto interrumpe a los subprocesos del sistema que manejan la entrada del mouse, la entrada del teclado, y las actualizaciones en segundo plano de los datos del disco. Esta clase puede ser apropiada para aplicaciones que le "hablen" directamente al hardware o que realicen breves tareas que deberían tener interrupciones limitadas.


Nivel de prioridad

  Los siguientes son los niveles de prioridad dentro de cada clase de prioridad:

Código:
THREAD_PRIORITY_IDLE                (Inactivo)
THREAD_PRIORITY_LOWEST              (Menor)
THREAD_PRIORITY_BELOW_NORMAL        (Por debajo de lo normal)
THREAD_PRIORITY_NORMAL              (Normal)
THREAD_PRIORITY_ABOVE_NORMAL        (Por arriba de lo normal)
THREAD_PRIORITY_HIGHEST             (Mayor)
THREAD_PRIORITY_TIME_CRITICAL

  Todos los subprocesos son creados usando THREAD_PRIORITY_NORMAL. Esto significa que la prioridad del subproceso es la misma que la clase de prioridad del proceso. Luego de crear un subproceso, se usa la función SetThreadPriority para ajustar su prioridad relativa a otros subprocesos en el proceso.

  Una estrategia típica es usar THREAD_PRIORITY_ABOVE_NORMAL o THREAD_PRIORITY_HIGHEST para el subproceso de entrada del proceso, para asegurar que la aplicación está respondiendo al usuario. Los subprocesos en segundo plano, particularmente aquellos que usan intensamente el procesador, pueden ser establecidos a THREAD_PRIORITY_BELOW_NORMAL o THREAD_PRIORITY_LOWEST, para asegurar que pueden ser apropiados cuando sea necesario. Sin embargo, si se tiene un subproceso esperando por otro subproceso que tiene una prioridad baja para completar alguna tarea, es seguro bloquear la ejecución del subproceso de mayor prioridad que está esperando. Para hacer esto, se usa alguna función de espera, sección crítica, o la función Sleep, SleepEx o SwitchToThread. Esto es preferible antes que tener a un subproceso ejecutando un bucle. Por otra parte, el proceso puede bloquearse, porque el subproceso con la prioridad más baja nunca es programado

  Para determinar el nivel de prioridad actual de un subproceso, se usa la función GetThreadPriority.


Prioridad Base

  El nivel de prioridad de un subproceso está determinado por la clase de prioridad de su proceso y su nivel de prioridad. La clase de prioridad y el nivel de prioridad son combinados para formar la prioridad base de cada subproceso.

La siguiente tabla muestra los niveles de prioridad base por combinaciones de clases de prioridad y valores de prioridad:

Código:
------------------------------------------------------------------------------
Base  Clase de Prioridad del Proceso      Nivel de Prioridad del Subproceso
------------------------------------------------------------------------------
1     IDLE_PRIORITY_CLASS,
      NORMAL_PRIORITY_CLASS o             THREAD_PRIORITY_IDLE
      HIGH_PRIORITY_CLASS
------------------------------------------------------------------------------
2     IDLE_PRIORITY_CLASS                 THREAD_PRIORITY_LOWEST
------------------------------------------------------------------------------
3     IDLE_PRIORITY_CLASS                 THREAD_PRIORITY_BELOW_NORMAL
------------------------------------------------------------------------------
4     IDLE_PRIORITY_CLASS                 THREAD_PRIORITY_NORMAL
------------------------------------------------------------------------------
5     Background NORMAL_PRIORITY_CLASS    THREAD_PRIORITY_LOWEST
      IDLE_PRIORITY_CLASS                 THREAD_PRIORITY_ABOVE_NORMAL
------------------------------------------------------------------------------
6     Background NORMAL_PRIORITY_CLASS    THREAD_PRIORITY_BELOW_NORMAL
      IDLE_PRIORITY_CLASS                 THREAD_PRIORITY_HIGHEST
------------------------------------------------------------------------------
7     Foreground NORMAL_PRIORITY_CLASS    THREAD_PRIORITY_LOWEST
      Background NORMAL_PRIORITY_CLASS    THREAD_PRIORITY_NORMAL
------------------------------------------------------------------------------
8     Foreground NORMAL_PRIORITY_CLASS    THREAD_PRIORITY_BELOW_NORMAL
      NORMAL_PRIORITY_CLASS               THREAD_PRIORITY_ABOVE_NORMAL
------------------------------------------------------------------------------
9     Foreground NORMAL_PRIORITY_CLASS    THREAD_PRIORITY_NORMAL
      NORMAL_PRIORITY_CLASS               THREAD_PRIORITY_HIGHEST
------------------------------------------------------------------------------
10    Foreground NORMAL_PRIORITY_CLASS    THREAD_PRIORITY_ABOVE_NORMAL
------------------------------------------------------------------------------
11    HIGH_PRIORITY_CLASS                 THREAD_PRIORITY_LOWEST
      Foreground NORMAL_PRIORITY_CLASS    THREAD_PRIORITY_HIGHEST
------------------------------------------------------------------------------
12    HIGH_PRIORITY_CLASS                 THREAD_PRIORITY_BELOW_NORMAL
------------------------------------------------------------------------------
13    HIGH_PRIORITY_CLASS                 THREAD_PRIORITY_NORMAL
------------------------------------------------------------------------------
14    HIGH_PRIORITY_CLASS                 THREAD_PRIORITY_ABOVE_NORMAL
------------------------------------------------------------------------------
15    IDLE_PRIORITY_CLASS                 THREAD_PRIORITY_TIME_CRITICAL
      NORMAL_PRIORITY_CLASS o
      HIGH_PRIORITY_CLASS
------------------------------------------------------------------------------
      HIGH_PRIORITY_CLASS                 THREAD_PRIORITY_HIGHEST
------------------------------------------------------------------------------
16    REALTIME_PRIORITY_CLASS             THREAD_PRIORITY_IDLE
------------------------------------------------------------------------------
22    REALTIME_PRIORITY_CLASS             THREAD_PRIORITY_LOWEST
------------------------------------------------------------------------------
23    REALTIME_PRIORITY_CLASS             THREAD_PRIORITY_BELOW_NORMAL
------------------------------------------------------------------------------
24    REALTIME_PRIORITY_CLASS             THREAD_PRIORITY_NORMAL
------------------------------------------------------------------------------
25    REALTIME_PRIORITY_CLASS             THREAD_PRIORITY_ABOVE_NORMAL
------------------------------------------------------------------------------
26    REALTIME_PRIORITY_CLASS             THREAD_PRIORITY_HIGHEST
------------------------------------------------------------------------------
31    REALTIME_PRIORITY_CLASS             THREAD_PRIORITY_TIME_CRITICAL
------------------------------------------------------------------------------



Cambios de Contexto

  El programador mantiene una cola de subprocesos ejecutables para cada nivel de prioridad. Estos son conocidos como subprocesos preparados. Cuando un procesador se vuelve disponible, el sistema realiza un cambio de contexto. Los pasos en un cambio de contexto son:

    1. Guarda el contexto del subproceso que se está ejecutando.
    2. Pone el subproceso que se está ejecutando en el final de la cola por su prioridad.
    3. Busca la cola de mayor prioridad que contiene subprocesos preparados.
    4. Quita el subproceso de la cabeza de la cola, carga su contexto, y lo ejecuta.

  Las siguientes clases no son subprocesos preparados:

    • Subprocesos creados con la opción CREATE_SUSPEND
    • Subprocesos detenidos durante la ejecución con las funciones SuspendThread o SwitchToThread.
    • Subprocesos esperando por un objeto de sincronización o una entrada.

  Hasta que los subprocesos que están suspendidos o bloqueados se preparen para ejecutarse, el programador no asigna ninguna porción de tiempo del procesador para ellos, indiferente de su prioridad.

  Las razones más comunes para un cambio de contexto son:

    • La porción de tiempo asignada a ese subproceso ha pasado.
    • Un subproceso con mayor prioridad se ha preparado para ser corrido.
    • Un subproceso que se está ejecutando necesita esperar.


Aumentos de prioridad

  Cada subproceso tiene una prioridad dinámica. Esta es la prioridad que el programador usa para determinar cual subproceso ejecutar. Inicialmente, la prioridad dinámica de un subproceso es la misma que su prioridad base. El sistema puede aumentar y disminuir la prioridad dinámica, para asegurarse que es obediente y que otros subprocesos no están muy necesitados de tiempo del procesador. El sistema no puede aumentar la prioridad de subprocesos con un nivel de prioridad base entre 16 y 31. Sólo subprocesos con una prioridad base entre 0 y 15 reciben aumentos de la prioridad dinámica.

  El sistema aumenta la prioridad dinámica de un subproceso para reforzar su sensibilidad como sigue:

    • Cuando un proceso que usa NORMAL_PRIORITY_CLASS es traído al primer plano, el programador
      aumenta la clase de prioridad del proceso asociado con la ventana de primer plano, entonces
      es mayor o igual a la clase de prioridad de cualquier proceso en segundo plano. La clase de
      prioridad vuelve a su estado original cuando el proceso no es más el del primer plano.

    • Cuando la ventana recibe entradas, tal como mensajes de un temporizador (Timer), mensajes del
      mouse, o entradas del teclado, el programador aumenta la prioridad del subproceso que tiene a la ventana.

    • Cuando las condiciones de espera para un subproceso bloqueado son cumplidas, el programador aumenta
      la prioridad del subproceso. Por ejemplo, cuando una operación de espera asociada con la finalización
      de funciones de I/O en el disco o entradas del teclado, el subproceso recibe un aumento de prioridad.

  Después de aumentar la prioridad dinámica de un subproceso, el programador reduce esa prioridad en un nivel cada vez que el subproceso completa su porción de tiempo, hasta que el subproceso vuelve a tener su prioridad base. La prioridad dinámica de un subproceso nunca es menor que su prioridad base.


Inversión de prioridad

  La inversión de prioridad ocurre cuando dos o más subprocesos con diferentes prioridades están en la contienda para ser programados. Por ejemplo un simple caso con tres subprocesos: thread1, thread2 y thread3. Thread1 es de prioridad alta y está preparado para ser programado. Thread2, un subproceso de prioridad baja, está ejecutando código en una sección crítica. Thread1, el subproceso de prioridad alta, comienza esperando por un recurso compartido de thread2. Thread3 tiene prioridad media. Thread3 recibe todo el tiempo del procesador, porque el subproceso de prioridad alta (thread1) está esperando por recursos compartidos del subproceso de prioridad baja (thread2). Thread2 no dejará la sección crítica, porque no tiene la mayor prioridad y no se programará.

  Windows NT: El programador resuelve este problema elevando al azar la prioridad de los subprocesos preparados. Los subprocesos de prioridad baja son ejecutados por bastante tiempo para salir de la sección crítica, y el subproceso de prioridad alta puede entrar en la sección crítica. Si el subproceso de prioridad baja no tiene suficiente tiempo de la CPU para salir de la sección crítica la primera vez, obtendrá otra oportunidad durante la siguiente ronda del programador.

  Windows 95: Si el subproceso de prioridad alta es dependiente de un subproceso de prioridad baja que no será asignado para correr porque un subproceso de prioridad media está obteniendo todo el tiempo de la CPU, el sistema reconoce que el proceso de prioridad alta es dependiente de un subproceso de prioridad baja. Será elevada la prioridad del subproceso de prioridad baja hasta el valor de la prioridad del subproceso de prioridad alta. Esto le permitirá al subproceso que anteriormente tenía la menor prioridad ejecutar y descargar el subproceso de prioridad alta que estaba esperándolo.



Subprocesos Múltiples

   Cada proceso es comenzado con un subproceso simple, pero puede crear más subprocesos desde cualquiera de sus subprocesos.

   Esta sección discute los siguientes temas:

    • Crear Subprocesos
    • Tamaño de la Pila del Subproceso
    • Referencias de Subprocesos e identificadores
    • Suspender la Ejecución de un Subproceso
    • Sincronizar la Ejecución de Subprocesos Múltiples
    • Subprocesos Múltiples y Objetos GDI
    • Almacén Local del Subproceso
    • Crear Ventanas en Subprocesos
    • Terminar un Subproceso
    • Momentos del Subproceso


Crear Subprocesos

  La función CreateThread crea un nuevo subproceso para un proceso. El subproceso que se está creando debe especificar la dirección inicial del código que el nuevo subproceso va a ejecutar. Típicamente, la dirección inicial es el nombre de la función definido en el código del programa. Esta función toma un parámetro simple y devuelve un valor (DWORD para C++ y Long para Visual Basic). Un proceso puede contener múltiples subprocesos ejecutando la misma función simultáneamente.

  El ejemplo siguiente muestra cómo crear un nuevo subproceso que ejecuta la función local definida como ThreadFunc. Para mayor comodidad voy a escribir el ejemplo en C++ y en Visual Basic.

Código:
#include "windows.h"

DWORD WINAPI ThreadFunc( LPVOID lpParam )
{
    char szMsg[80];

    wsprintf( szMsg, "ThreadFunc: Parameter = %d\n", *lpParam );
    MessageBox( NULL, szMsg, "Thread created.", MB_OK );

    return 0;
}
 
VOID main( VOID )
{
    DWORD dwThreadId, dwThrdParam = 1;
    HANDLE hThread;
    hThread = CreateThread(
        NULL,                        // sin atributos de seguridad
        0,                           // usar el tamaño de pila prederminado
        ThreadFunc,                  // la function del subproceso
        &dwThrdParam,                // argumento para la function del subproceso
        0,                           // usar las opviones de creación predeterminadas
        &dwThreadId);                // devuelve el identificador del subproceso
 
   // Verifica el valor devuelto para el éxito
 
   if (hThread == NULL)
      ErrorExit( "No se pudo crear el Subproceso" );

   CloseHandle( hThread );
}


Este es el mismo código para Visual Basic:

Código:
Private Declare Function CreateThread Lib “kernel32” _
(ByVal lpThreadAttributes As Long, _
ByVal dwStackSize As Long, _
ByVal lpStartAddress As Long, _
ByVal lpParameter As Long, _
ByVal swCreationFlags As Long, _
lpThreadID As Long) As Long

Function ThreadFunc(ByVal lParam As Long) As Long
MsgBox “Thread Created” & vbCrLf & “lpParam: “& lParam, vbExclamation
ThreadFunc = True
End Function

Sub Main()
Dim hThread&, dwThreadID&, dwThrdParam&

dwThrdParam = 1
hThread = CreateThread(0&, 0&, AddressOf ThreadFunc, dwThrdParam&, 0&, dwThreadID&)
End Sub

  Para simplificar, el ejemplo pasa un puntero a un valor DWORD como un argumento a la función del subproceso. Esto podría ser un puntero a cualquier tipo de datos o estructura, o podría omitirse totalmente pasando un puntero NULL y eliminando las referencias al parámetro en ThreadFunc.

  Es arriesgado pasar la dirección de una variable local si el subproceso que lo está creando existe antes que el nuevo subproceso, porque el puntero se invalidará. En cambio, tampoco se puede pasar un puntero a memoria asignada dinámicamente o hacer que el subproceso que está creando espere que el nuevo subproceso termine. Los datos también pueden ser pasados desde el subproceso que está creando al nuevo subproceso usando variables globales. Con variables globales, es usualmente necesario sincronizar el acceso por múltiples subprocesos. Para más información ver Sincronizar la Ejecución de Subprocesos Múltiples más adelante en esta sección.

  En procesos sonde los subprocesos pueden crear múltiples subprocesos para ejecutar el mismo código, es inconveniente usar variables globales. Por ejemplo, un proceso que habilita al usuario para abrir varios archivos al mismo tiempo, puede crear un nuevo subproceso para cada archivo, con cada uno de los subprocesos ejecutando el mismo código de la función del subproceso. El subproceso que está creando puede pasar información única (tal como el nombre del archivo) requerida por cada instancia de la función del subproceso como un argumento. No se puede usar una simple variable global para este propósito, pero se podría usar un búfer de cadena asignado dinámicamente.

  El subproceso que está creando al nuevo puede usar los argumentos de CreateThread para especificar lo siguiente:

    • Los atributos de seguridad para la referencia al nuevo subproceso. Estos atributos de seguridad incluye
      una opción de herencia que determina si la referencia puede ser heredada por los procesos hijos. Los
      atributos de seguridad también incluyen un descriptor de seguridad, al cual el sistema usa para realizar
      verificaciones de acceso en todos los usos subsecuentes de la referencia al subproceso antes de que se
      conceda el acceso.

    • El tamaño inicial de la pila del nuevo subproceso. La pila del subproceso es asignada automáticamente
      en el espacio de memoria del proceso; el sistema incrementa la pila como necesita y la libera cuando
      el subproceso termina.

    • Una opción de creación que te habilita para crear el subproceso en un estado de suspensión. Cuando
      está suspendido un subproceso no se ejecuta hasta que la función ResumeThread es llamada.

  También se puede crear un subproceso llamando a la función CreateRemoteThread. Esta función es usada por procesos debuggers (depuradores) para crear un subproceso que se ejecute en el espacio de direcciones en el proceso que está siendo depurado.


Tamaño de la Pila del Subproceso

  Cada nuevo subproceso recibe su propio espacio de pila, que consiste en memoria reservada y encargada. Prederminadamente, cada subproceso usa 1 MB de memoria reservada, y una página de memoria encargada. El sistema encargará un bloque de la memoria de la pila reservada como necesite, hasta que la pila no pueda crecer más. Para especificar un tamaño de pila diferente del predeterminado, se debe usar una opción de la línea de comandos que algún compilador traiga para especificar este tamaño.

  Para incrementar el espacio de pila que es inicialmente encargada por un proceso, hay que especificar el valor en el parámetro dwStackSize de la función CreateThread. Este valor es redondeado a la página más cercana y usado para establecer el tamaño inicial de la memoria encargada. La llamada a CreateThread fallará si no hay suficiente memoria para encargar el número de bytes que se especificó. Si el valor de dwStackSize es menor que el tamaño predeterminado, el nuevo subproceso usa el mismo tamaño que el subproceso que lo ha creado.

  La pila se libera cuando el subproceso termina.


Controladores de Subprocesos e identificadores

  Cuando un nuevo subproceso es creado por la función CreateThread o CreateRemoteThread, se devuelve el controlador de subproceso. Predeterminadamente, este controlador tiene todos los derechos de acceso, y –sujeto a la comprobación de acceso de seguridad- puede ser usada en cualquiera de las funciones que acepten un controlador de subproceso. Este controlador puede ser heredado por procesos hijos, dependiendo de la opción de herencia especificada cuando se crea. El controlador puede ser duplicado usando DuplicateHandle, la cual te habilita para crear un controlador de subproceso con un subconjunto de los derechos de acceso. El controlador es válido hasta que se cierre, incluso después de que el subproceso que lo representa ha terminado.

  Las funciones CreateThread y CreateRemoteThread también devuelven un identificador que identifican únicamente al subproceso en todo el sistema. Un subproceso puede usar la función GetCurrentThreadId para obtener su propio identificador de subproceso. Los identificadores son válidos desde el momento que los subprocesos fueron creados hasta que el subproceso haya terminado.

  La API de Win32 no proporciona ninguna manera de obtener el controlador de un subproceso desde el identificador de subproceso. Si los controladores fueran hechos disponibles de esta manera, el subproceso que los contiene podría fallar porque otro proceso inesperadamente realizó una operación en uno de sus subprocesos, tal como suspenderlo, hacer que continúe, ajustando su prioridad o terminándolo. En cambio, se debe pedir el controlador desde el subproceso creador o desde el mismo subproceso. Un subproceso puede usar GetCurrentThread para recuperar un pseudo-controlador de su mismo objeto de subproceso. Este pseudo controlador es válido sólo para el proceso que invocó la función; no puede ser heredado o duplicado para usar con otros procesos. Para obtener el controlador real de un subproceso, hay que pasar el pseudo-controlador a la función DuplicateHandle.


Suspender la Ejecución de un Subproceso

  Un subproceso puede suspender y continuar la ejecución de otro subproceso usando las funciones SuspendThread y ResumeThread. Mientras un subproceso está suspendido, no es programado y no se le asigna tiempo del procesador.

  La función SuspendThread no es particularmente útil para la sincronización porque no puede controlar el punto en el código en que el subproceso ha suspendido la ejecución. Sin embargo, se puede querer suspender un subproceso en una situación en donde se está esperando una entrada de usuario que podría cancelar el trabajo que el subproceso está realizando. Si la entrada de usuario cancela el trabajo, tiene que salir del subproceso; de otra forma llamar a ResumeThread.

  Si un subproceso es creado en estado de suspensión (con la opción CREATE_SUSPEND), no empieza a ejecutarse hasta que otro subproceso llame a ResumeThread con la referencia del subproceso suspendido. Esto puede ser útil para iniciar el estado del subproceso antes de que empiece a ejecutarse.

  El subproceso puede rendir su ejecución temporalmente por un intervalo especificado llamando a las funciones Sep y SleepEx. Esto es útil particularmente en casos donde un subproceso responde a la interacción del usuario, porque puede retrasar la ejecución el suficiente tiempo para permitir a los usuario observar los resultados de sus acciones. Durante el intervalo de tiempo, al subproceso no le es programado tiempo de procesador.


Sincronizar la Ejecución de Múltiples Subprocesos

  Para evitar las condiciones de carrera y los bloqueos, es necesario sincronizar el acceso de varios subprocesos a recursos compartidos. La sincronización también es necesaria para asegurar que el código independiente es ejecutado en la secuencia apropiada.

  La API de Win32 proporciona una serie de objetos cuyas referencias son usadas para sincronizar múltiples subprocesos. Estos objetos incluyen:

    • Búferes de Entrada de Consola
    • Eventos
    • Mutexes
    • Secciones Críticas
    • Procesos
    • Semáforos
    • Subprocesos
    • Temporizadores (Timers)

  El estado de cada uno de estos objetos está o señalado (Signaled) o no señalado (Unsignaled). Cuando se especifica un controlador a cualquiera de estos objetos en la llamada a alguna de las funciones de espera, la ejecución del subproceso actual es bloqueada hasta que el estado del objeto especificado sea señalado.

  Algunos de estos objetos son útiles para bloquear un subproceso mientras ocurren algunos eventos. Por ejemplo, una referencia a un búfer de entrada de consola es señalada cuando hay entradas no leídas, tal como pulsaciones de teclas o clic de los botones del mouse. Las referencias a los procesos y subprocesos son señaladas cuando el proceso o el subproceso terminan. Esto le permite a un proceso, por ejemplo, crear un proceso hijo y entonces bloquear su ejecución hasta que el nuevo proceso haya terminado.

  Otros objetos son útiles protegiendo los recursos compartidos de accesos simultáneos. Por ejemplo, múltiples subprocesos pueden tener cada uno una referencia a un objeto mutex. Antes de acceder a un recurso compartido, los subprocesos deben llamar a una de las funciones de espera para esperar que el estado del mutex sea señalado. Cuando el mutex haya sido señalado, sólo uno de los subprocesos en espera es descargado para acceder al recurso. El estado del mutex es inmediatamente restablecido a “no señalado” así cualquier otro de los subprocesos que están esperando permanecerán bloqueados. Cuando el subproceso hay terminado con el recurso, debe establecer el estado del mutex a señalado para permitirle a los otros subprocesos acceder al recurso.

  Para los subprocesos de un proceso simple, los objetos sección-crítica proporcionan un medio más eficiente de sincronización que los objetos mutex. Una sección crítica es usada como un mutex para habilitar a un subproceso a la vez para usar un recurso protegido. Un subproceso puede usar la función EnterCriticalSection para apropiarse de una sección crítica. Si ya fue obtenida por otro subproceso, el subproceso que la pide es bloqueado. Un subproceso puede usar la función TryEnterCriticalSection para apropiarse de una sección crítica, sin bloquearse en el fracaso de obtener la sección crítica. Después de que recibe la propiedad, el subproceso es liberado para usar el recurso protegido. La ejecución de los otros subprocesos del proceso no es afectada a menos que intenten entrar en la misma sección crítica.

  La función WaitForInputIdle hace que un subproceso espere hasta que un proceso especificado esté inicializado y esperando por una entrada de usuario sin una entrada pendiente. Llamar a WaitForInputIdle puede ser útil para sincronizar procesos padres e hijos, porque CreateProcess vuelve sin esperar que el proceso hijo termine su inicialización.

  Para más información ver Sincronización más adelante en este artículo.


Subprocesos Múltiples y Objetos GDI

  Para aumentar el desempeño, el acceso a objetos de la interfase de dispositivos gráficos (GDI) (tal como paletas, contextos de dispositivos, regiones, y lo demás) no es serializada. Esto crea un peligro potencial para procesos que tienen múltiples subprocesos compartiendo estos objetos. Por ejemplo, si un subproceso elimina un objeto GDI mientras otro subproceso lo está usando, los resultados son imprevisibles. Este peligro puede ser evitado simplemente no compartiendo los objetos GDI. Si compartir es inevitable (o deseado), la aplicación debe proporcionar sus propios mecanismos para sincronizar el acceso.


Almacén Local del Subproceso (TLS)

  Todos los subprocesos de un proceso comparten el espacio de direcciones virtual y las variables globales de ese proceso. Las variables locales de la función del subproceso son locales para cada subproceso que corra la función. Sin embargo, las variables globales o estáticas son usadas por esa función tienen el mismo valor para todos los subprocesos. Con el almacén local del subproceso (TLS) se puede crear una única copia de una variable para cada subproceso. Usando TLS, un subproceso asigna un índice que puede ser usado por cualquier subproceso del proceso para devolver su única copia.

  Hay que realizar los siguientes pasos para implementar TLS:

    1. Usar la función TlsAlloc durante la inicialización del proceso o la biblioteca de vínculos
       dinámicos (DLL) para asignar un índice TLS.

    2. Para cada subproceso que necesite usar un índice TLS, asignar almacenamiento dinámico, entonces
       usar la función TlsSetValue para asociar el índice con un puntero a un almacenamiento dinámico

    3. Cuando un subproceso necesita acceder a su almacén, especifica el índice TLS en la llamada a
       la función TlsGetValue para recuperar el puntero.

    4. Cuando cada subproceso no necesite más el almacenamiento dinámico que ha asociado con el índice
       TLS, debe liberar el índice. Cuando todos los subprocesos han terminado de usar un índice TLS,
       se usa la función TlsFree para liberar el índice.

  La constante TLS_MINIMUN_AVAILABLE define el número mínimo de índices TLS disponibles en cada proceso. Este mínimo se garantiza ser por lo menos 64 para todos los sistemas.

  Es ideal usar TLS en una DLL. Realizar las operaciones TLS iniciales en la función DllMain en el contexto del proceso o el subproceso atado a la DLL. Cuando un nuevo proceso se ata a la DLL, se llama a la función TlsAlloc en la función del punto de entrada para asignar un índice TLS para ese proceso. Entonces se almacena el índice TLS en una variable global que es privada para cada proceso atado. Cuando un nuevo subproceso se ata a la DLL, se asigna memoria dinámica para ese subproceso en la función del punto de entrada, y se usa la función TlsSetValue con el índice TLS desde TlsAlloc para guardar datos privados para el índice. Entonces se puede usar el índice TLS en una llamada a la función TlsGetValue para acceder a los datos privados del subproceso que llama a la función desde dentro de cualquier función en la DLL. Cuando un proceso se desata de la DLL (libera la referencia) se llama a TlsFree.
« Última modificación: 18 Abril 2005, 09:05 por Slasher Keeper » En línea



A la reina de las profundidades que cuida los pasos de una sombra en la noche :*
Slasher-K

Desconectado Desconectado

Mensajes: 1.480


Ver Perfil
Re: Procesos y Subprocesos en Windows
« Respuesta #1 en: 31 Marzo 2005, 17:20 »

Crear Ventanas en Subprocesos

  Un subproceso puede crear una ventana. Un subproceso que crea una ventana contiene a la ventana su cola de mensajes asociada. Por eso, el subproceso debe proporcionar un bucle de mensajes para procesar los mensajes en su cola de mensajes. Además se deben usar las funciones MsgWaitForMultipleObjects y MsgWaitForMultipleObjectsEx en ese subproceso, más que otras funciones de espera, para que pueda procesar mensajes. De otra manera, el sistema se bloqueará cuando el subproceso envíe un mensaje mientras está esperando.

  La función AttachThreadInput puede ser usada para permitirle a una serie de subprocesos compartir el mismo estado de entrada. Compartiendo el estado de entrada, los subprocesos comparten sus conceptos de la ventana activa. Haciendo esto, un subproceso siempre puede activar otra ventana de subproceso. Esta función es también útil para compartir los estados del enfoque, captura del mouse, teclado, y el estado del orden Z de la ventana entre las ventanas creadas por diferentes subprocesos que están compartiendo el estado de entrada.


Terminar un Subproceso

   Un subproceso se ejecuta hasta que ocurra uno de estos eventos:

        • El subproceso llame a la función ExitThread.
        • Algún subproceso del proceso llame a la función ExitProcess.
        • La función del subproceso termina.
        • Algún subproceso llama a la función TerminateThread con la referencia al subproceso.
        • Algún subproceso llama a la función TerminateProcess con la referencia al proceso.

  La función GetExitCodeThread devuelve el estado de terminación de un subproceso. Mientras un subproceso se está ejecutando, su estado de terminación es STILL_ACTIVE. Cuando un subproceso termina, su estado de terminación cambia de STILL_ACTIVE al código de salida del subproceso. El código de salida es el valor especificado en las llamadas a ExitThread, ExitProcess, TerminateThread, o TerminateProcess, o el valor devuelto por la función del subproceso.

  Cuando un subproceso termina, el estado del objeto subproceso cambia a señalado, descargando cualquier otro subproceso que había estado esperando la terminación de dicho subproceso.

  Si un subproceso es terminado con la función ExitThread, el sistema llama la función del punto de entrada de cada DLL atada con un valor indicando que el subproceso se está desatando de la DLL (a menos que se llame a la función DisableThreadLibraryCall). Si un subproceso es terminado con ExitProcess, las funciones del punto de entrada de la DLL son invocadas una vez, para indicar que el proceso se está desatando. Las DLL no son notificadas cuando un subproceso es terminado por TerminateThread o TerminateProcess. Para obtener más información ver Bibliotecas de Vínculos Dinámicos más adelante en este artículo.

    CUIDADO: Las funciones TerminateThread y TerminateProcess deberían ser usadas sólo en circunstancias
    extremas, ya que no le permiten a los subprocesos limpiarse, no notifican a las DLL atadas,
    y no liberan la pila inicial. Los siguientes pasos proporcionan una buena solución:

      • Crear un objeto de evento usando la función CreateEvent.
      • Crear los subprocesos.
      • Cada subproceso controla el evento llamando a la función WaitForSingleObject.
      • Cada subproceso termina su propia ejecución cuando el evento es establecido al
        estado "señalado" (WaitForSingleObject devuelve WAIT_OBJECT_0)


Momentos del Subproceso

La función GetThreadTimes obtiene información de los momentos del subproceso. Devuelve la fecha de creación del subproceso, cuánto tiempo el subproceso se ha estado ejecutando en modo kernel, y cuánto tiempo el subproceso se estuvo ejecutando en modo usuario. Estos tiempos no incluyen los tiempos perdidos ejecutando subprocesos del sistema o esperando en un estado de suspensión o bloqueado. Si el subproceso ha salido, GetThreadTime devuelve el momento de salida del subproceso.



Procesos Hijos

  Un proceso hijo es un proceso que es creado por otro proceso, llamado proceso padre.

  Esta sección discute los siguientes temas:

    • Crear Procesos
    • Establecer las Propiedades de Ventanas Usando STARTUPINFO
    • Referencias de Procesos e identificadores
    • Obtener Información Adicional de un Proceso
    • Herencia
    • Variables de Entorno
    • Terminar un Proceso


Crear Procesos

  La función CreateProcess crea un nuevo proceso, que se ejecuta independientemente del proceso que lo crea.
Sin embargo, para simplificar, la relación es llamada una relación de padre-hijo.

  El siguiente fragmento de código muestra cómo crear un proceso. Como ya pasó antes, primero voy a escribir el código para C++ y a continuación para Visual Basic.

Código:
void main( VOID )
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);

    // Inicia el proceso hijo.
    if( !CreateProcess( NULL, // Ningún nombre de módulo (se usa la linea de comandos).
        "MyChildProcess", // Linea de comandos.
        NULL,             // La referencia del proceso no es heredable.
        NULL,             // La referencia del subproceso no es heredable.
        FALSE,            // Establece la referencia de herencia a Falso.
        0,                // Ninguna opción de creación.
        NULL,             // Se usa el bloque de entorno del padre.
        NULL,             // Se usa el directorio de inicio del padre.
        &si,              // Puntero a la estructura STARTUPINFO.
        &pi )             // Puntero a la estructura PROCESS_INFORMATION.
    )
    {
        ErrorExit( "CreateProcess failed." );
    }

    // Espera hasta que el proceso hijo termine.
    WaitForSingleObject( pi.hProcess, INFINITE );

    // Cierra las referencias del proceso y el subproceso.
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
}

A continuación está el código para Visual Basic:

Código:
Declare Function CreateProcess Lib "kernel32" _
                        Alias "CreateProcessA" ( _
                        ByVal lpApplicationName As String, _
                        ByVal lpCommandLine As String, _
                        ByVal lpProcessAttributes As Long, _
                        ByVal lpThreadAttributes As Long, _
                        ByVal bInheritHandles As Long, _
                        ByVal dwCreationFlags As Long, _
                        lpEnvironment As Any, _
                        ByVal lpCurrentDirectory As String, _
                        lpStartupInfo As STARTUPINFO, _
                        lpProcessInformation As PROCESS_INFORMATION) As Long

Declare Function WaitForSingleObject Lib "kernel32" _
Alias "WaitForSingleObject" ( _
ByVal hHandle As Long, _
ByVal dwMilliseconds As Long) As Long

Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Const INFINITE = &HFFFF

Public Type PROCESS_INFORMATION
    hProcess As Long
    hThread As Long
    dwProcessId As Long
    dwThreadId As Long
End Type

Public Type STARTUPINFO
    cb As Long
    lpReserved As String
    lpDesktop As String
    lpTitle As String
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Long
    hStdInput As Long
    hStdOutput As Long
    hStdError As Long
End Type

Sub Main()
        Dim si As STARTUPINFO, pi As PROCESS_INFORMATION
        Dim r&

    si.cb = Len(si)
   
    'Inicia el proceso hijo
    r& = CreateProcess(vbNullString, "notepad.exe", 0&, 0&, 0&, _
               0&, 0&, vbNullString, si, pi)


    If r = 0 Then
        MsgBox "No se pudo crear el proceso", vbCritical
    End If

    'Espera hasta que el proceso hijo termine.
    r = WaitForSingleObject(pi.hProcess, INFINITE)

    'Cierra las referencias del proceso y el subproceso.
    r = CloseHandle(pi.hProcess)
    r = CloseHandle(pi.hThread)
End Sub


  Si CreateProcess tiene éxito, devuelve una estructura PROCESS_INFORMATION que contiene referencias e identificadores para el nuevo proceso y su subproceso primario. Las referencias al subproceso y al proceso son creadas con todo el derecho de acceso, aunque el acceso puede ser restringido si se especifican descriptores de seguridad. Cuando no se necesitan más las referencias, se cierran usando la función CloseHandle.

  También se puede crear un proceso usando la función CreateProcessAsUser. Esta función permite especificar el contexto de seguridad de la cuenta de usuario en donde se ejecutará el proceso.



Establecer las Propiedades de Ventana Usando STARTUPINFO

  El proceso padre puede especificar las propiedades de la ventana principal de su proceso hijo. La función CreateProcess toma una referencia a una estructura STARTUPINFO como uno de sus parámetros. Se usan miembros de esta estructura para especificar características de la ventana principal del proceso hijo. El miembro dwFlags contiene un capo de bit que cuales otro miembros de la estructura son usados. Esto permite especificar valores para cualquier subconjunto de las propiedades de la ventana activa. El sistema usa los valores predeterminados de las propiedades si no se especifican.

  Para los procesos GUI, STARTUPINFO especifica los valores iniciales que usará el nuevo proceso al llamar a las funciones CreateWindow y ShowWindow para crear y desplegar una ventana. Se pueden especificar los siguientes valores predeterminados:

    • El ancho y el alto, en píxeles, de la ventana creada por CreateWindow.
    • La ubicación, en coordenadas de la pantalla de la ventana creada por CreateWindow.
    • El parámetro nCmdShow de ShowWindow.

  Para procesos de consola, se usa STARTUPINFO para especificar las propiedades de la ventana sólo cuando se crea una nueva consola (usando CreateProcess con la opción CREATE_NEW_CONSOLE o con la función AllocConsole)
La estructura STARTUPINFO se puede usar para especificar las siguientes propiedades de la ventana de la consola:

    • El tamaño de la nueva ventana de la consola, en celdas de caracteres.
    • La ubicación de la nueva ventana de la consola, en coordenadas de la pantalla.
    • El tamaño, en celdas de caracteres, del nuevo búfer de pantalla de la consola.
    • El color del texto y del fondo del nuevo búfer de pantalla de la consola.
    • El título de la nueva ventana de la consola.


Controladores de Procesos e identificadores

  Cuando un nuevo proceso es creado con la función CreateProcess, son devueltos controladores del nuevo proceso y de su subproceso primario. Estos controladores son creados con todos los derechos de acceso, y –sujeto a la comprobación de acceso de seguridad- pueden ser usados en cualquiera de las funciones que acepten un controlador de proceso o subproceso. Estos controladores pueden ser heredados por procesos hijos, dependiendo de la opción de herencia especificada cuando son creados. Los controladores son válidos hasta el cierre, incluso después de que el proceso y el subproceso a los que representan han terminado.

  La función CreateProcess también devuelve un identificador que únicamente identifica al proceso dentro del sistema. El proceso puede usar la función GetCurrentProcessId para obtener su propio identificador de proceso. El identificador es válido desde el momento que el proceso es creado hasta que el proceso haya terminado.

  Si se tiene el identificador del proceso, se puede obtener el controlador del proceso llamando a la función OpenProcess. OpenProcess permite especificar los derechos de acceso del controlador y si puede ser heredado.

  Un proceso puede usar la función GetCurrentProcess para recuperar un pseudo-controlador de su mismo objeto de proceso. Este pseudo-controlador es válido sólo para el proceso que invocó a la función; no puede ser heredado o duplicado para usar con otros procesos. Para obtener el controlador real de un proceso, hay que pasar el pseudo controlador a la función DuplicateHandle.


Obtener Información Adicional del Proceso

  La API de Win32 proporciona funciones para obtener información adicional acerca de un proceso. Algunas de estas funciones pueden ser usadas sólo por el proceso que llama a la función, porque no toman una referencia a un proceso como parámetro. Se pueden usar funciones que toman una referencia a un proceso para obtener información acerca de otros procesos.

    • Para obtener la cadena de la linea de comandos para el proceso actual, se usa la función GetCommandLine.

    • Para analizar una cadena Unicode de la linea de comandos obtenida con la versión Unicode de GetCommandLine,
      se usa la función CommandLineToArgvW.

    • Para obtener la estructura STARTUPINFO especificada cuando el proceso actual fue creado, se usa la función
      GetStartUpInfo.

    • Para obtener información del encabezado ejecutable, se usa la función GetProcessVersion.

    • Para obtener la ruta completa y el nombre del archivo para un archivo ejecutable que contiene el código
      del proceso, se usa la función GetModuleFilename.

    • Para obtener la cantidad de referencias en uso a objetos GUI (Graphical User Interface), se usa la función
      GetGuiResources.

    • Para determinar si el proceso está siendo depurado, se usa la función IsDebuggerPresent.


Herencia

  Un subproceso hijo puede heredar varias propiedades y recursos de su proceso padre. También se puede prevenir que un proceso hijo herede propiedades de su proceso padre. Lo siguiente puede ser heredado:

    • Referencias abiertas devueltas por la función CreateFile. Esto incluyen referencias a archivos, búferes
      de entrada de consola, búferes de pantalla de consola, conductos con nombre, dispositivos de comunicación
      en serie y mailslots.

    • Referencias abiertas a procesos, subprocesos, mutex, eventos, semáforos, conductos con nombre, conductos
      anónimos, y objetos file-mapping.

    • Variables de entorno

    • El directorio actual

    • La consola, a menos que el proceso esté desatado o una nueva consola sea creada. Un proceso de consola hijo
      también hereda las referencias estándar del padre, así como el acceso al búfer de entrada y al búfer de pantalla
      activo.

  El proceso hijo no puede heredar lo siguiente:

    • La clase de prioridad

    • Referencias devueltas por LocalAlloc, GlobalAlloc, HeapCreate y HeapAlloc.

    • Pseudo-referencias, como referencias devueltas por GetCurrentProcess o GetCurrentThread.

    • Referencias a módulos DLL devueltas por la función LoadLibrary.

    • Referencias de GDI o de USER, tal como HBITMAP o HMENU.


Heredar Controladores

  Para causar que una referencia sea heredada, se deben hacer dos cosas:

    • Especificar que la referencia es para ser heredada cuando se crea, se abre, o se duplica la misma.
    • Especificar que las referencias heredables son para ser heredadas cuando se llama a CreateProcess.

  Esto le permite a un proceso hijo heredar algunos de los controladores de su padre, pero no heredar otros. Por ejemplo, las funciones de creación como CreateProcess o CreateFile toman un argumento de atributos de seguridad que determinan si el controlador puede ser heredado. Las funciones de apertura tales como OpenMutex y OpenEvent toman una opción de herencia de controlador que determina si el controlador puede ser heredado. La función DuplicateHandle toma una opción de herencia de controlador que determina si el controlador puede ser heredado.

  Cuando un proceso hijo es creado, el parámetro fInheritHandles de CreateProcess determina si los controladores heredables del proceso padre son heredados por el proceso hijo. Un controlador heredado se refiere a un mismo objeto en el proceso hijo que el que está en el proceso padre. También tiene el mismo valor y privilegios de acceso. Por eso, cuando un proceso cambia el estado de un objeto, el cambio afecta a ambos procesos. Para usar un controlador, el proceso hijo debe recuperar el valor del controlador y "conocer" el objeto al cual se refiere. Usualmente, el proceso padre comunica esta información al proceso hijo a través de la linea de comandos, bloque de entorno u otras formas de comunicación entre procesos.

  La función DuplicateHandle es útil si un proceso tiene un controlador heredado abierto y no se quiere que el controlador sea heredado por el proceso hijo. En este caso, se usa DuplicateHandle para abrir un duplicado del controlador que no quiere que se pueda heredar, entonces se usa la función CloseHandle para cerrar el controlador que no se quiere heredar.


Heredar Variables de Entorno

  De forma predeterminada un proceso hijo hereda las variables de entorno de su proceso padre. Sin embargo la función CreateProcess habilita al proceso padre para especificar un bloque diferente de variables de entorno. Para más información ver Variables de Entorno, más adelante en este artículo.


Heredar el Directorio Actual

  La función GetCurrentDirectory recupera el directorio actual del proceso que llama a la función. De forma predeterminada el proceso hijo hereda el directorio actual de su proceso padre. Sin embargo, CreateProcess habilita al proceso padre para especificar un directorio de trabajo diferente para el proceso hijo. Para cambiar el directorio actual del proceso activo, se usa la función SetCurrentDirectory.


Variables de Entorno

  Todos los procesos tienen un bloque de entorno que contiene una serie de variables de entorno y sus valores. El procesador de comandos proporciona el comando set para mostrar su bloque de entorno o crear nuevas variables de entorno. Los programas iniciados por el procesador de comandos heredan sus variables de entorno.

  De forma predeterminada, un proceso hijo hereda las variables de entorno de su proceso padre. Sin embargo, se puede especificar un entorno diferente para el proceso hijo creando un nuevo bloque de entorno y pasando un puntero a este como un parámetro para la función CreateProcess.

  La función GetEnvironmentStrings devuelve un puntero al bloque de entorno del proceso que llama a la función. Este debería ser tratado como un bloque sólo-lectura; no modificarlo directamente. En cambio, se usa la función SetEnvironmentVariable para cambiar una variable de entorno. Cuando se ha terminado de usar el bloque de entorno obtenido por la función GetEnvironmentStrings, se llama a la función FreeEnvironmentStrings para liberar el bloque.

  La función GetEnvironmentVariable determina si una variable especificada está definida en el entorno del proceso que llama a la función, y, si es así, cual es su valor.


Terminar un Proceso

Un proceso se ejecuta hasta que uno de estos eventos ocurran:

    • Algún subproceso del proceso llame a la función ExitProcess. Esto termina todos los subprocesos del proceso.

    • El subproceso primario del proceso termine. El subproceso primario puede evitar la terminación de los otros
      subprocesos llamando explícitamente a ExitThread antes de terminar. Uno de los subprocesos restantes todavía
      puede llamar a ExitProcess para asegurarse de que todos los subprocesos son terminados.

    • El último subproceso del proceso termina

    • Algún subproceso llama a la función TerminateProcess con una referencia al proceso. Esto termina todos los
      subprocesos del proceso, sin permitirles liberar o guardar los datos.

    • Para procesos de consola, la función controladora predeterminada llama a ExitProcess cuando la consola recibe
      una señal Ctrl+C o Ctrl+Interrupción. Para más información ver la función SetConsoleCtrlHandler.

    • El usuario cierra el sistema o la seción. Se usa la función SetProcessShutdownParameters para especificar los
      parámetros de cierre, tal como cuando el proceso debería terminar relativo a otros procesos en el sistema.
      GetProcessShutdownParameters recupera la prioridad de cierre actual del proceso y otras opciones de cierre.

  Cuando un proceso es terminado, todos los subprocesos del proceso son terminados sin ninguna oportunidad de ejecutar código adicional. Esto significa que el proceso no ejecuta código en bloques controladores de terminación.

  La función GetExitCodeProcess devuelve el estado de terminación de un proceso. Mientras un proceso se está ejecutando, su código de terminación es STILL_ACTIVE. Cuando un proceso termina el estado de terminación cambia de STILL_ACTIVE al código de terminación del proceso. El código de terminación es el valor especificado en las llamadas a ExitProcess o TerminateProcess, o el valor devuelto por la función principal o por la función WinMain del proceso. Si el proceso es terminado debido a una excepción fatal, el valor de salida es el valor de la excepción que ha causado la terminación. Además, este valor es usado como un código de salida para todos los subprocesos que se estaban ejecutando cuando ocurrió la excepción.

  Cuando un proceso termina, el estado del objeto proceso se establece a señalado, descargando cualquier subproceso que ha estado esperando por que el proceso termine. Para más información ver Sincronizar la Ejecución de Subprocesos Múltiples.

  Las referencias abiertas a archivos u otros recursos son cerradas automáticamente cuando un proceso termina. Sin embargo, sus objetos existen hasta que todas las referencias a ellos son cerradas. Esto significa que un objeto permanece válido depués de que el proceso los cierra, si otro proceso tiene una referencia a él.

  Si un proceso es terminado por ExitProcess, el sistema llama a la función del punto de entrada de cada DLL que esté atada al proceso, con un valor que indica que el proceso se está desatando de la DLL. Las DLLs no son notificadas cuando un proceso es terminado por TerminateProcess. Para más información acerca de las DLL ver Bibliotecas de Vínculos Dinámicos.

    CUIDADO: La función TerminateProcess debería ser usada sólo en circunstancias extremas, ya que no le permie a
    los subprocesos liberar o guardar los datos y no notifica a las DLL atadas. Si necesitas tener a un proceso que
    termine a otro proceso, los siguientes pasos proporcionan una buena solución:

        • Que ambos procesos llamen a la función RegisterWindowMessage para crear un mensaje privado.
        • Un proceso puede terminar al otro proceso transmitiendo el mensaje privado usando la función
          BroadcastSystemMessage como sigue:

Código:
          BroadcastSystemMessage(
                BSF_IGNORECURRENTTASK, // No le envía el mensaje al proceso actual
                BSM_APPLICATIONS,      // Transmitir solo a aplicaciones
                private message,       // mensaje registrado en el paso anterior
                wParam,                // valor específico del mensaje
                lParam );              // valor específico del mensaje


        • El proceso recibe el mensaje privado y llama a TerminateProcess para terminar su ejecución.

  Nota: Cuando el sistema termina un proceso no termina ningún proceso hijo que haya creado.



Espacio de Trabajo del Proceso (Working Set)->>

  El working set o espacio de trabajo de un programa es una colección de aquellas páginas en su espacio virtual de direcciones que han sido recientemente consultadas. Esto incluye datos privados y compartidos. Los datos compartidos incluyen páginas que contienen todas las instrucciones que la aplicación ejecuta, inclusive aquellas en las DLLs propias y en las DLLs del sistema. Como el tamaño del espacio de trabajo aumenta, las demandas de memoria también.

  Un proceso tiene asociado un tamaño mínimo y un máximo del espacio de trabajo. Cada vez que se llama a CreateProcess, reserva el mínimo tamaño para el espacio de trabajo del proceso. El administrador de memoria virtual intenta guardar la suficiente memoria para el mínimo espacio de trabajo residente cuando el proceso es activado, pero no guarda más que el tamaño máximo.

  Para obtener el tamaño máximo y mínimo requeridos por una aplicación, se llama a la función GetProcessWorkingSetSize.

  El sistema asigna los tamaños predeterminados del espacio de trabajo. También se pueden modificar los tamaños del espacio de trabajo usando la función SetProcessWorkingSetSizes. Establecer estos valores no es una garantía de que la memoria será reservada o residente. Hay que ser cuidadosos al requerir un mínimo o un máximo demasiado grande para el tamaño del espacio de trabajo, porque haciendolo pueden degradar el funcionamiento del sistema.


Fibers

  Un fiber es una unidad de ejecución que debe ser programada manualmente por la aplicación. Los fibers se ejecutan en el contexto de los subprocesos que los programan. Cada subproceso puede programar múltiples fibers. En general, los fibers no proporcionan ventaja sobre una aplicación multitarea bien diseñada. Sin embargo, usando fibers se hace más fácil llevar aplicaciones que fueron diseñadas para programar sus propios subprocesos.

  Desde un punto de vista del sistema, el fiber asume la identidad del subproceso que lo creo. Por ejemplo, si un fiber accede al almacén local del subproceso (TLS), está accediendo al almacén local del subproceso que lo creo. Además, si un fiber llama a la función ExitThread, terminará el subproceso que lo creó. Sin embargo, un fiber no tiene todos los mismos estados de información asociados con él como los que hay asociados con un subproceso. El único estado de información mantenido por un fiber es su pila, un subconjunto de sus registros, y los datos del fiber proporcionados durante su creación. Los registros guardados son una serie de registros típicamente reservados a través de la llamada a la función.

  Los fibers no son apropiadamente programados. Se programa un fiber cambiandolo por otro fiber. El sistema todavía programa los subprocesos para ejecutarse. Cuando un subproceso que está ejecutando fibers se prepara, el fiber que está actualmente ejecutandose es preparado. Los fibers se ejecutan cuando los subprocesos se ejecutan.

  Antes de programar el primer fiber, se llama a la función ConvertThreadToFiber para crear un área en la cual guardar información del estado del fiber. El subproceso que llama a la función es ahora el fiber que se está ejecutando actualmente. La información guardada del estado para este fiber incluyen los datos del fiber pasados como un argumento a ConvertThreadToFiber.

  La función CreateFiber es usada para crear un nuevo fiber desde uno existente; la llamada requiere el tamaño de la pila, la dirección inicial y los datos del fiber. La dirección inicial es típicamente una función proporcionada por el usuario, llamada la función del fiber, que toma un parámetro (los datos del fiber) y no devuelve ningún valor. Si la función del fiber termina, el subproceso ejecuta la salida del fiber. Para ejecutar cualquier fiber creado con CreateFiber, se llama a la función SwitchToFiber. Se puede llamar a la función SwitchToFiber con la dirección de un fiber creado por otro subproceso diferente. Para hacer esto, se debe tener la dirección devuelta por el otro subproceso cuando llamó a CreateFiber y se debe usar la propia sincronización.

  Un fiber puede recuperar los datos llamando a la función GetFiberData. Un fiber puede recuperar su dirección en cualquier momento llamando a la función GetCurrentFiber.

  Para limpiar los datos asociados con un fiber, se llama a la función DeleteFiber. Se debe tener cuidado cuando se llama a DeleteFiber. Si se llama a DeleteFiber para un fiber creado por otro subproceso, puede causar que el otro subproceso termine anormalmente. Si DeleteFiber es llamado desde el fiber que se está ejecutando actualmente, su subproceso llama a ExitThread.
« Última modificación: 18 Abril 2005, 09:06 por Slasher Keeper » En línea



A la reina de las profundidades que cuida los pasos de una sombra en la noche :*
Páginas: [1] Ir Arriba Imprimir 
Ir a:  







Consolas     La Web de Goku     MilW0rm     MundoDivx

Hispabyte     Truzone     TodoReviews     ZonaPhotoshop

hard-h2o modding    Foros de ayuda    Yashira.org    Videojuegos    indetectables.net   

Noticias Informatica    Seguridad Informática    ADSL    Foros en español    eNYe Sec

Todas las webs afiliadas están libres de publicidad engañosa.

Powered by SMF 1.1.6 | SMF © 2006-2008, Simple Machines LLC