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