Windows NT presenta una arquitectura del tipo
cliente-servidor. Los programas de aplicación son contemplados por el sistema operativo como si fueran clientes a los que hay que servir, y para lo cual viene equipado con distintas entidades servidoras.
Uno de los objetivos fundamentales de diseño fue el tener un núcleo tan pequeño como fuera posible, en el que estuvieran integrados módulos que dieran respuesta a aquellas llamadas al sistema que necesariamente se tuvieran que ejecutar en modo privilegiado (también llamado modo kernel, modo núcleo y modo supervisor). El resto de las llamadas se expulsarían del núcleo hacia otras entidades que se ejecutarían en modo no privilegiado (modo usuario), y de esta manera el núcleo resultaría una base compacta, robusta y estable. Por eso se dice que Windows NT es un sistema operativo basado en
micro-kernel.
Es por ello que en un primer acercamiento a la arquitectura distinguimos un núcleo que se ejecuta en modo privilegiado, y se denomina Executive, y unos módulos que se ejecutan en modo no privilegiado, llamados subsistemas protegidos.
Los programas de usuario (también llamados programas de aplicación) interaccionan con cualquier sistema operativo (SO) a través de un juego de llamadas al sistema, que es particular de cada SO. En el mundo Windows en general, las llamadas al sistema se denominan API (Application Programming Interfaces, interfaces para la programación de aplicaciones). En Windows NT y en Windows 95 se usa una versión del API llamada API Win32. Un programa escrito para Windows NT o Windows 95, y que por consiguiente hace uso del API Win32, se denomina genéricamente "programa Win32", y de hecho esta denominación es bastante frecuente en artículos y libros al respecto. Desgraciadamente, y conviene dejarlo claro cuanto antes, el término "Win32" tiene tres acepciones (al menos hasta ahora) totalmente distintas. Una es el API, otra es el nombre de uno de los subsistemas protegidos de Windows NT del que hablaremos más adelante, y por último se denomina Win32s a una plataforma desarrollada por Microsoft, similar a Windows 3.1, pero que usa el API Win32 en vez del API Win16 del Windows 3.1.
Apariencia de Windows NT 4.0 Server. Se puede ver una interfaz para el
usuario similar a Windows 9x, pero en el fondo no se parecen mucho.
Hechas estas aclaraciones, podemos continuar adelante. Algunas de las llamadas al sistema, debido a su naturaleza, son atendidas directamente por el Executive, mientras que otras son desviadas hacia algún subsistema. Esto lo veremos con detalle en breve.
El hecho de disponer de un núcleo rodeado de subsistemas que se ejecutan en modo usuario nos permite además añadir nuevos subsistemas sin producir ningún tipo de confrontación.
En el diseño de Windows NT han confluido aportaciones de tres modelos: el modelo
cliente-servidor, el modelo de objetos, y el modelo de multiprocesamiento simétrico.
Modelo cliente-servidor:
En la teoría de este modelo se establece un kernel que básicamente se encarga de recibir peticiones de procesos clientes y pasárselas a otros procesos servidores, ambos clientes y servidores ejecutándose en modo usuario. Windows NT pone el modelo en práctica pero no contempla el núcleo como un mero transportador de mensajes, sino que introduce en él aquellos servicios que sólo pueden ser ejecutados en modo kernel. El resto de servicios los asciende hacia subsistemas servidores que se ejecutan en modo usuario, independientes entre sí, y que por tanto pueden repartirse entre máquinas distintas, dando así soporte a un sistema distribuido (de hecho, el soportar los sistemas distribuidos fue otra de las grandes directivas de diseño de este
SO).
Modelo de objetos:
Decir que no implementa puramente la teoría de este modelo, sino que más bien lo que hace es simplemente contemplar los recursos (tanto internos como externos) como objetos. Más adelante daremos una lista de los objetos de Windows NT. Brevemente, señalar que todo objeto ha de poseer identidad propia (es único y distinguible de todos los demás), y una serie de atributos (variables) y métodos (funciones) que modifican sus atributos. Los objetos interaccionan entre sí a través del envío de mensajes. No sólo existen en Windows NT objetos software (lógicos), sino que los dispositivos hardware (físicos) también son tratados como objetos (a diferencia de UNIX, que recordemos trataba a los dispositivos como
archivos).
Modelo de multiprocesamiento simétrico:
Un SO multiproceso (o sea, aquel que cuenta con varias CPU y cada una puede estar ejecutando un proceso) puede ser simétrico (SMP) o asimétrico (ASMP). En los sistemas operativos SMP (entre los que se encuentran Windows NT y muchas versiones de UNIX) cualquier CPU puede ejecutar cualquier proceso, ya sea del SO o no, mientras que en los ASMP se elige una CPU para uso exclusivo del SO y el resto de CPU quedan para ejecutar programas de usuario. Los sistemas SMP son más complejos que los ASMP, contemplan un mejor balance de la carga y son más tolerantes a fallos (de manera que si un subproceso del SO falla, el SO no se caerá pues podrá ejecutarse sobre otra CPU, cosa que en los ASMP no sería posible, con lo que se bloquearía el sistema entero).
Comencemos describiendo los subsistemas protegidos, para seguidamente estudiar la estructura del
Executive.
Figura 1. El núcleo se ejecuta en modo privilegiado (Executive) y en modo no privilegiado (subsistemas protegidos)
Son una serie de procesos servidores que se ejecutan en modo usuario como cualquier proceso de usuario, pero que tienen algunas características propias que los hacen distintos. Al decir subsistemas protegidos nos referiremos, pues, a estos procesos. Se inician al arrancar el SO. Los hay de dos tipos: integrales y de entorno.
es aquel servidor que ejecuta una función crítica del SO (como por ejemplo el que gestiona la seguridad). Tenemos los siguientes:
El proceso de inicio (Logon Process) recibe las peticiones de conexión por parte de los usuarios. En realidad son dos procesos, cada uno encargándose de un tipo distinto de conexión:
El proceso de inicio local: gestiona la conexión de usuarios locales directamente a una máquina Windows NT.
El proceso de inicio remoto: gestiona la conexión de usuarios remotos a procesos servidores de Windows NT.
Figura 2. Diagrama de Flujo del Proceso de Inicio de Windows NT.
Este subsistema interacciona con el proceso de inicio y el llamado monitor de referencias de seguridad (se tratara en el Executive), y de esta forma se construye el modelo de seguridad en Windows NT.
El subsistema de seguridad interacciona con el proceso de inicio, atendiendo las peticiones de acceso al sistema. Consta de dos
subcomponentes:
La autoridad de seguridad local: es el corazón del subsistema. En general gestiona la política de seguridad local; así, se encarga de generar los permisos de acceso, de comprobar que el usuario que solicita conexión tiene acceso al sistema, de verificar todos los accesos sobre los objetos (para lo cual se ayuda del monitor de referencias a seguridad) y de controlar la política de auditorías, llevando la cuenta de los mensajes de auditoría generados por el monitor de referencias. Las auditorías son una facilidad que proporciona Windows NT para monitorizar diversos acontecimientos del sistema por parte del Administrador.
El administrador de cuentas: mantiene una base de datos con las cuentas de todos los usuarios (login, claves, identificaciones, etc.). Proporciona los servicios de validación de usuarios requeridos por el subcomponente anterior.
Da soporte a aplicaciones procedentes de SO distintos, adaptándolas para su ejecución bajo Windows NT. Existen tres de este tipo:
Es el más importante, ya que atiende no sólo a las aplicaciones nativas de Windows NT, sino que para aquellos programas no Win32, reconoce su tipo y los lanza hacia el subsistema correspondiente. En el caso de que la aplicación sea
MS-DOS o Windows de 16 bits (Windows 3.11 e inferiores), lo que hace es crear un nuevo subsistema protegido pero no servidor. Así, la aplicación DOS o Win16 se ejecutaría en el contexto de un proceso llamado VDM (Virtual DOS Machine, máquina virtual DOS), que no es más que un simulador de un
computador funcionando bajo
MS-DOS. Las llamadas al API Win16 serían correspondidas con las homónimas en API Win32. Microsoft llama a esto WOW (Windows On Win32).
El subsistema soporta una buena parte del API Win32. Así, se encarga de todo lo relacionado con la interfaz gráfica con el usuario (GUI), controlando las entradas del usuario y salidas de la aplicación. Por ejemplo, un buen número de funciones de las bibliotecas USER32 y GDI32 son atendidas por Win32, ayudándose del Executive cuando es necesario.
El funcionamiento como servidor de Win32 lo veremos un poco más adelante, en el apartado de llamadas a procedimientos locales.
La norma POSIX (Portable Operating System Interface for Unix) fue elaborada por IEEE para conseguir la portabilidad de las aplicaciones entre distintos entornos UNIX. La norma se ha implementado no sólo en muchas versiones de UNIX, sino también en otros SO como Windows NT, VMS, etc. Se trata de un conjunto de 23 normas, identificadas como IEEE 1003.0 a IEEE 1003.22, o también POSIX.0 a POSIX.22, de las cuales el subsistema POSIX soporta la POSIX.1, que define un conjunto de llamadas al sistema en lenguaje C.
El subsistema sirve las llamadas interaccionando con el Executive. Se encarga también de definir aspectos específicos del SO UNIX, como pueden ser las relaciones jerárquicas entre procesos padres e hijos (las cuales no existen en el subsistema Win32, por ejemplo, y que por consiguiente no aparecen implementadas directamente en el
Executive).
Igual que el subsistema POSIX proporciona un entorno para aplicaciones UNIX, este subsistema da soporte a las aplicaciones OS/2. Proporciona la interfaz gráfica y las llamadas al sistema; las llamadas son servidas con ayuda del Executive.
No se debe confundir el Executive con el núcleo de Windows NT, aunque muchas veces se usan (incorrectamente) como sinónimos. El Executive consta de una serie de componentes software, que se ejecutan en modo privilegiado, y uno de los cuales es el núcleo. Dichos componentes son totalmente independientes entre sí, y se comunican a través de interfaces bien definidas. Recordemos que en el diseño se procuró dejar el núcleo tan pequeño como fuera posible, y, como veremos, la funcionalidad del núcleo es mínima. Pasemos a comentar cada módulo.
Se encarga de crear, destruir y gestionar todos los objetos del Executive. Tenemos infinidad de objetos: procesos, subprocesos, archivos, segmentos de memoria compartida, semáforos, mutex, sucesos, etc. Los subsistemas de entorno (Win32, OS/2 y POSIX) también tienen sus propios objetos. Por ejemplo, un objeto ventana es creado (con ayuda del administrador de objetos) y gestionado por el subsistema Win32. La razón de no incluir la gestión de ese objeto en el Executive es que una ventana sólo es innata de las aplicaciones Windows, y no de las aplicaciones UNIX o OS/2. Por tanto, el Executive no se encarga de administrar los objetos relacionados con el entorno de cada SO concreto, sino de los objetos comunes a los tres.
Se encarga (en colaboración con el administrador e objetos) de crear, destruir y gestionar los procesos y subprocesos. Una de sus funciones es la de repartir el tiempo de CPU entre los distintos subprocesos (ver el capítulo de los procesos). Suministra sólo las relaciones más básicas entre procesos y subprocesos, dejando el resto de las interrelaciones entre ellos a cada subsistema protegido concreto. Por ejemplo, en el entorno POSIX existe una relación filial entre los procesos que no existe en Win32, de manera que se constituye una jerarquía de procesos. Como esto sólo es específico de ese subsistema, el administrador de objetos no se entromete en ese trabajo y lo deja en manos del subsistema.
Windows NT y UNIX implementan un direccionamiento lineal de 32 bits y memoria virtual paginada bajo demanda. El VMM se encarga de todo lo relacionado con la política de gestión de la memoria: determina los conjuntos de trabajo de cada proceso, mantiene un conjunto de páginas libres, elige páginas víctima, sube y baja páginas entre la memoria RAM y el archivo de intercambio en disco, etc. Una explicación detallada la dejaremos para el capítulo de la memoria.
Este módulo se encarga de recibir y envíar las llamadas a procedimiento local entre las aplicaciones cliente y los subsistemas servidores.
Consiste en una serie de subcomponentes, que son:
El administrador del sistema de archivos
El servidor y el redirector de red
Los drivers de dispositivo del sistema
El administrador de caches
Buena parte de su trabajo es la gestión de la comunicación entre los distintos drivers de dispositivo, para lo cual implementa una interfaz bien definida que permite el tratamiento de todos los drivers de una manera homogénea, sin que intervenga el cómo funciona específicamente cada uno.
Trabaja en conjunción con otros componentes del Executive, sobre todo con el VMM. Le proporciona la E/S síncrona y asíncrona, la E/S a archivos asignados en memoria y las caches de los
archivos.
El administrador de caches no se limita a gestionar unos cuantos buffers de tamaño fijo para cada
archivo abierto, sino que es capaz de estudiar las estadísticas sobre la carga del sistema y variar dinámicamente esos tamaños de acuerdo con la carga. El VMM realiza algo parecido en su trabajo, como veremos en su momento.
Este componente da soporte en modo privilegiado al subsistema de seguridad, con el que interacciona. Su misión es actuar de alguna manera como supervisor de accesos, ya que comprueba si un proceso determinado tiene permisos para acceder a un objeto determinado, y monitoriza sus acciones sobre dicho objeto.
De esta manera es capaz de generar los mensajes de auditorías. Soporta las validaciones de acceso que realiza el subsistema de seguridad local.
En UNIX, de la seguridad se encargaba un módulo llamado el Kerberos (Cancerbero), desarrollado por el MIT como parte del Proyecto Atenas. Kerberos se ha convertido en una norma de facto, y se incorporará a Windows NT en su versión 5.0.
Situado en el corazón de Windows NT, se trata de un micro-kernel que se encarga de las funciones más básicas de todo el SO:
Ejecución de subprocesos
Sincronización multiprocesador
Manejo de las interrupciones hardware
Es una capa de software incluida en el Executive que sirve de interfaz entre los distintos drivers de dispositivo y el resto del sistema operativo. Con HAL, los dispositivos se presentan al SO como un conjunto homogéneo, a través de un conjunto de funciones bien definidas. Estas funciones son llamadas tanto desde el SO como desde los propios drivers. Permite a los drivers de dispositivo adaptarse a distintas arquitecturas de E/S sin tener que ser modificados en gran medida. Además oculta los detalles hardware que conlleva el multiprocesamiento simétrico de los niveles superiores del SO.
Windows NT, al tener una arquitectura cliente-servidor, implementa el mecanismo de llamada a procedimiento remoto (RPC) como medio de comunicación entre procesos clientes y servidores, situados ambos en máquinas distintas de la misma red. Para clientes y servidores dentro de la misma máquina, la RPC toma la forma de llamada a procedimiento local (LPC). Vamos a estudiar en detalle ambos mecanismos pues constituyen un aspecto fundamental del diseño de Windows NT.
Se puede decir que el sueño de los diseñadores de Windows NT es que algún día se convierta en un sistema distribuido puro, es decir, que cualquiera de sus componentes pueda residir en máquinas distintas, siendo el kernel en cada máquina el coordinador general de mensajes entre los distintos componentes. En la última versión de Windows NT esto no es aún posible.
No obstante, el mecanismo de RPC permite a un proceso cliente acceder a una función situada en el espacio virtual de direcciones de otro proceso servidor situado en otra máquina de una manera totalmente transparente.
Vamos a explicar el proceso en conjunto. Supongamos que se tiene un proceso cliente ejecutándose bajo una máquina A, y un proceso servidor bajo una máquina B. El cliente llama a una función f de una biblioteca determinada. El código de f en su biblioteca es una versión especial del código real; el código real reside en el espacio de direcciones del servidor. Esa versión especial de la función f que posee el cliente se denomina proxy. El código proxy lo único que hace es recoger los parámetros de la llamada a f, construye con ellos un mensaje, y pasa dicho mensaje al Executive. El Executive analiza el mensaje, determina que va destinado a la máquina B, y se lo envía a través del interfaz de transporte. El Executive de la máquina B recibe el mensaje, determina a qué servidor va dirigido, y llama a un código especial de dicho servidor, denominado stub, al cual le pasa el mensaje. El stub desempaqueta el mensaje y llama a la función f con los parámetros adecuados, ya en el contexto del proceso servidor. Cuando f retorna, devuelve el control al código stub, que empaqueta todos los parámetros de salida (si los hay), forma así un mensaje y se lo pasa al Executive.
Ahora se repite el proceso inverso; el Executive de B envía el mensaje al Executive de A, y este reenvía el mensaje al proxy. El proxy desempaqueta el mensaje y devuelve al cliente los parámetros de retorno de f. Por tanto, para el cliente todo el mecanismo ha sido transparente. Ha hecho una llamada a f, y ha obtenido unos resultados; ni siquiera tiene que saber si el código real de f está en su biblioteca o se encuentra en una máquina situada tres plantas más abajo.
Las LPC se pueden considerar una versión descafeinada de las RPC. Se usan cuando un proceso necesita los servicios de algún subsistema protegido, típicamente Win32. Se intentara descubrir su funcionamiento.
El proceso cliente tiene un espacio virtual de 4 Gb. Los 2 Gb inferiores son para su uso (excepto 128 Kb). Los 2 Gb superiores son para uso del sistema.
Vamos a suponer que el cliente realiza una llamada a la función CreateWindow. Dicha función crea un objeto ventana y devuelve un descriptor al mismo. No es gestionada directamente por el Executive, sino por el subsistema Win32 (con algo de colaboración por parte del Executive, por supuesto; por ejemplo, para crear el objeto). El subsistema Win32 va guardando en su propio espacio de direcciones una lista con todos los objetos ventana que le van pidiendo los procesos. Por consiguiente, los procesos no tienen acceso a la memoria donde están los objetos; simplemente obtienen un descriptor para trabajar con ellos. Cuando el cliente llama a CreateWindow, se salta al código de esa función que reside en la biblioteca USER32.DLL asignada en el espacio de direcciones del cliente.
Por supuesto, ese no es el código real, sino el proxy. El proxy empaqueta los parámetros de la llamada, los coloca en una zona de memoria compartida entre el cliente y Win32, pone al cliente a dormir y ejecuta una LPC. La facilidad de llamada a procedimiento local del Executive captura esa llamada, y en el subsistema Win32 se crea un subproceso que va a atender a la petición del cliente. Ese subproceso es entonces despertado, y comienza a ejecutar el correspondiente código de stub. Los códigos de stub de los subsistemas se encuentran en los 2 Gb superiores (los reservados) del espacio virtual del proceso cliente. Aunque no he encontrado más documentación al respecto, es muy probable que dichos 2 Gb sean los mismos que se ven desde el espacio virtual de Win32. Sea como sea, el caso es que el stub correspondiente desempaqueta los parámetros del área de memoria compartida y se los pasa a la función CreateWindow situada en el espacio de Win32. Ése sí es el código real de la función. Cuando la función retorna, el stub continúa, coloca el descriptor a la ventana en la memoria compartida, y devuelve el control de la LPC al Executive. El subproceso del Win32 es puesto a dormir. El Executive despierta al subproceso cliente, que estaba ejecutando código proxy. El resto de ese código lo que hace es simplemente tomar el descriptor y devolverlo como resultado de la función CreateWindow.