4 个版本 (2 个重大更改)
0.3.0 | 2024 年 3 月 5 日 |
---|---|
0.2.2 | 2024 年 1 月 8 日 |
0.2.1 | 2023 年 10 月 29 日 |
0.1.2 | 2023 年 8 月 26 日 |
#655 in 异步
每月 56 次下载
用于 rublk
120KB
2.5K SLoC
Libublk
用于构建 Linux ublk 目标设备的 Rust 库,它与 Linux ublk 驱动
^1 通信以暴露标准 Linux 块设备,同时所有目标 I/O 逻辑都在用户空间实现。
Linux 内核 6.0 开始支持通过 CONFIG_BLK_DEV_UBLK 配置选项覆盖的 ublk。
文档
快速开始
遵循一个基于 libublk 构建的 2-队列 ublk-null 目标,运行代码后在 /dev/ublkbN 创建块设备。通过按 Ctrl+C 终止此进程后,设备将被删除。
use libublk::{ctrl::UblkCtrlBuilder, io::UblkDev, io::UblkQueue};
// async/.await IO handling
async fn handle_io_cmd(q: &UblkQueue<'_>, tag: u16) -> i32 {
(q.get_iod(tag).nr_sectors << 9) as i32
}
// implement whole ublk IO level protocol
async fn io_task(q: &UblkQueue<'_>, tag: u16) {
// IO buffer for exchange data with /dev/ublkbN
let buf_bytes = q.dev.dev_info.max_io_buf_bytes as usize;
let buf = libublk::helpers::IoBuf::<u8>::new(buf_bytes);
let mut cmd_op = libublk::sys::UBLK_U_IO_FETCH_REQ;
let mut res = 0;
// Register IO buffer, so that buffer pages can be discarded
// when queue becomes idle
q.register_io_buf(tag, &buf);
loop {
// Complete previous command with result and re-submit
// IO command for fetching new IO request from /dev/ublkbN
res = q.submit_io_cmd(tag, cmd_op, buf.as_mut_ptr(), res).await;
if res == libublk::sys::UBLK_IO_RES_ABORT {
break;
}
// Handle this incoming IO command
res = handle_io_cmd(&q, tag).await;
cmd_op = libublk::sys::UBLK_U_IO_COMMIT_AND_FETCH_REQ;
}
}
fn q_fn(qid: u16, dev: &UblkDev) {
let q_rc = std::rc::Rc::new(UblkQueue::new(qid as u16, &dev).unwrap());
let exe = smol::LocalExecutor::new();
let mut f_vec = Vec::new();
for tag in 0..dev.dev_info.queue_depth {
let q = q_rc.clone();
f_vec.push(exe.spawn(async move { io_task(&q, tag).await }));
}
// Drive smol executor, won't exit until queue is dead
libublk::uring_async::ublk_wait_and_handle_ios(&exe, &q_rc);
smol::block_on(async { futures::future::join_all(f_vec).await });
}
fn main() {
// Create ublk device
let ctrl = std::sync::Arc::new(
UblkCtrlBuilder::default()
.name("async_null")
.nr_queues(2)
.dev_flags(libublk::UblkFlags::UBLK_DEV_F_ADD_DEV)
.build()
.unwrap(),
);
// Kill ublk device by handling "Ctrl + C"
let ctrl_sig = ctrl.clone();
let _ = ctrlc::set_handler(move || {
ctrl_sig.kill_dev().unwrap();
});
// Now start this ublk target
ctrl.run_target(
// target initialization
|dev| {
dev.set_default_params(250_u64 << 30);
Ok(())
},
// queue IO logic
|tag, dev| q_fn(tag, dev),
// dump device after it is started
|dev| dev.dump(),
)
.unwrap();
// Usually device is deleted automatically when `ctrl` drops, but
// here `ctrl` is leaked by the global sig handler closure actually,
// so we have to delete it explicitly
ctrl.del_dev().unwrap();
}
-
examples/loop.rs
:使用 async/await & io_uring 的真实示例。 -
examples/ramdisk.rs
:单线程 & async/.await 用于控制和 IO,此技术将在未来扩展以从单线程创建多个设备。
rublk
^4 基于 libublk,并支持 null、loop、zoned & qcow2 目标。
非特权 ublk 支持
在非特权模式(UBLK_F_UNPRIVILEGED_DEV
)下,ublk 设备可以在非管理员用户会话中创建。为了支持此功能
- 安装 udev 规则
KERNEL=="ublk-control", MODE="0666", OPTIONS+="static_node=ublk-control"
ACTION=="add",KERNEL=="ublk[bc]*",RUN+="/usr/local/sbin/ublk_chown.sh %k 'add' '%M' '%m'"
ACTION=="remove",KERNEL=="ublk[bc]*",RUN+="/usr/local/sbin/ublk_chown.sh %k 'remove' '%M' '%m'"
- 安装实用程序和脚本
utils/ublk_chown.sh
和 utils/ublk_user_id.rs
的二进制文件需要安装在 /usr/local/sbin 或其他与 udev 规则匹配的目录下。
测试
您可以使用 cargo test
运行库的测试。
性能
当运行fio t/io_uring /dev/ublkb0
^2时,IOPS基本上与在由blktests创建的ublk设备上运行相同测试的结果相同miniublk
^3,该测试是用纯C编写的。ublk设备为空,有2个队列,每个队列的深度为64。
示例
循环
cargo run --example loop help
空
cargo run --example null help
许可证
该项目可以选择Apache License,Version 2.0或MIT许可证。
贡献
欢迎各种贡献!
参考文献
依赖项
~10–22MB
~320K SLoC