1.Linux内存管理 页表和虚拟地址
-
每个进程访问的都是虚拟地址,而不是直接操作物理地址。
-
每个进程的虚拟地址空间是独立的,不会影响其他进程。
-
每个进程都有自己的页表,用来记录虚拟地址到物理地址的映射关系。
-
CPU 通过页表找到物理地址,然后才能真正访问内存。
这样就实现了进程隔离、内存保护,同时也能让物理内存更高效地管理和分配。
物理内存不够:
- 使用 Swap(交换分区/交换文件),把部分内存数据写入磁盘。
- 按需分配(Demand Paging),避免无用的内存分配。
- 触发 OOM 机制,杀死占用过多资源的进程。
2. 每个进程的地址空间同样大吗
- 地址空间大小理论上相同,但进程实际分配和使用的内存因程序不同而不同。
- 物理内存有限时,进程可能无法用满整个地址空间,会触发 swap 或 OOM 机制。
3. 多进程 多线程 线程池区别 使用场景举例
- 多进程:稳定性高(一个崩了不影响其他),但进程间通信(IPC)复杂,开销大。
- 多线程:稳定性低(一个线程崩溃可能影响整个进程),但数据共享和通信简单,切换速度快。
- 线程池:管理多个线程,避免频繁创建销毁线程,提高效率,资源管理更高效。
需求 | 选择方案 |
---|---|
CPU 密集型计算(如图像处理、AI 计算) | 多进程 |
I/O 密集型(如爬虫、日志处理、Web 服务器) | 多线程/线程池 |
需要稳定性(崩溃不影响整体) | 多进程 |
需要高并发但任务较小 | 线程池 |
需要大量数据共享 | 多线程/线程池 |
如果你的任务是:
- 计算量大(CPU 密集) → 多进程
- 涉及大量 I/O 操作(I/O 密集) → 多线程/线程池
- 大量短时任务 → 线程池
进程:
1.进程号:PID
ps -aux查看进程信息
进程的环境变量:env
export
export LINUX_APP=123456 # 添加 LINUX_APP 环境变量
命令还可以添加一个新的环境变量或删除一个环境变量
使用"export -n LINUX_APP"命令则可以删除 LINUX_APP 环境变量
内存管理

进程的常见状态
状态 | 描述 |
---|---|
运行(Running) | 进程正在运行,或者正在准备运行(被 CPU 调度) |
就绪(Ready) | 进程可执行,但 CPU 还未调度它 |
阻塞(Blocked) | 进程正在等待某个事件(如 I/O) |
挂起(Stopped) | 进程暂停运行(如 Ctrl+Z ),可用 SIGCONT 继续 |
终止(Terminated) | 进程已执行完成或被杀死(exit() 、kill ) |
STAT 状态字段:
R
:运行状态(Running)S
:睡眠(Sleeping)D
:不可中断睡眠(I/O 等待)Z
:僵尸进程(Zombie)T
:暂停(Stopped)
进程和线程的区别
对比项 | 进程(Process) | 线程(Thread) |
---|---|---|
定义 | 资源分配的基本单位 | CPU 调度的基本单位 |
地址空间 | 进程独立的地址空间 | 线程共享进程的地址空间 |
资源 | 进程有独立的内存、文件描述符等 | 线程共享进程的资源 |
通信 | 进程间通信(IPC)复杂,需要共享内存、管道等 | 线程间通信简单,可直接共享变量 |
开销 | 创建、切换成本高,需要分配独立资源 | 创建、切换成本低,只需切换寄存器 |
稳定性 | 一个进程崩溃不会影响其他进程 | 一个线程崩溃可能导致整个进程崩溃 |
适用场景 | 适用于大型独立任务(如 Web 服务器中的多个进程) | 适用于密集计算、并发任务(如 Web 服务器的多线程处理请求) |
总结:进程更独立,安全性高但开销大;线程更轻量,效率高但共享资源需要注意同步。
fork()创建子进程
init进程
僵尸进程与孤儿进程
孤儿进程:父进程先于子进程结束

