Ventanas.  virus  Cuadernos.  Internet.  oficina.  Utilidades.  Conductores

El libro Sistema Operativo 0 a 1 está publicado en GitHub y tiene más de 2000 estrellas y 100 bifurcaciones. Como su nombre lo indica, después de leerlo, puede crear su propio sistema operativo y, tal vez, pocas cosas en el mundo de los programadores pueden ser más geniales.

A través de este libro, aprenderá lo siguiente:

  • Aprenda a crear un sistema operativo basado en la documentación técnica del hardware. Así es como funciona en el mundo real, no puedes usar Google para obtener respuestas rápidas.
  • Entender cómo Componentes de la computadora interactúan entre sí, desde el software hasta el hardware.
  • Aprende a escribir código tú mismo. Copiar código a ciegas no es aprender, en realidad aprenderá a resolver problemas. Por cierto, la copia oculta también es peligrosa.
  • Domine las herramientas familiares para el desarrollo de bajo nivel.
  • Familiarízate con el lenguaje ensamblador.
  • Descubre en qué consisten los programas y cómo Sistema operativo los lanza. Pequeño resumen dimos este tema para los curiosos en.
  • Aprenda a depurar un programa directamente en el hardware con GDB y QEMU.
  • Lenguaje de programación C. Puedes dominarlo rápidamente siguiendo.
  • Conocimientos básicos de Linux. Es suficiente para estudiar en nuestro sitio web.
  • Conocimientos básicos de física: átomos, electrones, protones, neutrones, voltaje.

Digo de inmediato, no cierre el artículo con los pensamientos "Maldita sea, otro Popov". El solo tiene un Ubuntu lamido, y yo tengo todo desde cero, incluido el kernel y las aplicaciones. Entonces, continuación bajo el corte.

grupo de SO: Aquí.
Primero, te daré una captura de pantalla.

No hay más de ellos, y ahora con más detalle sobre por qué lo estoy escribiendo.

Era una cálida tarde de abril, jueves. Desde niño, soñaba con escribir un sistema operativo, cuando de repente pensé: "Ahora que conozco los pros y los asm, ¿por qué no hacer realidad mi sueño?". Busqué en Google sitios sobre este tema y encontré un artículo de Habr: "Cómo comenzar y no dejar de escribir SO". Gracias a su autor por el enlace OSDev Wiki a continuación. Fui allí y comencé a trabajar. Había en un artículo todos los datos sobre el sistema operativo mínimo. Comencé a construir cross-gcc y binutils y luego reescribí todo desde allí. Deberías haber visto mi alegría cuando vi la inscripción "¡Hola, kernel World!" Salté de la silla y me di cuenta: no me rendiré. Escribí "consola" (entre comillas, no tenía acceso a un teclado), pero luego decidí escribir un sistema de ventanas. Como resultado, funcionó, pero no tenía acceso al teclado. Y luego decidí pensar en un nombre basado en X Sistema de ventanas. Sistema de ventana Y buscado en Google: lo es. Como resultado, llamó a Z Window System 0.1, que se incluye en OS365 pre-alpha 0.1. Y sí, nadie lo vio excepto yo. Luego descubrí cómo implementar la compatibilidad con el teclado. Captura de pantalla de la primera versión, cuando no había nada, ni siquiera un sistema de ventanas:

Ni siquiera movió el cursor de texto, como puede ver. Luego escribí un par aplicaciones simples basado en Z. Y aquí está la versión 1.0.0 alfa. Había muchas cosas, incluso el menú del sistema. A administrador de archivos y la calculadora simplemente no funcionó.

Fui aterrorizado directamente por una amiga que solo se preocupa por la belleza (Mitrofan, lo siento). Dijo: “¡Lavado el modo VBE 1024 * 768 * 32, lavado, lavado! ¡Pues se emborracharon! Bueno, ya estaba cansada de escucharlo y aún así lo lavé. Más sobre la implementación a continuación.

Hice todo con mi cargador de arranque, a saber, GRUB "ohm. Con él, puede configurar el modo de gráficos sin complicaciones agregando algunas líneas mágicas al encabezado Multiboot.

Establecer ALINEAR, 1<<0 .set MEMINFO, 1<<1 .set GRAPH, 1<<2 .set FLAGS, ALIGN | MEMINFO | GRAPH .set MAGIC, 0x1BADB002 .set CHECKSUM, -(MAGIC + FLAGS) .align 4 .long MAGIC .long FLAGS .long CHECKSUM .long 0, 0, 0, 0, 0 .long 0 # 0 = set graphics mode .long 1024, 768, 32 # Width, height, depth
Y luego tomo la dirección del búfer de fotogramas y la resolución de pantalla de la estructura de información Multiboot y escribo píxeles allí. VESA hizo todo muy confuso: los colores RGB deben ingresarse en orden inverso (no R G B, sino B G R). No entendí durante varios días: ¿por qué no se muestran los píxeles? Al final, me di cuenta que olvidé cambiar los valores de 16 constantes de color de 0...15 a sus equivalentes RGB. Como resultado, lo liberé, al mismo tiempo que lavé el fondo degradado. Luego hice una consola, 2 aplicaciones y lancé 1.2. Ah, sí, casi lo olvido: puedes descargar el sistema operativo en

