接下来讲解一下用unshare
和clone
来创建NetWork NameSpace,先讲解一下这两个API:
unshare
unshare
用于将当前进程分离(unshare)出指定的命名空间(namespace)。它允许进程创建独立于父进程的命名空间,从而实现进程在不同命名空间中运行的隔离。
c
#include <sched.h>
int unshare(int flags);
flags
:指定要分离的命名空间类型,可以使用以下标志的按位或运算进行组合:
CLONE_NEWNS
:分离挂载命名空间(Mount Namespace)。CLONE_NEWUTS
:分离UTS命名空间。CLONE_NEWIPC
:分离IPC命名空间。CLONE_NEWPID
:分离PID命名空间。CLONE_NEWNET
:分离网络命名空间。CLONE_NEWUSER
:分离用户命名空间。CLONE_NEWCGROUP
:分离控制组命名空间。CLONE_NEWTIME
:分离时间命名空间(自Linux 5.6起可用)。成功调用unshare
函数会将当前进程分离出指定的命名空间,并创建一个新的独立命名空间。在新的命名空间中,进程可以执行与父进程隔离的操作,例如挂载文件系统、更改主机名、配置网络接口等。
unshare
函数只分离命名空间,具体的命名空间配置需要在调用unshare
之后进行,例如使用mount
函数挂载文件系统、sethostname
函数设置主机名等。
clone
clone
用于创建一个新的进程,并可以选择性地与父进程共享或分离多个资源,包括进程空间、文件系统、信号处理等。
c
#include <sched.h>
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...);
fn
:指向子进程要执行的函数的指针。该函数应该具有 int
类型的返回值,接受一个 void*
类型的参数。child_stack
:指向子进程栈的指针。子进程会在一个新的栈空间中执行,child_stack
指向该栈的顶部。栈应该是独立分配的,并且具有足够的空间来容纳子进程执行所需的栈帧。flags
:用于指定创建子进程时的行为和资源共享方式的标志位,可以使用以下标志的按位或运算进行组合:
CLONE_CHILD_CLEARTID
:子进程在调用exec
函数或退出时,会将tid
置为0。CLONE_CHILD_SETTID
:子进程在调用exec
函数或退出时,会将tid
置为子进程ID。CLONE_FILES
:子进程与父进程共享文件描述符表。CLONE_FS
:子进程与父进程共享文件系统信息。CLONE_IO
:子进程与父进程共享I/O上下文。CLONE_NEWIPC
:子进程创建一个新的IPC命名空间。CLONE_NEWNET
:子进程创建一个新的网络命名空间。CLONE_NEWNS
:子进程创建一个新的挂载命名空间。CLONE_NEWPID
:子进程创建一个新的PID命名空间。CLONE_NEWUTS
:子进程创建一个新的UTS命名空间。CLONE_NEWUSER
:子进程创建一个新的用户命名空间。CLONE_NEWCGROUP
:子进程创建一个新的控制组命名空间。CLONE_PARENT
:子进程的父进程为调用clone
的进程。CLONE_PARENT_SETTID
:子进程的父进程ID会存储在ptid
中。CLONE_PTRACE
:子进程与父进程共享ptrace
。CLONE_SETTLS
:子进程的线程局部存储与父进程相同。CLONE_SIGHAND
:子进程与父进程共享信号处理程序。CLONE_SYSVSEM
:子进程与父进程共享System V信号量。CLONE_THREAD
:子进程创建为与父进程共享线程组的线程。CLONE_UNTRACED
:子进程创建后不会受到ptrace
跟踪。CLONE_VFORK
:子进程使用父进程的页表直到调用exec
或者调用_exit
退出。CLONE_VM
:子进程与父进程共享内存空间。arg
:传递给子进程函数的参数。clone
函数创建一个新的进程,并在子进程中执行指定的函数。子进程的执行流程会从 fn
指向的函数开始。child_stack
参数指定子进程的栈空间,flags
参数用于指定资源的共享方式。子进程创建成功后,clone
函数会返回子进程的进程ID。
接下来是调用示例:
c#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define STACK_SIZE 65536
static int child_func(void *arg) {
// 在子进程中,进行需要在独立网络命名空间中运行的操作
printf("Child process: PID=%ld\n", (long) getpid());
// 在子进程中,可以进行网络相关的操作,例如配置网络接口、设置IP地址等
sleep(10); // 模拟子进程在网络命名空间中的操作
printf("Child process: Exiting\n");
return 0;
}
int main() {
printf("Parent process: PID=%ld\n", (long) getpid());
// 为子进程分配栈空间
char *stack = malloc(STACK_SIZE);
if (stack == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
// 使用clone创建子进程,并指定CLONE_NEWNET标志来创建独立网络命名空间
pid_t child_pid = clone(child_func, stack + STACK_SIZE, CLONE_NEWNET | SIGCHLD, NULL);
if (child_pid == -1) {
perror("clone");
exit(EXIT_FAILURE);
}
// 在父进程中,可以继续进行其他操作
sleep(5); // 模拟父进程的操作
printf("Parent process: Waiting for child process to exit...\n");
waitpid(child_pid, NULL, 0); // 等待子进程退出
printf("Parent process: Exiting\n");
return 0;
}
c#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
printf("Parent process: PID=%ld\n", (long) getpid());
printf("Parent process: Creating a new network namespace...\n");
// 创建独立的网络命名空间
if (unshare(CLONE_NEWNET) == -1) {
perror("unshare");
exit(EXIT_FAILURE);
}
printf("Parent process: In the new network namespace\n");
// 在新的网络命名空间中,可以进行网络相关的操作,例如配置网络接口、设置IP地址等
sleep(10); // 模拟在网络命名空间中的操作
printf("Parent process: Exiting\n");
return 0;
}
以上程序经过运行测试,必须要用root权限才能创建namespace
。
本文作者:yowayimono
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!