Threads::create_vm

本文深入解析JVM初始化过程中的核心代码段,详细介绍了如何通过宏定义实现异常处理机制,并解释了关键宏的作用及其在实际代码中的应用。
代码位于\hotspot\src\share\vm\runtime\Thread.cpp  Line 2891~Line 3226
这个方法是JVM初始化的主要方法,300多行代码,代码虽然很多,但是思路很清晰,比较容易阅读。
 
JVM初始化无外乎:解析参数、分配内存、创建内部数据结构、创建主线程、加载系统class等步骤。
这些代码均是相当好地被放在各个函数中,代码写的相对直白。
 
下面讲讲初次看这个方法,比较难理解的一个地方
Thread.cpp Line 3138
  SystemDictionary::compute_java_system_loader(THREAD);
这里面突然出现一个THREAD,很是突兀。
 
JVM是C++写成的,自然也继承了C的光荣传统:宏。
 
THREAD是宏,和它相关的宏还有:EXCEPTION_MARK、TRAPS、CHECK_0等。
它们均定义在:\hotspot\src\share\vm\utilities\exceptions.hpp
正如文件名exceptions.hpp所暗示的,这些宏和Exception处理有关系。这里的异常是指JVM代码中的异常,不是指Java程序的异常。
 
exceptions.hpp Line 270
#define EXCEPTION_MARK Thread* THREAD; ExceptionMark __em(THREAD);
 
exceptions.hpp Line 155
#define THREAD __the_thread__
 
所以THREAD实际上是 Thread* __the_thread__,这个变量在用VS调试的时候能清楚地通过IDE看到。
为了能使用THREAD必须首先使用宏:EXCEPTION_MARK
回到Thread::create_vm,在Line 3024处首先使用了EXCEPTION_MARK宏。
 
JVM代码中处理异常并没有使用C++的异常机制,而是用C的方式模拟了异常处理。
先看CHECK_0的定义
Exceptions.hpp Line179
#define CHECK_(result)         THREAD); if (HAS_PENDING_EXCEPTION) return result; (0
#define CHECK_0                CHECK_(0)
 
其中的HAS_PENDING_EXCEPTION的定义是
#define HAS_PENDING_EXCEPTION  (((ThreadShadow*)THREAD)->has_pending_exception())
是查询当前Thread是否有没有处理的异常,有的话就返回true。
 
CHECK_0的含义就是如果有没有处理的异常,就返回0。
 
再看一个CHECK_0的使用
Thread::create_vm, Line 3050
      initialize_class(vmSymbolHandles::java_lang_String(), CHECK_0);
 
initialize_class的原型是
static void initialize_class(symbolHandle class_name, TRAPS) {
  // ....
}
它的第二个参数是TRAPS,定义为
Exceptions.hpp Line 156
#define TRAPS  Thread* THREAD
 
那么代码initialize_class(vmSymbolHandles::java_lang_String(), CHECK_0);经过宏展开之后是:
initialize_class(vmSymbolHandles::java_lang_String(), __the_thread__);
if (((ThreadShadow*)__the_thread__)->has_pending_expection())
 return 0;
(0);
 
注意:上面的 (0); 不是我误写,是实实在在的宏产生的代码。
也就说initialize_class(vmSymbolHandles::java_lang_String(), CHECK_0);经过宏替换变成了三句代码。
第一句自然是对initialize_class的正常调用,其最后一个参数为__the_thread__
第二句是检查异常是否都已经被处理完毕。
第三句就是一个占位符性质的语句,什么也没有作,只是为了让产生的语句能顺利被编译,抵消多余的一个右括号。
 
