ホームページ  >  記事  >  バックエンド開発  >  Go から Setns を呼び出すと Mnt 名前空間の EINVAL が返されるのはなぜですか?

Go から Setns を呼び出すと Mnt 名前空間の EINVAL が返されるのはなぜですか?

Susan Sarandon
Susan Sarandonオリジナル
2024-11-01 05:35:02567ブラウズ

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

Go から Setns を呼び出すと、Mnt 名前空間の EINVAL が返されます

背景

目標は次のとおりです。 C または Go コードを使用して、コンテナーの mnt 名前空間を入力します。ただし、Go コードは、mnt 名前空間に入ろうとすると、setns 呼び出しから一貫して EINVAL を返します。

動作する C コード

次の C コードは、指定されたすべての名前空間に正常に入ることができます。 :

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

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

Failing Go Code

一方、同等の Go コードは mnt 名前空間に対して EINVAL エラーを返します:

<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>

答え

問題は Go のマルチスレッドの性質にあります。 Go がマルチスレッド コンテキストから setns を呼び出すと、シングルスレッドの呼び出し元が必要なため、mnt 名前空間では失敗します。これを解決するには、Go ランタイムが追加のスレッドを作成する前に setns 呼び出しを行う必要があります。

解決策: シングルスレッド コンストラクターのトリック

これを実現する 1 つの方法は次のとおりです。 CGO コンストラクター トリックを使用するには、Go の起動前に C 関数が実行されます。

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

このコンストラクターを Go コードに追加し、PID をハードコーディングすると、mnt 名前空間に正常に入ることができます。

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

ただし、このアプローチでは PID をハードコーディングする必要があり、理想的ではありません。

代替: Linux カーネル 4.16 以降

Linux カーネルの場合4.16 以降では、マルチスレッド コンテキストから安全に呼び出すことができる新しいモードが setns に導入されました。追加の引数 (CLONE_IOCLONE_COMMON) を受け取る setns 呼び出しの修正バージョンを使用すると、マルチスレッドであっても Go から mnt 名前空間に入ることができます。

以上がGo から Setns を呼び出すと Mnt 名前空間の EINVAL が返されるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。