Proceso y método de llamada al sistema en Linux

Tabla de contenido

1. Proceso de llamada al sistema

2. Tres métodos de llamada al sistema

2.1. Funciones de biblioteca proporcionadas por glibc

2.2, use syscall para llamar directamente

2.3, atrapado por instrucción int


System Call es un conjunto de interfaces proporcionadas por el sistema operativo para la interacción entre los procesos que se ejecutan en modo de usuario y los dispositivos de hardware (como CPU, discos, impresoras, etc.). Cuando el proceso del usuario necesita realizar una llamada al sistema, la CPU cambia al modo kernel mediante una interrupción suave para comenzar a ejecutar la función de llamada al sistema kernel.

1. Proceso de llamada al sistema

Tome Linux 0.11 como ejemplo para describir brevemente el proceso de llamada, y no verificó si el sistema operativo moderno ha cambiado, pero la idea básica debería ser similar. Como se muestra abajo:

Introducir el proceso de llamada al sistema en Linux Introducir el proceso de llamada al sistema en Linux

En primer lugar, el programa de aplicación puede llamar directamente a la API proporcionada por el sistema, lo que se puede hacer en el modo de usuario (Ring3).

Luego, la API correspondiente guardará el número de llamada del sistema correspondiente en el registro eax (este paso se implementa mediante ensamblado en línea), y luego usará int 0x80 para activar la interrupción (ensamblado en línea) e ingresará la función de procesamiento de interrupciones (la función es Completamente escrito por código ensamblador), esta vez ingresa al modo kernel (Ring0).

En la función de manejo de interrupciones, se llama a la llamada al sistema correspondiente al número de llamada al sistema. En esta función, los dos registros ds (registro de segmento de datos) y es (registro extra) se establecerán para apuntar al espacio del kernel . De esta manera, no podemos transferir datos del modo de usuario al modo de kernel (como open (const char * filename, int flag, ...), la dirección de la cadena a la que apunta el puntero del nombre de archivo está en el espacio de usuario, Si ocupa el lugar correspondiente en el espacio del kernel, no existe tal cadena en absoluto), ¿qué debo hacer? El registro fs en la función de manejo de interrupciones está configurado para apuntar al espacio de usuario , por lo que el problema está resuelto.

En la llamada al sistema, se realizan las operaciones correspondientes, como abrir archivos, escribir archivos, etc.

Después del procesamiento, volverá a la función de procesamiento de interrupciones y el valor de retorno se almacenará en el registro eax.

Al regresar de la función de manejo de interrupciones a la API, aún se guarda el valor de retorno en el registro eax. En este momento, se restaura del modo kernel al modo usuario.

Tome el valor de eax en la API y haga el juicio correspondiente para devolver un valor diferente para indicar la finalización de la operación.

¿Por qué se pueden realizar tantas llamadas al sistema utilizando int 0x80 interrupt?

En el modo protegido, hay varias interrupciones y la llamada al sistema está vinculada al número de interrupción 0x80. Cuando se va a llamar a una llamada al sistema, se activa int 0x80, y la función de manejo de interrupciones sabe qué llamada del sistema quiere llamar a través de eax. La razón de esto es que hay demasiadas llamadas al sistema y el número de interrupción no será suficiente, por lo que se usa uno para la administración centralizada.

Hay una tabla en el sistema operativo, que se utiliza para almacenar las direcciones de varias funciones de llamada del sistema. Esta tabla es una matriz, por lo que se puede acceder a las direcciones de diferentes funciones mediante subíndices. Por lo tanto, un número de interrupción + varios números de llamada al sistema pueden administrar múltiples llamadas al sistema.

Lo anterior se tomó de "Introducción al proceso de llamada al sistema en Linux"

2. Tres métodos de llamada al sistema

A continuación se presentan tres métodos de ocurrencia de llamadas al sistema en Linux.

2.1. Funciones de biblioteca proporcionadas por glibc

glibc es una biblioteca C estándar de código abierto utilizada en Linux. Es la biblioteca libc publicada por GNU, es decir, la biblioteca en tiempo de ejecución. glibc proporciona a los programadores una rica API (Interfaz de programación de aplicaciones). Además de los servicios en modo de usuario, como el procesamiento de cadenas y las operaciones matemáticas, lo más importante es encapsular los servicios del sistema proporcionados por el sistema operativo, es decir, la encapsulación de las llamadas al sistema. Entonces, ¿cuál es la relación entre la API de llamada al sistema proporcionada por glibc y la llamada al sistema específica del kernel?

  • Por lo general, cada llamada al sistema específica corresponde al menos a una función de biblioteca encapsulada por glibc. Por ejemplo, la llamada al sistema de archivos abiertos proporcionada por el sistema  sys_open corresponde a una open función en glibc  ;
  • En segundo lugar, glibc una llamada de API puede invocar múltiples sistemas, como proporcionado por glibc  printf función será llamada tales como  sys_open, sys_mmap, sys_write, sys_close como la llamada de sistema;
  • Además, varias API solo pueden corresponder a la misma llamada al sistema. Por ejemplo , las  funciones implementadas bajo glibc, como ,,  mallocy así sucesivamente calloc, freese usan para asignar y liberar memoria, todas usando las sys_brk llamadas al sistema del kernel  .