Original: AsmSchool: Hacer un sistema operativo
Autor: Mike Saunders
Fecha de publicación: 15 de abril de 2016
Traducción: A. Panin
Fecha de transferencia: 16 de abril de 2016

Parte 4: Con las habilidades que ha aprendido de los artículos anteriores de la serie, ¡puede comenzar a desarrollar su propio sistema operativo!

¿Para qué sirve?

  • Entender cómo funcionan los compiladores.
  • Comprender las instrucciones de la unidad central de procesamiento.
  • Para optimizar su código en términos de rendimiento.

En el transcurso de unos pocos meses, hemos recorrido un largo camino, que comenzó con el desarrollo de programas sencillos en lenguaje ensamblador para Linux y terminó en el último artículo de la serie con el desarrollo de código autónomo que se ejecuta en una computadora personal. sin un sistema operativo. Bueno, ahora intentaremos recopilar toda la información y crear un sistema operativo real. Sí, seguiremos los pasos de Linus Torvalds, pero antes conviene responder a las siguientes preguntas: "¿Qué es un sistema operativo? ¿Cuáles de sus funciones tendremos que recrear?".

En este artículo, nos centraremos solo en las funciones principales del sistema operativo: cargar y ejecutar programas. Los sistemas operativos complejos realizan muchas más funciones, como administrar la memoria virtual y procesar paquetes de red, pero requieren años de trabajo continuo para implementarse correctamente, por lo que en este artículo solo consideraremos las funciones principales que están presentes en cualquier sistema operativo. El mes pasado desarrollamos un pequeño programa que cabe en un sector de 512 bytes de un disquete (su primer sector), y ahora lo modificaremos un poco para agregar la función de cargar datos adicionales desde el disco.

Desarrollo del cargador de arranque

Podríamos tratar de mantener el binario de nuestro sistema operativo lo más pequeño posible para ubicarlo en el primer sector de 512 bytes del disquete, el que carga la BIOS, pero en este caso no podremos implementar cualquier función interesante. Por lo tanto, usaremos estos 512 bytes para colocar el código binario de un cargador de sistema simple que cargará el código binario del kernel del sistema operativo en la RAM y lo ejecutará. (Después de eso, desarrollaremos el propio kernel del sistema operativo, que cargará el código binario de otros programas desde el disco y también lo ejecutará, pero hablaremos de esto un poco más adelante).

Puede descargar el código fuente de los ejemplos discutidos en este artículo en www.linuxvoice.com/code/lv015/asmschool.zip. Y este es el código de nuestro gestor de arranque de un archivo llamado boot.asm:

BITS 16 jmp inicio corto; Saltar a la etiqueta omitiendo la descripción del disco nop; Adición antes de la descripción del disco %incluye "bpb.asm" start: mov ax, 07C0h ; Cargar dirección mov ds, ax ; Eje de movimiento del segmento de datos, 9000h; Preparar stack mov ss, ax mov sp, 0FFFFh ; ¡La pila está creciendo hacia abajo! cld; Establecer indicador de dirección mov si, kern_filename call load_file jmp 2000h:0000h ; Saltar al binario del núcleo del SO cargado desde el archivo kern_filename db "MYKERNELBIN" %include "disk.asm" veces 510-($-$$) db 0 ; Relleno con ceros de código binario hasta 510 bytes dw 0AA55h ; Búfer de marca final del código binario del cargador de arranque: ; Búfer de inicio para el contenido del disco

En este código, la primera instrucción de la CPU es la instrucción jmp, que se encuentra después de la directiva BITS, que le dice al ensamblador NASM que se está utilizando el modo de 16 bits. Como probablemente recordará del artículo anterior de la serie, la ejecución del código binario de 512 bytes cargado por el BIOS desde el disco comienza desde el principio, pero tenemos que saltar a la etiqueta para omitir el conjunto de datos especial. Obviamente, el mes pasado simplemente escribimos el código al principio del disco (usando la utilidad dd) y dejamos el resto del espacio del disco vacío.

Ahora tendremos que usar un disquete con un sistema de archivos MS-DOS (FAT12) adecuado, y para que funcione correctamente con este sistema de archivos, debemos agregar un conjunto de datos especiales cerca del comienzo del sector. Este conjunto se denomina "Bloque de parámetros del BIOS" (BPB) y contiene datos como la etiqueta del disco, el número de sectores, etc. No debería interesarnos en esta etapa, ya que dichos temas pueden estar dedicados a más de una serie de artículos, por lo que colocamos todas las instrucciones y datos relacionados en un archivo de código fuente separado llamado bpb.asm.

Basado en lo anterior, esta directiva de nuestro código es extremadamente importante:

%incluye "bpb.asm"

