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 : comandos o funciones en el mapa del lenguaje que son estructuralmente similares a las instrucciones del procesador. Generalmente, esto se refiere ya sea a código de máquina o a 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 portátiles , debido a que están optimizados para cierto tipo de arquitectura de sistema. [1]

Los lenguajes de bajo nivel pueden convertirse a código de máquina sin un compilador o intérprete ( los lenguajes de programación de segunda generación utilizan un procesador más simple llamado ensamblador ) y el código resultante se ejecuta directamente en el procesador. Se puede hacer que un programa escrito en un lenguaje de bajo nivel se ejecute 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 informática de la especificación del programa, lo que simplifica el desarrollo. [1]

Codigo de maquina

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

El código de máquina es el único lenguaje que una computadora puede procesar directamente sin una transformación previa. Actualmente, los programadores casi nunca escriben programas directamente en código de máquina, porque requiere atención a numerosos detalles que un lenguaje de programación de alto nivel maneja automáticamente. [1] Además, a diferencia de la programación en lenguaje ensamblador , requiere memorizar o buscar códigos numéricos para cada instrucción, y es extremadamente difícil de modificar.

El verdadero código de máquina es un flujo de datos sin procesar, generalmente binarios . Un programador que codifica en "código de máquina" normalmente codifica instrucciones y datos en una forma más legible, como decimal , octal o hexadecimal , que se traduce al formato interno mediante un programa llamado cargador o se activa en la memoria de la computadora desde un panel frontal . [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 núcleo o depurando desde el panel frontal.

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

89f885 y siguientes74 2683 y siguientes 0276 1c89f9ba 01 00 00 00ser 01 00 00 008d 04 1683 f9 0274 0d89 d6c989c2f0b8 01 00 00c3

lenguaje ensamblador

Los lenguajes de segunda generación proporcionan un nivel de abstracción además del 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 piratas informáticos del MIT fue escribir ensambladores. [2] El lenguaje ensamblador tiene poca semántica o especificación formal, siendo solo un mapeo de símbolos legibles por humanos, incluidas direcciones simbólicas, 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. Los ensambladores producen archivos de objetos que pueden vincularse con otros archivos de objetos o cargarse por sí solos.

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 usando la sintaxis de AT&T :

fib: movl %edi , %eax ; poner el argumento en %eax testl %edi , %edi ; ¿es cero? je .return_from_fib ; sí - devuelve 0, que ya está en %eax cmpl $2 , %edi ; ¿2 es 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 ; poner 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 ; haga que el número anterior sea el número anterior al anterior decl %ecx ; disminuir el contador movl %eax , %edx ; convierte el número actual en el número anterior jmp .fib_loop ; continúa .return_1_from_fib: movl $1 , %eax ; establezca el valor de retorno en 1 .return_from_fib: ret ; devolver                                                             

En este ejemplo de código, los registros del procesador x86-64 se nombran y se manipulan directamente. La función carga su argumento de 32 bits de %ediacuerdo con la interfaz binaria de la aplicación System V para x86-64 y realiza su cálculo manipulando valores en los registros %eax, %ecx, %esiy %edihasta que finaliza 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 la aplicación System V, la retinstrucción simplemente elimina el elemento superior de 64 bits de la pila y hace que la siguiente instrucción se obtenga de esa ubicación (esa instrucción suele ser la instrucción inmediatamente después del que llamó a esta función), con el resultado de la función almacenado en %eax. El lenguaje ensamblador x86-64 no impone ningún estándar para pasar valores a una función o devolver valores de una función (y de hecho, no tiene ningún concepto de función); estos se definen mediante una interfaz binaria de aplicación , como System V ABI para un conjunto de instrucciones en particular.

Compare esto con la misma función en C :

fib int sin signo ( int sin signo n ) { si ( ! n ) devuelve 0 ; de lo contrario, si ( n <= 2 ) devuelve 1 ; else { unsigned int f_nminus2 , f_nminus1 , f_n ; for ( f_nminus2 = f_nminus1 = 1 , f_n = 0 ; ; - n ) { f_n = f_nminus2 + f_nminus1 ; si ( n <= 2 ) devuelve f_n ; f_nmenos2 = f_nmenos1 ; } } }                                                    

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 del lenguaje ensamblador x86 es específico de la arquitectura x86-64 y 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 ESPOL (para grandes sistemas de Burroughs ) y C. . Un método para esto es el ensamblado en línea , en el que el código ensamblador está incrustado 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 usa la arquitectura del procesador de destino.

Referencias

  1. ^ abcd "3.1: Estructura de programas de bajo nivel". Mano de obra LibreTexts . 2021-03-05 . Consultado el 3 de abril de 2023 .
  2. ^ Levy, Stephen (1994). Hackers: héroes de la revolución informática . Libros de pingüinos. pag. 32.ISBN 0-14-100051-1.