El interior de Windows

Rafael Ontivero

Programador sistemas embebidos

http://geeks.ms/blogs/rfog/

Microsoft MVP

LinkedIn: http://es.linkedin.com/pub/rafael-ontivero/54/622/8aa

Todos sabemos que Windows 8 viene de Windows 7 y éste, a su vez, de Windows XP. Antes de eso existía una separación entre dos sistemas operativos que hacían lo mismo pero que internamente funcionaban de forma completamente diferente. Hablo, claro está, de Windows 95/98/ME y Windows NT. Cuando Microsoft unificó las dos ramas, eligió poner como núcleo el de NT como elemento central de lo que, al cabo de los años, terminó siendo Windows 8.

La idea original estaba basada en una capa de bajo nivel llamada HAL, un núcleo más o menos monolítico basado en MACH, y encima de ello una serie de subsistemas que serían con los que las aplicaciones de usuario deberían interactuar.

Todo esto vino explicado, por primera vez y de forma oficial, en el libro de Cuter, base de los actuales Windows Internals. La idea era explicar de una forma lógica la estructura interna del sistema operativo, que vamos a resumir no sin antes recordar que NT podía ejecutarse en una gran gama de arquitecturas, partiendo de la x86 y pasando por la PPC, no sin olvidar SuperH y Mips. Quizás a la mayoría no le suene mucho, pero PPC era la tecnología que había debajo de los primeros MAC.

Ello venía posibilitado por la estructura del sistema operativo. La parte de más bajo nivel se conocía como capa HAL, y debía ser una finísima capa que convirtiera todo el hardware en algo unificado y comprensible para el núcleo, que es el corazón del sistema operativo y el encargado de ofrecer el primer nivel de servicios a los subsistemas.

Luego estaban los citados subsistemas, y había varios. Posix. Sí, el Posix en el que está basado Linux o Unix. OS/2. Win32, que es el actual y el único que queda en Windows 8. Es decir, Windows NT podía ejecutar programas que cumplieran el estándar Posix e incluso aplicaciones de OS/2. Y también, por supuesto, las clásicas de Windows basadas en Win32.

Nos podemos fijar en la potencia de la idea. Con esa arquitectura, Windows era capaz de ejecutar casi cualquier programa existente en el mercado. Por desgracia las expectativas no se cumplieron y al final todo quedó en un sistema operativo llamado Windows XP que incluía dentro de él todas esas semillas.

XP tenía su capa HAL, su núcleo y su subsistema de Win32. De nuevo la idea es más que potente. Una aplicación, en teoría, era incapaz de tumbar al sistema hiciera lo que hiciese ya que hablaba con Win32 que a su vez lo hace con el núcleo.

Aquí debemos introducir el tema de la protección de tareas y el modo protegido de un microprocesador. Para hacernos una idea, podemos imaginar que el modo protegido es una especie de habitación ultrasegura que representa al núcleo y en donde se ejecutan todas las tareas vitales del sistema operativo. Esa habitación contiene el motor real que mueve el ordenador: la gestión de procesos, de memoria, de acceso a disco, etc., todo ello organizado de una forma rigurosa y estricta que impedía que cualquier petición que entrara por la puerta pudiera tumbar el sistema.

Y sí, la mayoría de veces, cuando XP ofrecía al usuario una pantalla azul en general se debía a dos causas: fallo de hardware (mayormente memoria defectuosa, sin descartar cables mal pinchados o viejos) y drivers defectuosos. De hecho Microsoft inició una cruzada sobre esto último que terminó en la increíble estabilidad de Windows 7 y de Windows 8. Una pantalla azul (o roja) actual en alguno de esos sistemas operativos casi siempre es debida a un fallo en el hardware.

Fuera del modo protegido está el modo usuario (ambos también se conocen como anillo 0 y anillo 3, habiendo en medio dos anillos más que Windows no usa), que es donde se ejecuta el subsistema Win32 y las aplicaciones de usuario. Cuando una aplicación quiere que Windows haga algo, realiza una llamada a alguna función de Win32, y esta, a su vez, y tras algún tipo de proceso, termina llamando a otras del núcleo, pero siempre a través de la citada puerta.

De este modo se aísla la aplicación de usuario del núcleo. Cuando se realiza una llamada a Win32, éste verifica los parámetros antes de realizar el traspaso al núcleo, de forma que éste recibirá siempre datos correctos. También hay que contar que muchas de las operaciones ni siquiera entran al núcleo, como por ejemplo todas las primitivas gráficas, que son tratadas en el modo de usuario antes de ser pasadas al driver de vídeo, que según en qué versión de Windows, está en el anillo 0 o en el 3.

E incluso si nuestra aplicación llegara a tumbar al subsistema de Win32, esto no afectaría a la estabilidad final del sistema. En principio la aplicación faltante se cerraría, Win32 se recargaría y no habría pasado nada serio.

A fecha de hoy Win32 también es enormemente estable y no suele caerse nunca.

¿Dónde está .NET y las aplicaciones RT?

