stringtranslate.com

Lenguaje de programación de bajo nivel

Un lenguaje de programación de bajo nivel es un lenguaje de programación que proporciona poca o ninguna abstracción de la arquitectura del conjunto de instrucciones de una computadora ; los comandos o funciones en el lenguaje son estructuralmente similares a las instrucciones de un procesador. Generalmente, esto se refiere al código de máquina o al lenguaje ensamblador . Debido a la baja abstracción (de ahí la palabra) entre el lenguaje y el lenguaje de máquina, los lenguajes de bajo nivel a veces se describen como "cercanos al hardware". Los programas escritos en lenguajes de bajo nivel tienden a ser relativamente no portables , debido a que están optimizados para un cierto tipo de arquitectura de sistema. [1] [2] [3] [4]

Los lenguajes de bajo nivel pueden convertirse en código máquina sin un compilador o intérprete ( los lenguajes de programación de segunda generación [5] [6] utilizan un procesador más simple llamado ensamblador ) y el código resultante se ejecuta directamente en el procesador. Un programa escrito en un lenguaje de bajo nivel puede ejecutarse muy rápidamente, con una pequeña huella de memoria . Un programa equivalente en un lenguaje de alto nivel puede ser menos eficiente y utilizar más memoria. Los lenguajes de bajo nivel son simples, pero se consideran difíciles de usar, debido a numerosos detalles técnicos que el programador debe recordar. En comparación, un lenguaje de programación de alto nivel aísla la semántica de ejecución de una arquitectura de computadora de la especificación del programa, lo que simplifica el desarrollo. [1]

Código máquina

Panel frontal de una minicomputadora PDP-8/E. La fila de interruptores de la parte inferior se puede utilizar para activar un programa en lenguaje de máquina.

El código de máquina es la forma en que se almacena en una computadora el código que se puede ejecutar directamente. Consiste en instrucciones en lenguaje de máquina , almacenadas en la memoria, que realizan operaciones como mover valores dentro y fuera de las ubicaciones de la memoria, aritmética y lógica booleana, y probar valores y, en función de la prueba, ejecutar la siguiente instrucción en la memoria o ejecutar una instrucción en otra ubicación.

El código de máquina se almacena generalmente en la memoria como datos binarios . Los programadores casi nunca escriben programas directamente en código de máquina; en cambio, escriben el código en lenguaje ensamblador o en lenguajes de programación de nivel superior. [1]

Aunque pocos programas están escritos en lenguajes de máquina, los programadores a menudo se vuelven expertos en leerlos trabajando con volcados de memoria o depurándolos desde el panel frontal.

Ejemplo de una función en representación hexadecimal de código máquina x86-64 para calcular el n- ésimo número de Fibonacci , con cada línea correspondiente a una instrucción:

89f885 y siguientes74 2683 y siguientes 0276 1c89f9ba 01 00 00 00ser 01 00 00 008d 04 1683 f9 0274 0d89d6y siguientes c989c2en el f0b8 01 00 00c3

Lenguaje ensamblador

Los lenguajes de segunda generación proporcionan un nivel de abstracción sobre el código de máquina. En los primeros días de la codificación en computadoras como TX-0 y PDP-1 , lo primero que hicieron los hackers del MIT fue escribir ensambladores. [7] El lenguaje ensamblador tiene poca semántica o especificación formal, siendo solo un mapeo de símbolos legibles por humanos, incluyendo direcciones simbólicas, a códigos de operación , direcciones , constantes numéricas, cadenas , etc. Normalmente, una instrucción de máquina se representa como una línea de código ensamblador, comúnmente llamado mnemónicos. [8] Los ensambladores producen archivos de objeto que pueden vincularse con otros archivos de objeto o cargarse por sí mismos.

La mayoría de los ensambladores proporcionan macros para generar secuencias comunes de instrucciones.

Ejemplo: La misma calculadora de números de Fibonacci que la anterior, pero en lenguaje ensamblador x86-64 utilizando la sintaxis AT&T :

fib: movl %edi , %eax ; coloca el argumento en %eax testl %edi , %edi ; ¿es cero? je.return_from_fib ; sí: devuelve 0 , que ya está en %eax cmpl $2 , %edi ; ¿es 2 mayor o igual que él? jbe.return_1_from_fib ; sí (es decir, es 1 o 2): devuelve 1 movl %edi , %ecx ; no: póngalo en %ecx, para usarlo como contador movl $1 , %edx ; el número anterior en la secuencia, que comienza como 1 movl $1 , %esi ; el número anterior a ese, que también comienza como 1 .fib_loop: leal ( %rsi , %rdx ), %eax ; pone la suma de los dos números anteriores en %eax cmpl $2 , %ecx ; ¿el contador es 2? je .return_from_fib ; sí - %eax contiene el resultado movl %edx , %esi ; hace que el número anterior sea el número anterior al anterior decl %ecx ; decrementa el contador movl %eax , %edx ; hace que el número actual sea el número anterior jmp .fib_loop ; continúa .return_1_from_fib: movl $1 , %eax ; establece el valor de retorno en 1 .return_from_fib: ret ; retorna                                                             

