Processus et méthode d'appel système sous Linux

table des matières

1. Processus d'appel système

2. Trois méthodes d'appel système

2.1. Fonctions de bibliothèque fournies par la glibc

2.2, utilisez syscall pour appeler directement

2.3, piégé par l'instruction int


L'appel système est un ensemble d'interfaces fournies par le système d'exploitation pour l'interaction entre les processus exécutés en mode utilisateur et les périphériques matériels (tels que les processeurs, les disques, les imprimantes, etc.). Lorsque le processus utilisateur doit effectuer un appel système, la CPU passe en mode noyau via une interruption logicielle pour démarrer l'exécution de la fonction d'appel système du noyau.

1. Processus d'appel système

Prenez Linux 0.11 comme exemple pour décrire brièvement le processus d'appel, sans vérifier si le système d'exploitation moderne a changé, mais l'idée de base devrait être similaire. Comme indiqué ci-dessous:

Introduire le processus d'appel système sous Linux Introduire le processus d'appel système sous Linux

Tout d'abord, le programme d'application peut appeler directement l'API fournie par le système, ce qui peut être fait en mode utilisateur (Ring3).

Ensuite, l'API correspondante enregistrera le numéro d'appel système correspondant dans le registre eax (cette étape est implémentée par l'assemblage en ligne), puis utilisera int 0x80 pour déclencher l'interruption (assemblage en ligne), et entrera la fonction de traitement d'interruption (la fonction est Entièrement écrit par code assembleur), cette fois il entre en mode noyau (Ring0).

Dans la fonction de gestion des interruptions, l'appel système correspondant au numéro d'appel système est appelé. Dans cette fonction, les deux registres ds (registre de segment de données) et es (registre supplémentaire) seront positionnés pour pointer vers l'espace du noyau . De cette façon, nous ne pouvons pas transférer de données du mode utilisateur au mode noyau (comme open (const char * filename, int flag, ...), l'adresse de la chaîne pointée par le pointeur de nom de fichier est dans l'espace utilisateur, Si vous prenez la place correspondante dans l'espace noyau, il n'y a pas du tout de chaîne de caractères.) Que dois-je faire? Le registre fs dans la fonction de gestion des interruptions est défini pour pointer vers l'espace utilisateur , le problème est donc résolu.

Dans l'appel système, les opérations correspondantes sont effectuées, telles que l'ouverture de fichiers, l'écriture de fichiers, etc.

Après le traitement, il retournera à la fonction de traitement d'interruption et la valeur de retour sera stockée dans le registre eax.

Le retour de la fonction de gestion des interruptions à l'API enregistre toujours la valeur de retour dans le registre eax. À ce stade, il est restauré du mode noyau au mode utilisateur.

Prenez la valeur d'Eax dans l'API et effectuez le jugement correspondant pour renvoyer une valeur différente pour indiquer l'achèvement de l'opération.

Pourquoi tant d'appels système peuvent-ils être appelés en utilisant l'interruption int 0x80?

Dans le mode protégé, il existe diverses interruptions et l'appel système est lié à l'interruption 0x80. Lorsqu'un appel système doit être appelé, int 0x80 est déclenché et la fonction de gestion des interruptions sait quel appel système elle souhaite appeler via eax. La raison en est qu'il y a trop d'appels système et que le numéro d'interruption ne sera pas suffisant, donc l'un est utilisé pour une gestion centralisée.

Il existe une table dans le système d'exploitation, qui est utilisée pour stocker les adresses de diverses fonctions d'appel système. Cette table est un tableau, donc les adresses des différentes fonctions sont accessibles par des indices. Par conséquent, un numéro d'interruption + divers numéros d'appel système peuvent gérer plusieurs appels système.

Ce qui précède est tiré de "Présentation du processus d'appel système sous Linux"

2. Trois méthodes d'appel système

Ce qui suit présente trois méthodes d'occurrence d'appel système sous Linux.

2.1. Fonctions de bibliothèque fournies par la glibc

glibc est une bibliothèque C standard open source utilisée sous Linux, c'est la bibliothèque libc publiée par GNU, c'est-à-dire la bibliothèque d'exécution. La glibc fournit aux programmeurs une riche API (Application Programming Interface). Outre les services en mode utilisateur tels que le traitement des chaînes et les opérations mathématiques, le plus important est d'encapsuler les services système fournis par le système d'exploitation, c'est-à-dire l'encapsulation des appels système. Alors, quelle est la relation entre l'API d'appel système fournie par la glibc et l'appel système spécifique au noyau?

  • Habituellement, chaque appel système spécifique correspond à au moins une fonction de bibliothèque encapsulée par la glibc Par exemple, l'appel de système de fichiers ouvert fourni par le système  sys_open correspond à une open fonction de la glibc  ;
  • En second lieu , glibc un appel API séparée peut invoquer plusieurs systèmes, tels que fournis par la glibc  printf fonction appellera par exemple  sys_open, sys_mmap, sys_write, sys_close comme l'appel système;
  • De plus, plusieurs API peuvent uniquement correspondre au même appel système. Par exemple , les  fonctions implémentées sous glibc, telles que ,,  mallocetc. calloc, freesont utilisées pour allouer et libérer de la mémoire, toutes utilisant les sys_brk appels système du noyau  .

Par exemple, nous utilisons la chmod fonction fournie par la glibc pour changer l' etc/passwd attribut de fichier  en 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 utilisation normale, l'utilisateur compile , en sortie:
le chmod a échoué, errno = 1
L'appel système ci-dessus renvoie une valeur de -1, indiquant que l'appel système a échoué, un code d'erreur, dans le fichier / usr / include / asm-generic / errno-base La description du code d'erreur dans le fichier .h est la suivante:
#define EPERM 1 / * Opération non autorisée * /
Autrement dit, il n'y a pas d'autorisation pour effectuer l'opération. Nous ne pouvons pas modifier les attributs du fichier / etc / passwd avec des autorisations utilisateur ordinaires. Le résultat est correct.

2.2, utilisez syscall pour appeler directement

L'utilisation de la méthode ci-dessus présente de nombreux avantages. Premièrement, vous n'avez pas besoin de connaître plus de détails, tels que le numéro d'appel du système chmod. Il vous suffit de comprendre le prototype de l'API fourni par la glibc. Deuxièmement, la méthode a une meilleure portabilité et vous pouvez facilement changer Si le programme est porté sur d'autres plates-formes, ou si la bibliothèque glibc est remplacée par une autre bibliothèque, le programme n'a besoin que d'être légèrement modifié.
Mais un inconvénient est que si la glibc n'encapsule pas un appel système fourni par un certain noyau, je ne peux pas appeler l'appel système via la méthode ci-dessus . Par exemple, j'ai ajouté un appel système en compilant le noyau. Pour le moment, la glibc ne peut pas avoir l'API d'encapsulation de votre nouvel appel système. À ce stade, nous pouvons utiliser la syscall fonction fournie par la glibc pour appeler directement. La fonction est définie dans le unistd.h fichier d' en-  tête et le prototype de fonction est le suivant:

long int syscall (long int sysno, ...)
  • sysno  est le numéro d'appel système, et chaque appel système a un numéro d'appel système unique pour l'identifier. Dans sys/syscall.h là tout système possible appelle la définitionmacro.
  • ...  Sont les paramètres de longueur variable restants, qui sont les paramètres de l'appel système. En fonction de l'appel système, il peut prendre de 0 à 5 paramètres. S'il dépasse les paramètres qu'un appel système spécifique peut prendre, les paramètres supplémentaires seront ignorer.
  • Valeur de retour  La valeur de retour de cette fonction est la valeur de retour d'un appel système spécifique. Une fois l'appel système réussi, vous pouvez convertir la valeur de retour en un type spécifique. Si l'appel système échoue, il renvoie -1 et le code d'erreur y est stocké errno .

Prenez également la modification ci-dessus des attributs du fichier / etc / passwd comme exemple, cette fois, utilisez syscall pour appeler directement:

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

Compilez et exécutez sous des utilisateurs ordinaires, et le résultat de sortie est le même que dans l'exemple ci-dessus.

2.3, piégé par l'instruction int

Si nous connaissons l'ensemble du processus de l'appel système, nous devrions être en mesure de savoir que le programme en mode utilisateur int 0x80 entre en mode noyau via l'instruction d'interruption logicielle (les sysenterinstructions sont introduites dans Intel Pentium II ), le paramètre est passé à travers le registre et eax est le numéro d'appel système. , Ebx, ecx, edx, esi et edi pour passer jusqu'à cinq paramètres à tour de rôle, lorsque l'appel système revient, la valeur de retour est stockée dans eax.

En prenant toujours la modification ci-dessus des attributs de fichier comme exemple, écrivez la section qui appelle l'appel système en tant que code d'assemblage en ligne:

#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 la valeur de retour stockée dans le registre eax (stockée dans la variable rc) est comprise entre -1 et -132, elle doit être interprétée comme un code d'erreur ( /usr/include/asm-generic/errno.h le code d'erreur maximal défini dans le fichier est 132). À ce stade, écrivez le code d'erreur Dans errno, définissez la valeur de retour de l'appel système sur -1; sinon, elle renvoie la valeur dans eax.

Le programme ci-dessus est compilé et exécuté sous Linux 32 bits avec des droits d'utilisateur ordinaires, et le résultat est le même que les deux précédents! , Dans un environnement 64 bits, chmode a échoué, errno = 22: le paramètre n'est pas valide.

 

Je suppose que tu aimes

Origine blog.csdn.net/wangquan1992/article/details/108496821
conseillé
Classement