守护进程:
进程同步:
同步方式 | 特点 | 适用场景 |
---|---|---|
互斥锁(Mutex) | 保证同时只有一个进程访问资源 | 临界区保护 |
信号量(Semaphore) | 可用于多个进程同步或控制资源并发数量 | 资源池控制(数据库连接池) |
文件锁(File Lock) | 锁定文件,防止多个进程同时写入 | 日志文件、数据库文件 |
管道(Pipe) | 进程间传递小量数据 | 父子进程通信 |
共享内存(Shared Memory) | 最快的通信方式,多个进程共享数据 | 高性能进程间通信 |
消息队列(Message Queue) | 进程安全的消息传递方式 | 任务调度 |
信号(Signal) | 进程发送异步通知 | 进程控制(kill、alarm) |
进程之间的通信:
管道:
信号(Signal):
进程间发送简单的通知信号
例如:
1.譬如在终端上按下 CTRL + C 组合按键可以产生中断信号(SIGINT),通过这个方法可以终止在前台运行的进程;按下 CTRL + Z 组合按键可以产生 暂停信号(SIGCONT),通过这个方法可以暂停当前前台运行的进程。
2.过 kill 命令将信号发送给其它进程
消息队列:
套接字
共享内存
通信方式 | 特点 | 适用场景 |
---|---|---|
管道(Pipe) | 单向通信,适用于父子进程 | 适合简单数据传输 |
命名管道(FIFO) | 可以跨无关进程,但仍然是单向通信 | 适用于本机进程间通信 |
消息队列(Message Queue) | 内核维护队列,进程间发送结构化消息 | 适用于多个进程的消息传递 |
共享内存(Shared Memory) | 速度最快,多个进程共享同一块内存,但需同步机制(如信号量) | 适用于大数据量、高速通信 |
信号量(Semaphore) | 控制进程同步,避免竞争 | 适用于进程同步、互斥 |
信号(Signal) | 进程间发送简单的通知信号 | 适用于异步事件通知 |
Socket(套接字) | 适用于本地进程和远程网络进程通信 | 适用于分布式系统、网络通信 |
线程:
gcc -o testApp testApp.c -lpthread
1. pthread_join()
- 作用:阻塞等待线程执行完毕,并回收该线程的资源。
- 特点:
- 调用
pthread_join()
的线程会阻塞,直到目标线程结束。 - 线程结束后,其资源会被回收,可以获取线程的返回值。
- 调用
- 适用场景:需要等待线程执行完成,例如计算任务。
2. pthread_detach()
- 作用:让线程自动释放资源,不阻塞调用线程。
- 特点:
- 线程终止后,系统会自动回收资源,但无法
pthread_join()
获取返回值。 - 适用于不关心线程返回状态的场景。
- 线程终止后,系统会自动回收资源,但无法
- 适用场景:后台任务、守护线程,不需要等待其结束。
线程的通信方式:
- 多线程通信主要依赖共享内存,但需要互斥锁、条件变量、信号量等同步手段。
- 多进程通信(IPC)由于地址空间独立,可以使用管道、共享内存、信号量、消息队列、套接字等方式。
- 多线程适用于高效任务并行,多进程适用于独立任务的隔离执行。
通信方式 | 适用于多线程 | 适用于多进程 | 说明 |
---|---|---|---|
共享内存 | ✅ | ✅ | 线程天然共享内存,多进程需要 shmget 等创建共享内存段。 |
互斥锁(Mutex) | ✅ | ❌ | 线程同步方式,防止数据竞争。 |
条件变量(Condition Variable) | ✅ | ❌ | 线程间等待某个条件满足。 |
信号量(Semaphore) | ✅ | ✅ | 可用于线程同步,也可用于进程同步。 |
管道(Pipe) | ❌ | ✅ | 进程间数据传输,线程不需要。 |
消息队列(Message Queue) | ❌ | ✅ | 进程间发送结构化数据。 |
套接字(Socket) | ❌ | ✅ | 进程间或跨机器通信。 |
线程同步:
互斥锁,读写锁,条件变量,自旋锁,条件变量,信号量。
读写锁:读多写少。
同步方式 | 特点 | 适用场景 |
---|---|---|
互斥锁(Mutex) | 保证同一时刻只有一个线程访问资源 | 线程间互斥访问共享数据 |
读写锁(RWLock) | 允许多个线程同时读,但写操作互斥 | 读多写少的场景(如数据库缓存) |
自旋锁(Spinlock) | 线程忙等,适合短时间锁定 | 轻量级锁,适用于高频访问 |
条件变量(Condition Variable) | 线程等待某个条件后再执行 | 生产者-消费者模型 |
信号量(Semaphore) | 控制多个线程同时访问共享资源 | 资源池控制(如连接池) |
用户态和内核态
方式 | 触发原因 | 示例 |
---|---|---|
系统调用 | 用户进程主动请求 OS 服务 | read() , write() , fork() |
异常 | 进程运行时出现错误 | 缺页异常、除零错误 |
中断 | 设备事件或外部信号 | 键盘输入、网络数据 |
(1)用户态(User Mode)
- 权限受限:只能访问自己的虚拟地址空间,不能直接操作硬件(CPU、内存、IO)。
- 只能执行普通指令,不能执行特权指令(如
cli
关闭中断)。 - 应用程序(如 Chrome、VS Code)都运行在用户态,通过系统调用与内核交互。
(2)内核态(Kernel Mode)
- 权限最高:可以访问所有硬件资源(CPU、内存、IO 设备)。
- 能执行特权指令,如内存管理、进程调度、文件系统操作。
- 操作系统内核代码运行在内核态(Linux 内核、Windows 内核)。
linux查看内存占用
命令 | 用途 |
---|---|
top | 实时监视进程的 CPU/内存占用 |
htop | top 的增强版,更直观 |
free -h | 查看系统整体内存使用情况 |
ps aux | 查看每个进程的内存占用 |
/proc/meminfo | 查看系统内存详细信息 |
vmstat 1 5 | 监视内存、CPU、IO 使用情况 |
smem | 查看每个进程的真实物理内存(PSS) |
进程和线程的调度
调度(Scheduling)就是操作系统决定哪个进程/线程获得 CPU 资源的过程。
对比项 | 进程调度 | 线程调度 |
---|---|---|
调度单位 | 进程(Process) | 线程(Thread) |
上下文切换 | 需要切换整个进程(包括虚拟地址空间、寄存器、页表等) | 只需要切换线程的寄存器,不需要切换地址空间 |
调度粒度 | 较大,进程开销大 | 较小,线程切换更快 |
调度控制 | 操作系统内核负责 | 由内核调度,部分语言(如 Java)支持用户态调度 |
开销 | 高(进程切换涉及内存管理) | 低(线程切换更快) |
适用场景 | 多进程适用于隔离性要求高的任务,如浏览器多标签页 | 多线程适用于高并发计算,如 Web 服务器 |
select
vs. epoll
实现机制对比
对比项 | select (轮询) | epoll (事件驱动) |
---|---|---|
监听方式 | 遍历整个 fd_set ,逐个检查 | 基于回调事件通知,触发时才返回 |
开销 | 高,每次调用都要拷贝 fd_set 并遍历所有 fd | 低,仅在 fd 变化时内核才通知 |
性能 | O(n),fd 多时效率极低 | O(1) 或 O(log n),适用于高并发 |
fd 限制 | 默认 1024,可修改 | 几乎无限制(受内存影响) |
支持触发方式 | 仅 LT(水平触发) | 支持 LT 和 ET(边缘触发) |
适用场景 | 小规模并发 | 高并发服务器,如 Nginx |
LT 与 ET 的区别总结
特性 | LT(水平触发) | ET(边缘触发) |
---|---|---|
触发条件 | 当文件描述符的状态满足条件时,持续触发 | 只在文件描述符的状态发生变化时触发(例如:从不可读到可读) |
通知次数 | 持续通知,只要文件描述符的状态不改变 | 一次通知,文件描述符的状态变化后,若不处理数据,不会再次通知 |
处理要求 | 只需按需读取数据,不需要处理完所有数据 | 必须一次性处理所有数据,否则不会再收到通知 |
性能 | 较低效,可能会多次通知同一个文件描述符 | 更高效,只通知状态改变的 fd ,避免无谓的通知 |
线程的生命周期
C++11 中的线程生命周期管理主要依靠 std::thread
类来实现。每个线程从创建到执行到结束,都需要正确地管理其生命周期:
- 创建线程:使用
std::thread
对象。 - 结束线程:使用
join()
或detach()
。 - 清理线程资源:确保线程被正确地回收,否则可能导致资源泄漏。
通过合理使用 join()
和 detach()
,可以确保程序在并发执行时的稳定性与效率。
特性 | TCP | UDP |
---|---|---|
连接方式 | 面向连接,建立连接(三次握手) | 无连接,直接发送数据 |
可靠性 | 提供可靠性保证(数据完整性、重传、顺序保证) | 不保证可靠性(可能丢包、乱序) |
传输速度 | 速度较慢(由于控制机制、确认、重传等) | 速度较快(没有过多控制和确认) |
开销 | 高(需要连接管理、控制信息) | 低(仅数据和少量控制信息) |
流量控制与拥塞控制 | 提供流量控制和拥塞控制,避免网络过载 | 不提供流量控制和拥塞控制 |
顺序保证 | 保证顺序,接收方按顺序接收数据 | 不保证顺序,接收方可能乱序接收数据 |
应用场景 | 文件传输、电子邮件、网页浏览等需要可靠传输的应用 | 实时通信、视频流、在线游戏、DNS 查询等对速度要求高、容忍丢包的应用 |
视频通话需要进行 实时数据传输,主要涉及语音和视频流的即时传输。如果数据传输的延迟过高,用户会感到 卡顿 或者声音与画面不同步,极大影响通话体验。因此,在视频通话中,传输的延迟是一个非常重要的考虑因素。
-
TCP 在传输过程中有很多 可靠性控制 和 重传机制,这虽然确保了数据的完整性,但在出现丢包或网络延迟时,TCP 会阻塞等待确认和重传,从而增加了延迟。
-
UDP 不进行任何确认、重传或顺序控制,其数据包一旦发送就会尽快到达接收方,即使数据丢失,也不会阻塞数据的传输。这种方式有助于减少传输的延迟,使得视频和语音流更加流畅。
UDP 的低延迟与高效性
-
无需建立连接:与 TCP 不同,UDP 是一个 无连接协议,这意味着在数据传输前不需要建立三次握手连接。TCP 的握手过程会增加时间延迟,而 UDP 可以直接发送数据,减少了连接建立的时间。
-
较低的协议开销:UDP 协议头部较小,只有 8 字节,而 TCP 的头部最小为 20 字节,这意味着 UDP 在数据传输上具有更低的开销,这对于高频数据(如视频帧、音频数据)至关重要。
-
丢包容忍性:在视频通话中,偶尔丢失几个数据包不会对体验产生太大影响。视频流和音频流具有 冗余性,丢失某些数据包时,画面或声音会稍微变差,但不至于造成严重影响。UDP 的设计正是为了容忍丢包,因此适合实时性要求较高的应用。
I/O
1.1 阻塞I/O
阻塞I/O是指在进行I/O操作时,如果数据没有准备好,进程会被挂起(阻塞),直到数据准备好为止。比如,当程序调用read()
函数从文件中读取数据时,如果文件中没有数据,程序会被挂起,直到有数据可读。
1.2 非阻塞I/O
非阻塞I/O是指在进行I/O操作时,如果数据没有准备好,进程不会被挂起,而是直接返回一个错误(例如EAGAIN
或者EWOULDBLOCK
),告知程序数据没有准备好。程序可以选择稍后重试,或进行其他任务。
1.3 I/O复用(select和poll)
I/O复用是一种机制,允许单个进程能够同时监听多个I/O流(如多个文件描述符、网络连接等),当其中一个或多个I/O流准备好时,进程可以执行相应的操作。通过I/O复用,程序不需要为每个I/O流创建一个单独的线程或进程,从而提高了资源的使用效率。
常见的I/O复用方式包括:
-
select:
select
函数允许进程等待多个I/O事件的发生,并在事件发生时返回,告诉进程哪些文件描述符已经准备好进行读、写或者异常处理。 -
poll:
poll
与select
类似,也是用于等待多个I/O事件。不同之处在于poll
支持更大的文件描述符集合,并且没有select
的最大文件描述符限制。poll
使用一个pollfd
结构来描述文件描述符的状态。