Pese a la información que suele suministrar Microsoft, Tanto .NET como RT se ejecutan sobre Win32, que se ha dicho es el único subsistema que queda de los tiempos de NT. Esto lo demostraremos en la parte práctica de este artículo.

Es decir, si continuamos con la descripción de la arquitectura de Windows, tendríamos a .NET como un superconjunto de Win32 y a RT como algo a caballo entre Win32 y .NET. Es un tanto complejo de explicar, pero el siguiente gráfico, sin ser de todo veraz, explica bastante bien la situación teórica del tema:

Dn197254.54D9D3CF714C66832FA50EAD41844894(es-es,MSDN.10).png

Si se ha seguido lo descrito hasta ahora, se verá que la ampliación de Windows a procesadores ARM (para la versión RT) no ha debido ser demasiado traumática a poco que hayan mantenido la arquitectura citada al principio de este artículo, como seguro que así ha sido. De hecho, Microsoft ya tenía Windows corriendo no sólo en plataformas ARM, sino en otras muchas. Hablamos de Windows CE, que parece ser muere (es un decir ya que seguro que hay mucho código compartido con su hermano de escritorio) en Windows Phone 7.5 ya que la versión 8 comparte el núcleo RT.

Cuatro API y un subsistema para unificarlos a todos

Por un lado tenemos Win32 y todas sus extensiones de terceros o de la propia Microsoft. Podríamos citar Visual Basic (6, el antiguo), Delphi, C++Builder, MFC, QT, wxWidgets y toda una gran extensión de bibliotecas y lenguajes que terminan llamando al subsistema de Win32.

Por otro tenemos .NET y todas sus variantes, que finalizan en la versión 4.5 no RT y que podemos considerar un API por sí mismo pese a terminar dependiendo de Win32 ya que incluyen un Framework completamente independiente y tanto o más potente que el citado.

Con estos dos API podemos desarrollar las clásicas aplicaciones de escritorio, que todavía están disponibles bajo Windows 8 y Windows 8 PRO.

En la actualidad tenemos dos API más, a saber. La de Windows RT que está basada en C++/CX y que usa .NET 4.5 para tabletas (RT), con la interfaz basada en XAML y una cuarta algo más esotérica respecto a crear aplicaciones para tabletas: HTML/CSS, que nos permite desarrollar aplicaciones RT basadas en HTML5 y JavaScript.

Podríamos citar un quinto API, DirectX, que permite el desarrollo tanto de aplicaciones de escritorio clásico como RT, pero realmente está basado, de nuevo, en Win32 en ambas variantes y, cuando no realiza llamadas a dicho subsistema, lo hace directamente al driver de la tarjeta de vídeo.

Finalmente podríamos resumir lo descrito aquí con el siguiente gráfico que, de nuevo sin ser estrictamente correcto, sí que presenta con suficiente claridad los temas tratados:

Dn197254.5C7A4F1364D367E7866D90B3FA15D97B(es-es,MSDN.10).png

El error está, para aquél que sienta curiosidad, en que ese cuadradito tan chiquito que pone Win32 se extiende justo por encima de Windows Kernel Services como base de cualquier otro módulo descrito.

¿C++/CX? ¿Otra variante de C++?

Antes se ha hablado de C++/CX. Pese a que se tratará el tema en un artículo posterior, debemos hacer una pequeña introducción. Ya se ha dicho que todo termina en Win32, que ofrece un interfaz de desarrollo basado en el lenguaje C.

Win32 adolece de ciertas carencias para la creación de aplicaciones RT, más que nada por lo venerable de su situación y por que no se acomoda muy bien a un modelo de desarrollo moderno.

¿Qué ha hecho Microsoft para solucionar esto? Pues ha modificado partes importantes de Win32, dejando la interfaz clásica sin tocar pero sí cambiando muchas cosas de su interior, ha ampliado todavía más el API y, creando una extensión al lenguaje C++, ha construido una capa nativa sobre la que se asienta una parte importante del desarrollo para tabletas RT.

C++/CX es, por lo tanto, el venerable lenguaje C++ en todo su esplendor al que Microsoft le ha añadido una especie de sugar syntax para poder construir partes del API que se usan en el desarrollo de aplicaciones RT.

Y esto nos lleva a la parte final del artículo, que es demostrar de forma tremendamente sencilla que Windows RT y el desarrollo con .NET para RT continua teniendo a Win32 debajo de toda su arquitectura.

¿Qué hay debajo de Windows RT?

Necesitamos un equipo de desarrollo que pueda generar aplicaciones para Windows RT. Es decir, debe poder ejecutar Visual Studio 2012 completo, aunque las versiones Express podrían funcionar pese a que el autor no ha realizado la prueba con ellas.

Una vez que tenemos una máquina con Visual Studio 2012, lanzamos el entorno y creamos dos aplicaciones. Nos vamos a la pestaña de Visual C++ y elegimos una aplicación de tipo Win32 y le damos el nombre de TestWin32. Como en la imagen:

Dn197254.59F3659299063EA643D7477C0F4628F4(es-es,MSDN.10).png

