Tutoriales

Syscalls: todo sobre el funcionamiento de las llamadas al sistema

Entender las syscalls, o llamadas al sistema, es fundamental para comprender cómo el kernel del sistema operativo gestiona los recursos de hardware. Por ejemplo, cuando un programa necesita guardar un archivo en un disco duro, o cuando se necesita crear un proceso para cargarlo en memoria y que la CPU lo procese, etc. Vamos a ver todo esto en este mismo artículo…

Modos del sistema (Anillos de protección)

anillos de protección o modos

Antes de nada, es importante conocer en los modos en los que puede trabajar el software del sistema, puesto que te ayudará a comprender las syscalls:

Modo kernel o privilegiado

Los diseñadores de ordenadores han creado capas de seguridad llamadas anillos, y el modo kernel o privilegiado es el anillo central con acceso total a funciones críticas del sistema. Este nivel de privilegio permite la gestión de recursos de hardware, control del sistema de archivos, asignación de memoria RAM, creación de archivos y tareas de entrada/salida.

Aunque algunos controladores están en anillos menos privilegiados, hay controladores críticos integrados en el kernel.

Modo usuario o no privilegiado

El modo usuario es no privilegiado y corresponde al anillo verde (Ring 3) en la imagen. En este nivel, las aplicaciones y el espacio de usuario operan sin privilegios para controlar tareas esenciales como la gestión de hardware.

Sin embargo, surge la pregunta de cómo un videojuego puede utilizar la GPU, o cómo un procesador de texto puede guardar un archivo sin tener privilegios para realizar estas tareas. Aquí es donde entra en juego la syscall o llamada al sistema…

¿Qué es una SCI?

syscalls Linux

La imagen muestra las diferentes capas en un sistema informático. La capa más baja es el hardware, seguida del kernel del sistema operativo (Linux) con sus controladores. El kernel tiene una capa llamada SCI (System Call Interface), que permite a las aplicaciones realizar llamadas al sistema para acceder a recursos privilegiados.

Encima de la SCI están las bibliotecas del sistema operativo, que proporcionan funcionalidades a las aplicaciones. Finalmente, en la capa más alta se encuentran las aplicaciones o espacio de usuario en modo no privilegiado.

También te puede interesar conocer más información sobre los sistemas operativos

¿Qué es una syscall o llamada al sistema?

Ahora llegamos a la parte crucial, que es comprender qué significa la syscall o llamada al sistema que hemos estado mencionando. Básicamente, una llamada al sistema es una función proporcionada por la SCI que permite a las aplicaciones o código del espacio de usuario solicitar acceso a recursos restringidos debido a la falta de privilegios. En otras palabras, es una medida de seguridad que impide que una aplicación acceda directamente a esos recursos.

Sin embargo, como puedes estar pensando, para que estas llamadas se lleven a cabo, se requiere una acción y algo debe suceder en el sistema operativo para atenderlas. Explicaremos esto en los próximos apartados.

¿Cómo funcionan las syscalls?

SCI syscalls

Imagínate que estás utilizando un editor de texto. Al abrirlo, el sistema operativo carga el proceso en la memoria RAM y la CPU ejecuta las instrucciones y datos para que el editor funcione. Cuando escribes y deseas guardar el archivo, como por ejemplo «ejemplo.txt«, surge la pregunta de cómo el editor, siendo una aplicación del espacio de usuario no privilegiada, puede acceder al sistema de archivos para realizar el guardado. La respuesta es que utiliza una llamada al sistema, una syscall, que envía a la SCI. El kernel del sistema operativo ejecuta un código específico que permite la creación y el guardado del archivo.

Tipos de syscalls

