Quel fichier du noyau spécifie fork (), vfork ()… pour utiliser l'appel système sys_clone ()

9

Lorsque ltrace est utilisé pour tracer les appels système, je pouvais voir que fork () utilise sys_clone () plutôt que sys_fork (). Mais je n'ai pas pu trouver la source Linux où elle est définie.

Mon programme est

#include<stdio.h>
main()
{
        int pid,i=0,j=0;
        pid=fork();
        if(pid==0)
                printf("\nI am child\n");
        else
                printf("\nI am parent\n");

}

Et la sortie ltrace est

SYS_brk(NULL)                                                                               = 0x019d0000
SYS_access("/etc/ld.so.nohwcap", 00)                                                        = -2
SYS_mmap(0, 8192, 3, 34, 0xffffffff)                                                        = 0x7fe3cf84f000
SYS_access("/etc/ld.so.preload", 04)                                                        = -2
SYS_open("/etc/ld.so.cache", 0, 01)                                                         = 3
SYS_fstat(3, 0x7fff47007890)                                                                = 0
SYS_mmap(0, 103967, 1, 2, 3)                                                                = 0x7fe3cf835000
SYS_close(3)                                                                                = 0
SYS_access("/etc/ld.so.nohwcap", 00)                                                        = -2
SYS_open("/lib/x86_64-linux-gnu/libc.so.6", 0, 00)                                          = 3
SYS_read(3, "\177ELF\002\001\001", 832)                                                     = 832
SYS_fstat(3, 0x7fff470078e0)                                                                = 0
SYS_mmap(0, 0x389858, 5, 2050, 3)                                                           = 0x7fe3cf2a8000
SYS_mprotect(0x7fe3cf428000, 2097152, 0)                                                    = 0
SYS_mmap(0x7fe3cf628000, 20480, 3, 2066, 3)                                                 = 0x7fe3cf628000
SYS_mmap(0x7fe3cf62d000, 18520, 3, 50, 0xffffffff)                                          = 0x7fe3cf62d000
SYS_close(3)                                                                                = 0
SYS_mmap(0, 4096, 3, 34, 0xffffffff)                                                        = 0x7fe3cf834000
SYS_mmap(0, 4096, 3, 34, 0xffffffff)                                                        = 0x7fe3cf833000
SYS_mmap(0, 4096, 3, 34, 0xffffffff)                                                        = 0x7fe3cf832000
SYS_arch_prctl(4098, 0x7fe3cf833700, 0x7fe3cf832000, 34, 0xffffffff)                        = 0
SYS_mprotect(0x7fe3cf628000, 16384, 1)                                                      = 0
SYS_mprotect(0x7fe3cf851000, 4096, 1)                                                       = 0
SYS_munmap(0x7fe3cf835000, 103967)                                                          = 0
__libc_start_main(0x40054c, 1, 0x7fff47008298, 0x4005a0, 0x400590 <unfinished ...>
fork( <unfinished ...>
SYS_clone(0x1200011, 0, 0, 0x7fe3cf8339d0, 0)                                               = 5967
<... fork resumed> )                                                                        = 5967
puts("\nI am parent" <unfinished ...>
SYS_fstat(1, 0x7fff47008060)                                                                = 0
SYS_mmap(0, 4096, 3, 34, 0xffffffff
)                                                        = 0x7fe3cf84e000
I am child
SYS_write(1, "\n", 1
)                                                                       = 1
SYS_write(1, "I am parent\n", 12)                                                           = -512
--- SIGCHLD (Child exited) ---
SYS_write(1, "I am parent\n", 12I am parent
)                                                           = 12
<... puts resumed> )                                                                        = 13
SYS_exit_group(13 <no return ...>
+++ exited (status 13) +++
user3539
la source
Cela pourrait vous être utile: lxr.linux.no/linux+v3.10.9
rejouer
@ mauro.stettler Je ne pouvais pas le trouver dans lxr
user3539
Voulez-vous dire git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/… autour de la ligne 1700? Qu'espériez-vous découvrir?
msw

Réponses:

29

Les wrappers fork()et vfork()dans la glibc sont implémentés via l' clone()appel système. Pour mieux comprendre la relation entre fork()et clone(), nous devons considérer la relation entre les processus et les threads sous Linux.

Traditionnellement, la fork()duplication de toutes les ressources détenues par le processus parent était affectée et la copie au processus enfant. Cette approche entraîne des frais généraux considérables, qui pourraient tous être inutiles si l'enfant appelle immédiatement exec(). Sous Linux, fork()utilise des pages de copie sur écriture pour retarder ou éviter complètement de copier les données qui peuvent être partagées entre les processus parent et enfant. Ainsi, la seule surcharge qui est encourue pendant une normale fork()est la copie des tables de pages du parent et l'affectation d'une structure de descripteur de processus unique,, task_structpour l'enfant.

Linux adopte également une approche exceptionnelle des threads. Sous Linux, les threads ne sont que des processus ordinaires qui partagent des ressources avec d'autres processus. Il s'agit d'une approche radicalement différente des threads par rapport à d'autres systèmes d'exploitation tels que Windows ou Solaris, où les processus et les threads sont des types de bêtes entièrement différents. Sous Linux, chaque thread possède un ordinaire task_structqui se trouve être configuré de telle sorte qu'il partage certaines ressources, comme un espace d'adressage, avec le processus parent.

Le flagsparamètre de l' clone()appel système comprend un ensemble d'indicateurs qui indiquent, le cas échéant, les ressources que les processus parent et enfant doivent partager. Les processus et les threads sont tous deux créés via clone(), la seule différence est l'ensemble de drapeaux transmis clone().

Un normal fork()pourrait être implémenté comme:

clone(SIGCHLD, 0);

Cela crée une tâche qui ne partage aucune ressource avec son parent et est définie pour envoyer le SIGCHLDsignal de terminaison au parent à sa sortie.

En revanche, une tâche qui partage l'espace d'adressage, les ressources du système de fichiers, les descripteurs de fichiers et les gestionnaires de signaux avec le parent, en d'autres termes un thread , pourrait être créée avec:

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

vfork()à son tour, est implémenté via un CLONE_VFORKindicateur distinct , ce qui entraînera le processus parent en veille jusqu'à ce que le processus enfant le réveille via un signal. L'enfant sera le seul fil d'exécution dans l'espace de noms du parent, jusqu'à ce qu'il appelle exec()ou quitte. L'enfant n'est pas autorisé à écrire dans la mémoire. L' clone()appel correspondant pourrait être le suivant:

clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0)

L'implémentation de sys_clone()est spécifique à l'architecture, mais l'essentiel du travail se déroule dans do_fork()défini dans kernel/fork.c. Cette fonction appelle le statique clone_process(), qui crée un nouveau processus comme copie du parent, mais ne le démarre pas encore. clone_process()copie les registres, attribue un PID à la nouvelle tâche et duplique ou partage les parties appropriées de l'environnement de processus comme spécifié par le clone flags. À son clone_process()retour, do_clone()réveillera le processus nouvellement créé et planifiera son exécution.

Thomas Nyman
la source
2
+1 Belle explication de l'importance de clone()par rapport aux fils et fourchettes.
goldilocks
1
A effacé tous mes doutes
user3539
2

Le composant responsable de la traduction des fonctions d'appel système de l'espace utilisateur en appels système du noyau sous Linux est la libc. Dans GLibC, la bibliothèque NPTL redirige cela vers l' clone(2)appel système.

Ignacio Vazquez-Abrams
la source