Esta es una directiva NASM que permite que el contenido del archivo fuente especificado se incluya en el archivo fuente actual durante el ensamblaje. Por lo tanto, podremos hacer que el código de nuestro cargador del sistema sea lo más breve y comprensible posible moviendo todos los detalles de la implementación del bloque de parámetros del BIOS en un archivo separado. El bloque de parámetros del BIOS debe ubicarse tres bytes después del inicio del sector, y dado que la instrucción jmp solo ocupa dos bytes, debemos usar la instrucción nop (su nombre significa "sin operación"; esta es una instrucción que no hace nada pero desperdicia ciclos de CPU) para llenar el byte restante.

Trabajando con la pila

A continuación, tendremos que usar instrucciones similares a las discutidas en el artículo anterior para preparar los registros y la pila, así como la instrucción cld (significa "dirección clara"), que le permite establecer la bandera de dirección para ciertas instrucciones, como la instrucción lodsb, que, después de la ejecución, incrementará el valor en el registro SI en lugar de disminuirlo.

Después de eso, colocamos la dirección de la cadena en el registro SI y llamamos a nuestra función load_file. Pero piense por un momento: ¡aún no hemos desarrollado esta función! Sí, eso es cierto, pero su implementación se puede encontrar en otro archivo de código fuente que incluimos llamado disk.asm.

