① Linux 中大量代码为 C with class 实现,容易将其转换为 Rust struct
C:
int journal_dirty_data(
handle_t *handle,
struct buffer_head *bh
)
Rust:
impl Handle {
fn dirty_data(
buf: &Rc<dyn Buffer>
) -> JBDResult {...}
}
② 使用 Rust 特性实现更简洁、不易出错的代码
C:
err = do_get_write_access(...);
if (err)
goto out;
Rust:
self.do_get_write_access(...)?;
当然,有时 Rust 代码更啰嗦...
let jb_rc: Rc<RefCell<JournalBuffer>> = ...;
let mut jb: RefMut<JournalBuffer> = jb_rc.borrow_mut();
let tx_rc: &Rc<RefCell<Transaction>> = ...;
let mut tx: RefMut<Transaction> = tx_rc.borrow_mut();
③ Rust 的所有权有时成为问题...
/* [j_list_lock] [jbd_lock_bh_state()] */
transaction_t *b_transaction;
/* [t_list_lock] [jbd_lock_bh_state()] */
struct journal_head *b_tnext, *b_tprev;
多重且共用的锁难以用 safe rust 实现...
解决:使用 C 风格的锁,或者只实现单线程
Linux 中 JBD 与 Linux 是紧耦合的,且某些功能必须依赖于操作系统:
#include <buffer_head.h>
if (buffer_dirty(buf)) clear_buffer_dirty(bh);
如何实现独立的模块?使用 trait 进行抽象!
trait Buffer {
fn dirty(&self) -> bool;
fn clear_dirty(&self) -> bool;
...
}
带来的额外工作:使用时需要将已有的操作系统接口包装
impl Buffer for BlockCache {
fn dirty(&self) -> bool {
buffer_dirty(...)
}
fn clear_dirty(&mut self) {
clear_buffer_dirty(...);
}
...
}
Arc<Mutex<_>>
变为 Rc<RefCell<_>>
BufferProvider
等 trait 使得 JBD 可以使用文件系统/操作系统实现pub struct SuperBlock {
magic: u32,
pub total_blocks: u32,
pub inode_bitmap_blocks: u32,
pub inode_area_blocks: u32,
pub data_bitmap_blocks: u32,
pub data_area_blocks: u32,
pub journal_start: u32,
pub journal_len: u32,
}
BufferProvider
等 trait 使得 JBD 可以使用文件系统/操作系统实现#[cfg(feature = "journal")]
let handle_rc = fs.journal_start(128).unwrap();
#[cfg(feature = "journal")]
let mut handle = handle_rc.as_ref().borrow_mut();
self.increase_size(
(offset + buf.len()) as u32, disk_inode, &mut fs,
#[cfg(feature = "journal")]
(self.block_id as u32),
#[cfg(feature = "journal")]
&mut handle,
);
let size = disk_inode.write_at(
offset, buf, &self.block_device,
#[cfg(feature = "journal")]
&mut handle,
#[cfg(feature = "journal")]
false,
);
#[cfg(feature = "journal")]
handle.stop().unwrap();
#[cfg(feature = "journal")]
fs.journal_commit();
BufferProvider
等 trait 使得 JBD 可以使用文件系统/操作系统实现#[cfg(feature = "data")]
handle.dirty_metadata(&j_buf).unwrap();
#[cfg(not(feature = "data"))]
handle.dirty_data(&j_buf).unwrap();
BufferProvider
等 trait 使得 JBD 可以调用文件系统/操作系统相关的实现pub struct CacheManagerWrapper;
impl jbd::sal::BufferProvider for CacheManagerWrapper {
fn get_buffer(
&self,
dev: &Rc<dyn jbd::sal::BlockDevice>,
block_id: usize,
) -> Option<Rc<dyn jbd::sal::Buffer>> {
block_cache_manager()
.get_block_cache(block_id, dev.clone())
}
}
对 JBD 进行性能测试(写 512 个元数据/数据块,与直接写回相比):
瓶颈:单线程、同步写日志/检查点
A shell that can crash
arceos:/$ echo hello > a.txt
...
arceos:/$ crash
[ 35.834442 0 jbd::recovery:97] Start recovery pass Scan
[ 35.834796 0 jbd::recovery:97] Start recovery pass Revoke
[ 35.834942 0 jbd::recovery:61] Recovery pass 1 complete, 0 revokes
[ 35.835028 0 jbd::recovery:97] Start recovery pass Replay
[ 35.836428 0 jbd::recovery:64] Recovery complete, recovered transactions 1 to 4
[ 35.836602 0 jbd::recovery:70] Recovery stats: 7 replayed, 0 revoked, 0 revoke hits
arceos:/$ ls
-rwxr-xr-x 4 a.txt
arceos:/$ cat a.txt
hello
优势:
劣势:
成果:
不足: