前言
这篇文章是对前面讲的 JavaEE进程 的补充,可以参考我上一篇文章。
进程/任务 补充
什么是进程?
-
进程 是 操作系统 分配资源的 基本单位。
-
多任务操作系统(Windows、Linux、Android、iOS等)可以同时管理多个进程。
-
单任务操作系统(如老式功能机)一次只能运行一个程序。
进程管理流程
- 描述进程:使用结构体(如
task_struct
)列出每个进程的各种信息。这个结构体叫 PCB(Process Control Block)。 - 组织进程:用数据结构(比如链表)将所有 PCB 串联起来,方便管理和调度。
PCB(进程控制块)的核心属性
属性 | 说明 |
---|---|
pid | 进程ID,唯一标识一个进程。通常是32位整数。 |
内存指针 | 记录该进程所能使用的内存区域(比如指令段、数据段、堆、栈)。 |
文件描述表 | 记录进程打开的文件、设备等资源的列表(每打开一个文件,分配一个描述符)。 |
CPU资源 | 包括进程消耗的CPU时间,调度信息,寄存器状态等(用于进程切换时的保存与恢复)。 |
进程状态 | 就绪、运行、阻塞等状态,决定进程是否能上CPU执行。 |
优先级 | 不同进程有不同优先级,影响系统调度的先后顺序和时间配额。 |
记账信息 | 记录进程消耗的CPU时间、内存资源等,为系统调度提供依据。 |
重要补充概念
内存指针(Memory Pointer)
- 进程在运行时需要指令和数据。
- 系统分配一片内存区域给进程使用,并用指针(地址)来标记。
- 没有申请的内存是不能随便访问的,否则就是非法访问(可能会触发段错误 segmentation fault)。
文件描述表(File Descriptor Table)
- 每个进程都有自己的文件描述表。
- 文件、硬盘、键盘、鼠标等都是设备文件,统一以文件形式操作。
CPU资源管理
- 一个CPU核心 相当于 一个舞台。
- 一个时间点 只能有一个进程在执行。
- 多核CPU 可以真正做到并行执行。
- 单核CPU 通过快速切换(分时复用)来实现 并发执行 的“假象”。
进程的状态变化
- 就绪: 等待被调度到CPU上。
- 运行: 正在CPU上执行。
- 阻塞: 等待某个事件完成(如等待输入/输出)。
进程状态切换是操作系统调度器负责的,程序员一般不需要直接干预。
并发 vs 并行
- 并发(Concurrency):宏观上同时,微观上轮流切换。
- 并行(Parallelism):物理上真正同时(多核、多CPU实现)。
额外补充一点专业词汇
- 上下文切换(Context Switch):在多任务切换时,保存当前进程的状态到 PCB,并恢复另一个进程的状态。
- 系统调用(System Call):用户程序向操作系统请求服务(如申请内存、打开文件)的接口。
几个问题
一. 优先级是怎么决定的?
1. 系统默认值
- 每个新创建的进程,系统会给它一个 默认优先级。
- 比如在Linux里,普通程序默认优先级是 0,而系统重要任务(比如守护进程)可能优先级更高一点。
2. 用户手动调整
- 有些系统允许用户手动改优先级,比如 Windows 的任务管理器里,你右键一个进程,可以选"设置优先级",设置成“高”、“实时”、“低”之类。
- 在Linux中,可以用
nice
和renice
命令调整优先级。
nice值范围:-20(最高优先级)到19(最低优先级)。通常来说,nice值越小,优先级越高。
注意
- 每个新创建的进程,系统会给它一个默认优先级。
- 比如在Linux里,普通程序默认优先级是0,而系统重要任务(比如守护进程)可能优先级更高一点。
3. 进程的类型
不同类型的任务,系统会预设不同优先级。
比如:
- 和用户交互相关的程序(像你打字的程序、还有正在运行的游戏)—— 优先级高。
- 后台守护程序(比如qq等聊天工具)—— 优先级低。
比如我们打游戏时,后台挂着qq,我们现在运行的游戏就是优先级高的。如果肉眼感知的卡,这时我们可能会破口大骂,而后台的慢一点无所谓。
4. 进程的历史表现
有些高级操作系统会根据进程的历史CPU占用情况,自动调整它的优先级。
比如:
- 一个进程最近疯狂占用CPU -> 系统可能会降低它的优先级,让其他进程能上来。
- 一个进程很久没被调度了 -> 系统可能会提高它的优先级,给它一点机会。
这种机制就是 动态优先级调整,也叫 公平调度(CFS, Completely Fair Scheduler),比如Linux用的就是这种。这就涉及到我们上篇文章讲到的记账信息了。
5. 实时性需求
- 有些系统有"实时进程"。
- 这些实时进程优先级非常高,必须及时运行,否则就出大事(比如机器停摆)。
在Linux中,实时进程(RT进程)优先级范围是 1~99,普通进程优先级范围是 0。
优先级总结:
优先级 = 系统默认 + 用户调整 + 进程类型 + 动态调整 + 特殊需求
系统最后会综合这些因素,给每个进程打分(优先级),然后调度器根据优先级高低,来安排谁上CPU、谁排队等。
二. 进程如何管理内存?
1. 进程之间的内存隔离
核心结论:每个进程之间的内存是彼此独立且互不干扰的,通常情况下,进程A不能直接访问进程B的内存。
进程间通信(IPC)
虽然进程之间是彼此相互独立的,但是有时候也需要多个进程相互配合来完成某个工作。
如何实现进程间通信?
系统提供了一些公共空间,多个进程都能访问到,让两个进程借助这种公共空间来共享数据。
系统层面实际上提供了很多种:
- 管道
- 共享内存
- 文件
- 网络
- 信号
- 信号量
- 其中,文件、网络,是我们使用到的主要方式。这也是我们 Java 程序员主要使用的 进程间通信方式。
- 在 Java 中,虽然我们不太自己直接操作这些底层IPC接口,但像
java.nio
、RMI
(远程方法调用)、Socket
编程等其实就是在做这些事的 高级封装。
注意:虽然有这个方法,但是不推荐在java中的“多进程编程”,我们一般使用的是“多线程”编程。
三. 为什么 Java 更倾向多线程而不是多进程?
- Java的JVM 本身就是一个进程,管理自己的内存、线程调度等。
- 启动和管理多个进程开销大,通信复杂;相比之下,线程共享同一个进程的内存空间,通信非常高效(直接访问对象即可)。
- Java有很强的线程并发库支持(
java.util.concurrent
),用线程能轻松实现 高并发 程序。 - 跨进程通信需要序列化、反序列化对象,性能开销较大,而线程之间可以直接共享对象。(参考json)