是不是仍然有点迷糊?OK,我们再来一遍。
因为:#define CHECK_(result)   THREAD); if (HAS_PENDING_EXCEPTION) return result; (0
所以:CHECK_(0) <==> THREAD); if (HAS_PENDING_EXCEPTION) return 0; (0
 
因为:#define CHECK_0          CHECK_(0)
所以:CHECK_0 <==> THREAD); if (HAS_PENDING_EXCEPTION) return 0; (0
 
那么  initialize_class(... , CHECK_0);
 <==> initialize_class(... , THREAD); if (HAS_PENDING_EXCEPTION) return 0; (0);
 

总结一下
1、一个函数的最后一个参数是TRAPS, 则说明该函数需要应用到如上的异常处理场景。
2、为了让一个行数应用到如上场景,它只能是最后一个参数是TRAPS。否则经过宏替换后,不能正常编译。
3、在调用该函数之前,需要先用宏EXCEPTION_MARK来声明变量。
pub fn run_gpu_device(opts: Options) -> anyhow::Result<()> { let Options { x_display, params: mut gpu_parameters, resource_bridge, socket, wayland_sock, } = opts; let channels: BTreeMap<_, _> = wayland_sock.into_iter().collect(); let resource_bridge_listeners = resource_bridge .into_iter() .map(|p| { UnixSeqpacketListener::bind(&p) .map(UnlinkUnixSeqpacketListener) .with_context(|| format!("failed to bind socket at path {}", p)) }) .collect::<anyhow::Result<Vec<_>>>()?; if gpu_parameters.display_params.is_empty() { gpu_parameters .display_params .push(GpuDisplayParameters::default()); } let ex = Executor::new().context("failed to create executor")?; // We don't know the order in which other devices are going to connect to the resource bridges // so start listening for all of them on separate threads. Any devices that connect after the // gpu device starts its queues will not have its resource bridges processed. In practice this // should be fine since the devices that use the resource bridge always try to connect to the // gpu device before handling messages from the VM. let resource_bridges = Arc::new(Mutex::new(Vec::with_capacity( resource_bridge_listeners.len(), ))); for listener in resource_bridge_listeners { let resource_bridges = Arc::clone(&resource_bridges); ex.spawn_blocking(move || match listener.accept() { Ok(stream) => resource_bridges .lock() .push(Tube::new_from_unix_seqpacket(stream).unwrap()), Err(e) => { let path = listener .path() .unwrap_or_else(|_| PathBuf::from("{unknown}")); error!( "Failed to accept resource bridge connection for socket {}: {}", path.display(), e ); } }) .detach(); } // TODO(b/232344535): Read side of the tube is ignored currently. // Complete the implementation by polling `exit_evt_rdtube` and // kill the sibling VM. let (exit_evt_wrtube, _) = Tube::directional_pair().context("failed to create vm event tube")?; let (gpu_control_tube, _) = Tube::pair().context("failed to create gpu control tube")?; let mut display_backends = vec![ virtio::DisplayBackend::X(x_display), virtio::DisplayBackend::Stub, ]; if let Some(p) = channels.get("") { display_backends.insert(0, virtio::DisplayBackend::Wayland(Some(p.to_owned()))); } // These are only used when there is an input device. let event_devices = Vec::new(); let base_features = virtio::base_features(ProtectionType::Unprotected); let listener = VhostUserListener::new_socket(&socket, None)?; let gpu = Rc::new(RefCell::new(Gpu::new( exit_evt_wrtube, gpu_control_tube, Vec::new(), // resource_bridges, handled separately by us display_backends, &gpu_parameters, /* rutabaga_server_descriptor */ None, event_devices, base_features, &channels, /* gpu_cgroup_path */ None, ))); let backend = GpuBackend { ex: ex.clone(), gpu, resource_bridges, state: None, fence_state: Default::default(), queue_workers: Default::default(), platform_workers: Default::default(), shmem_mapper: Arc::new(Mutex::new(None)), }; // Run until the backend is finished. let _ = ex.run_until(listener.run_backend(backend, &ex))?; // Process any tasks from the backend's destructor. Ok(ex.run_until(async {})?) }
最新发布
07-25
这段 Rust 代码定义了一个名为 `run_gpu_device` 的函数,用于启动一个 GPU 设备的 vhost-user 服务端。它主要负责: - 接受来自 QEMU 的 vhost-user 协议连接; - 设置 GPU 显示后端(如 X11、Wayland); - 启动异步事件循环; - 处理资源桥接(resource bridge)连接; - 初始化 GPU 设备并绑定到 vhost-user 协议处理逻辑。 --- ## 代码详解 ### 函数签名 ```rust pub fn run_gpu_device(opts: Options) -> anyhow::Result<()> ``` - `opts: Options`:包含 GPU 设备的配置参数,如 socket 路径、Wayland socket、X11 显示等。 - 返回值:`anyhow::Result<()>`,表示可能失败的运行结果。 --- ### 解构 `Options` ```rust let Options { x_display, params: mut gpu_parameters, resource_bridge, socket, wayland_sock, } = opts; ``` - `x_display`:X11 显示信息。 - `gpu_parameters`:GPU 设备的参数配置。 - `resource_bridge`:资源桥接 socket 路径。 - `socket`:vhost-user 主 socket 路径。 - `wayland_sock`:Wayland socket 路径。 --- ### 创建 `BTreeMap` 映射 Wayland socket ```rust let channels: BTreeMap<_, _> = wayland_sock.into_iter().collect(); ``` - 用于后续设置 Wayland 显示后端。 --- ### 绑定资源桥接监听器 ```rust let resource_bridge_listeners = resource_bridge .into_iter() .map(|p| { UnixSeqpacketListener::bind(&p) .map(UnlinkUnixSeqpacketListener) .with_context(|| format!("failed to bind socket at path {}", p)) }) .collect::<anyhow::Result<Vec<_>>>()?; ``` - 创建 Unix Seqpacket 监听器,用于 GPU 设备与其他设备之间的资源共享通信。 - 使用 `UnlinkUnixSeqpacketListener` 自动清理 socket 文件。 --- ### 设置默认的显示参数 ```rust if gpu_parameters.display_params.is_empty() { gpu_parameters .display_params .push(GpuDisplayParameters::default()); } ``` - 如果没有指定显示参数,则使用默认值。 --- ### 创建异步执行器(Executor) ```rust let ex = Executor::new().context("failed to create executor")?; ``` - `Executor` 是一个异步任务调度器,用来运行异步逻辑。 --- ### 异步接受资源桥接连接 ```rust let resource_bridges = Arc::new(Mutex::new(Vec::with_capacity(resource_bridge_listeners.len()))); for listener in resource_bridge_listeners { let resource_bridges = Arc::clone(&resource_bridges); ex.spawn_blocking(move || match listener.accept() { Ok(stream) => resource_bridges .lock() .push(Tube::new_from_unix_seqpacket(stream).unwrap()), Err(e) => { let path = listener .path() .unwrap_or_else(|_| PathBuf::from("{unknown}")); error!( "Failed to accept resource bridge connection for socket {}: {}", path.display(), e ); } }) .detach(); } ``` - 使用 `spawn_blocking` 在独立线程中监听资源桥接 socket。 - 接收到连接后,使用 `Tube` 封装 socket 流,并保存到 `resource_bridges` 中。 --- ### 创建 GPU 控制通道和退出事件通道 ```rust let (exit_evt_wrtube, _) = Tube::directional_pair()?; let (gpu_control_tube, _) = Tube::pair()?; ``` - `Tube` 是 Crosvm 中用于进程间通信的一种封装。 - `exit_evt_wrtube` 用于通知 GPU 设备退出。 - `gpu_control_tube` 用于控制 GPU 设备。 --- ### 设置显示后端 ```rust let mut display_backends = vec![ virtio::DisplayBackend::X(x_display), virtio::DisplayBackend::Stub, ]; if let Some(p) = channels.get("") { display_backends.insert(0, virtio::DisplayBackend::Wayland(Some(p.to_owned()))); } ``` - 支持 X11 和 Wayland 显示后端。 - 如果有 Wayland socket,则优先使用。 --- ### 初始化 GPU 设备 ```rust let base_features = virtio::base_features(ProtectionType::Unprotected); let listener = VhostUserListener::new_socket(&socket, None)?; let gpu = Rc::new(RefCell::new(Gpu::new( exit_evt_wrtube, gpu_control_tube, Vec::new(), // resource_bridges, handled separately by us display_backends, &gpu_parameters, None, event_devices, base_features, &channels, None, ))); ``` - `Gpu::new(...)` 创建 GPU 设备实例。 - 使用 `Rc<RefCell<Gpu>>` 实现共享可变状态。 --- ### 定义 `GpuBackend` 结构体 ```rust struct GpuBackend { ex: Executor, gpu: Rc<RefCell<Gpu>>, resource_bridges: Arc<Mutex<Vec<Tube>>>, state: Option<BackendState>, fence_state: Default::default(), queue_workers: Default::default(), platform_workers: Default::default(), shmem_mapper: Arc::new(Mutex::new(None)), } ``` - `GpuBackend` 是 `vmm_vhost::Backend` trait 的实现者,负责处理 vhost-user 协议消息。 - 包含 GPU 状态、线程池、共享内存映射器等。 --- ### 启动 vhost-user 服务端循环 ```rust let _ = ex.run_until(listener.run_backend(backend, &ex))?; ``` - 启动异步循环,监听客户端连接并处理请求。 --- ### 清理异步任务 ```rust Ok(ex.run_until(async {})?) ``` - 确保所有后台任务完成后再退出。 --- ## 总结 - `run_gpu_device` 是一个完整的 GPU vhost-user 服务端启动函数。 - 它支持 X11 和 Wayland 显示后端。 - 使用 `Tube` 实现进程间通信。 - 使用 `Executor` 运行异步任务。 - 支持多资源桥接连接。 - 是 Crosvm 中 GPU 虚拟化的核心实现之一。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值