web-dev-qa-db-fra.com

Comment les appels du système fonctionnent-ils?

Je comprends qu'un utilisateur peut posséder un processus et chaque processus dispose d'un espace d'adressage (qui contient des emplacements de mémoire valides, ce processus peut faire référence). Je sais qu'un processus peut appeler un appel système et transmettre des paramètres, comme toute autre fonction de bibliothèque. Cela semble suggérer que tous les appels de système sont dans un espace d'adressage de processus en partageant la mémoire, etc. Mais peut-être, ce n'est peut-être qu'une illusion créée par le fait que dans le langage de programmation de haut niveau, les appels système ressemblent à toute autre fonction, lorsqu'un processus appelle ça.

Mais, laissez-moi maintenant prendre un pas plus profondément et analyser de plus près ce qui se passe sous la hotte. Comment compilateur compilait-il un appel système? Il pousse peut-être le nom d'appel système et les paramètres fournis par le processus dans une pile, puis mettre l'instruction de montage indique "piège" ou quelque chose - essentiellement l'instruction de montage pour appeler une interruption logicielle.

Cette instruction d'assemblage de piège est exécutée par matériel en utilisant d'abord le bit de mode de l'utilisateur au noyau, puis de définir le pointeur de code pour indiquer le début des routines de service d'interruption. À partir de ce point, l'ISR s'exécute en mode noyau, qui relie les paramètres de la pile (c'est possible, car le noyau a accès à n'importe quel emplacement de mémoire, même celles appartenant à des processus utilisateur) et exécute l'appel système et dans la L'extrémité relie la CPU, qui bascule à nouveau le bit de mode et le processus utilisateur commence à partir de l'endroit où il s'est arrêté.

Est-ce que ma compréhension est correcte?

Attaché est un diagramme rugueux de ma compréhension: enter image description here

42
xyz

Votre compréhension est assez proche; L'astuce est que la plupart des compilateurs n'écriront jamais les appels de systèmes, car les fonctions qui appellent des programmes (par exemple getpid(2), chdir(2), etc.) sont réellement fournies par la bibliothèque Standard C. La bibliothèque C standard contient le code de l'appel système, qu'il soit appelé via INT 0x80 Ou SYSENTER. Ce serait un programme étrange qui fait des appels de système sans une bibliothèque qui fait le travail. (Même si Perl fournit une fonction syscall() pouvant effectuer directement des appels système! Crazy, Droite?)

Ensuite, la mémoire. Le kernel du système d'exploitation Parfois a un accès facile à l'espace d'adresse à la mémoire du processus utilisateur. Bien sûr, les modes de protection sont différents et les données fournies par l'utilisateur doivent être copiées dans l'espace d'adressage protégé du noyau pour empêcher la modification des données fournies par l'utilisateur tandis que l'appel système est en vol. :

static int do_getname(const char __user *filename, char *page)
{
    int retval;
    unsigned long len = PATH_MAX;

    if (!segment_eq(get_fs(), KERNEL_DS)) {
        if ((unsigned long) filename >= TASK_SIZE)
            return -EFAULT;
        if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
            len = TASK_SIZE - (unsigned long) filename;
    }

    retval = strncpy_from_user(page, filename, len);
    if (retval > 0) {
        if (retval < len)
            return 0;
        return -ENAMETOOLONG;
    } else if (!retval)
        retval = -ENOENT;
    return retval;
}

Ceci, alors que ce n'est pas un appel système lui-même, est une fonction Fonction d'assistance appelé par des fonctions d'appel système qui copie des noms de fichiers dans l'espace d'adresses du noyau. Il vérifie que l'ensemble du nom de fichier réside dans la plage de données de l'utilisateur, appelle une fonction qui copie la chaîne dans l'espace utilisateur et effectue des chèques de santé en mentale avant le retour.

get_fs() et les fonctions similaires sont des restes de X86-Roots de Linux. Les fonctions ont des implémentations de travail pour toutes les architectures, mais les noms restent archaïques.

Tout le travail supplémentaire avec segments est parce que le noyau et les utilisateursPace pourrait Partager une partie de l'espace d'adressage disponible. Sur une plate-forme 32 bits (où les chiffres sont faciles à comprendre), le noyau disposera généralement d'un gigaoctet d'espace d'adresses virtuel et des processus utilisateur disposeront généralement de trois gigaoctets d'espace d'adresses virtuel.

Lorsqu'un processus appelle au noyau, le noyau "réparera" les autorisations de la table des pages pour permettre l'accès à l'ensemble de la plage et bénéficiera des entrées pré-remplies entrées TLB pour la mémoire fournie par l'utilisateur . Grand succès. Mais lorsque le noyau doit contourner le passage à des utilisateurs, il doit rincer la TLB pour éliminer les privilèges mis en cache sur les pages de l'espace d'adresses du noyau.

Mais l'astuce est, un gigaoctet d'espace d'adresses virtuel est pas suffisant pour toutes les structures de données du noyau sur d'énormes machines. Entretien des métadonnées des systèmes de fichiers mis en cache et des pilotes de périphérique de blocage, des piles de réseau et des mappages de mémoire pour tous les processus du système, peuvent prendre une énorme quantité de données.

Des "scissions" différentes sont différentes: deux concerts pour l'utilisateur, deux concerts pour le noyau, une gigue pour l'utilisateur, trois concerts pour le noyau, etc. Comme l'espace du noyau monte, l'espace des processus d'utilisateur diminue. Donc, il y a un 4:4 Split de mémoire qui donne quatre gigaoctets au processus utilisateur, quatre gigaoctets au noyau, et le noyau doit jouer avec des descripteurs de segment pour pouvoir accéder à la mémoire de l'utilisateur. La TLB est rinçue d'entrer et de quitter des appels de système, qui constitue une pénalité de vitesse assez importante. Mais cela permet au noyau de maintenir des structures de données significativement plus grandes.

Les tables et les gammes d'adresses de page beaucoup plus grandes de 64 bits plate-forme rendent probablement tout le look précédent pittoresque. J'espère bien sûr, de toute façon.

14
sarnold

Oui, vous l'avez à peu près bien. Un détail cependant, lorsque le compilateur compile un appel système, il utilisera le numéro de l'appel du système plutôt que le Nom . Par exemple, voici une liste des syscalls Linux (pour une ancienne version, mais le concept est toujours le même).

8
Greg Hewgill

Si vous souhaitez effectuer un appel système directement à partir de votre programme, vous pourriez le faire facilement. C'est une plate-forme dépendante, mais disons que vous vouliez lire dans un fichier. Chaque appel système a un numéro. Dans ce cas, vous placez le nombre de read_from_file Appel système dans le registre EAX. Les arguments de l'appel du système sont placés dans différents registres ou la pile (en fonction de l'appel du système). Une fois les registres remplis des données correctes et que vous êtes prêt à effectuer l'appel système, vous exécutez l'instruction INT 0x80 (dépend de l'architecture). Cette instruction est une interruption qui provoque le contrôle du système d'exploitation. Le système d'exploitation identifie ensuite le numéro d'appel système dans le registre EAX, agit en conséquence et donne le contrôle au processus de l'appel du système.

La manière dont les appels système sont utilisés sont sujets à modifier et dépend de la plate-forme donnée. En utilisant des bibliothèques qui fournit des interfaces faciles à ces appels système, vous rendez vos programmes plus indépendants de la plate-forme et votre code sera beaucoup plus lisible et plus rapide à écrire. Envisager d'implémenter des appels système directement dans une langue de haut niveau. Vous auriez besoin de quelque chose comme l'assemblage en ligne pour assurer que les données sont placées dans les bons registres.

3

Oui, votre compréhension est absolument correcte, un programme C peut appeler un appel système direct, lorsque cet appel système se produit peut-être une série d'appels jusqu'au piège de montage. Je pense vraiment que votre compréhension peut aider un nouveau-là.check ce code dans lequel j'appelle un appel "système".

#include < stdio.h  >    
#include < stdlib.h >    
int main()    
{    
    printf("Running ps with "system" system call ");    
    system("ps ax");    
    printf("Done.\n");    
    exit(0);    
}
0
user1011455