mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 14:16:47 +00:00
Patch feat robust futex (#682)
* feat: 实现robust lock机制 * 前面更改vscode,修改回来 * 修改dadk的路径 * 提交.gitnore和.cargo,删除LICENSE,修改README * 修改一个warn * 删除.rustc_info.json * 删除target文件夹 * 恢复DragonOS的LICENSE,删除Cargo.lock * 将校验用户空间地址的代码写入函数内;将部分match分支用ok_or代替 * 修改wakeup函数获取running queue时unwrap一个None值发生panic * 测试程序使用syscalls库进行系统调用
This commit is contained in:
parent
23ef2b33d1
commit
06560afa2a
@ -3,7 +3,12 @@ use alloc::{
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::{intrinsics::likely, sync::atomic::AtomicU64};
|
||||
use core::{
|
||||
intrinsics::{likely, unlikely},
|
||||
mem,
|
||||
sync::atomic::AtomicU64,
|
||||
};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use system_error::SystemError;
|
||||
|
||||
@ -12,9 +17,9 @@ use crate::{
|
||||
exception::InterruptArch,
|
||||
libs::spinlock::{SpinLock, SpinLockGuard},
|
||||
mm::{ucontext::AddressSpace, MemoryManagementArch, VirtAddr},
|
||||
process::{ProcessControlBlock, ProcessManager},
|
||||
process::{Pid, ProcessControlBlock, ProcessManager},
|
||||
sched::{schedule, SchedMode},
|
||||
syscall::user_access::UserBufferReader,
|
||||
syscall::user_access::{UserBufferReader, UserBufferWriter},
|
||||
time::{
|
||||
timer::{next_n_us_timer_jiffies, Timer, WakeUpHelper},
|
||||
TimeSpec,
|
||||
@ -635,3 +640,251 @@ impl Futex {
|
||||
Ok(*oldval)
|
||||
}
|
||||
}
|
||||
|
||||
//用于指示在处理robust list是最多处理多少个条目
|
||||
const ROBUST_LIST_LIMIT: isize = 2048;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RobustList {
|
||||
next: VirtAddr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RobustListHead {
|
||||
list: RobustList,
|
||||
futex_offset: isize,
|
||||
list_op_pending: VirtAddr,
|
||||
}
|
||||
|
||||
impl RobustListHead {
|
||||
/// # 获得futex的用户空间地址
|
||||
pub fn futex_uaddr(&self, entry: VirtAddr) -> VirtAddr {
|
||||
return VirtAddr::new(entry.data() + self.futex_offset as usize);
|
||||
}
|
||||
|
||||
/// #获得list_op_peding的用户空间地址
|
||||
pub fn pending_uaddr(&self) -> Option<VirtAddr> {
|
||||
if self.list_op_pending.is_null() {
|
||||
return None;
|
||||
} else {
|
||||
return Some(self.futex_uaddr(self.list_op_pending));
|
||||
}
|
||||
}
|
||||
|
||||
/// # 在内核注册robust list
|
||||
/// ## 参数
|
||||
/// - head_uaddr:robust list head用户空间地址
|
||||
/// - len:robust list head的长度
|
||||
pub fn set_robust_list(head_uaddr: VirtAddr, len: usize) -> Result<usize, SystemError> {
|
||||
let robust_list_head_len = mem::size_of::<&RobustListHead>();
|
||||
if unlikely(len != robust_list_head_len) {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
let user_buffer_reader = UserBufferReader::new(
|
||||
head_uaddr.as_ptr::<RobustListHead>(),
|
||||
mem::size_of::<RobustListHead>(),
|
||||
true,
|
||||
)?;
|
||||
let robust_list_head = *user_buffer_reader.read_one_from_user::<RobustListHead>(0)?;
|
||||
|
||||
// 向内核注册robust list
|
||||
ProcessManager::current_pcb().set_robust_list(Some(robust_list_head));
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
/// # 获取robust list head到用户空间
|
||||
/// ## 参数
|
||||
/// - pid:当前进程/线程的pid
|
||||
/// -
|
||||
pub fn get_robust_list(
|
||||
pid: usize,
|
||||
head_uaddr: VirtAddr,
|
||||
len_ptr_uaddr: VirtAddr,
|
||||
) -> Result<usize, SystemError> {
|
||||
// 获取当前进程的process control block
|
||||
let pcb: Arc<ProcessControlBlock> = if pid == 0 {
|
||||
ProcessManager::current_pcb()
|
||||
} else {
|
||||
ProcessManager::find(Pid::new(pid)).ok_or(SystemError::ESRCH)?
|
||||
};
|
||||
|
||||
// TODO: 检查当前进程是否能ptrace另一个进程
|
||||
let ptrace = true;
|
||||
if !ptrace {
|
||||
return Err(SystemError::EPERM);
|
||||
}
|
||||
|
||||
//获取当前线程的robust list head 和 长度
|
||||
let robust_list_head = (*pcb.get_robust_list()).ok_or(SystemError::EINVAL)?;
|
||||
|
||||
// 将len拷贝到用户空间len_ptr
|
||||
let mut user_writer = UserBufferWriter::new(
|
||||
len_ptr_uaddr.as_ptr::<usize>(),
|
||||
core::mem::size_of::<usize>(),
|
||||
true,
|
||||
)?;
|
||||
user_writer.copy_one_to_user(&mem::size_of::<RobustListHead>(), 0)?;
|
||||
// 将head拷贝到用户空间head
|
||||
let mut user_writer = UserBufferWriter::new(
|
||||
head_uaddr.as_ptr::<RobustListHead>(),
|
||||
mem::size_of::<RobustListHead>(),
|
||||
true,
|
||||
)?;
|
||||
user_writer.copy_one_to_user(&robust_list_head, 0)?;
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
/// # 进程/线程退出时清理工作
|
||||
/// ## 参数
|
||||
/// - current:当前进程/线程的pcb
|
||||
/// - pid:当前进程/线程的pid
|
||||
pub fn exit_robust_list(pcb: Arc<ProcessControlBlock>) {
|
||||
//指向当前进程的robust list头部的指针
|
||||
let head = match *pcb.get_robust_list() {
|
||||
Some(rl) => rl,
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
// 遍历当前进程/线程的robust list
|
||||
for futex_uaddr in head.futexes() {
|
||||
let ret = Self::handle_futex_death(futex_uaddr, pcb.pid().into() as u32);
|
||||
if ret.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
pcb.set_robust_list(None);
|
||||
}
|
||||
|
||||
/// # 返回robust list的迭代器,将robust list list_op_pending 放到最后(如果存在)
|
||||
fn futexes(&self) -> FutexIterator<'_> {
|
||||
return FutexIterator::new(self);
|
||||
}
|
||||
|
||||
/// # 处理进程即将死亡时,进程已经持有的futex,唤醒其他等待该futex的线程
|
||||
/// ## 参数
|
||||
/// - futex_uaddr:futex的用户空间地址
|
||||
/// - pid: 当前进程/线程的pid
|
||||
fn handle_futex_death(futex_uaddr: VirtAddr, pid: u32) -> Result<usize, SystemError> {
|
||||
let futex_val = {
|
||||
if futex_uaddr.is_null() {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
let user_buffer_reader = UserBufferReader::new(
|
||||
futex_uaddr.as_ptr::<u32>(),
|
||||
core::mem::size_of::<u32>(),
|
||||
true,
|
||||
)?;
|
||||
*user_buffer_reader.read_one_from_user::<u32>(0)?
|
||||
};
|
||||
|
||||
let mut uval = futex_val;
|
||||
loop {
|
||||
// 该futex可能被其他进程占有
|
||||
let owner = uval & FUTEX_TID_MASK;
|
||||
if owner != pid {
|
||||
break;
|
||||
}
|
||||
|
||||
// 判断是否有FUTEX_WAITERS和标记FUTEX_OWNER_DIED
|
||||
let mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
|
||||
|
||||
// 读取用户空间目前的futex字段,判断在此之前futex是否有被修改
|
||||
let user_buffer_reader = UserBufferReader::new(
|
||||
futex_uaddr.as_ptr::<u32>(),
|
||||
core::mem::size_of::<u32>(),
|
||||
true,
|
||||
)?;
|
||||
let nval = *user_buffer_reader.read_one_from_user::<u32>(0)?;
|
||||
if nval != mval {
|
||||
uval = nval;
|
||||
let mut user_buffer_writer = UserBufferWriter::new(
|
||||
futex_uaddr.as_ptr::<u32>(),
|
||||
core::mem::size_of::<u32>(),
|
||||
true,
|
||||
)?;
|
||||
user_buffer_writer.copy_one_to_user(&mval, 0)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 有等待者,进行唤醒操作
|
||||
if nval & FUTEX_WAITERS != 0 {
|
||||
let mut flags = FutexFlag::FLAGS_MATCH_NONE;
|
||||
flags.insert(FutexFlag::FLAGS_SHARED);
|
||||
Futex::futex_wake(futex_uaddr, flags, 1, FUTEX_BITSET_MATCH_ANY)?;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FutexIterator<'a> {
|
||||
robust_list_head: &'a RobustListHead,
|
||||
entry: VirtAddr,
|
||||
count: isize,
|
||||
}
|
||||
|
||||
impl<'a> FutexIterator<'a> {
|
||||
pub fn new(robust_list_head: &'a RobustListHead) -> Self {
|
||||
return Self {
|
||||
robust_list_head,
|
||||
entry: robust_list_head.list.next,
|
||||
count: 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn is_end(&mut self) -> bool {
|
||||
return self.count < 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FutexIterator<'a> {
|
||||
type Item = VirtAddr;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.is_end() {
|
||||
return None;
|
||||
}
|
||||
|
||||
while self.entry.data() != &self.robust_list_head.list as *const RobustList as usize {
|
||||
if self.count == ROBUST_LIST_LIMIT {
|
||||
break;
|
||||
}
|
||||
if self.entry.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
//获取futex val地址
|
||||
let futex_uaddr = if self.entry.data() != self.robust_list_head.list_op_pending.data() {
|
||||
Some(self.robust_list_head.futex_uaddr(self.entry))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let user_buffer_reader = UserBufferReader::new(
|
||||
self.entry.as_ptr::<RobustList>(),
|
||||
mem::size_of::<RobustList>(),
|
||||
true,
|
||||
)
|
||||
.ok()?;
|
||||
let next_entry = user_buffer_reader
|
||||
.read_one_from_user::<RobustList>(0)
|
||||
.ok()?;
|
||||
|
||||
self.entry = next_entry.next;
|
||||
|
||||
self.count += 1;
|
||||
|
||||
if futex_uaddr.is_some() {
|
||||
return futex_uaddr;
|
||||
}
|
||||
}
|
||||
self.count -= 1;
|
||||
self.robust_list_head.pending_uaddr()
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,15 @@
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{mm::VirtAddr, syscall::Syscall, time::TimeSpec};
|
||||
use crate::{
|
||||
mm::{verify_area, VirtAddr},
|
||||
syscall::Syscall,
|
||||
time::TimeSpec,
|
||||
};
|
||||
|
||||
use super::{constant::*, futex::Futex};
|
||||
use super::{
|
||||
constant::*,
|
||||
futex::{Futex, RobustListHead},
|
||||
};
|
||||
|
||||
impl Syscall {
|
||||
pub fn do_futex(
|
||||
@ -14,6 +21,9 @@ impl Syscall {
|
||||
val2: u32,
|
||||
val3: u32,
|
||||
) -> Result<usize, SystemError> {
|
||||
verify_area(uaddr, core::mem::size_of::<u32>())?;
|
||||
verify_area(uaddr2, core::mem::size_of::<u32>())?;
|
||||
|
||||
let cmd = FutexArg::from_bits(operation.bits() & FutexFlag::FUTEX_CMD_MASK.bits())
|
||||
.ok_or(SystemError::ENOSYS)?;
|
||||
|
||||
@ -101,4 +111,25 @@ impl Syscall {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_robust_list(head_uaddr: VirtAddr, len: usize) -> Result<usize, SystemError> {
|
||||
//判断用户空间地址的合法性
|
||||
verify_area(head_uaddr, core::mem::size_of::<u32>())?;
|
||||
|
||||
let ret = RobustListHead::set_robust_list(head_uaddr, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pub fn get_robust_list(
|
||||
pid: usize,
|
||||
head_uaddr: VirtAddr,
|
||||
len_ptr_uaddr: VirtAddr,
|
||||
) -> Result<usize, SystemError> {
|
||||
//判断用户空间地址的合法性
|
||||
verify_area(head_uaddr, core::mem::size_of::<u32>())?;
|
||||
verify_area(len_ptr_uaddr, core::mem::size_of::<u32>())?;
|
||||
|
||||
let ret = RobustListHead::get_robust_list(pid, head_uaddr, len_ptr_uaddr);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
cpu::current_cpu_id,
|
||||
ipc::signal::{AtomicSignal, SigSet, Signal},
|
||||
process::ArchPCBInfo,
|
||||
CurrentIrqArch,
|
||||
@ -33,7 +34,7 @@ use crate::{
|
||||
casting::DowncastArc,
|
||||
futex::{
|
||||
constant::{FutexFlag, FUTEX_BITSET_MATCH_ANY},
|
||||
futex::Futex,
|
||||
futex::{Futex, RobustListHead},
|
||||
},
|
||||
lock_free_flags::LockFreeFlags,
|
||||
rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard},
|
||||
@ -230,7 +231,8 @@ impl ProcessManager {
|
||||
// avoid deadlock
|
||||
drop(writer);
|
||||
|
||||
let rq = cpu_rq(pcb.sched_info().on_cpu().unwrap().data() as usize);
|
||||
let rq =
|
||||
cpu_rq(pcb.sched_info().on_cpu().unwrap_or(current_cpu_id()).data() as usize);
|
||||
|
||||
let (rq, _guard) = rq.self_lock();
|
||||
rq.update_rq_clock();
|
||||
@ -405,6 +407,8 @@ impl ProcessManager {
|
||||
unsafe { clear_user(addr, core::mem::size_of::<i32>()).expect("clear tid failed") };
|
||||
}
|
||||
|
||||
RobustListHead::exit_robust_list(pcb.clone());
|
||||
|
||||
// 如果是vfork出来的进程,则需要处理completion
|
||||
if thread.vfork_done.is_some() {
|
||||
thread.vfork_done.as_ref().unwrap().complete_all();
|
||||
@ -629,6 +633,9 @@ pub struct ProcessControlBlock {
|
||||
|
||||
/// 线程信息
|
||||
thread: RwLock<ThreadInfo>,
|
||||
|
||||
/// 进程的robust lock列表
|
||||
robust_list: RwLock<Option<RobustListHead>>,
|
||||
}
|
||||
|
||||
impl ProcessControlBlock {
|
||||
@ -694,6 +701,7 @@ impl ProcessControlBlock {
|
||||
children: RwLock::new(Vec::new()),
|
||||
wait_queue: WaitQueue::default(),
|
||||
thread: RwLock::new(ThreadInfo::new()),
|
||||
robust_list: RwLock::new(None),
|
||||
};
|
||||
|
||||
// 初始化系统调用栈
|
||||
@ -940,6 +948,16 @@ impl ProcessControlBlock {
|
||||
pub fn sig_struct_irqsave(&self) -> SpinLockGuard<SignalStruct> {
|
||||
self.sig_struct.lock_irqsave()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_robust_list(&self) -> RwLockReadGuard<Option<RobustListHead>> {
|
||||
return self.robust_list.read_irqsave();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_robust_list(&self, new_robust_list: Option<RobustListHead>) {
|
||||
*self.robust_list.write_irqsave() = new_robust_list;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ProcessControlBlock {
|
||||
|
@ -690,9 +690,6 @@ impl Syscall {
|
||||
let uaddr2 = VirtAddr::new(args[4]);
|
||||
let val3 = args[5] as u32;
|
||||
|
||||
verify_area(uaddr, core::mem::size_of::<u32>())?;
|
||||
verify_area(uaddr2, core::mem::size_of::<u32>())?;
|
||||
|
||||
let mut timespec = None;
|
||||
if utime != 0 && operation.contains(FutexFlag::FLAGS_HAS_TIMEOUT) {
|
||||
let reader = UserBufferReader::new(
|
||||
@ -707,6 +704,26 @@ impl Syscall {
|
||||
Self::do_futex(uaddr, operation, val, timespec, uaddr2, utime as u32, val3)
|
||||
}
|
||||
|
||||
SYS_SET_ROBUST_LIST => {
|
||||
let head = args[0];
|
||||
let head_uaddr = VirtAddr::new(head);
|
||||
let len = args[1];
|
||||
|
||||
let ret = Self::set_robust_list(head_uaddr, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SYS_GET_ROBUST_LIST => {
|
||||
let pid = args[0];
|
||||
let head = args[1];
|
||||
let head_uaddr = VirtAddr::new(head);
|
||||
let len_ptr = args[2];
|
||||
let len_ptr_uaddr = VirtAddr::new(len_ptr);
|
||||
|
||||
let ret = Self::get_robust_list(pid, head_uaddr, len_ptr_uaddr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SYS_READV => Self::readv(args[0] as i32, args[1], args[2]),
|
||||
SYS_WRITEV => Self::writev(args[0] as i32, args[1], args[2]),
|
||||
|
||||
@ -952,11 +969,6 @@ impl Syscall {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
SYS_SET_ROBUST_LIST => {
|
||||
kwarn!("SYS_SET_ROBUST_LIST has not yet been implemented");
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
SYS_RSEQ => {
|
||||
kwarn!("SYS_RSEQ has not yet been implemented");
|
||||
Ok(0)
|
||||
|
2
user/apps/test-for-robustfutex/.cargo/config.toml
Normal file
2
user/apps/test-for-robustfutex/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "x86_64-unknown-linux-musl"
|
3
user/apps/test-for-robustfutex/.gitignore
vendored
Normal file
3
user/apps/test-for-robustfutex/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
/install/
|
12
user/apps/test-for-robustfutex/Cargo.toml
Normal file
12
user/apps/test-for-robustfutex/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "test-for-robustfutex"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "some tests for robust futex"
|
||||
authors = [ "hmt <1037827920@qq.com>" ]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
syscalls = '0.6.18'
|
||||
libc = '0.2.153'
|
56
user/apps/test-for-robustfutex/Makefile
Normal file
56
user/apps/test-for-robustfutex/Makefile
Normal file
@ -0,0 +1,56 @@
|
||||
TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu"
|
||||
RUSTFLAGS+=""
|
||||
|
||||
ifdef DADK_CURRENT_BUILD_DIR
|
||||
# 如果是在dadk中编译,那么安装到dadk的安装目录中
|
||||
INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR)
|
||||
else
|
||||
# 如果是在本地编译,那么安装到当前目录下的install目录中
|
||||
INSTALL_DIR = ./install
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
export RUST_TARGET=x86_64-unknown-linux-musl
|
||||
else ifeq ($(ARCH), riscv64)
|
||||
export RUST_TARGET=riscv64gc-unknown-linux-gnu
|
||||
else
|
||||
# 默认为x86_86,用于本地编译
|
||||
export RUST_TARGET=x86_64-unknown-linux-musl
|
||||
endif
|
||||
|
||||
run:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET)
|
||||
|
||||
build:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET)
|
||||
|
||||
clean:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET)
|
||||
|
||||
test:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET)
|
||||
|
||||
doc:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET)
|
||||
|
||||
fmt:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt
|
||||
|
||||
fmt-check:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check
|
||||
|
||||
run-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release
|
||||
|
||||
build-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release
|
||||
|
||||
clean-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release
|
||||
|
||||
test-release:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force
|
5
user/apps/test-for-robustfutex/README.md
Normal file
5
user/apps/test-for-robustfutex/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 测试robust lock机制
|
||||
|
||||
此代码主要是用于测试:
|
||||
1、robust lock机制中的两个系统调用是否能正常使用
|
||||
2、当持有锁的线程异常终止时,等待该锁的其他线程能够被通知到,以避免死锁或数据不一样
|
394
user/apps/test-for-robustfutex/src/main.rs
Normal file
394
user/apps/test-for-robustfutex/src/main.rs
Normal file
@ -0,0 +1,394 @@
|
||||
extern crate libc;
|
||||
extern crate syscalls;
|
||||
|
||||
use std::{
|
||||
ffi::c_void,
|
||||
mem::{self, size_of},
|
||||
process,
|
||||
ptr::{self, NonNull},
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use syscalls::{
|
||||
syscall0, syscall2, syscall3, syscall6,
|
||||
Sysno::{futex, get_robust_list, gettid, set_robust_list},
|
||||
};
|
||||
|
||||
use libc::{
|
||||
c_int, mmap, perror, EXIT_FAILURE, MAP_ANONYMOUS, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE,
|
||||
};
|
||||
|
||||
const FUTEX_WAIT: usize = 0;
|
||||
const FUTEX_WAKE: usize = 1;
|
||||
|
||||
// 封装futex
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Futex {
|
||||
addr: *mut u32,
|
||||
}
|
||||
|
||||
impl Futex {
|
||||
pub fn new(addr: *mut u32) -> Self {
|
||||
return Futex { addr };
|
||||
}
|
||||
|
||||
pub fn get_addr(&self, offset: isize) -> *mut u32 {
|
||||
return unsafe { self.addr.offset(offset) };
|
||||
}
|
||||
|
||||
pub fn get_val(&self, offset: isize) -> u32 {
|
||||
return unsafe { self.addr.offset(offset).read() };
|
||||
}
|
||||
|
||||
pub fn set_val(&self, val: u32, offset: isize) {
|
||||
unsafe {
|
||||
self.addr.offset(offset).write(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Futex {}
|
||||
unsafe impl Sync for Futex {}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Lock {
|
||||
addr: *mut i32,
|
||||
}
|
||||
|
||||
impl Lock {
|
||||
pub fn new(addr: *mut i32) -> Self {
|
||||
return Lock { addr };
|
||||
}
|
||||
|
||||
pub fn get_val(&self, offset: isize) -> i32 {
|
||||
return unsafe { self.addr.offset(offset).read() };
|
||||
}
|
||||
|
||||
pub fn set_val(&self, val: i32, offset: isize) {
|
||||
unsafe {
|
||||
self.addr.offset(offset).write(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Lock {}
|
||||
unsafe impl Sync for Lock {}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct RobustList {
|
||||
next: *const RobustList,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct RobustListHead {
|
||||
list: RobustList,
|
||||
/// 向kernel提供了要检查的futex字段的相对位置,保持用户空间的灵活性,可以自由
|
||||
/// 地调整其数据结构,而无需向内核硬编码任何特定的偏移量
|
||||
/// futexes中前面的地址是用来存入robust list中(list.next),后面是存放具体的futex val
|
||||
/// 这个字段的作用就是从前面的地址偏移到后面的地址中从而获取futex val
|
||||
#[allow(dead_code)]
|
||||
futex_offset: isize,
|
||||
/// 潜在的竞争条件:由于添加和删除列表是在获取锁之后进行的,这給线程留下了一个小窗口,在此期间可能会导致异常退出,
|
||||
/// 从而使锁被悬挂,为了防止这种可能性。用户空间还维护了一个简单的list_op_pending字段,允许线程在获取锁后但还未添加到
|
||||
/// 列表时就异常退出时进行清理。并且在完成列表添加或删除操作后将其清除
|
||||
/// 这里没有测试这个,在内核中实现实际上就是把list_op_pending地址进行一次唤醒(如果有等待者)
|
||||
#[allow(dead_code)]
|
||||
list_op_pending: *const RobustList,
|
||||
}
|
||||
|
||||
fn error_handle(msg: &str) -> ! {
|
||||
unsafe { perror(msg.as_ptr() as *const i8) };
|
||||
process::exit(EXIT_FAILURE)
|
||||
}
|
||||
|
||||
fn futex_wait(futexes: Futex, thread: &str, offset_futex: isize, lock: Lock, offset_count: isize) {
|
||||
loop {
|
||||
let atomic_count = AtomicI32::new(lock.get_val(offset_count));
|
||||
if atomic_count
|
||||
.compare_exchange(1, 0, Ordering::SeqCst, Ordering::SeqCst)
|
||||
.is_ok()
|
||||
{
|
||||
lock.set_val(0, offset_count);
|
||||
|
||||
// 设置futex锁当前被哪个线程占用
|
||||
let tid = unsafe { syscall0(gettid).unwrap() as u32 };
|
||||
futexes.set_val(futexes.get_val(offset_futex) | tid, offset_futex);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
println!("{} wating...", thread);
|
||||
let futex_val = futexes.get_val(offset_futex);
|
||||
futexes.set_val(futex_val | 0x8000_0000, offset_futex);
|
||||
let ret = unsafe {
|
||||
syscall6(
|
||||
futex,
|
||||
futexes.get_addr(offset_futex) as usize,
|
||||
FUTEX_WAIT,
|
||||
futexes.get_val(offset_futex) as usize,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if ret.is_err() {
|
||||
error_handle("futex_wait failed");
|
||||
}
|
||||
|
||||
// 被唤醒后释放锁
|
||||
let atomic_count = AtomicI32::new(lock.get_val(offset_count));
|
||||
if atomic_count
|
||||
.compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst)
|
||||
.is_ok()
|
||||
{
|
||||
lock.set_val(1, offset_count);
|
||||
|
||||
// 释放futex锁,不被任何线程占用
|
||||
futexes.set_val(futexes.get_val(offset_futex) & 0xc000_0000, offset_futex);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn futex_wake(futexes: Futex, thread: &str, offset_futex: isize, lock: Lock, offset_count: isize) {
|
||||
let atomic_count = AtomicI32::new(lock.get_val(offset_count));
|
||||
if atomic_count
|
||||
.compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst)
|
||||
.is_ok()
|
||||
{
|
||||
lock.set_val(1, offset_count);
|
||||
|
||||
// 释放futex锁,不被任何线程占用
|
||||
futexes.set_val(futexes.get_val(offset_futex) & 0xc000_0000, offset_futex);
|
||||
|
||||
// 如果没有线程/进程在等这个futex,则不必唤醒, 释放改锁即可
|
||||
let futex_val = futexes.get_val(offset_futex);
|
||||
if futex_val & 0x8000_0000 == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
futexes.set_val(futex_val & !(1 << 31), offset_futex);
|
||||
let ret = unsafe {
|
||||
syscall6(
|
||||
futex,
|
||||
futexes.get_addr(offset_futex) as usize,
|
||||
FUTEX_WAKE,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if ret.is_err() {
|
||||
error_handle("futex wake failed");
|
||||
}
|
||||
println!("{} waked", thread);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_list(futexes: Futex) {
|
||||
let head = RobustListHead {
|
||||
list: RobustList { next: ptr::null() },
|
||||
futex_offset: 44,
|
||||
list_op_pending: ptr::null(),
|
||||
};
|
||||
let head = NonNull::from(&head).as_ptr();
|
||||
unsafe {
|
||||
// 加入第一个futex
|
||||
let head_ref_mut = &mut *head;
|
||||
head_ref_mut.list.next = futexes.get_addr(0) as *const RobustList;
|
||||
|
||||
// 加入第二个futex
|
||||
let list_2 = NonNull::from(&*head_ref_mut.list.next).as_ptr();
|
||||
let list_2_ref_mut = &mut *list_2;
|
||||
list_2_ref_mut.next = futexes.get_addr(1) as *const RobustList;
|
||||
|
||||
//println!("robust list next: {:?}", (*head).list.next );
|
||||
//println!("robust list next next: {:?}", (*(*head).list.next).next );
|
||||
|
||||
// 向内核注册robust list
|
||||
let len = mem::size_of::<*mut RobustListHead>();
|
||||
let ret = syscall2(set_robust_list, head as usize, len);
|
||||
if ret.is_err() {
|
||||
println!("failed to set_robust_list, ret = {:?}", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test01();
|
||||
|
||||
println!("-------------");
|
||||
|
||||
test02();
|
||||
|
||||
println!("-------------");
|
||||
}
|
||||
|
||||
//测试set_robust_list和get_robust_list两个系统调用是否能正常使用
|
||||
fn test01() {
|
||||
// 创建robust list 头指针
|
||||
let head = RobustListHead {
|
||||
list: RobustList { next: ptr::null() },
|
||||
futex_offset: 8,
|
||||
list_op_pending: ptr::null(),
|
||||
};
|
||||
let head = NonNull::from(&head).as_ptr();
|
||||
|
||||
let futexes = unsafe {
|
||||
mmap(
|
||||
ptr::null_mut::<c_void>(),
|
||||
(size_of::<c_int>() * 2) as libc::size_t,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_SHARED,
|
||||
-1,
|
||||
0,
|
||||
) as *mut u32
|
||||
};
|
||||
if futexes == MAP_FAILED as *mut u32 {
|
||||
error_handle("futexes_addr mmap failed");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
futexes.offset(11).write(0x0000_0000);
|
||||
futexes.offset(12).write(0x8000_0000);
|
||||
println!("futex1 next addr: {:?}", futexes.offset(0));
|
||||
println!("futex2 next addr: {:?}", futexes.offset(1));
|
||||
println!("futex1 val addr: {:?}", futexes.offset(11));
|
||||
println!("futex2 val addr: {:?}", futexes.offset(12));
|
||||
println!("futex1 val: {:#x?}", futexes.offset(11).read());
|
||||
println!("futex2 val: {:#x?}", futexes.offset(12).read());
|
||||
}
|
||||
|
||||
// 打印注册之前的robust list
|
||||
println!("robust list next(get behind): {:?}", &unsafe { *head });
|
||||
|
||||
unsafe {
|
||||
let head_ref_mut = &mut *head;
|
||||
head_ref_mut.list.next = futexes.offset(0) as *const RobustList;
|
||||
let list_2 = NonNull::from(&*head_ref_mut.list.next).as_ptr();
|
||||
let list_2_ref_mut = &mut *list_2;
|
||||
list_2_ref_mut.next = futexes.offset(1) as *const RobustList;
|
||||
println!("robust list next addr: {:?}", (*head).list.next);
|
||||
println!(
|
||||
"robust list next next addr: {:?}",
|
||||
(*(*head).list.next).next
|
||||
);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let len = mem::size_of::<*mut RobustListHead>();
|
||||
let ret = syscall2(set_robust_list, head as usize, len);
|
||||
if ret.is_err() {
|
||||
println!("failed to set_robust_list, ret = {:?}", ret);
|
||||
}
|
||||
}
|
||||
|
||||
println!("get before, set after: {:?}", head);
|
||||
println!("get before, set after: {:?}", &unsafe { *head });
|
||||
unsafe {
|
||||
let len: usize = 0;
|
||||
println!("len = {}", len);
|
||||
let len_ptr = NonNull::from(&len).as_ptr();
|
||||
let ret = syscall3(get_robust_list, 0, head as usize, len_ptr as usize);
|
||||
println!("get len = {}", len);
|
||||
if ret.is_err() {
|
||||
println!("failed to get_robust_list, ret = {:?}", ret);
|
||||
}
|
||||
|
||||
println!("futex1 val: {:#x}", futexes.offset(11).read());
|
||||
println!("futex2 val: {:#x}", futexes.offset(12).read());
|
||||
println!("robust list next: {:?}", futexes.offset(0));
|
||||
println!("robust list next next: {:#x?}", futexes.offset(0).read());
|
||||
}
|
||||
println!("robust list head(get after): {:?}", head);
|
||||
println!("robust list next(get after): {:?}", &unsafe { *head });
|
||||
}
|
||||
|
||||
//测试一个线程异常退出时futex的robustness(多线程测试,目前futex还不支持多进程)
|
||||
fn test02() {
|
||||
let futexes = unsafe {
|
||||
mmap(
|
||||
ptr::null_mut::<c_void>(),
|
||||
(size_of::<c_int>() * 2) as libc::size_t,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_SHARED,
|
||||
-1,
|
||||
0,
|
||||
) as *mut u32
|
||||
};
|
||||
if futexes == MAP_FAILED as *mut u32 {
|
||||
error_handle("mmap failed");
|
||||
}
|
||||
let count = unsafe {
|
||||
mmap(
|
||||
ptr::null_mut::<c_void>(),
|
||||
(size_of::<c_int>() * 2) as libc::size_t,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_SHARED,
|
||||
-1,
|
||||
0,
|
||||
) as *mut i32
|
||||
};
|
||||
if count == MAP_FAILED as *mut i32 {
|
||||
error_handle("mmap failed");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// 在这个示例中,第一段和第二段地址放入robust list,第11段地址和第12段地址存放futex val
|
||||
futexes.offset(11).write(0x0000_0000);
|
||||
futexes.offset(12).write(0x0000_0000);
|
||||
println!("futex1 next addr: {:?}", futexes.offset(0));
|
||||
println!("futex2 next addr: {:?}", futexes.offset(1));
|
||||
println!("futex1 val addr: {:?}", futexes.offset(11));
|
||||
println!("futex2 val addr: {:?}", futexes.offset(12));
|
||||
println!("futex1 val: {:#x?}", futexes.offset(11).read());
|
||||
println!("futex2 val: {:#x?}", futexes.offset(12).read());
|
||||
|
||||
count.offset(0).write(1);
|
||||
count.offset(1).write(0);
|
||||
println!("count1 val: {:?}", count.offset(0).read());
|
||||
println!("count2 val: {:?}", count.offset(1).read());
|
||||
}
|
||||
|
||||
let futexes = Futex::new(futexes);
|
||||
let locks = Lock::new(count);
|
||||
|
||||
// tid1 = 7
|
||||
let thread1 = thread::spawn(move || {
|
||||
set_list(futexes);
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
for i in 0..2 {
|
||||
futex_wait(futexes, "thread1", 11, locks, 0);
|
||||
println!("thread1 times: {}", i);
|
||||
thread::sleep(Duration::from_secs(3));
|
||||
|
||||
let tid = unsafe { syscall0(gettid).unwrap() as u32 };
|
||||
futexes.set_val(futexes.get_val(12) | tid, 12);
|
||||
|
||||
if i == 1 {
|
||||
// 让thread1异常退出,从而无法唤醒thread2,检测robustness
|
||||
println!("Thread1 exiting early due to simulated error.");
|
||||
return;
|
||||
}
|
||||
futex_wake(futexes, "thread2", 12, locks, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// tid2 = 6
|
||||
set_list(futexes);
|
||||
for i in 0..2 {
|
||||
futex_wait(futexes, "thread2", 12, locks, 1);
|
||||
println!("thread2 times: {}", i);
|
||||
|
||||
let tid = unsafe { syscall0(gettid).unwrap() as u32 };
|
||||
futexes.set_val(futexes.get_val(11) | tid, 11);
|
||||
|
||||
futex_wake(futexes, "thread1", 11, locks, 0);
|
||||
}
|
||||
|
||||
thread1.join().unwrap();
|
||||
}
|
24
user/dadk/config/test_for_robustfutex_0_1_0.dadk
Normal file
24
user/dadk/config/test_for_robustfutex_0_1_0.dadk
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "test-for-robustfutex",
|
||||
"version": "0.1.0",
|
||||
"description": "some tests for robust futex",
|
||||
"rust_target": null,
|
||||
"task_type": {
|
||||
"BuildFromSource": {
|
||||
"Local": {
|
||||
"path": "apps/test-for-robustfutex"
|
||||
}
|
||||
}
|
||||
},
|
||||
"depends": [],
|
||||
"build": {
|
||||
"build_command": "make install"
|
||||
},
|
||||
"install": {
|
||||
"in_dragonos_path": "/"
|
||||
},
|
||||
"clean": {
|
||||
"clean_command": "make clean"
|
||||
},
|
||||
"envs": []
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user