El sistema de archivos FAT12, utilizado en disquetes formateados en MS-DOS, es uno de los sistemas de archivos más simples que existen, pero también requiere una buena cantidad de código para trabajar con su contenido. La subrutina load_file tiene aproximadamente 200 líneas y no se mostrará en este artículo, ya que estamos considerando el desarrollo de un sistema operativo, no un controlador para un sistema de archivos específico, por lo tanto, no es muy inteligente desperdiciar espacio en las páginas de registro. De este modo. En general, incluimos el archivo de código fuente disk.asm casi antes del final del archivo fuente actual y podemos olvidarnos de él. (Si aún está interesado en la estructura del sistema de archivos FAT12, puede leer la excelente descripción general en http://tinyurl.com/fat12spec y luego buscar en el archivo de código fuente disk.asm; el código que contiene es bien comentado)

En cualquier caso, la subrutina cargar_archivo carga el código binario del archivo con el nombre dado en el registro SI en el segmento 2000 con desplazamiento 0, después de lo cual saltamos a su inicio para su ejecución. Y eso es todo: ¡el kernel del sistema operativo está cargado y el cargador del sistema ha completado su tarea!

Es posible que haya notado que nuestro código usa MYKERNELBIN en lugar de MYKERNEL.BIN como el nombre de archivo del núcleo del sistema operativo, lo que encaja bien con el esquema de nombres 8+3 que se usa en los disquetes en DOS. De hecho, el sistema de archivos FAT12 usa una representación interna de los nombres de archivo, y ahorramos espacio usando un nombre de archivo que garantiza que no requerirá que nuestra subrutina load_file implemente un mecanismo para buscar un carácter de punto y convertir el nombre de archivo a la representación interna del sistema de archivos

Después de la línea con la directiva para conectar el archivo de código fuente disk.asm, hay dos líneas diseñadas para rellenar el código binario del cargador del sistema con ceros hasta 512 bytes e incluir la marca final de su código binario (esto se discutió en el último artículo). Finalmente, al final del código está la etiqueta "búfer", que es utilizada por la subrutina load_file. En general, la subrutina load_file necesita espacio libre en la RAM para realizar algunas acciones intermedias en el proceso de encontrar un archivo en el disco, y tenemos suficiente espacio libre después de cargar el gestor de arranque, por lo que colocamos el búfer aquí.

Para ensamblar el cargador de arranque, use el siguiente comando:

nasm -f bin -o arranque.bin arranque.asm

Ahora necesitamos crear una imagen de disquete virtual de MS-DOS y agregar nuestro binario de cargador de arranque a sus primeros 512 bytes usando los siguientes comandos:

Mkdosfs -C disquete.img 1440 dd conv=notrunc if=boot.bin of=disquete.img

¡Esto completa el proceso de desarrollo del gestor de arranque! Ahora tenemos una imagen de disquete de arranque que nos permite cargar el binario del kernel del sistema operativo desde un archivo llamado mykernel.bin y ejecutarlo. A continuación, estamos esperando una parte más interesante del trabajo: el desarrollo del kernel del sistema operativo en sí.

núcleo del sistema operativo

Queremos que el núcleo de nuestro sistema operativo realice muchas tareas importantes: mostrar un saludo, aceptar la entrada del usuario, determinar si la entrada es un comando compatible y ejecutar programas desde el disco después de que el usuario especifique sus nombres. Este es el código del núcleo del sistema operativo del archivo mykernel.asm:

Mov ax, 2000h mov ds, ax mov es, ax loop: mov si, prompt call lib_print_string mov si, user_input call lib_input_string cmp byte, 0 je loop cmp word, "ls" je list_files mov ax, si mov cx, 32768 call lib_load_file jc load_fail call 32768 jmp loop load_fail: mov si, load_fail_msg call lib_print_string jmp loop list_files: mov si, file_list call lib_get_file_list call lib_print_string jmp loop prompt db 13, 10, "MyOS > ", 0 load_fail_msg db 13, 10, "¡No encontrado! ", 0 entrada de usuario por 256 db 0 lista de archivos por 1024 db 0 % incluye "lib.asm"

Antes de mirar el código, preste atención a la última línea con la directiva para incluir el archivo de código fuente lib.asm, que también se encuentra en el archivo asmschool.zip de nuestro sitio web. Esta es una biblioteca de subrutinas útiles para trabajar con la pantalla, el teclado, las líneas y los discos que también puede usar; en este caso, incluimos este archivo de código fuente al final del archivo de código fuente principal del kernel del sistema operativo para poder para que este último sea lo más compacto y bonito posible. Consulte la sección "Rutinas de la biblioteca lib.asm" para obtener más información sobre todas las rutinas disponibles.

En las tres primeras líneas del código del kernel del sistema operativo, llenamos los registros de segmento con datos para señalar el segmento 2000 en el que se cargó el código binario. Esto es importante para garantizar que instrucciones como lodsb , que deben leer datos del segmento actual y no de ningún otro, funcionen correctamente. Después de eso, no realizaremos ninguna operación adicional en los segmentos; ¡Nuestro sistema operativo funcionará con 64 KB de RAM!

Más adelante en el código hay una etiqueta correspondiente al comienzo del ciclo. En primer lugar, usamos una de las rutinas de la biblioteca lib.asm, a saber, lib_print_string, para imprimir el saludo. Los bytes 13 y 10 antes de la línea de saludo son caracteres de nueva línea, por lo que el saludo no se mostrará inmediatamente después de la salida de ningún programa, sino siempre en una nueva línea.

Después de eso, usamos otra rutina de la biblioteca lib.asm llamada lib_input_string, que toma los caracteres ingresados ​​por el usuario usando el teclado y los almacena en un búfer, cuyo puntero está en el registro SI. En nuestro caso, el búfer se declara hacia el final del código del kernel del sistema operativo de la siguiente manera:

Entrada de usuario veces 256 db 0

¡Esta declaración permite un búfer de 256 caracteres lleno de ceros, que debería ser lo suficientemente largo para contener comandos para un sistema operativo simple como el nuestro!

A continuación, realizamos la validación de entrada del usuario. Si el primer byte del búfer de entrada de usuario es nulo, entonces el usuario simplemente presionó la tecla Intro sin ingresar ningún comando; no olvide que todas las cadenas terminan con caracteres nulos. Entonces, en este caso, deberíamos saltar al comienzo del ciclo e imprimir el saludo nuevamente. Sin embargo, si el usuario ingresa algún comando, primero tendremos que verificar si ingresó el comando ls. Hasta ahora, solo ha visto comparaciones de bytes individuales en nuestros programas de lenguaje ensamblador, pero no olvide que también es posible comparar valores de dos bytes o palabras de máquina. En este código, comparamos la primera palabra de máquina del búfer de entrada de usuario con la palabra de máquina correspondiente a la línea ls, y si son idénticas, pasamos al bloque de código a continuación. Dentro de este bloque de código, usamos otra rutina de la biblioteca lib.asm para obtener una lista de archivos separados por comas en el disco (que debe almacenarse en el búfer file_list), imprimir esa lista en la pantalla y volver al proceso entrada del usuario.

Ejecución de programas de terceros

Si el usuario no ingresa el comando ls, asumimos que ingresó el nombre del programa desde el disco, por lo que tiene sentido intentar cargarlo. Nuestra biblioteca lib.asm contiene una implementación de una subrutina útil lib_load_file , que analiza las tablas del sistema de archivos FAT12 de un disco: toma un puntero al comienzo de una línea con un nombre de archivo usando el registro AX, así como un valor de compensación para cargar un código binario desde un archivo de programa utilizando el registro CX. Ya estamos usando el registro SI para almacenar un puntero a la cadena de entrada del usuario, así que copiamos ese puntero al registro AX y luego ponemos el valor 32768, que se usa como compensación para cargar el binario desde el archivo del programa, en el registro CX.

Pero, ¿por qué usamos este valor como compensación para cargar código binario desde un archivo de programa? Pues es solo una de las opciones de mapa de memoria de nuestro sistema operativo. Debido a que estamos trabajando en un solo segmento de 64 KB y nuestro kernel binario se carga en el desplazamiento 0, tenemos que usar los primeros 32 KB de memoria para los datos del kernel y los 32 KB restantes para los datos del programa cargable. Por lo tanto, el desplazamiento 32768 es el medio de nuestro segmento y nos permite proporcionar una cantidad suficiente de RAM tanto para el kernel del sistema operativo como para los programas cargados.

Después de eso, la rutina lib_load_file realiza una operación muy importante: si no puede encontrar un archivo con el nombre dado en el disco, o por alguna razón no puede leerlo desde el disco, simplemente sale y establece una bandera de acarreo especial. Este es un indicador de estado de la CPU que se establece durante algunas operaciones matemáticas y no debería ser de nuestro interés en este momento, pero podemos determinar la presencia de este indicador para tomar decisiones rápidas. Si la subrutina lib_load_asm establece la bandera de acarreo, usamos la instrucción jc (saltar si acarrea) para saltar al bloque de código que imprime el mensaje de error y regresa al inicio del ciclo de entrada del usuario.

En el mismo caso, si el indicador de transferencia no está configurado, podemos concluir que la subrutina lib_load_asm cargó con éxito el código binario del archivo de programa en la RAM en la dirección 32768. Todo lo que necesitamos en este caso es iniciar la ejecución del código binario. cargado en esta dirección, es decir, ¡comienza a ejecutar el programa especificado por el usuario! Y después de usar la instrucción ret en este programa (para volver al código de llamada), solo tendremos que volver al ciclo de entrada del usuario. Por lo tanto, hemos creado un sistema operativo: consta de los mecanismos más simples para analizar comandos y cargar programas, implementado dentro de unas 40 líneas de código ensamblador, aunque con mucha ayuda de las subrutinas de la biblioteca lib.asm.

Para ensamblar el código del kernel del sistema operativo, use el siguiente comando:

Nasm -f bin -o mikernel.bin mikernel.asm

Después de eso, tendremos que agregar de alguna manera el archivo mykernel.bin al archivo de imagen del disquete. Si está familiarizado con el truco de montar imágenes de disco con dispositivos de bucle invertido, puede acceder al contenido de la imagen de disco floppy.img usándola, pero hay una manera más fácil, que es usar GNU Mtools (www.gnu.org /software /mtools). Este es un conjunto de programas de disquetes que usan sistemas de archivos MS-DOS/FAT12, disponibles en los repositorios de paquetes de software de todas las distribuciones populares de Linux, por lo que solo necesita usar apt-get , yum , pacman o cualquier otra utilidad que solía instalar paquetes de software en su distribución.

Después de instalar el paquete de software adecuado, para agregar el archivo mykernel.bin al archivo de imagen de disco floppy.img, deberá ejecutar el siguiente comando:

Mcopy -i disquete.img mikernel.bin::/

Observe los caracteres divertidos al final del comando: dos puntos, dos puntos y barra oblicua. Ahora estamos casi listos para lanzar nuestro sistema operativo, pero ¿de qué sirve hasta que haya aplicaciones para él? Corrijamos este malentendido desarrollando una aplicación extremadamente simple. Sí, ahora desarrollará una aplicación para su propio sistema operativo; imagínese cuánto aumentará su autoridad en las filas de los geeks. Guarde el siguiente código en un archivo llamado test.asm:

Org 32768 mov ah, 0Eh mov al, "X" int 10h ret

Este código simplemente usa la función BIOS para mostrar el carácter "X" en la pantalla, luego de lo cual devuelve el control al código que lo llamó; en nuestro caso, este código es el código del sistema operativo. La línea de organización que inicia el código fuente de la aplicación no es una instrucción de la CPU, sino una directiva del ensamblador NASM que le indica que el código binario se cargará en la RAM en el desplazamiento 32768, por lo tanto, es necesario volver a calcular todos los desplazamientos teniendo en cuenta esta circunstancia. .

Este código también debe ensamblarse y el archivo binario resultante debe agregarse al archivo de imagen del disquete:

Nasm -f bin -o prueba.bin prueba.asm mcopy -i floppy.img prueba.bin::/

Ahora, respire hondo, prepárese para presenciar los resultados insuperables de su propio trabajo y arranque la imagen del disquete con un emulador de PC como Qemu o VirtualBox. Por ejemplo, el siguiente comando se puede utilizar para este propósito:

Qemu-system-i386 -fda disquete.img

Listo: el cargador de arranque boot.img que integramos en el primer sector de la imagen del disco carga el kernel del sistema operativo mykernel.bin, que muestra un saludo. Escriba el comando ls para obtener los nombres de los dos archivos en el disco (mykernel.bin y test.bin), luego escriba el nombre del último archivo para ejecutar y mostrar el carácter X en la pantalla.

Es genial, ¿no? Ahora puede comenzar a personalizar el shell de su sistema operativo, agregar implementaciones de nuevos comandos y agregar archivos de programa adicionales al disco. Si desea ejecutar este sistema operativo en una PC real, debe consultar la sección "Ejecutar el cargador de arranque en una plataforma de hardware real" del artículo anterior de la serie; necesitará exactamente los mismos comandos. El próximo mes haremos que nuestro sistema operativo sea más poderoso al permitir que los programas descargables usen las funciones del sistema, implementando así el concepto de separación de código para reducir la duplicación de código. Gran parte del trabajo aún está por delante.

rutinas de la biblioteca lib.asm

Como se mencionó anteriormente, la biblioteca lib.asm proporciona un gran conjunto de subrutinas útiles para usar dentro de los núcleos de su sistema operativo y programas individuales. Algunos de ellos utilizan instrucciones y conceptos que aún no se han tratado en los artículos de esta serie, otros (como las rutinas para trabajar con discos) están estrechamente relacionados con la estructura de los sistemas de archivos, pero si te consideras competente en estos temas, puede familiarizarse con sus implementaciones y comprender el principio de trabajo. Sin embargo, es más importante entender cómo llamarlos desde su propio código:

  • lib_print_string: lleva un puntero a una cadena terminada en nulo a través del registro SI e imprime esa cadena en la pantalla.
  • lib_input_string: lleva un puntero a un búfer a través del registro SI y llena este búfer con caracteres ingresados ​​por el usuario usando el teclado. Después de que el usuario presiona la tecla Intro, la cadena en el búfer termina en nulo y el control vuelve al código del programa de llamada.
  • lib_move_cursor: mueve el cursor en la pantalla a la posición con las coordenadas pasadas a través de los registros DH (número de fila) y DL (número de columna).
  • lib_get_cursor_pos: llame a esta subrutina para obtener los números de fila y columna actuales utilizando los registros DH y DL, respectivamente.
  • lib_string_uppercase: lleva un puntero al comienzo de una cadena terminada en nulo mediante el registro AX y convierte los caracteres de la cadena a mayúsculas.
  • lib_string_length: toma un puntero al comienzo de una cadena terminada en nulo usando el registro AX y devuelve su longitud usando el registro AX.
  • lib_string_compare: toma punteros al comienzo de dos cadenas terminadas en nulo a través de los registros SI y DI y compara esas cadenas. Establece la bandera de acarreo si las cadenas son idénticas (para usar una instrucción de salto dependiendo de la bandera de acarreo jc) o borra esta bandera si las cadenas son diferentes (para usar la instrucción jnc).
  • lib_get_file_list: lleva un puntero al inicio de un búfer a través del registro SI y coloca una cadena terminada en nulo que contiene una lista de nombres de archivos del disco separados por comas en ese búfer.
  • lib_load_file: toma un puntero al comienzo de una cadena que contiene un nombre de archivo mediante el registro AX y carga el contenido del archivo en el desplazamiento proporcionado por el registro CX. Devuelve el número de bytes copiados en la memoria (es decir, el tamaño del archivo) usando el registro BX, o establece la bandera de acarreo si no se encuentra ningún archivo con el nombre dado.

Esta serie de artículos está dedicada a la programación de bajo nivel, es decir, arquitectura de computadoras, diseño de sistemas operativos, programación en lenguaje ensamblador y áreas relacionadas. Hasta ahora, dos habrausers se dedican a escribir, y. Para muchos estudiantes de secundaria, estudiantes y programadores profesionales, estos temas resultan muy difíciles de aprender. Hay mucha literatura y cursos dedicados a la programación de bajo nivel, pero es difícil obtener una imagen completa y completa de ellos. Es difícil, después de leer uno o dos libros sobre ensambladores y sistemas operativos, imaginar al menos en términos generales cómo funciona realmente este complejo sistema de hierro, silicio y muchos programas, una computadora.

Cada uno resuelve el problema del aprendizaje a su manera. Alguien lee mucha literatura, alguien trata de pasar rápidamente a la práctica y comprender en el camino, alguien trata de explicar a sus amigos todo lo que está estudiando. Y decidimos combinar estos enfoques. Entonces, en este curso de artículos, demostraremos paso a paso cómo escribir un sistema operativo simple. Los artículos tendrán un carácter general, es decir, no contendrán información teórica exhaustiva, sin embargo, siempre intentaremos proporcionar enlaces a buenos materiales teóricos y responder a todas las preguntas que surjan. No tenemos un plan claro, por lo que se tomarán muchas decisiones importantes en el camino, teniendo en cuenta sus comentarios.

Podemos llevar deliberadamente el proceso de desarrollo a un callejón sin salida para permitirle a usted y a nosotros mismos comprender completamente las consecuencias de una decisión equivocada, así como perfeccionar algunas habilidades técnicas al respecto. Así que no tomes nuestras decisiones como las únicas verdaderas y créanos ciegamente. Hacemos hincapié una vez más en que esperamos que los lectores participen activamente en la discusión de los artículos, lo que debería influir en gran medida en el proceso general de desarrollo y redacción de artículos posteriores. Idealmente, me gustaría que algunos de los lectores se unan al desarrollo del sistema a lo largo del tiempo.

Asumiremos que el lector ya está familiarizado con los conceptos básicos de los lenguajes ensamblador y C, así como con los conceptos elementales de la arquitectura de la computadora. Es decir, no explicaremos qué es un registro o, digamos, RAM. Si le falta conocimiento, siempre puede recurrir a literatura adicional. Una breve lista de referencias y enlaces a sitios con buenos artículos se encuentran al final del artículo. También es deseable poder usar Linux, ya que se darán todas las instrucciones de compilación para este sistema.

Y ahora, más cerca del punto. En el resto del artículo, tú y yo escribiremos el clásico programa "Hello World". Nuestro mundo de Halloween va a ser un poco específico. No se ejecutará desde ningún sistema operativo, sino directamente, por así decirlo, "on bare metal". Antes de proceder directamente a escribir el código, averigüemos exactamente cómo estamos tratando de hacer esto. Y para esto, debe considerar el proceso de arranque de la computadora.

Entonces, tome su computadora favorita y presione el botón más grande en la unidad del sistema. Vemos un protector de pantalla alegre, la unidad del sistema chirría alegremente con un altavoz y, después de un tiempo, se carga el sistema operativo. Como comprenderá, el sistema operativo se almacena en el disco duro, y aquí surge la pregunta: ¿cómo se inició mágicamente el sistema operativo en la RAM y comenzó a ejecutarse?

Sepa esto: el sistema que está en cualquier computadora es responsable de esto, y su nombre no es Windows, muerde la lengua, se llama BIOS. Su nombre significa Sistema Básico de Entrada-Salida, es decir, el sistema básico de entrada-salida. El BIOS está ubicado en un pequeño microcircuito en la placa base y se inicia inmediatamente después de presionar el botón grande de ENCENDIDO. El BIOS tiene tres tareas principales:

  1. Detecta todos los dispositivos conectados (procesador, teclado, monitor, RAM, tarjeta de video, cabeza, brazos, alas, piernas y colas...) y prueba su funcionalidad. El programa POST (Power On Self Test) es responsable de esto. Si no se encuentra hardware vital, entonces ningún software puede ayudar, y en este punto el altavoz del sistema chirriará algo siniestro y el sistema operativo no llegará en absoluto. No hablemos de cosas tristes, supongamos que tenemos una computadora en pleno funcionamiento, regocijémonos y pasemos a considerar la segunda función del BIOS:
  2. Proporcionar al sistema operativo un conjunto básico de funciones para trabajar con hierro. Por ejemplo, a través de las funciones del BIOS, puede mostrar texto en la pantalla o leer datos del teclado. Por eso se le llama sistema básico de entrada-salida. Normalmente, el sistema operativo accede a estas funciones a través de interrupciones.
  3. Iniciando el cargador del sistema operativo. En este caso, por regla general, se lee el sector de arranque, el primer sector del soporte de información (disquete, disco duro, CD, unidad flash). El orden de sondeo de los medios se puede configurar en la CONFIGURACIÓN DEL BIOS. El sector de arranque contiene un programa que a veces se denomina gestor de arranque principal. En términos generales, la tarea del gestor de arranque es iniciar el sistema operativo. El proceso de carga de un sistema operativo puede ser muy específico y depende en gran medida de sus características. Por lo tanto, el cargador principal lo escriben directamente los desarrolladores del sistema operativo y se escribe en el sector de arranque durante la instalación. Cuando se inicia el gestor de arranque, el procesador está en modo real.
La triste noticia: el tamaño del cargador de arranque debería ser de solo 512 bytes. ¿Por qué tan pocos? Para hacer esto, debemos familiarizarnos con el dispositivo del disquete. Aquí hay una imagen educativa:

La imagen muestra la superficie de la unidad de disco. Un disquete tiene 2 superficies. En cada superficie hay pistas en forma de anillo (pistas). Cada pista se divide en pequeñas piezas en forma de arco llamadas sectores. Entonces, históricamente, un sector de disquete tiene un tamaño de 512 bytes. El BIOS lee el primer sector del disco, el sector de arranque, "en el segmento de memoria cero en el desplazamiento 0x7C00, y luego el control se transfiere a esta dirección. El cargador de arranque generalmente carga en la memoria no el sistema operativo en sí, sino otro programa de carga almacenado en el disco, pero por alguna razón (probablemente, esta razón es el tamaño) que no encaja en un sector. Y dado que hasta ahora el papel de nuestro sistema operativo lo desempeña un helloworld banal, nuestro objetivo principal es hacer que la computadora crea en la existencia de nuestro sistema operativo, incluso si está en un sector, y ejecutarlo.

¿Cómo está organizado el sector del maletero? En una PC, el único requisito para un sector de arranque es que sus dos últimos bytes contengan los valores 0x55 y 0xAA, la firma del sector de arranque. Entonces, ya está más o menos claro lo que tenemos que hacer. ¡Escribamos código! El código anterior está escrito para el ensamblador de yasm.

Sección.texto use16 org 0x7C00 ; nuestro programa se carga en 0x7C00 start: mov ax, cs mov ds, ax ; seleccionar segmento de datos mov si, mensaje cld ; dirección para comandos de cadena mov ah, 0x0E ; Número de función del BIOS mov bh, 0x00 ; página de memoria de vídeo puts_loop: lodsb ; carga el siguiente caracter en al test al, al ; carácter nulo significa final de línea jz puts_loop_exit int 0x10 ; llame a la función BIOS jmp puts_loop puts_loop_exit: jmp $ ; mensaje de bucle eterno: db "¡Hola mundo!", 0 fin: tiempos 0x1FE-finalizar+inicio db 0 db 0x55, 0xAA; firma del sector de arranque

Este breve programa requiere una serie de explicaciones importantes. La línea org 0x7C00 es necesaria para que el ensamblador (es decir, el programa, no el lenguaje) calcule correctamente las direcciones de las etiquetas y variables (puts_loop, puts_loop_exit, mensaje). Así que le decimos que el programa se cargará en memoria a las 0x7C00.
En lineas
mov hacha, cs mov ds, hacha
el segmento de datos (ds) se iguala al segmento de código (cs), ya que en nuestro programa tanto los datos como el código se almacenan en el mismo segmento.

Luego, el mensaje "¡Hola mundo!" se muestra carácter por carácter en el bucle. Para ello se utiliza la función 0x0E de la interrupción 0x10. Tiene las siguientes opciones:
AH = 0x0E (número de función)
BH = número de página del video (todavía no se moleste, indique 0)
AL = código de caracteres ASCII

En la línea "jmp $" el programa se cuelga. Y con razón, no hay necesidad de que ella ejecute código extra. Sin embargo, para que la computadora vuelva a funcionar, deberá reiniciar.

En la línea " times 0x1FE-finish+start db 0 ", el resto del código del programa (excepto los dos últimos bytes) se rellena con ceros. Esto se hace para que después de la compilación, la firma del sector de arranque aparezca en los dos últimos bytes del programa.

De alguna manera descubrimos el código del programa, ahora intentemos compilar esta felicidad. Para compilar, necesitamos, de hecho, un ensamblador: el yasm mencionado anteriormente. Está disponible en la mayoría de los repositorios de Linux. El programa se puede compilar así:

$ yasm -f bin -o hola.bin hola.asm

El archivo hello.bin resultante debe escribirse en el sector de arranque del disquete. Esto se hace de esta manera (por supuesto, en lugar de fd, debe sustituir el nombre de su unidad).

$ dd if=hola.bin of=/dev/fd

Dado que no todos tienen unidades de disco y disquetes, puede usar una máquina virtual, por ejemplo, qemu o VirtualBox. Para hacer esto, debe crear una imagen de disquete con nuestro gestor de arranque e insertarla en la "unidad de disco virtual".
Cree una imagen de disco y rellénela con ceros:

$ dd if=/dev/cero of=disco.img bs=1024 cuenta=1440

Escribimos nuestro programa al principio de la imagen:
$ dd if=hola.bin of=disk.img conv=notrunc

Ejecute la imagen resultante en qemu:
$ qemu -fda disk.img -boot a

Después del lanzamiento, debería ver una ventana qemu con una línea alegre "¡Hola mundo!". Aquí es donde termina el primer artículo. Estaremos encantados de ver sus comentarios y deseos.

Lo que necesitas saber para escribir un sistema operativo

Crear un sistema operativo es una de las tareas más difíciles en la programación, ya que requiere un conocimiento extenso y complejo sobre el funcionamiento de una computadora. ¿Cuáles? Entendemos a continuación.

¿Qué es el sistema operativo?

El sistema operativo (SO) es el software que funciona con el hardware de la computadora y sus recursos y es el puente entre el hardware y el software de la computadora.

Las computadoras de primera generación no tenían sistemas operativos. Los programas de las primeras computadoras incluían código para la operación directa del sistema, comunicación con dispositivos periféricos y cálculos, para lo cual se escribió este programa. Debido a esta alineación, incluso los programas que eran simples en lógica eran difíciles de implementar en el software.

A medida que las computadoras se han vuelto más diversas y complejas, escribir programas que funcionen tanto como un sistema operativo como una aplicación se ha vuelto simplemente inconveniente. Por lo tanto, para que los programas fueran más fáciles de escribir, los propietarios de computadoras comenzaron a desarrollar software. Así es como surgieron los sistemas operativos.

El sistema operativo proporciona todo lo necesario para el funcionamiento de los programas de usuario. Su aparición significó que ahora los programas no necesitan controlar la cantidad total de trabajo en la computadora (este es un gran ejemplo de encapsulación). Ahora los programas necesitaban trabajar con el sistema operativo, y el propio sistema se ocupaba de los recursos y trabajaba con los periféricos (teclado, impresora).

Brevemente sobre la historia de los sistemas operativos.

lenguaje C

Como se mencionó anteriormente, existen varios lenguajes de programación de alto nivel para escribir un sistema operativo. Sin embargo, el más popular de ellos es C.

Puedes empezar a aprender este idioma desde aquí. Este recurso lo introducirá a los conceptos básicos y lo preparará para tareas más avanzadas.

Learn C the Hard Way es el título de otro libro. Además de la teoría habitual, contiene muchas soluciones prácticas. Este tutorial cubrirá todos los aspectos del lenguaje.

O puedes elegir uno de estos libros:

  • "El lenguaje de programación C" de Kernighan y Ritchie;
  • "Guía absoluta para principiantes de programación en C" de Parry y Miller.

desarrollo del sistema operativo

Una vez que haya dominado todo lo que necesita saber sobre informática, lenguaje ensamblador y C, debe leer al menos uno o dos libros sobre el desarrollo directo del sistema operativo. Aquí hay algunos recursos para eso:

Linux desde cero. Aquí se considera el proceso de ensamblaje del sistema operativo Linux (el tutorial se ha traducido a muchos idiomas, incluido el ruso). Aquí, como en otros libros de texto, se le proporcionarán todos los conocimientos básicos necesarios. Confiando en ellos, puede intentar crear un sistema operativo. Para que el software sea parte del sistema operativo más profesional, hay adiciones al tutorial: "

Si nota un error, seleccione un fragmento de texto y presione Ctrl + Enter
COMPARTIR: