Contexte:
La surcharge des appels système est beaucoup plus importante que la surcharge des appels de fonction (les estimations vont de 20 à 100x), principalement en raison du changement de contexte de l'espace utilisateur vers l'espace noyau et inversement. Il est courant d'utiliser des fonctions en ligne pour économiser la surcharge des appels de fonction et les appels de fonction sont beaucoup moins chers que les appels système. Il va de soi que les développeurs voudraient éviter une partie de la surcharge des appels système en prenant en charge autant d'opérations dans le noyau en un seul appel système que possible.
Problème:
Cela a créé beaucoup d'appels système (superflu?) Comme sendmmsg () , recvmmsg () , ainsi que le chdir, ouvert, lseek et / ou des combinaisons symlink comme: openat
, mkdirat
, mknodat
, fchownat
, futimesat
, newfstatat
, unlinkat
, fchdir
, ftruncate
, fchmod
, renameat
, linkat
, symlinkat
, readlinkat
, fchmodat
, faccessat
, lsetxattr
, fsetxattr
, execveat
, lgetxattr
, llistxattr
, lremovexattr
, fremovexattr
, flistxattr
, fgetxattr
, pread
, pwrite
etc ...
Maintenant, Linux a ajouté copy_file_range()
qui combine apparemment lseek et write syscalls. Ce n'est qu'une question de temps avant que cela ne devienne fcopy_file_range (), lcopy_file_range (), copy_file_rangeat (), fcopy_file_rangeat () et lcopy_file_rangeat () ... mais comme il y a 2 fichiers impliqués au lieu de X appels supplémentaires, cela pourrait devenir X ^ 2 plus. OK, Linus et les différents développeurs BSD ne laisseraient pas aller aussi loin, mais mon point est que s'il y avait un appel système par lots, tous (la plupart?) Pourraient être implémentés dans l'espace utilisateur et réduire la complexité du noyau sans ajouter beaucoup s'il y a des frais généraux sur le côté libc.
De nombreuses solutions complexes ont été proposées qui incluent une certaine forme de thread syscall spécial pour les appels sys non bloquants pour les appels sys de traitement par lots; cependant, ces méthodes ajoutent une complexité significative au noyau et à l'espace utilisateur de la même manière que libxcb vs libX11 (les appels asynchrones nécessitent beaucoup plus de configuration)
Solution?:
Un appel système générique par lots. Cela réduirait le coût le plus élevé (commutateurs multi-modes) sans les complexités associées à la présence d'un thread noyau spécialisé (bien que cette fonctionnalité puisse être ajoutée plus tard).
Il existe fondamentalement déjà une bonne base pour un prototype dans le syscall socketcall (). Il suffit de l'étendre de prendre un tableau d'arguments pour prendre à la place un tableau de retours, un pointeur sur des tableaux d'arguments (qui inclut le numéro de syscall), le nombre de syscalls et un argument flags ... quelque chose comme:
batch(void *returns, void *args, long ncalls, long flags);
Une différence majeure serait que les arguments devraient probablement tous être des pointeurs de simplicité afin que les résultats des appels système précédents puissent être utilisés par les appels système suivants (par exemple, le descripteur de fichier open()
à utiliser dans read()
/ write()
)
Quelques avantages possibles:
- moins d'espace utilisateur -> espace noyau -> changement d'espace utilisateur
- commutateur de compilateur possible -fcombine-syscalls pour essayer de créer un lot de manière automatique
- drapeau optionnel pour un fonctionnement asynchrone (retournez fd pour regarder immédiatement)
- pouvoir implémenter les futures fonctions de syscall combinées dans l'espace utilisateur
Question:
Est-il possible de mettre en œuvre un appel système par lots?
- Suis-je en train de rater des problèmes évidents?
- Suis-je surestimer les avantages?
Vaut-il la peine de mettre en œuvre un appel système par lots (je ne travaille pas chez Intel, Google ou Redhat)?
- J'ai déjà patché mon propre noyau, mais je crains d'avoir affaire au LKML.
- L'histoire a montré que même si quelque chose est largement utile aux utilisateurs "normaux" (utilisateurs finaux non-entreprise sans accès en écriture git), il peut ne jamais être accepté en amont (unionfs, aufs, cryptodev, tuxonice, etc ...)
Les références:
la source
batch
appels système dans desbatch
appels système, vous pouvez créer une arborescence d'appels arbitrairement approfondie de appels système arbitraires. Fondamentalement, vous pouvez mettre votre application entière dans un seul appel système.Réponses:
J'ai essayé ceci sur x86_64
Patch contre 94836ecf1e7378b64d37624fbb81fe48fbd4c772: (également ici https://github.com/pskocik/linux/tree/supersyscall )
Et cela semble fonctionner - je peux écrire bonjour à fd 1 et world à fd 2 avec un seul syscall:
Fondamentalement, j'utilise:
comme un prototype de syscall universel, qui semble être la façon dont les choses fonctionnent sur x86_64, donc mon "super" syscall est:
Il retourne le nombre d'appels sys essayés (
==Nargs
si leSUPERSYSCALL__continue_on_failure
drapeau est passé, sinon>0 && <=Nargs
) et les échecs de copie entre l'espace noyaux et l'espace utilisateur sont signalés par segfaults au lieu de l'habituel-EFAULT
.Ce que je ne sais pas, c'est comment cela pourrait porter sur d'autres architectures, mais ce serait bien d'avoir quelque chose comme ça dans le noyau.
Si cela était possible pour toutes les arches, j'imagine qu'il pourrait y avoir un wrapper d'espace utilisateur qui fournirait une sécurité de type via certaines unions et macros (il pourrait sélectionner un membre de l'union en fonction du nom de l'appel système et toutes les unions seraient ensuite converties en 6 longs ou quel que soit l'équivalent en architecture de jour des 6 longs).
la source
open
inwrite
etclose
. Cela augmenterait un peu la complexité en raison de get / put_user, mais cela en vaut probablement la peine. En ce qui concerne la portabilité IIRC, certaines architectures peuvent encombrer les registres d'appel système pour les arguments 5 et 6 si un appel système 5 ou 6 arguments est groupé ... l'ajout de 2 arguments supplémentaires pour une utilisation future résoudrait cela et pourrait être utilisé à l'avenir pour les paramètres d'appel asynchrones si un drapeau SUPERSYSCALL__async est définiDeux principaux problèmes qui viennent immédiatement à l'esprit sont:
Gestion des erreurs: chaque appel système individuel peut se terminer par une erreur qui doit être vérifiée et gérée par votre code d'espace utilisateur. Un appel par lots devrait donc de toute façon exécuter du code d'espace utilisateur après chaque appel individuel, de sorte que les avantages des appels par lots d'espace noyau seraient annulés. De plus, l'API devrait être très complexe (si possible à concevoir) - par exemple, comment exprimer une logique telle que "si le troisième appel a échoué, faire quelque chose et sauter le quatrième appel mais continuer avec le cinquième")?
De nombreux appels "combinés" qui sont effectivement mis en œuvre offrent des avantages supplémentaires en plus de ne pas avoir à se déplacer entre l'espace utilisateur et le noyau. Par exemple, ils éviteront souvent de copier la mémoire et d'utiliser des tampons (par exemple, transférer des données directement d'un endroit dans le tampon de page à un autre au lieu de les copier via un tampon intermédiaire). Bien sûr, cela n'a de sens que pour des combinaisons spécifiques d'appels (par exemple, lecture-écriture), et non pour des combinaisons arbitraires d'appels groupés.
la source