Maison  >  Article  >  développement back-end  >  Pourquoi l'appel de Setns depuis Go renvoie-t-il EINVAL pour l'espace de noms Mnt ?

Pourquoi l'appel de Setns depuis Go renvoie-t-il EINVAL pour l'espace de noms Mnt ?

Susan Sarandon
Susan Sarandonoriginal
2024-11-01 05:35:02567parcourir

Why Does Calling Setns from Go Return EINVAL for the Mnt Namespace?

L'appel de Setns depuis Go renvoie EINVAL pour l'espace de noms Mnt

Contexte

Le but est de entrez l'espace de noms mnt d'un conteneur en utilisant le code C ou Go. Cependant, le code Go renvoie systématiquement EINVAL à partir de l'appel setns lors de la tentative d'entrée dans l'espace de noms mnt.

Code C de travail

Le code C suivant entre avec succès dans tous les espaces de noms spécifiés :

<code class="c">#include <sched.h>

main() {
    // ...
    for (i=0; i<5; i++) {
        setns(fd, 0); // Join the provided namespace
    }
}</code>

Échec du code Go

En revanche, le code Go équivalent renvoie une erreur EINVAL pour l'espace de noms mnt :

<code class="go">import (
    "syscall"
)

func main() {
    // ...
    err := syscall.RawSyscall(308, fd, 0, 0) // Calling setns
    if err != 0 {
        fmt.Println("setns on", namespaces[i], "namespace failed")
    }
}</code>

Réponse

Le problème réside dans la nature multithread de Go. Lorsque Go appelle setns à partir d'un contexte multithread, il échoue pour l'espace de noms mnt car il nécessite un appelant monothread. Pour résoudre ce problème, l'appel setns doit être effectué avant que le runtime Go ne crée des threads supplémentaires.

Solution : astuce du constructeur à thread unique

Une façon d'y parvenir est pour utiliser l'astuce du constructeur CGO, où une fonction C est exécutée avant le démarrage de Go :

<code class="c">__attribute__((constructor)) void enter_namespace(void) { setns(...); }</code>

L'ajout de ce constructeur au code Go, ainsi que le codage en dur du PID, permettent une entrée réussie dans l'espace de noms mnt :

<code class="go">/*
__attribute__((constructor)) void enter_namespace(void) { ... }
*/
import "C"</code>

Cependant, cette approche nécessite de coder en dur le PID, ce qui n'est pas idéal.

Alternative : Linux Kernel 4.16 et supérieur

Dans le noyau Linux 4.16 et versions ultérieures, un nouveau mode a été introduit pour setns qui permet de l'appeler en toute sécurité à partir de contextes multithread. En utilisant une version modifiée de l'appel setns qui prend un argument supplémentaire (CLONE_IOCLONE_COMMON), il est possible de saisir l'espace de noms mnt depuis Go, même s'il est multi-thread.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn