这是个非常经典的问题,也是牛客上面试Top100的面试题!我们来直观地总结一下 线程和进程之间资源的共享情况。
🧠 一、进程和线程定义先对上:
- 进程(Process):操作系统资源分配的最小单位。
- 线程(Thread):CPU 调度的最小单位,是进程内的执行单元。
🔍 二、共享情况一览表:
| 资源 | 进程 | 线程(同一进程下) |
|---|---|---|
| 内存空间(代码段、堆、全局变量) | ❌ 不共享 | ✅ 共享 |
| 文件描述符(FD) | ❌ 不共享 | ✅ 共享 |
| 用户 ID、组 ID | ❌ 不共享 | ✅ 共享 |
| 栈空间(Stack) | ❌ 独立 | ❌ 独立(每个线程有自己的栈) |
| 程序计数器(PC) | ❌ 独立 | ❌ 独立(控制执行流) |
| 信号处理方式 | ❌ 不共享 | ✅ 共享 |
| 当前工作目录 | ❌ 不共享 | ✅ 共享 |
| 父进程 PID | ❌ 不共享 | ✅ 共享(因为在同一进程内) |
✅ 所以,总结一句话:
同一进程下的多个线程:共享内存和文件资源,但每个线程都有自己的栈和执行流。
而不同进程之间,默认是完全隔离的,除非使用进程间通信(IPC)机制。
📌 举个例子来加深理解:
假设你在写一个 Web 服务器(比如 SpringBoot + Tomcat):
- 每个请求来时,Tomcat 会启一个新线程处理。
- 这些线程共享内存中的缓存、数据库连接池、日志对象(heap 区);
- 但它们各自有自己的调用栈(方法调用顺序、局部变量等);
- 所以一个线程崩了,不一定会影响别的线程;
- 但如果线程把共享数据搞乱了(比如多个线程同时改一个变量),就可能引发线程安全问题 🧨。
Docker 的进程隔离能力是其最核心的功能之一,它通过 Linux 内核提供的一系列技术 实现进程之间的「隔离」,主要包括:
🧱 一、Docker进程隔离关键技术
| 技术 | 作用 |
|---|---|
| Namespaces(命名空间) | 实现资源隔离 |
| Cgroups(控制组) | 实现资源限制 |
| UnionFS / OverlayFS | 实现镜像层叠、文件系统隔离 |
| Seccomp / AppArmor / SELinux | 安全限制,防止容器越权操作宿主机 |
🧠 二、进程隔离的核心:Namespaces
Docker 利用 Linux 的 Namespaces 实现容器内的进程和宿主机、其他容器进程的隔离。主要的几种:
1. PID namespace(进程号隔离)
- 容器中的进程有自己独立的 PID 编号。
- 宿主机可以看到容器的所有进程,但容器内只能看到自己的。
- 容器内的
init进程通常是 PID 1。
2. NET namespace(网络隔离)
- 每个容器拥有独立的网络设备(如
eth0)、IP、端口等。 - 宿主机或其他容器要访问,需要通过 bridge / host 网络模式等。
3. MNT namespace(挂载点隔离)
- 容器看到的是自己的文件系统挂载点。
- 宿主机上的目录可以通过
-v或--mount映射到容器中。
4. UTS namespace(主机名隔离)
- 容器可以有自己的 hostname,不影响宿主机。
5. IPC namespace(进程间通信隔离)
- 不同容器的共享内存、信号等 IPC 资源是隔离的。
6. User namespace(用户权限隔离)
- 容器内的 root 用户可以被映射为宿主机的普通用户,增强安全性(但默认关闭)。
💡 三、Cgroups(控制组)限制资源
- 限制每个容器的 CPU、内存、磁盘 IO、网络带宽等资源
- 防止某个容器把系统资源吃光
例如:
docker run --cpus=1 --memory=512m ubuntu
就利用了 cgroups 来限制容器的资源。
🧊 四、总结一句话:
Docker 的进程隔离是通过 Linux 的 Namespaces 实现“看不见彼此”,通过 Cgroups 实现“抢不到资源”,再配合文件系统隔离和安全机制,构建了一个像“轻量级虚拟机”的容器环境。
如果你想深入了解某个 namespace 或在宿主机上实验一下,我也可以手把手演示 👨💻