>  기사  >  백엔드 개발  >  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이 반환됩니다.

Background

목표는 다음과 같습니다. 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>

출발 실패 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>

Answer

문제는 Go의 멀티스레드 특성에 있습니다. Go가 다중 스레드 컨텍스트에서 setns를 호출하면 단일 스레드 호출자가 필요하기 때문에 mnt 네임스페이스에 대해 실패합니다. 이 문제를 해결하려면 Go 런타임이 추가 스레드를 생성하기 전에 setns 호출을 수행해야 합니다.

해결책: 단일 스레드 생성자 트릭

이를 달성하는 한 가지 방법은 다음과 같습니다. Go가 시작되기 전에 C 함수가 실행되는 CGO 생성자 트릭을 사용하려면:

<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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.