En el asistente, aceptamos todas las opciones por defecto y se nos creará un proyecto que si compilamos y lanzamos nos creará una ventana de escritorio clásica:

Dn197254.C983C2C5C9A5EA290FDC07EBA2AC38F5(es-es,MSDN.10).png

Esta es nuestra aplicación de referencia.

Luego tenemos que crear una aplicación de tipo Windows Store como se muestra en la captura:

Dn197254.D78820DE76BFDDFBFCF5D4DA1A52030B(es-es,MSDN.10).png

Realmente da igual el tipo de aplicación que elijamos siempre que sea de la tienda.

Ahora es tiempo de compilar ambos proyecto si todavía no lo hemos hecho. Los ejecutamos para ver que cada uno funciona como debe.

Pero todavía nos falta algo más, para poder demostrar que debajo de la aplicación Windows RT se encuentra el subsistema Win32 y no otra cosa. Debemos bajarnos un programa llamado Dependency Walker. En su momento era una herramienta que venía incluida en los SDK de Windows, pero ya no. Este programa abre un ejecutable o una DLL y es capaz de mirar las tablas de importación y exportación del fichero para determinar qué DLL se van a cargar cuando lo lancemos.

Cuando os bajéis el programa, que es de uso libre, podréis comprobar que todavía existen versiones para otras arquitecturas de Windows como MIPS o PowerPC, lo que corrobora nuestra tesis de que Windows NT podía correr bajo muchos otros procesadores.

Debemos navegar por las carpetas del proyecto, fuera del IDE (aunque no es necesario cerrarlo), para localizar ambos ejecutables. Pero antes, vayamos al desplegable del IDE en donde nos indica la configuración y la cambiamos a Release:

Dn197254.3483DB930FDC8E272CE7D95BFCFC4E1C(es-es,MSDN.10).png

De este modo nos aseguramos que los ejecutables creados serán los que finalmente se distribuirían.

En la máquina del autor la forma más rápida de construir una solución multiproyecto es presionar Mayúsculas Control y B.

Ahora sí, ahora nos vamos a la carpeta raíz de la solución. Allí debe haber una carpeta con el nombre de Release, y dentro de ella nuestro ejecutable TestWin32.exe por un lado y el TestWinRT.exe dentro de otra carpeta con el mismo nombre, que son los dos ficheros que vamos a inspeccionar.

Si abrimos el primero de ellos con Dependency Walker, y colapsamos el árbol de dependencias al primer nivel, nos encontraremos con una imagen similar a esta (ignoraremos los posibles errores que nos de al abrir):

Dn197254.7884B4A2F2D9CA722067122BFFCE219E(es-es,MSDN.10).png

Hagamos lo mismo con la aplicación TestWinRT.exe:

Dn197254.77D81A1B771C6F14021ECF90AD6D5BF1(es-es,MSDN.10).png

Observemos que ambas realizan llamadas a Kernel32.DLL, que es, mayormente, el subsistema Win32. Todas las DLL que comienzan con MSVC y con VCC son los runtimes de C++, que en la primera no están porque no hemos hecho uso de ninguna función de C++ como puede ser utilizar la STL.

Fijémonos que incluso la segunda hace uso de OLE32.DLL, que es el motor COM, OCX o como queramos llamarlo y que es la base de Windows 8.

¿Pero qué pasa con ARM y RT?

Pues más de lo mismo. Para comprobar que incluso una aplicación compilada para ARM y que sólo se puede ejecutar en una tableta RT pura tiene debajo a Win32, tenemos que volver a Visual Studio y cambiar algunas cosas.

Nos vamos al menú BUILD -> CONFIGURATION MANAGER y se nos abrirá una ventana como la de la captura. Cambiamos la parte Configuration a Release si no está, y la de Platform a ARM:

Dn197254.E797FCB578057E303CBADBFFCEA592CD(es-es,MSDN.10).png

Volvemos a recompilar y debe aparecernos una nueva carpeta llamada ARM. Dentro de ella, en algún subnivel, encontraremos el ejecutable TestWinRT.exe, esta vez para la arquitectura que nos ocupa.

De nuevo lo abrimos con el Dependency Walker, y voila, ahí están las DLL de siempre:

Dn197254.61408226BF1001D89ECA2928A06FA604(es-es,MSDN.10).png

Por tanto, no hay duda de que cualquier plataforma tiene debajo de sí el subsistema Win32, que era nuestra tesis a demostrar.

Podemos navegar con el programa y ver cómo unas DLL necesitan de otras, y al final todas terminan llamando a Win32.

Y finalmente, para aquellos que quieran ver más partes de las tripas de un ejecutable, pueden utilizar la aplicación llamada dumpbin.exe. Su mayor problema es que es de línea de comandos, y hay que ir redireccionando su salida a un fichero de texto dado que es capaz de generar una increíble cantidad de información.

El código fuente de este ejemplo se puede obtener de aquí: https://github.com/rfog/WinRTvsWin32/

| Página de inicio |Artículos Técnicos | Comunidad