您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Linux Namespace怎么使用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
Linux Namespace
是 Linux 提供的一種內(nèi)核級(jí)別環(huán)境隔離的方法。用官方的話來(lái)說(shuō),Linux Namespace 將全局系統(tǒng)資源封裝在一個(gè)抽象中,從而使 namespace 內(nèi)的進(jìn)程認(rèn)為自己具有獨(dú)立的資源實(shí)例。這項(xiàng)技術(shù)本來(lái)沒(méi)有掀起多大的波瀾,是容器技術(shù)的崛起讓他重新引起了大家的注意。
Linux Namespace 有如下 6 個(gè)種類:
分類 | 系統(tǒng)調(diào)用參數(shù) | 相關(guān)內(nèi)核版本 |
---|---|---|
Mount namespaces | CLONE_NEWNS | Linux 2.4.19 |
UTS namespaces | CLONE_NEWUTS | Linux 2.6.19 |
IPC namespaces | CLONE_NEWIPC | Linux 2.6.19 |
PID namespaces | CLONE_NEWPID | Linux 2.6.24 |
Network namespaces | CLONE_NEWNET | 始于Linux 2.6.24 完成于 Linux 2.6.29 |
User namespaces | CLONE_NEWUSER | 始于 Linux 2.6.23 完成于 Linux 3.8 |
namespace 的 API 由三個(gè)系統(tǒng)調(diào)用和一系列 /proc
文件組成,本文將會(huì)詳細(xì)介紹這些系統(tǒng)調(diào)用和 /proc
文件。為了指定要操作的 namespace 類型,需要在系統(tǒng)調(diào)用的 flag 中通過(guò)常量 CLONE_NEW*
指定(包括 CLONE_NEWIPC
,CLONE_NEWNS
, CLONE_NEWNET
,CLONE_NEWPID
,CLONE_NEWUSER
和 `CLONE_NEWUTS),可以指定多個(gè)常量,通過(guò) |(位或)操作來(lái)實(shí)現(xiàn)。
簡(jiǎn)單描述一下三個(gè)系統(tǒng)調(diào)用的功能:
clone() : 實(shí)現(xiàn)線程的系統(tǒng)調(diào)用,用來(lái)創(chuàng)建一個(gè)新的進(jìn)程,并可以通過(guò)設(shè)計(jì)上述系統(tǒng)調(diào)用參數(shù)達(dá)到隔離的目的。
unshare() : 使某進(jìn)程脫離某個(gè) namespace。
setns() : 把某進(jìn)程加入到某個(gè) namespace。
具體的實(shí)現(xiàn)原理請(qǐng)往下看。
clone()
的原型如下:
int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);
child_func : 傳入子進(jìn)程運(yùn)行的程序主函數(shù)。
child_stack : 傳入子進(jìn)程使用的??臻g。
flags : 表示使用哪些 CLONE_*
標(biāo)志位。
args : 用于傳入用戶參數(shù)。
clone()
與 fork()
類似,都相當(dāng)于把當(dāng)前進(jìn)程復(fù)制了一份,但 clone()
可以更細(xì)粒度地控制與子進(jìn)程共享的資源(其實(shí)就是通過(guò) flags 來(lái)控制),包括虛擬內(nèi)存、打開(kāi)的文件描述符和信號(hào)量等等。一旦指定了標(biāo)志位 CLONE_NEW*
,相對(duì)應(yīng)類型的 namespace 就會(huì)被創(chuàng)建,新創(chuàng)建的進(jìn)程也會(huì)成為該 namespace 中的一員。
clone() 的原型并不是最底層的系統(tǒng)調(diào)用,而是封裝過(guò)的,真正的系統(tǒng)調(diào)用內(nèi)核實(shí)現(xiàn)函數(shù)為 do_fork()
,形式如下:
long do_fork(unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr)
其中 clone_flags
可以賦值為上面提到的標(biāo)志。
下面來(lái)看一個(gè)例子:
/* demo_uts_namespaces.c Copyright 2013, Michael Kerrisk Licensed under GNU General Public License v2 or later Demonstrate the operation of UTS namespaces. */ #define _GNU_SOURCE #include <sys/wait.h> #include <sys/utsname.h> #include <sched.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> /* A simple error-handling function: print an error message based on the value in 'errno' and terminate the calling process */ #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) static int /* Start function for cloned child */ childFunc(void *arg) { struct utsname uts; /* 在新的 UTS namespace 中修改主機(jī)名 */ if (sethostname(arg, strlen(arg)) == -1) errExit("sethostname"); /* 獲取并顯示主機(jī)名 */ if (uname(&uts) == -1) errExit("uname"); printf("uts.nodename in child: %s\n", uts.nodename); /* Keep the namespace open for a while, by sleeping. This allows some experimentation--for example, another process might join the namespace. */ sleep(100); return 0; /* Terminates child */ } /* 定義一個(gè)給 clone 用的棧,棧大小1M */ #define STACK_SIZE (1024 * 1024) static char child_stack[STACK_SIZE]; int main(int argc, char *argv[]) { pid_t child_pid; struct utsname uts; if (argc < 2) { fprintf(stderr, "Usage: %s <child-hostname>\n", argv[0]); exit(EXIT_FAILURE); } /* 調(diào)用 clone 函數(shù)創(chuàng)建一個(gè)新的 UTS namespace,其中傳出一個(gè)函數(shù),還有一個(gè)??臻g(為什么傳尾指針,因?yàn)闂J欠粗模? 新的進(jìn)程將在用戶定義的函數(shù) childFunc() 中執(zhí)行 */ child_pid = clone(childFunc, child_stack + STACK_SIZE, /* 因?yàn)闂J欠粗模?nbsp; 所以傳尾指針 */ CLONE_NEWUTS | SIGCHLD, argv[1]); if (child_pid == -1) errExit("clone"); printf("PID of child created by clone() is %ld\n", (long) child_pid); /* Parent falls through to here */ sleep(1); /* 給子進(jìn)程預(yù)留一定的時(shí)間來(lái)改變主機(jī)名 */ /* 顯示當(dāng)前 UTS namespace 中的主機(jī)名,和 子進(jìn)程所在的 UTS namespace 中的主機(jī)名不同 */ if (uname(&uts) == -1) errExit("uname"); printf("uts.nodename in parent: %s\n", uts.nodename); if (waitpid(child_pid, NULL, 0) == -1) /* 等待子進(jìn)程結(jié)束 */ errExit("waitpid"); printf("child has terminated\n"); exit(EXIT_SUCCESS); }
該程序通過(guò)標(biāo)志位 CLONE_NEWUTS
調(diào)用 clone()
函數(shù)創(chuàng)建一個(gè) UTS namespace。UTS namespace 隔離了兩個(gè)系統(tǒng)標(biāo)識(shí)符 — 主機(jī)名和 NIS 域名 —它們分別通過(guò) sethostname()
和 setdomainname()
這兩個(gè)系統(tǒng)調(diào)用來(lái)設(shè)置,并通過(guò)系統(tǒng)調(diào)用 uname()
來(lái)獲取。
下面將對(duì)程序中的一些關(guān)鍵部分進(jìn)行解讀(為了簡(jiǎn)單起見(jiàn),我們將省略其中的錯(cuò)誤檢查)。
程序運(yùn)行時(shí)后面需要跟上一個(gè)命令行參數(shù),它將會(huì)創(chuàng)建一個(gè)在新的 UTS namespace 中執(zhí)行的子進(jìn)程,該子進(jìn)程會(huì)在新的 UTS namespace 中將主機(jī)名改為命令行參數(shù)中提供的值。
主程序的第一個(gè)關(guān)鍵部分是通過(guò)系統(tǒng)調(diào)用 clone()
來(lái)創(chuàng)建子進(jìn)程:
child_pid = clone(childFunc, child_stack + STACK_SIZE, /* Points to start of downwardly growing stack */ CLONE_NEWUTS | SIGCHLD, argv[1]); printf("PID of child created by clone() is %ld\n", (long) child_pid);
子進(jìn)程將會(huì)在用戶定義的函數(shù) childFunc()
中開(kāi)始執(zhí)行,該函數(shù)將會(huì)接收 clone()
最后的參數(shù)(argv[1])作為自己的參數(shù),并且標(biāo)志位包含了 CLONE_NEWUTS
,所以子進(jìn)程會(huì)在新創(chuàng)建的 UTS namespace 中執(zhí)行。
接下來(lái)主進(jìn)程睡眠一段時(shí)間,讓子進(jìn)程能夠有時(shí)間更改其 UTS namespace 中的主機(jī)名。然后調(diào)用 uname()
來(lái)檢索當(dāng)前 UTS namespace 中的主機(jī)名,并顯示該主機(jī)名:
sleep(1); /* Give child time to change its hostname */ uname(&uts); printf("uts.nodename in parent: %s\n", uts.nodename);
與此同時(shí),由 clone()
創(chuàng)建的子進(jìn)程執(zhí)行的函數(shù) childFunc()
首先將主機(jī)名改為命令行參數(shù)中提供的值,然后檢索并顯示修改后的主機(jī)名:
sethostname(arg, strlen(arg); uname(&uts); printf("uts.nodename in child: %s\n", uts.nodename);
子進(jìn)程退出之前也睡眠了一段時(shí)間,這樣可以防止新的 UTS namespace 不會(huì)被關(guān)閉,讓我們能夠有機(jī)會(huì)進(jìn)行后續(xù)的實(shí)驗(yàn)。
執(zhí)行程序,觀察父進(jìn)程和子進(jìn)程是否處于不同的 UTS namespace 中:
$ su # 需要特權(quán)才能創(chuàng)建 UTS namespace Password: # uname -n antero # ./demo_uts_namespaces bizarro PID of child created by clone() is 27514 uts.nodename in child: bizarro uts.nodename in parent: antero
除了 User namespace 之外,創(chuàng)建其他的 namespace 都需要特權(quán),更確切地說(shuō),是需要相應(yīng)的 Linux Capabilities
,即 CAP_SYS_ADMIN
。這樣就可以避免設(shè)置了 SUID(Set User ID on execution)的程序因?yàn)橹鳈C(jī)名不同而做出一些愚蠢的行為。如果對(duì) Linux Capabilities 不是很熟悉,可以參考我之前的文章:Linux Capabilities 入門教程:概念篇。
每個(gè)進(jìn)程都有一個(gè) /proc/PID/ns
目錄,其下面的文件依次表示每個(gè) namespace, 例如 user 就表示 user namespace。從 3.8 版本的內(nèi)核開(kāi)始,該目錄下的每個(gè)文件都是一個(gè)特殊的符號(hào)鏈接,鏈接指向 $namespace:[$namespace-inode-number]
,前半部份為 namespace 的名稱,后半部份的數(shù)字表示這個(gè) namespace 的句柄號(hào)。句柄號(hào)用來(lái)對(duì)進(jìn)程所關(guān)聯(lián)的 namespace 執(zhí)行某些操作。
$ ls -l /proc/$$/ns # $$ 表示當(dāng)前所在的 shell 的 PID total 0 lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 ipc -> ipc:[4026531839] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 mnt -> mnt:[4026531840] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 net -> net:[4026531956] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 pid -> pid:[4026531836] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 user -> user:[4026531837] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 uts -> uts:[4026531838]
這些符號(hào)鏈接的用途之一是用來(lái)確認(rèn)兩個(gè)不同的進(jìn)程是否處于同一 namespace 中。如果兩個(gè)進(jìn)程指向的 namespace inode number 相同,就說(shuō)明他們?cè)谕粋€(gè) namespace 下,否則就在不同的 namespace 下。這些符號(hào)鏈接指向的文件比較特殊,不能直接訪問(wèn),事實(shí)上指向的文件存放在被稱為 nsfs
的文件系統(tǒng)中,該文件系統(tǒng)用戶不可見(jiàn),可以使用系統(tǒng)調(diào)用 stat() 在返回的結(jié)構(gòu)體的 st_ino
字段中獲取 inode number。在 shell 終端中可以用命令(實(shí)際上就是調(diào)用了 stat())看到指向文件的 inode 信息:
$ stat -L /proc/$$/ns/net File: /proc/3232/ns/net Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 4h/4d Inode: 4026531956 Links: 1 Access: (0444/-r--r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2020-01-17 15:45:23.783304900 +0800 Modify: 2020-01-17 15:45:23.783304900 +0800 Change: 2020-01-17 15:45:23.783304900 +0800 Birth: -
除了上述用途之外,這些符號(hào)鏈接還有其他的用途,如果我們打開(kāi)了其中一個(gè)文件,那么只要與該文件相關(guān)聯(lián)的文件描述符處于打開(kāi)狀態(tài),即使該 namespace 中的所有進(jìn)程都終止了,該 namespace 依然不會(huì)被刪除。通過(guò) bind mount 將符號(hào)鏈接掛載到系統(tǒng)的其他位置,也可以獲得相同的效果:
$ touch ~/uts $ mount --bind /proc/27514/ns/uts ~/uts
加入一個(gè)已經(jīng)存在的 namespace 可以通過(guò)系統(tǒng)調(diào)用 setns()
來(lái)完成。它的原型如下:
int setns(int fd, int nstype);
更確切的說(shuō)法是:setns()
將調(diào)用的進(jìn)程與特定類型 namespace 的一個(gè)實(shí)例分離,并將該進(jìn)程與該類型 namespace 的另一個(gè)實(shí)例重新關(guān)聯(lián)。
fd
表示要加入的 namespace 的文件描述符,可以通過(guò)打開(kāi)其中一個(gè)符號(hào)鏈接來(lái)獲取,也可以通過(guò)打開(kāi) bind mount 到其中一個(gè)鏈接的文件來(lái)獲取。
nstype
讓調(diào)用者可以去檢查 fd 指向的 namespace 類型,值可以設(shè)置為前文提到的常量 CLONE_NEW*
,填 0
表示不檢查。如果調(diào)用者已經(jīng)明確知道自己要加入了 namespace 類型,或者不關(guān)心 namespace 類型,就可以使用該參數(shù)來(lái)自動(dòng)校驗(yàn)。
結(jié)合 setns()
和 execve()
可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單但非常有用的功能:將某個(gè)進(jìn)程加入某個(gè)特定的 namespace,然后在該 namespace 中執(zhí)行命令。直接來(lái)看例子:
/* ns_exec.c Copyright 2013, Michael Kerrisk Licensed under GNU General Public License v2 or later Join a namespace and execute a command in the namespace */ #define _GNU_SOURCE #include <fcntl.h> #include <sched.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> /* A simple error-handling function: print an error message based on the value in 'errno' and terminate the calling process */ #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) int main(int argc, char *argv[]) { int fd; if (argc < 3) { fprintf(stderr, "%s /proc/PID/ns/FILE cmd [arg...]\n", argv[0]); exit(EXIT_FAILURE); } fd = open(argv[1], O_RDONLY); /* 獲取想要加入的 namespace 的文件描述符 */ if (fd == -1) errExit("open"); if (setns(fd, 0) == -1) /* 加入該 namespace */ errExit("setns"); execvp(argv[2], &argv[2]); /* 在加入的 namespace 中執(zhí)行相應(yīng)的命令 */ errExit("execvp"); }
該程序運(yùn)行需要兩個(gè)或兩個(gè)以上的命令行參數(shù),第一個(gè)參數(shù)表示特定的 namespace 符號(hào)鏈接的路徑(或者 bind mount 到這些符號(hào)鏈接的文件路徑);第二個(gè)參數(shù)表示要在該符號(hào)鏈接相對(duì)應(yīng)的 namespace 中執(zhí)行的程序名稱,以及執(zhí)行這個(gè)程序所需的命令行參數(shù)。關(guān)鍵步驟如下:
fd = open(argv[1], O_RDONLY); /* 獲取想要加入的 namespace 的文件描述符 */ setns(fd, 0); /* 加入該 namespace */ execvp(argv[2], &argv[2]); /* 在加入的 namespace 中執(zhí)行相應(yīng)的命令 */
還記得我們之前已經(jīng)通過(guò) bind mount 將 demo_uts_namespaces
創(chuàng)建的 UTS namespace 掛載到 ~/uts
中了嗎?可以將本例中的程序與之結(jié)合,讓新進(jìn)程可以在該 UTS namespace 中執(zhí)行 shell:
$ ./ns_exec ~/uts /bin/bash # ~/uts 被 bind mount 到了 /proc/27514/ns/uts My PID is: 28788
驗(yàn)證新的 shell 是否與 demo_uts_namespaces
創(chuàng)建的子進(jìn)程處于同一個(gè) UTS namespace:
$ hostname bizarro $ readlink /proc/27514/ns/uts uts:[4026532338] $ readlink /proc/$$/ns/uts # $$ 表示當(dāng)前 shell 的 PID uts:[4026532338]
在早期的內(nèi)核版本中,不能使用 setns()
來(lái)加入 mount namespace、PID namespace 和 user namespace,從 3.8 版本的內(nèi)核開(kāi)始,setns()
支持加入所有的 namespace。
util-linux 包里提供了nsenter
命令,其提供了一種方式將新創(chuàng)建的進(jìn)程運(yùn)行在指定的 namespace 里面,它的實(shí)現(xiàn)很簡(jiǎn)單,就是通過(guò)命令行(-t 參數(shù))指定要進(jìn)入的 namespace 的符號(hào)鏈接,然后利用 setns()
將當(dāng)前的進(jìn)程放到指定的 namespace 里面,再調(diào)用 clone()
運(yùn)行指定的執(zhí)行文件。我們可以用 strace
來(lái)看看它的運(yùn)行情況:
# strace nsenter -t 27242 -i -m -n -p -u /bin/bash execve("/usr/bin/nsenter", ["nsenter", "-t", "27242", "-i", "-m", "-n", "-p", "-u", "/bin/bash"], [/* 21 vars */]) = 0 ………… ………… pen("/proc/27242/ns/ipc", O_RDONLY) = 3 open("/proc/27242/ns/uts", O_RDONLY) = 4 open("/proc/27242/ns/net", O_RDONLY) = 5 open("/proc/27242/ns/pid", O_RDONLY) = 6 open("/proc/27242/ns/mnt", O_RDONLY) = 7 setns(3, CLONE_NEWIPC) = 0 close(3) = 0 setns(4, CLONE_NEWUTS) = 0 close(4) = 0 setns(5, CLONE_NEWNET) = 0 close(5) = 0 setns(6, CLONE_NEWPID) = 0 close(6) = 0 setns(7, CLONE_NEWNS) = 0 close(7) = 0 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f4deb1faad0) = 4968
最后一個(gè)要介紹的系統(tǒng)調(diào)用是 unshare()
,它的原型如下:
int unshare(int flags);
unshare()
與 clone()
類似,但它運(yùn)行在原先的進(jìn)程上,不需要?jiǎng)?chuàng)建一個(gè)新進(jìn)程,即:先通過(guò)指定的 flags 參數(shù) CLONE_NEW*
創(chuàng)建一個(gè)新的 namespace,然后將調(diào)用者加入該 namespace。最后實(shí)現(xiàn)的效果其實(shí)就是將調(diào)用者從當(dāng)前的 namespace 分離,然后加入一個(gè)新的 namespace。
Linux 中自帶的 unshare
命令,就是通過(guò) unshare() 系統(tǒng)調(diào)用實(shí)現(xiàn)的,使用方法如下:
$ unshare [options] program [arguments]
options
指定要?jiǎng)?chuàng)建的 namespace 類型。
unshare 命令的主要實(shí)現(xiàn)如下:
/* 通過(guò)提供的命令行參數(shù)初始化 'flags' */ unshare(flags); /* Now execute 'program' with 'arguments'; 'optind' is the index of the next command-line argument after options */ execvp(argv[optind], &argv[optind]);
unshare 命令的完整實(shí)現(xiàn)如下:
/* unshare.c Copyright 2013, Michael Kerrisk Licensed under GNU General Public License v2 or later A simple implementation of the unshare(1) command: unshare namespaces and execute a command. */ #define _GNU_SOURCE #include <sched.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> /* A simple error-handling function: print an error message based on the value in 'errno' and terminate the calling process */ #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) static void usage(char *pname) { fprintf(stderr, "Usage: %s [options] program [arg...]\n", pname); fprintf(stderr, "Options can be:\n"); fprintf(stderr, " -i unshare IPC namespace\n"); fprintf(stderr, " -m unshare mount namespace\n"); fprintf(stderr, " -n unshare network namespace\n"); fprintf(stderr, " -p unshare PID namespace\n"); fprintf(stderr, " -u unshare UTS namespace\n"); fprintf(stderr, " -U unshare user namespace\n"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int flags, opt; flags = 0; while ((opt = getopt(argc, argv, "imnpuU")) != -1) { switch (opt) { case 'i': flags |= CLONE_NEWIPC; break; case 'm': flags |= CLONE_NEWNS; break; case 'n': flags |= CLONE_NEWNET; break; case 'p': flags |= CLONE_NEWPID; break; case 'u': flags |= CLONE_NEWUTS; break; case 'U': flags |= CLONE_NEWUSER; break; default: usage(argv[0]); } } if (optind >= argc) usage(argv[0]); if (unshare(flags) == -1) errExit("unshare"); execvp(argv[optind], &argv[optind]); errExit("execvp"); }
下面我們執(zhí)行 unshare.c
程序在一個(gè)新的 mount namespace 中執(zhí)行 shell:
$ echo $$ # 顯示當(dāng)前 shell 的 PID 8490 $ cat /proc/8490/mounts | grep mq # 顯示當(dāng)前 namespace 中的某個(gè)掛載點(diǎn) mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0 $ readlink /proc/8490/ns/mnt # 顯示當(dāng)前 namespace 的 ID mnt:[4026531840] $ ./unshare -m /bin/bash # 在新創(chuàng)建的 mount namespace 中執(zhí)行新的 shell $ readlink /proc/$$/ns/mnt # 顯示新 namespace 的 ID mnt:[4026532325]
對(duì)比兩個(gè) readlink
命令的輸出,可以知道兩個(gè)shell 處于不同的 mount namespace 中。改變新的 namespace 中的某個(gè)掛載點(diǎn),然后觀察兩個(gè) namespace 的掛載點(diǎn)是否有變化:
$ umount /dev/mqueue # 移除新 namespace 中的掛載點(diǎn) $ cat /proc/$$/mounts | grep mq # 檢查是否生效 $ cat /proc/8490/mounts | grep mq # 查看原來(lái)的 namespace 中的掛載點(diǎn)是否依然存在? mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0
可以看出,新的 namespace 中的掛載點(diǎn) /dev/mqueue
已經(jīng)消失了,但在原來(lái)的 namespace 中依然存在。
“Linux Namespace怎么使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。