"Java多线程基础-2:简介虚拟地址空间——保障进程间独立性的机制 "

简介: 如何保障进程之间这样的独立性?操作系统采用了“虚拟地址空间”的方式。

我们知道,进程之间是相互独立的,在操作系统级别中,一个进程所执行的程序无法直接访问另一个进程所执行的内存区域(即实现进程间通信比较困难);一个进程运行的失败也不会影响其它进程的运行。这使我们的操作系统功能更加稳定。


如何保障进程之间这样的独立性?操作系统采用了“虚拟地址空间”的方式。


一、每个进程都直接访问物理内存的地址会怎样?

物理内存是一块内存条:




内存条上有许多内存颗粒(黑色块状的东西就是内存颗粒),它是内存条最核心的部件,是内存的储存介质。里面有许多的存储单元,类似于“小房间”。每个“小房间”都有编号,即“内存地址”。这时我们假设现在有两个进程,它们分别使用一定的内存资源:



此时,就出现了一个严重的问题:由于进程1和进程2使用的是同一块物理内存,且彼此之间没有约束,那么当进程1代码出现bug(如数组下标越界、野指针等问题时),就可能把进程2内存里的内容也搞坏了。进程1的bug,就很有可能引起进程2也bug。试想你浏览器用着用着,QQ崩了;QQ用着用着,桌面崩了……两个进程之间能直接相互影响,这极大地影响了操作系统整体的稳定性。


因而,虚拟地址空间就是来解决这个麻烦问题的。


二、“虚拟内存空间”是如何解决上述问题的?


现在有进程1和进程2,我们给两个进程分别分配一块虚拟的内存空间。然后,操作系统中提供了一个专门的数据结构:页表,通过页表,实现虚拟地址与实际的物理地址之间的映射。如进程1的虚拟地址空间是 0x00 - 0xff,页表就会将这段虚拟地址空间映射到物理内存上的真实地址 0xaa00 - 0xaaff 。同样的,进程2被分配的虚拟地址也会在页表的映射下对应到真实的物理内存上。



此时,站在这两个进程的角度看,它们的代码中操作的内存地址就是 0x00 - 0xff 这一段。这里访问的内存虽然会被操作系统自动映射到真实的物理内存上,但是进程自身是感知不到实际的物理地址是啥的。如果这时,进程1代码bug (比如数组下标越界,野指针等),就没事了。


因为任何一个内存操作都需要通过页表来“翻译”一下地址。如果出现野指针0x2ff,拿着这个地址去页表上找,发现页表上就没有这个地址!那页表便无法翻译,同时也就无法真正修改物理内存。这样一来,也就不会对别的进程的内存数据造成干扰了。


页表最大的作用就是校验,方便知道当前的虚拟地址是否有效。这样,一个进程崩了,不会对别的进程产生影响;进程之间无法直接干预,操作系统的稳定性也就上来了。


三、简述进程间通信的实现


有的情况下,我们需要进程之间能进行交互,相互配合。


但正如上面提到的,如果每个进程可以直接访问物理内存也即进程间没有隔离性,也就不需要进程间通信。进程1直接把算好的结果写到进程2的内存里就行。


但进程之间是有隔离性的。所谓进程间通信,就是在进程间隔离性的前提下,找一个公共的区域,让两个进程借助这个区域来完成数据交换。


这就好像疫情期间封小区,外面的人进不来,小区里的人也出不去(隔离性)。这时,如果小区里的人要买菜,就让小区外头送菜的人把菜放在保安亭,然后自己也去保安亭拿菜。那此时,这个保安亭就是公共空间,它帮助我们小区里被隔离的人和小区外的人实现了“通信”。


这可以理解为,所谓“公共空间”,就是在进程隔离性的情况下,作出了一些小小的妥协。


操作系统提供的实现进程间通信的方式有很多种,如管道、消息队列、共享内存、信号等等。由于编程语言的特点,C++对这部分的研究会比Java更加细致。因为在Java圈子中,并不是很鼓励多进程编程。Java更多地使用文件和socket这两种方式来完成进程间的通信。


在多线程与多进程编程都能满足并发编程的需求的前提下,Java也更鼓励多线程编程。



相关文章
|
存储 算法 Java
解释 Java 堆空间和垃圾收集
【8月更文挑战第22天】
248 0
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
289 1
|
消息中间件 算法 Java
深入浅出操作系统:进程管理的艺术掌握Java中的异常处理机制
【8月更文挑战第30天】在数字世界的舞台上,操作系统扮演着导演的角色,精心安排着每一个进程的表演。本文将揭开进程管理的神秘面纱,从进程的诞生到终结,探究它们如何在操作系统的指挥下和谐共舞。通过生动的比喻和直观的代码示例,我们将一同走进操作系统的核心,理解进程调度、同步与通信的内在机制,以及它们对计算生态的重要性。让我们跟随代码的节奏,一起感受操作系统的魅力吧!
|
存储 Java 程序员
Java面试题:请解释Java中的永久代(PermGen)和元空间(Metaspace)的区别
Java面试题:请解释Java中的永久代(PermGen)和元空间(Metaspace)的区别
1002 11
|
Java 运维
开发与运维命令问题之使用jstack命令查看Java进程的线程栈如何解决
开发与运维命令问题之使用jstack命令查看Java进程的线程栈如何解决
364 2
|
存储 Java 编译器
Java面试题:描述方法区(Method Area)的作用以及它在JVM中的演变(从永久代到元空间)
Java面试题:描述方法区(Method Area)的作用以及它在JVM中的演变(从永久代到元空间)
482 3
|
存储 安全 Java
深入理解操作系统:从用户空间到内核空间的旅程深入浅出Java异常处理机制
【8月更文挑战第28天】在数字世界的海洋中,操作系统是承载软件与硬件沟通的巨轮。本文将揭开操作系统神秘的面纱,通过一次思维的航行,带领读者从应用程序的用户空间出发,穿越系统调用的大门,深入内核空间的心脏。我们将探索进程管理、内存分配、文件系统等核心概念,并借助代码示例,揭示操作系统背后的魔法。准备好了吗?让我们启航,去发现那些隐藏在日常计算活动背后的秘密。 【8月更文挑战第28天】在Java编程世界中,异常处理就像是我们生活中的急救包。它不仅保护程序不因意外而崩溃,还确保了代码的健壮性和可靠性。本文将通过简洁明了的语言和生动的比喻,带你了解Java异常处理的奥秘,从基本的try-catch语
|
Ubuntu Java Linux
Java演进问题之Java 16对元空间优化如何解决
Java演进问题之Java 16对元空间优化如何解决
253 0
|
Java Linux 编译器
【Linux】深挖进程地址空间
【Linux】深挖进程地址空间
225 0
|
监控 Java
Java面试题:Java内存、多线程与并发工具包的深度探索,Java内存管理策略及其优化技巧,Java多线程并发控制的工具类与机制,Java并发工具包在实际项目中的应用
Java面试题:Java内存、多线程与并发工具包的深度探索,Java内存管理策略及其优化技巧,Java多线程并发控制的工具类与机制,Java并发工具包在实际项目中的应用
320 0