【通用】操作系统 知识总结:IPC方式 / 进程线程 / 死锁 / 虚拟内存 / 段页存储
常见问题:
- 跨进程通信方式(IPC)有哪些?其底层原理及其对应应用场景分别是什么?
- 进程与线程的定义、特点及其区别是什么?
- 死锁的必要条件是什么?该如何处理?
- 虚拟内存的定义及其特点。
- 虚拟地址、逻辑地址、线性地址、物理地址的区别。
- 段存储、页存储、段页存储的定义及其区别。
1 跨进程通信方式(IPC)有哪些?其底层原理及其对应应用场景分别是什么?
1 共享内存
多个进程通过读/写 同1个共享文件来交换数据
进程间是独立的,若需要共享空间,需要通过特殊的系统调用
原理
- 内存映射,即通过把1个共享文件映射到自己的进程地址空间,实现进程间通信
- 1个文件可以同时被多个进程操作,其他进程也可以通过相同的文件进行映射
在 Android 中的应用场景
- 高性能数据传输:
- 当需要在不同进程之间快速传输大量数据时,共享内存可以提供比传统的进程间通信方式(如 Binder、Socket 等)更高的性能。
- 例如,在视频处理、图像渲染等需要大量数据交换的应用中,可以使用共享内存来提高数据传输的效率。
- 跨进程资源共享:
- 多个进程可以共享同一块内存区域,从而实现资源的共享。例如,多个进程可以共享一个数据库缓存、一个图像缓冲区等。
- 这可以减少资源的重复分配和释放,提高系统的性能和资源利用率。
实现方法
- 使用 Android NDK:
- Android NDK(Native Development Kit)提供了一些接口来创建和管理共享内存。可以使用 C/C++ 代码来实现共享内存的创建、映射和访问。
- 例如,可以使用
mmap
函数来将一个文件或一块匿名内存映射到进程的地址空间。然后,其他进程可以通过相同的文件或内存区域进行映射,从而实现共享访问。
- 使用 Java 层的接口:
- 虽然 Java 本身没有直接提供共享内存的接口,但可以通过 JNI(Java Native Interface)调用 C/C++ 代码来实现共享内存的功能。
- 另外,也可以使用一些第三方库或框架来实现共享内存的功能。例如,Android 的 Parcelable 接口可以用于在不同进程之间传递复杂的数据结构,但它实际上是通过序列化和反序列化来实现的,性能可能不如共享内存。
2 Bundle
消息传递:
- 进程简单数据交换 是以格式化的消息为单位的;
- 通过系统提供的发生消息 & 接收消息 2个原语进行通信
Bundle的底层原理是:实现了序列化接口Parcelable
在Bundle中添加需要传输的数据 & 通过IntentService发送出去
基本用法
// 创建 Bundle 对象:Bundle bundle = new Bundle();// 放入基本数据类型:bundle.putInt("key_int", 10);bundle.putString("key_string", "Hello");bundle.putBoolean("key_boolean", true);
// 放入自定义对象(需实现 Serializable 或 Parcelable 接口):MyObject myObject = new MyObject();bundle.putSerializable("key_object", myObject);
// 从 Bundle 中取出数据:int valueInt = bundle.getInt("key_int", 0);String valueString = bundle.getString("key_string", null);boolean valueBoolean = bundle.getBoolean("key_boolean", false);
// 取出自定义对象:MyObject myObject = (MyObject) bundle.getSerializable("key_object");
实际应用
- Activity 之间传递数据:
当启动一个新的 Activity 时,可以通过 Intent 携带 Bundle 来传递数据。
Intent intent = new Intent(this, SecondActivity.class);Bundle bundle = new Bundle();bundle.putString("data_key", "Data to pass");intent.putExtras(bundle);startActivity(intent);
在接收数据的 Activity 中获取数据:
Bundle extras = getIntent().getExtras();if (extras!= null) {String data = extras.getString("data_key");}
-
Fragment 之间传递数据:
与Activity之间传递数据类似 -
保存和恢复状态:
在 Activity 或 Fragment 的生命周期方法中,可以使用 Bundle 来保存和恢复状态。
例如,在onSaveInstanceState()方法中保存数据:
@Overrideprotected void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);outState.putString("saved_data_key", "Data to save");}
在onCreate()或onViewStateRestored()方法中恢复数据:
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (savedInstanceState!= null) {String savedData = savedInstanceState.getString("saved_data_key");}}
3 信使 Messenger
本质
同2,也属于消息传递
基于Binder机制实现的跨进程通信
基本用法
- 在Messenger中放入需要传递的数据 即可传递
- 应用场景:服务端串行处理客户端消息
- 创建 Messenger:
在服务端(Service)创建一个Messenger对象,并将其与一个Handler关联,用于处理接收到的消息。
public class MyService extends Service {private final Messenger messenger = new Messenger(new IncomingHandler());private static class IncomingHandler extends Handler {@Overridepublic void handleMessage(Message msg) {// 处理来自客户端的消息switch (msg.what) {case MSG_FROM_CLIENT:// 执行相应操作break;default:super.handleMessage(msg);}}}@Overridepublic IBinder onBind(Intent intent) {return messenger.getBinder();}}
- 在客户端(Activity 等)获取 Messenger:
通过绑定服务获取服务端返回的IBinder,然后创建一个Messenger对象指向服务端的Messenger。
public class MyActivity extends Activity {Messenger serviceMessenger;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Intent intent = new Intent();intent.setComponent(new ComponentName("com.example.otherprocess", "com.example.otherprocess.MyService"));bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);}private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName className, IBinder service) {serviceMessenger = new Messenger(service);// 发送消息给服务端Message msg = Message.obtain(null, MSG_FROM_CLIENT);try {serviceMessenger.send(msg);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName className) {serviceMessenger = null;}};}
Messenger 特点
- 简单易用:
Messenger的使用相对简单,不需要处理复杂的 AIDL(Android Interface Definition Language)接口定义和跨进程异常处理。 - 单向通信:
它通常用于单向通信,即客户端向服务端发送消息,服务端处理消息后不需要直接回复客户端。如果需要双向通信,可以结合多个Messenger对象或者使用其他更复杂的 IPC 机制。 - 安全可靠:
Android 系统会处理消息的传递和接收,确保通信的安全性和可靠性。同时,Messenger基于Handler机制,消息的处理在服务端的主线程中进行,避免了多线程同步的问题。
4 AIDL Android接口定义语言
底层基于Binder
通过将Binder对象转换成AIDL接口类型 & 调用其内方法,实现跨进程通信
应用场景
1对多的并发跨进程通信
具体应用
一、创建 AIDL 文件
在 Android Studio 中,在app/src/main/aidl目录下创建一个 AIDL 文件,比如IMyService.aidl:
package com.example.myapp;interface IMyService {String getMessage();}
二、实现服务端
- 创建一个服务类,实现 AIDL 接口定义的方法:
public class MyService extends Service {private final IMyService.Stub binder = new IMyService.Stub() {@Overridepublic String getMessage() {return "Hello from service";}};@Nullable@Overridepublic IBinder onBind(Intent intent) {return binder;}}
- 在AndroidManifest.xml中注册服务:
<service android:name=".MyService"><intent-filter><action android:name="com.example.myapp.IMyService" /></intent-filter></service>
三、实现客户端
绑定服务并获取 AIDL 接口的实例:
public class MainActivity extends AppCompatActivity {private IMyService myService;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent intent = new Intent();intent.setComponent(new ComponentName("com.example.myapp", "com.example.myapp.MyService"));bindService(intent, connection, Context.BIND_AUTO_CREATE);}private ServiceConnection connection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName className, IBinder service) {myService = IMyService.Stub.asInterface(service);try {String message = myService.getMessage();Log.d("MainActivity", message);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName className) {myService = null;}};}
在这个示例中,服务端实现了一个 AIDL 接口定义的方法,并在客户端绑定服务后获取了服务端的 AIDL 接口实例,从而可以调用服务端的方法实现跨进程通信。
5 ContentProvider
四大组件之一
底层基于Binder
Android中专门用于跨进程通信
6 套接字 Socket
底层原理是:TCP/IP协议族
应用层 与 TCP/IP协议族通信的中间软件抽象层,表现为一个编程接口
使用类型主要有两种:
- 流套接字:基于TCP协议,采用字节流的方式传输数据
- 数据报套接字:基于UDP协议,采用数据报文方式传输数据
2. 进程与线程的定义、特点及其区别是什么?
2.1 进程
-
定义
是进程实体的运行过程 & 系统进行资源分配和调度的一个独立单位 -
作用
使多个程序可 并发执行,以提高系统的资源利用率和吞吐量 -
进程状态说明(前三个为基础状态)
进程顺利运行需要的资源:2类:
- 某资源/某事件
- 处理机(CPU时间片)
- 只缺前者 阻塞,只缺后者 就绪,二者兼备则 运行
- 状态转换
2.2 线程
- 定义
一个基本的CPU执行单元 & 程序执行流的最小单元
- 比进程更小的可独立运行的基本单位,可理解为:轻量级进程
- 组成:线程ID + 程序计数器 + 寄存器集合 + 堆栈
- 注:线程自己不拥有系统资源,与其他线程共享进程所拥有的全部资源。 作用
减少程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
状态说明
拥有类似于进程的就绪、阻塞、运行3种基本状态,具体如下图:
2.3 区别
3. 死锁的必要条件是什么?该如何处理?
3.1 定义
运行过程中,多个进程因争夺资源而造成的一种互相等待的僵局
若无外力作用,这些进程都将无法向前推进
3.2 原因
- 竞争资源:请求同一有限资源的进程数 > 可用资源数
- 进程推进顺序非法:请求 & 释放资源顺序不合理,如资源等待链
3.3 死锁的必要条件
1. 互斥条件
- 一段时间内资源仅为一个进程占有,即 排他
- 此时若有其他进程请求该资源,则请求进程只能等待
2. 不可剥夺
- 进程已经获得的资源在未使用完毕前,不能被其他进程强行夺走
- 只能由获得资源的进程自己主动释放
3. 请求&保持
- 请求 已被其他进程占有的资源时,该请求会被阻塞,但自身的资源保持不释放
4. 循环等待
- 存在一种 进程资源的循环等待链,链中每一个进程已获得资源的同时 被链中下个进程所请求
3.4 处理策略
3.5 其他办法:银行家算法
最著名的死锁避免算法
- 当进程首次申请资源时,要测试该进程对资源的最大需求量
- 若系统现存的资源可满足它的最大需求量,则按当前的申请量分配资源;否则,推迟分配
4. 虚拟内存的定义及其特点
4.1 定义
一个通过对内、外存调度、只是从逻辑上扩充了内存、但实际不存在的内存存储器
4.2 原理
- 部分装入:基于局部性原理,在程序装入时,可以将程序的一部分装入内存,而将其他部分留在外存,就可以启动程序执行
- 请求调入:在程序执行时,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存 & 继续执行程序
- 置换功能:操作系统将内存中暂时不使用的内容换出到外存上,从而腾出空间存放将要调入内存的信息
4.3 特性
- 多次性:一个作业可分多次被调入内存
- 对换性:作业运行过程中存在换进换出的过程,即 换出暂时不用的数据,换入需要的数据
- 虚拟性:从逻辑上扩充了内存的容量
4.4 实现方式
- 请求分页存储管理
- 请求分段存储管理
- 请求段页式存储管理
4.5 大小
- 由计算机的地址结构决定
- 并非是内存和外存的简单相加
5. 虚拟地址、逻辑地址、线性地址、物理地址的区别
- 虚拟地址:虚拟内存中的地址
- 逻辑地址:由程序产生的与段相关的偏移地址部分。如C语言中 &取值操作 所得的地址
- 物理地址:出现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。
a. 若启用分页机制,线性地址会使用页目录 & 页表中的项变换成物理地址
b. 若无启用分页机制,线性地址就是物理地址 - 线性地址:逻辑地址到物理地址变换之间的中间层
6. 段存储、页存储、段页存储的定义及其区别
6.1 页存储
6.1.1 储备知识
-
内存页
用户程序的逻辑地址空间 被划分成的若干个固定大小的区域 -
块的定义
内存物理空间也分成相对应的若干个物理块,与页大小相等
即 从主内存空间被划分出来的内存块
注:
- 块 是主存的基本单位
- 每个进程以块为单位进行划分 & 以块为单位逐个申请主存中的块空间
6.1.2 定义
将用户程序的任一页放在内存的任一块中,实现了离散分配 & 页的存储
6.1.3 特点
优点:无外碎片 & 每个内碎片不超过页的大小
缺点:程序需全部装入内存 & 要求有相应的硬件支持
6.1.4 地址结构
分页存储管理的逻辑地址结构:
6.1.5 页表
- 因数据存储在不同的页面中,而页面又离散的分布在内存中的不同中
- 为了便于在内存中找到进程的每个页面所对应的物理块,系统为每个进程建立一张页表,记录页面在内存中对应的物理块号,即 逻辑地址 & 实际存储地址之间的映射关系,以实现从页号到物理块号的映射
页表一般存放在内存中
6.2 段存储
6.2.1 定义
将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配
6.2.2 特点
- 优:可编写 & 编译、可针对不同类型的段采用不同的保护、可按段为单位共享(含 通过动态链接进行代码共享)
- 缺:会产生碎片
6.3 段页存储
6.3.1 定义
段式管理 & 页式管理方案结合而成的方案
- 作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号
- 再将每段分成若干个大小相等的页
对于主存空间也分成大小相等的页,主存的分配以页为单位。
6.3.2 特点
- 优:具有段式管理 & 页式管理方案的优点
- 缺:由于管理软件的增加,复杂性和开销也就随之增加