Por ejemplo, usamos la chmod función proporcionada por glibc para cambiar el etc/passwd atributo del archivo  a 444:

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>

int main()
{
	int rc = 0;
	rc = chmod("/etc/passwd", 0444);
	if (rc == -1)
		fprintf(stderr, "chmod failed, errno = %d\n", errno);
	else
		printf("chmod success!\n");
	return 0;
}

En uso normal, el usuario para compilar , como salida:
chmod falló, errno = 1
La llamada al sistema anterior devuelve un valor de -1, lo que indica que la llamada al sistema falló, un código de error, en / usr / include / asm-generic / errno-base La descripción del código de error en el archivo .h es la siguiente:
#define EPERM 1 / * Operación no permitida * /
Es decir, no hay permiso para realizar la operación. No podemos modificar los atributos del archivo / etc / passwd con permisos de usuario normales. El resultado es correcto.

2.2, use syscall para llamar directamente

Existen muchas ventajas al utilizar el método anterior. En primer lugar, no necesita conocer más detalles, como el número de llamada del sistema chmod. Solo necesita comprender el prototipo de la API proporcionada por glibc. En segundo lugar, el método tiene una mejor portabilidad y puede cambiar fácilmente Si el programa se transfiere a otras plataformas, o la biblioteca glibc se reemplaza por otra biblioteca, el programa solo necesita cambiarse un poco.
Pero una desventaja es que si glibc no encapsula una llamada al sistema proporcionada por un determinado kernel, no puedo llamar a la llamada al sistema a través del método anterior . Por ejemplo, agregué una llamada al sistema compilando el kernel. En este momento, glibc no puede tener la API de encapsulación de su nueva llamada al sistema. En este momento, podemos usar la syscall función proporcionada por glibc para llamar directamente. La función se define en el  unistd.h archivo de encabezado y el prototipo de función es el siguiente:

long int syscall (long int sysno, ...)
  • sysno  es el número de llamada del sistema y cada llamada del sistema tiene un número de llamada del sistema único para identificarlo. En sys/syscall.h no todos los sistemas posibles llama a la definición de la macro.
  • ...  Son los parámetros restantes de longitud variable, que son los parámetros de la llamada al sistema. Dependiendo de la llamada al sistema, puede tomar de 0 a 5 parámetros. Si excede los parámetros que puede tomar una llamada al sistema específica, los parámetros adicionales serán ignorar.
  • Valor de retorno  El valor de retorno de esta función es el valor de retorno de una llamada al sistema específica. Una vez que la llamada al sistema es exitosa, puede convertir el valor de retorno a un tipo específico. Si la llamada del sistema falla, devuelve -1 y el código de error se almacena en errno él.

También tome la modificación anterior de los atributos del archivo / etc / passwd como ejemplo, esta vez use syscall para llamar directamente:

    ...
	//rc = chmod("/etc/passwd", 0444);
	rc = syscall(SYS_chmod, "/etc/passwd", 0444);
    ...

Compile y ejecute con usuarios normales, y el resultado de salida es el mismo que el del ejemplo anterior.

2.3, atrapado por instrucción int

Si conocemos todo el proceso de la llamada al sistema, deberíamos poder saber que el programa int 0x80 en modo usuario entra en modo kernel a través de la instrucción de interrupción suave (las sysenterinstrucciones se introducen en Intel Pentium II ), el parámetro pasa a través del registro y eax es el número de llamada del sistema. , Ebx, ecx, edx, esi y edi para pasar hasta cinco parámetros a su vez, cuando la llamada al sistema regresa, el valor de retorno se almacena en eax.

Aún tomando la modificación anterior de los atributos del archivo como ejemplo, escriba la sección que llama a la llamada al sistema como código ensamblador en línea:

#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <errno.h>

int main()
{
	long rc;
	char *file_name = "/etc/passwd";
	unsigned short mode = 0444;

	asm(
		"int $0x80"
		: "=a" (rc)
		: "0" (SYS_chmod), "b" ((long)file_name), "c" ((long)mode)
	);

	if ((unsigned long)rc >= (unsigned long)-132) {
		errno = -rc;
		rc = -1;
	}

	if (rc == -1)
		fprintf(stderr, "chmode failed, errno = %d\n", errno);
	else
		printf("success!\n");

	return 0;
}

Si el valor de retorno almacenado en el registro eax (almacenado en la variable rc) está entre -1 y -132, debe interpretarse como un código de error ( /usr/include/asm-generic/errno.h el código de error máximo definido en el archivo es 132). En este momento, escriba el código de error En errno, establezca el valor de retorno de la llamada al sistema en -1; de lo contrario, devuelve el valor en eax.

El programa anterior se compila y ejecuta en Linux de 32 bits con derechos de usuario normales, ¡y el resultado es el mismo que los dos anteriores! , En un entorno de 64 bits, chmode falló, errno = 22: el parámetro no es válido.

 

Supongo que te gusta

Origin blog.csdn.net/wangquan1992/article/details/108496821
Recomendado
Clasificación