Algunos de los tipos de syscalls más importantes que existen son:

  • Mantenimiento de la información: este tipo de syscall se utiliza para mantener información básica del sistema operativo, como la hora y la fecha, que son importantes para algunas aplicaciones.
  • Comunicaciones: se encargan de la comunicación entre procesos, gestionando los mensajes o señales que se envían para transmitir información.
  • Control de procesos: estas son las funciones que permiten controlar los procesos, como crear, eliminar, cambiar la prioridad o poner en espera. Están estrechamente relacionadas con el planificador del sistema operativo, la gestión de la memoria y el uso de la CPU.
  • Gestión de archivos: se centran en la manipulación de archivos, como crear, leer, escribir o eliminar. Están relacionadas con el control de los sistemas de archivos en las unidades de almacenamiento internas y externas.
  • Gestión de dispositivos: estas llamadas al sistema administran los dispositivos y permiten su uso, como conectar, desconectar o liberar. Están vinculadas a los periféricos y dispositivos que se pueden utilizar.

Por ejemplo, para que lo veas algo más claro, puedes consultar esta tabla con las syscalls típicas de Windows NT y en Linux (también válidas para Unix):

Tipos de syscall Windows Linux
CreateProcess() fork()
Control de procesos ExitProcess() exit()
WaitForSingleObject() wait()
CreateFile() open()
ReadFile() read()
Gestión de archivos WriteFile() write()
CloseHandle() close()
SetConsoleMode() ioctl()
Gestión de dispositivos ReadConsole() read()
WriteConsole() write()
GetCurrentProcessID() getpid()
Mantenimiento de información SetTimer() alarm()
Sleep() sleep()
CreatePipe() pipe()
Comunicación CreateFileMapping() shmget()
MapViewOfFile() mmap()

Es importante tener en cuenta que las llamadas al sistema pueden variar entre diferentes sistemas operativos, incluso si realizan funciones similares. Por lo tanto, al programar para una plataforma específica, es necesario conocer la ABI (Interfaz Binaria de Aplicaciones) y las bibliotecas disponibles en ese sistema operativo. Esto implica comprender las llamadas al sistema específicas y evitar utilizar las de otro sistema operativo.

Ejemplo de funcionamiento

Imagina la llamada read() de Linux, que es de tipo de administración de archivos y sirve para leer un archivo dado. Por ejemplo, en el siguiente código fuente en lenguaje de programación C te he puesto un ejemplo claro de este tipo de programa donde se hace uso de la llamada read() para abrir y leer un archivo denominado en este caso profesionalreview.txt:

#include <stdio.h>

int main() {
    FILE *archivo;
    char caracter;

    // Abrir el archivo en modo lectura
    archivo = fopen("profesionalreview.txt", "r");

    // Verificar si el archivo se abrió correctamente
    if (archivo == NULL) {
        printf("No se pudo abrir el archivo.\n");
        return 1;
    }

    // Leer y mostrar el contenido del archivo
    printf("Contenido del archivo:\n");
    while ((caracter = fgetc(archivo)) != EOF) {
        printf("%c", caracter);
    }

    // Cerrar el archivo
    fclose(archivo);

    return 0;
}

En este código fuente, también se utilizan otras operaciones del sistema, como cerrar el archivo con close(). Sin embargo, nos enfocaremos únicamente en la operación de lectura con read(). A continuación, se describe el proceso que ocurre al compilar y ejecutar este programa:

  1. Este pequeño programa se ejecuta en el espacio de usuario y carece de privilegios. Aunque necesita abrir, leer y cerrar un archivo, lo hace mediante una llamada al sistema o syscall que se dirige a la interfaz de llamadas al sistema del kernel (SCI), en este caso del kernel Linux.
  2. El kernel atiende esta llamada en el espacio privilegiado y ejecuta la rutina correspondiente a la operación de lectura. Para ello, carga en memoria el proceso encargado de ejecutar esta llamada y la CPU la atiende.
  3. Al iniciar la rutina, se produce una interrupción o trap que transfiere el control al kernel. La llamada al sistema se identifica con un código único en un registro, lo cual permite su reconocimiento, ya que puede haber otras llamadas en curso o incluso otras llamadas de lectura similares (read).
  4. El kernel, a través de su control sobre el sistema de archivos, realiza la lectura del archivo solicitado.
  5. Una vez finalizada la ejecución de la rutina read(), el kernel devuelve el control al modo usuario y proporciona la salida del proceso al programa que realizó la llamada, en este caso, nuestro programa de ejemplo.
  6. Al recibir la salida, el proceso del programa continúa su ejecución desde donde se detuvo.

Como resultado, al ejecutar nuestro programa, a pesar de carecer de privilegios para acceder al control del sistema de archivos, puede llevar a cabo esta tarea sin problemas y obtener el contenido del archivo solicitado, en este ejemplo, un archivo llamado profesionalreview.txt.

La función «fopen» en el código fuente en lenguaje C desencadena este proceso. Esta función utiliza la biblioteca C. Como puedes ver, es importante comprender todos los conceptos previamente mencionados sobre el kernel, bibliotecas, modos, etc., para comprender este funcionamiento.

El kernel, en este caso Linux, sabe qué hacer gracias a la presencia de una función específica llamada read(). Esta función está implementada en la GNU C Library, que es utilizada en Linux. A continuación, se muestra el código fuente correspondiente a la función read():

/* Linux read syscall implementation.
   Copyright (C) 2017-2022 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#include <unistd.h>
#include <sysdep-cancel.h>
/* Read NBYTES into BUF from FD.  Return the number read or -1.  */
ssize_t
__libc_read (int fd, void *buf, size_t nbytes)
{
  return SYSCALL_CANCEL (read, fd, buf, nbytes);
}
libc_hidden_def (__libc_read)
libc_hidden_def (__read)
weak_alias (__libc_read, __read)
libc_hidden_def (read)
weak_alias (__libc_read, read)

Hay que decir también que dependiendo de la arquitectura o ISA sobre la que se trabaje, la interrupción será tratada de una u otra forma. Por ejemplo, en ASM o ensamblador para x86, el código resultante del programa en C anterior sería:

.LC0:
        .string "r"
.LC1:
        .string "profesionalreview.txt"
.LC2:
        .string "No se pudo abrir el archivo."
.LC3:
        .string "Contenido del archivo:"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:.LC1
        call    fopen
        mov     QWORD PTR [rbp-8], rax
        cmp     QWORD PTR [rbp-8], 0
        jne     .L2
        mov     edi, OFFSET FLAT:.LC2
        call    puts
        mov     eax, 1
        jmp     .L3
.L2:
        mov     edi, OFFSET FLAT:.LC3
        call    puts
        jmp     .L4
.L5:
        movsx   eax, BYTE PTR [rbp-9]
        mov     edi, eax
        call    putchar
.L4:
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    fgetc
        mov     BYTE PTR [rbp-9], al
        cmp     BYTE PTR [rbp-9], -1
        setne   al
        test    al, al
        jne     .L5
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    fclose
        mov     eax, 0
.L3:
        leave
        ret

Como se puede ver, he resaltado la línea call fopen, ya que es la instrucción que se ejecutará en la CPU para realizar esa llamada al sistema. En este caso, call es una instrucción presente en la ISA x86-64 y que sirve precisamente para eso. Además, como ves, existen otras syscalls en el código ASM o ensamblador que ves aquí, como la fclose, para cerrar el archivo profesionalreview.txt una vez se ha completado la ejecución del programa…

También te puede interesar conocer más sobre la programación ASM o en ensamblador

No olvides comentar con dudas…

Isaac

Geek de los sistemas electrónicos, especialmente del hardware informático. Con alma de escritor y pasión por compartir todo el conocimiento sobre tecnología.
Los datos de carácter personal que nos facilite mediante este formulario quedarán registrados en un fichero de Miguel Ángel Navas Carrera, con la finalidad de gestionar los comentarios que realizas en este blog. La legitimación se realiza a través del consentimiento del interesado. Si no se acepta no podrás comentar en este blog. Puedes consultar Política de privacidad. Puede ejercitar los derechos de acceso, rectificación, cancelación y oposición en [email protected]
Botón volver arriba