En este ejemplo de código, los registros del procesador x86-64 se nombran y manipulan directamente. La función carga su argumento de 32 bits de %ediacuerdo con la interfaz binaria de aplicación System V para x86-64 y realiza su cálculo manipulando valores en los registros %eax, %ecx, %esiy %edihasta que termina y regresa. Tenga en cuenta que en este lenguaje ensamblador, no existe el concepto de devolver un valor. Una vez almacenado el resultado en el %eaxregistro, nuevamente de acuerdo con la interfaz binaria de aplicación System V, la retinstrucción simplemente elimina el elemento superior de 64 bits en la pila y hace que se obtenga la siguiente instrucción de esa ubicación (esa instrucción suele ser la instrucción inmediatamente posterior a la que llamó a esta función), y el resultado de la función se almacena en %eax. El lenguaje ensamblador x86-64 no impone ningún estándar para pasar valores a una función o devolver valores desde una función (y, de hecho, no tiene el concepto de función); estos se definen mediante una interfaz binaria de aplicación , como la ABI System V para un conjunto de instrucciones en particular.

Compare esto con la misma función en C :

entero sin signo fib ( entero sin signo n ) { si ( ! n ) { devuelve 0 ; } de lo contrario si ( n <= 2 ) { devuelve 1 ; } de lo contrario { entero sin signo f_nminus2 , f_nminus1 , f_n ; para ( f_nminus2 = f_nminus1 = 1 , f_n = 0 ; ; -- n ) { f_n = f_nminus2 + f_nminus1 ; si ( n <= 2 ) { devuelve f_n ; } f_nminus2 = f_nminus1 ; } } }                                                         

Este código es similar en estructura al ejemplo en lenguaje ensamblador, pero existen diferencias significativas en términos de abstracción:

Estas abstracciones hacen que el código C sea compilable sin modificaciones en cualquier arquitectura para la que se haya escrito un compilador de C. El código en lenguaje ensamblador x86 es específico de la arquitectura x86-64 y de la interfaz binaria de la aplicación System V para esa arquitectura.

Programación de bajo nivel en lenguajes de alto nivel

A finales de los años 1960 y 1970, se introdujeron lenguajes de alto nivel que incluían cierto grado de acceso a funciones de programación de bajo nivel, como PL/S , BLISS , BCPL , ALGOL extendido y NEWP (para sistemas grandes de Burroughs /sistemas MCP de Unisys Clearpath) y C. Un método para esto es el ensamblaje en línea , en el que el código ensamblador se incrusta en un lenguaje de alto nivel que admite esta característica. Algunos de estos lenguajes también permiten directivas de optimización del compilador dependientes de la arquitectura para ajustar la forma en que un compilador utiliza la arquitectura del procesador de destino.

Aunque un lenguaje como C es de alto nivel, no abstrae por completo la capacidad de gestionar la memoria como otros lenguajes. [9] En un lenguaje de alto nivel como Python, el programador no puede acceder directamente a la memoria debido a las abstracciones entre el intérprete y la máquina. Por lo tanto, C puede permitir un mayor control al exponer herramientas de gestión de memoria a través de herramientas como la asignación de memoria (malloc). [10]

Además, como se mencionó anteriormente, el siguiente bloque de C proviene del compilador GNU y muestra la capacidad de ensamblaje en línea de C. Según la documentación de GCC, este es un código simple de copia y adición. Este código muestra la interacción entre un lenguaje de alto nivel como C y su contraparte de nivel medio/bajo, el ensamblaje. Aunque esto puede no convertir a C en un lenguaje nativo de bajo nivel, estas funciones expresan las interacciones de una manera más directa. [11]

int origen = 1 ; int dst ;     asm ( "mov %1, %0 \n\t " "suma $1, %0" : "=r" ( dst ) : "r" ( src ));         printf ( "%d \n " , dst ); 

Referencias

  1. ^ abc «3.1: Estructura de programas de bajo nivel». Workforce LibreTexts . 2021-03-05 . Consultado el 2023-04-03 .
  2. ^ "¿Qué es un lenguaje de bajo nivel?". GeeksforGeeks . 2023-11-19 . Consultado el 2024-04-27 .
  3. ^ "¿Lenguaje de bajo nivel? Lo que necesita saber | Lenovo US". www.lenovo.com . Consultado el 27 de abril de 2024 .
  4. ^ "Lenguajes de bajo nivel - Clasificación de lenguajes de programación y traductores - AQA - Revisión de Ciencias de la Computación de GCSE - AQA". BBC Bitesize . Consultado el 27 de abril de 2024 .
  5. ^ "Generación de lenguajes de programación". GeeksforGeeks . 2017-10-22 . Consultado el 2024-04-27 .
  6. ^ "¿Qué es una generación de lenguajes?". www.computerhope.com . Consultado el 27 de abril de 2024 .
  7. ^ Levy, Stephen (1994). Hackers: Héroes de la revolución informática . Penguin Books. pág. 32. ISBN 0-14-100051-1.
  8. ^ "Lenguaje de máquina/Lenguaje ensamblador/Lenguaje de alto nivel". www.cs.mtsu.edu . Consultado el 27 de abril de 2024 .
  9. ^ Kernighan, Brian W.; Ritchie, Dennis M. (2014). El lenguaje de programación C. Serie de software de Prentice-Hall (2.ª ed., 52.ª edición impresa). Upper Saddle River, NJ: Prentice-Hall PTR. ISBN 978-0-13-110362-7.
  10. ^ "malloc(3) - Página del manual de Linux". man7.org . Consultado el 21 de abril de 2024 .
  11. ^ "Asamblea extendida (utilizando la colección de compiladores GNU (GCC))". gcc.gnu.org . Consultado el 27 de abril de 2024 .