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 サイトの他の関連記事を参照してください。