mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-24 09:03:23 +00:00
实现SystemV共享内存 (#690)
* 实现SystemV共享内存 * 测试shm * 添加测试程序 * 完善细节 * 修正shm的时间数据错误的问题 * fix: devfs的metadata权限为0x777的错误 --------- Co-authored-by: longjin <longjin@DragonOS.org>
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
pub mod pipe;
|
||||
pub mod shm;
|
||||
pub mod signal;
|
||||
pub mod signal_types;
|
||||
pub mod syscall;
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
net::event_poll::{EPollEventType, EPollItem, EventPoll},
|
||||
process::ProcessState,
|
||||
sched::{schedule, SchedMode},
|
||||
time::TimeSpec,
|
||||
time::PosixTimeSpec,
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
@ -121,9 +121,9 @@ impl LockedPipeInode {
|
||||
size: PIPE_BUFF_SIZE as i64,
|
||||
blk_size: 0,
|
||||
blocks: 0,
|
||||
atime: TimeSpec::default(),
|
||||
mtime: TimeSpec::default(),
|
||||
ctime: TimeSpec::default(),
|
||||
atime: PosixTimeSpec::default(),
|
||||
mtime: PosixTimeSpec::default(),
|
||||
ctime: PosixTimeSpec::default(),
|
||||
file_type: FileType::Pipe,
|
||||
mode: ModeType::from_bits_truncate(0o666),
|
||||
nlinks: 1,
|
||||
|
658
kernel/src/ipc/shm.rs
Normal file
658
kernel/src/ipc/shm.rs
Normal file
@ -0,0 +1,658 @@
|
||||
use crate::{
|
||||
arch::mm::LockedFrameAllocator,
|
||||
filesystem::vfs::syscall::ModeType,
|
||||
libs::{
|
||||
align::page_align_up,
|
||||
spinlock::{SpinLock, SpinLockGuard},
|
||||
},
|
||||
mm::{
|
||||
allocator::page_frame::{FrameAllocator, PageFrameCount, PhysPageFrame},
|
||||
page::{page_manager_lock_irqsave, Page},
|
||||
PhysAddr,
|
||||
},
|
||||
process::{Pid, ProcessManager},
|
||||
syscall::user_access::{UserBufferReader, UserBufferWriter},
|
||||
time::PosixTimeSpec,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use ida::IdAllocator;
|
||||
use num::ToPrimitive;
|
||||
use system_error::SystemError;
|
||||
|
||||
pub static mut SHM_MANAGER: Option<SpinLock<ShmManager>> = None;
|
||||
|
||||
/// 用于创建新的私有IPC对象
|
||||
pub const IPC_PRIVATE: ShmKey = ShmKey::new(0);
|
||||
|
||||
/// 初始化SHM_MANAGER
|
||||
pub fn shm_manager_init() {
|
||||
kinfo!("shm_manager_init");
|
||||
let shm_manager = SpinLock::new(ShmManager::new());
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
unsafe { SHM_MANAGER = Some(shm_manager) };
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
kinfo!("shm_manager_init done");
|
||||
}
|
||||
|
||||
pub fn shm_manager_lock() -> SpinLockGuard<'static, ShmManager> {
|
||||
unsafe { SHM_MANAGER.as_ref().unwrap().lock() }
|
||||
}
|
||||
|
||||
int_like!(ShmId, usize);
|
||||
int_like!(ShmKey, usize);
|
||||
|
||||
bitflags! {
|
||||
pub struct ShmFlags:u32{
|
||||
const SHM_RDONLY = 0o10000;
|
||||
const SHM_RND = 0o20000;
|
||||
const SHM_REMAP = 0o40000;
|
||||
const SHM_EXEC = 0o100000;
|
||||
const SHM_HUGETLB = 0o4000;
|
||||
|
||||
const IPC_CREAT = 0o1000;
|
||||
const IPC_EXCL = 0o2000;
|
||||
|
||||
const SHM_DEST = 0o1000;
|
||||
const SHM_LOCKED = 0o2000;
|
||||
}
|
||||
}
|
||||
|
||||
/// 管理共享内存段信息的操作码
|
||||
#[derive(Eq, Clone, Copy)]
|
||||
pub enum ShmCtlCmd {
|
||||
/// 删除共享内存段
|
||||
IpcRmid = 0,
|
||||
/// 设置KernIpcPerm选项
|
||||
IpcSet = 1,
|
||||
/// 获取ShmIdDs
|
||||
IpcStat = 2,
|
||||
/// 查看ShmMetaData
|
||||
IpcInfo = 3,
|
||||
|
||||
/// 不允许共享内存段被置换出物理内存
|
||||
ShmLock = 11,
|
||||
/// 允许共享内存段被置换出物理内存
|
||||
ShmUnlock = 12,
|
||||
/// 查看ShmMetaData
|
||||
ShmStat = 13,
|
||||
/// 查看ShmInfo
|
||||
ShmInfo = 14,
|
||||
/// 查看ShmMetaData
|
||||
ShmtStatAny = 15,
|
||||
|
||||
Default,
|
||||
}
|
||||
|
||||
impl From<usize> for ShmCtlCmd {
|
||||
fn from(cmd: usize) -> ShmCtlCmd {
|
||||
match cmd {
|
||||
0 => Self::IpcRmid,
|
||||
1 => Self::IpcSet,
|
||||
2 => Self::IpcStat,
|
||||
3 => Self::IpcInfo,
|
||||
11 => Self::ShmLock,
|
||||
12 => Self::ShmUnlock,
|
||||
13 => Self::ShmStat,
|
||||
14 => Self::ShmInfo,
|
||||
15 => Self::ShmtStatAny,
|
||||
_ => Self::Default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ShmCtlCmd {
|
||||
fn eq(&self, other: &ShmCtlCmd) -> bool {
|
||||
*self as usize == *other as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// 共享内存管理器
|
||||
#[derive(Debug)]
|
||||
pub struct ShmManager {
|
||||
/// ShmId分配器
|
||||
id_allocator: IdAllocator,
|
||||
/// ShmId映射共享内存信息表
|
||||
id2shm: HashMap<ShmId, KernelShm>,
|
||||
/// ShmKey映射ShmId表
|
||||
key2id: HashMap<ShmKey, ShmId>,
|
||||
}
|
||||
|
||||
impl ShmManager {
|
||||
pub fn new() -> Self {
|
||||
ShmManager {
|
||||
id_allocator: IdAllocator::new(0, usize::MAX - 1),
|
||||
id2shm: HashMap::new(),
|
||||
key2id: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// # 添加共享内存段
|
||||
///
|
||||
/// ## 参数
|
||||
///
|
||||
/// - `key`: 共享内存键值
|
||||
/// - `size`: 共享内存大小
|
||||
/// - `shmflg`: 共享内存标志
|
||||
///
|
||||
/// ## 返回值
|
||||
///
|
||||
/// 成功:共享内存id
|
||||
/// 失败:对应错误码
|
||||
pub fn add(
|
||||
&mut self,
|
||||
key: ShmKey,
|
||||
size: usize,
|
||||
shmflg: ShmFlags,
|
||||
) -> Result<usize, SystemError> {
|
||||
// 判断共享内存大小是否过小或溢出
|
||||
if !(PosixShmMetaInfo::SHMMIN..=PosixShmMetaInfo::SHMMAX).contains(&size) {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
let id = self.id_allocator.alloc().expect("No more id to allocate.");
|
||||
let shm_id = ShmId::new(id);
|
||||
|
||||
// 分配共享内存页面
|
||||
let page_count = PageFrameCount::from_bytes(page_align_up(size)).unwrap();
|
||||
let phys_page =
|
||||
unsafe { LockedFrameAllocator.allocate(page_count) }.ok_or(SystemError::EINVAL)?;
|
||||
// 创建共享内存page,并添加到PAGE_MANAGER中
|
||||
let mut page_manager_guard = page_manager_lock_irqsave();
|
||||
let mut cur_phys = PhysPageFrame::new(phys_page.0);
|
||||
for _ in 0..page_count.data() {
|
||||
let mut page = Page::new(true);
|
||||
page.set_shm_id(shm_id);
|
||||
let paddr = cur_phys.phys_address();
|
||||
page_manager_guard.insert(paddr, page);
|
||||
cur_phys = cur_phys.next();
|
||||
}
|
||||
|
||||
// 创建共享内存信息结构体
|
||||
let paddr = phys_page.0;
|
||||
let kern_ipc_perm = KernIpcPerm {
|
||||
id: shm_id,
|
||||
key,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
_cuid: 0,
|
||||
_cgid: 0,
|
||||
mode: shmflg & ShmFlags::from_bits_truncate(ModeType::S_IRWXUGO.bits()),
|
||||
_seq: 0,
|
||||
};
|
||||
let shm_kernel = KernelShm::new(kern_ipc_perm, paddr, size);
|
||||
|
||||
// 将key、id及其对应KernelShm添加到表中
|
||||
self.id2shm.insert(shm_id, shm_kernel);
|
||||
self.key2id.insert(key, shm_id);
|
||||
|
||||
return Ok(shm_id.data());
|
||||
}
|
||||
|
||||
pub fn contains_key(&self, key: &ShmKey) -> Option<&ShmId> {
|
||||
self.key2id.get(key)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: &ShmId) -> Option<&mut KernelShm> {
|
||||
self.id2shm.get_mut(id)
|
||||
}
|
||||
|
||||
pub fn free_key(&mut self, key: &ShmKey) {
|
||||
self.key2id.remove(key);
|
||||
}
|
||||
|
||||
pub fn free_id(&mut self, id: &ShmId) {
|
||||
self.id2shm.remove(id);
|
||||
self.id_allocator.free(id.0);
|
||||
}
|
||||
|
||||
pub fn ipc_info(&self, user_buf: *const u8, from_user: bool) -> Result<usize, SystemError> {
|
||||
let mut user_buffer_writer = UserBufferWriter::new(
|
||||
user_buf as *mut u8,
|
||||
core::mem::size_of::<PosixShmMetaInfo>(),
|
||||
from_user,
|
||||
)?;
|
||||
|
||||
let shm_meta_info = PosixShmMetaInfo::new();
|
||||
user_buffer_writer.copy_one_to_user(&shm_meta_info, 0)?;
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
pub fn shm_info(&self, user_buf: *const u8, from_user: bool) -> Result<usize, SystemError> {
|
||||
// 已使用id数量
|
||||
let used_ids = self.id2shm.len().to_i32().unwrap();
|
||||
// 共享内存总和
|
||||
let shm_tot = self.id2shm.iter().fold(0, |acc, (_, kernel_shm)| {
|
||||
acc + PageFrameCount::from_bytes(page_align_up(kernel_shm.shm_size))
|
||||
.unwrap()
|
||||
.data()
|
||||
});
|
||||
let shm_info = PosixShmInfo::new(used_ids, shm_tot, 0, 0, 0, 0);
|
||||
|
||||
let mut user_buffer_writer = UserBufferWriter::new(
|
||||
user_buf as *mut u8,
|
||||
core::mem::size_of::<PosixShmInfo>(),
|
||||
from_user,
|
||||
)?;
|
||||
user_buffer_writer.copy_one_to_user(&shm_info, 0)?;
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
pub fn shm_stat(
|
||||
&self,
|
||||
id: ShmId,
|
||||
cmd: ShmCtlCmd,
|
||||
user_buf: *const u8,
|
||||
from_user: bool,
|
||||
) -> Result<usize, SystemError> {
|
||||
let kernel_shm = self.id2shm.get(&id).ok_or(SystemError::EINVAL)?;
|
||||
let key = kernel_shm.kern_ipc_perm.key.data().to_i32().unwrap();
|
||||
let mode = kernel_shm.kern_ipc_perm.mode.bits();
|
||||
|
||||
let shm_perm = PosixIpcPerm::new(key, 0, 0, 0, 0, mode);
|
||||
let shm_segsz = kernel_shm.shm_size;
|
||||
let shm_atime = kernel_shm.shm_atim.total_nanos();
|
||||
let shm_dtime = kernel_shm.shm_dtim.total_nanos();
|
||||
let shm_ctime = kernel_shm.shm_ctim.total_nanos();
|
||||
let shm_cpid = kernel_shm.shm_cprid.data().to_u32().unwrap();
|
||||
let shm_lpid = kernel_shm.shm_lprid.data().to_u32().unwrap();
|
||||
let shm_map_count = kernel_shm.map_count();
|
||||
let shm_id_ds = PosixShmIdDs {
|
||||
shm_perm,
|
||||
shm_segsz,
|
||||
shm_atime,
|
||||
shm_dtime,
|
||||
shm_ctime,
|
||||
shm_cpid,
|
||||
shm_lpid,
|
||||
shm_map_count,
|
||||
_unused1: 0,
|
||||
_unused2: 0,
|
||||
};
|
||||
|
||||
let mut user_buffer_writer = UserBufferWriter::new(
|
||||
user_buf as *mut u8,
|
||||
core::mem::size_of::<PosixShmIdDs>(),
|
||||
from_user,
|
||||
)?;
|
||||
user_buffer_writer.copy_one_to_user(&shm_id_ds, 0)?;
|
||||
|
||||
let r: usize = if cmd == ShmCtlCmd::IpcStat {
|
||||
0
|
||||
} else {
|
||||
id.data()
|
||||
};
|
||||
|
||||
return Ok(r);
|
||||
}
|
||||
|
||||
pub fn ipc_set(
|
||||
&mut self,
|
||||
id: ShmId,
|
||||
user_buf: *const u8,
|
||||
from_user: bool,
|
||||
) -> Result<usize, SystemError> {
|
||||
let kernel_shm = self.id2shm.get_mut(&id).ok_or(SystemError::EINVAL)?;
|
||||
|
||||
let user_buffer_reader =
|
||||
UserBufferReader::new(user_buf, core::mem::size_of::<PosixShmIdDs>(), from_user)?;
|
||||
let mut shm_id_ds = PosixShmIdDs::default();
|
||||
user_buffer_reader.copy_one_from_user(&mut shm_id_ds, 0)?;
|
||||
|
||||
kernel_shm.copy_from(shm_id_ds);
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
pub fn ipc_rmid(&mut self, id: ShmId) -> Result<usize, SystemError> {
|
||||
let kernel_shm = self.id2shm.get_mut(&id).ok_or(SystemError::EINVAL)?;
|
||||
kernel_shm.set_mode(ShmFlags::SHM_DEST, true);
|
||||
|
||||
let mut cur_phys = PhysPageFrame::new(kernel_shm.shm_start_paddr);
|
||||
let count = PageFrameCount::from_bytes(page_align_up(kernel_shm.shm_size)).unwrap();
|
||||
let key = kernel_shm.kern_ipc_perm.key;
|
||||
let id = kernel_shm.kern_ipc_perm.id;
|
||||
let map_count = kernel_shm.map_count();
|
||||
|
||||
let mut page_manager_guard = page_manager_lock_irqsave();
|
||||
if map_count > 0 {
|
||||
// 设置共享内存物理页当映射计数等于0时可被回收
|
||||
for _ in 0..count.data() {
|
||||
let page = page_manager_guard.get_mut(&cur_phys.phys_address());
|
||||
page.set_dealloc_when_zero(true);
|
||||
|
||||
cur_phys = cur_phys.next();
|
||||
}
|
||||
|
||||
// 释放key,不让后续进程连接
|
||||
self.free_key(&key);
|
||||
} else {
|
||||
// 释放共享内存物理页
|
||||
for _ in 0..count.data() {
|
||||
let paddr = cur_phys.phys_address();
|
||||
unsafe {
|
||||
LockedFrameAllocator.free(paddr, PageFrameCount::new(1));
|
||||
}
|
||||
// 将已回收的物理页面对应的Page从PAGE_MANAGER中删去
|
||||
page_manager_guard.remove_page(&paddr);
|
||||
cur_phys = cur_phys.next();
|
||||
}
|
||||
|
||||
// 释放key和id
|
||||
self.free_id(&id);
|
||||
self.free_key(&key)
|
||||
}
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
pub fn shm_lock(&mut self, id: ShmId) -> Result<usize, SystemError> {
|
||||
let kernel_shm = self.id2shm.get_mut(&id).ok_or(SystemError::EINVAL)?;
|
||||
kernel_shm.set_mode(ShmFlags::SHM_LOCKED, true);
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
pub fn shm_unlock(&mut self, id: ShmId) -> Result<usize, SystemError> {
|
||||
let kernel_shm = self.id2shm.get_mut(&id).ok_or(SystemError::EINVAL)?;
|
||||
kernel_shm.set_mode(ShmFlags::SHM_LOCKED, false);
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
/// 共享内存信息
|
||||
#[derive(Debug)]
|
||||
pub struct KernelShm {
|
||||
/// 权限信息
|
||||
kern_ipc_perm: KernIpcPerm,
|
||||
/// 共享内存起始物理地址
|
||||
shm_start_paddr: PhysAddr,
|
||||
/// 共享内存大小(bytes),注意是用户指定的大小(未经过页面对齐)
|
||||
shm_size: usize,
|
||||
/// 最后一次连接的时间
|
||||
shm_atim: PosixTimeSpec,
|
||||
/// 最后一次断开连接的时间
|
||||
shm_dtim: PosixTimeSpec,
|
||||
/// 最后一次更改信息的时间
|
||||
shm_ctim: PosixTimeSpec,
|
||||
/// 创建者进程id
|
||||
shm_cprid: Pid,
|
||||
/// 最后操作者进程id
|
||||
shm_lprid: Pid,
|
||||
}
|
||||
|
||||
impl KernelShm {
|
||||
pub fn new(kern_ipc_perm: KernIpcPerm, shm_start_paddr: PhysAddr, shm_size: usize) -> Self {
|
||||
let shm_cprid = ProcessManager::current_pid();
|
||||
KernelShm {
|
||||
kern_ipc_perm,
|
||||
shm_start_paddr,
|
||||
shm_size,
|
||||
shm_atim: PosixTimeSpec::new(0, 0),
|
||||
shm_dtim: PosixTimeSpec::new(0, 0),
|
||||
shm_ctim: PosixTimeSpec::now(),
|
||||
shm_cprid,
|
||||
shm_lprid: shm_cprid,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_paddr(&self) -> PhysAddr {
|
||||
self.shm_start_paddr
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.shm_size
|
||||
}
|
||||
|
||||
/// 更新最后连接时间
|
||||
pub fn update_atim(&mut self) {
|
||||
// 更新最后一次连接时间
|
||||
self.shm_atim = PosixTimeSpec::now();
|
||||
|
||||
// 更新最后操作当前共享内存的进程ID
|
||||
self.shm_lprid = ProcessManager::current_pid();
|
||||
}
|
||||
|
||||
/// 更新最后断开连接时间
|
||||
pub fn update_dtim(&mut self) {
|
||||
// 更新最后一次断开连接时间
|
||||
self.shm_dtim = PosixTimeSpec::now();
|
||||
|
||||
// 更新最后操作当前共享内存的进程ID
|
||||
self.shm_lprid = ProcessManager::current_pid();
|
||||
}
|
||||
|
||||
/// 更新最后一次修改信息的时间
|
||||
pub fn update_ctim(&mut self) {
|
||||
// 更新最后一次修改信息的时间
|
||||
self.shm_ctim = PosixTimeSpec::now();
|
||||
}
|
||||
|
||||
/// 共享内存段的映射计数(有多少个不同的VMA映射)
|
||||
pub fn map_count(&self) -> usize {
|
||||
let page_manager_guard = page_manager_lock_irqsave();
|
||||
let mut id_set: HashSet<usize> = HashSet::new();
|
||||
let mut cur_phys = PhysPageFrame::new(self.shm_start_paddr);
|
||||
let page_count = PageFrameCount::from_bytes(page_align_up(self.shm_size)).unwrap();
|
||||
|
||||
for _ in 0..page_count.data() {
|
||||
let page = page_manager_guard.get(&cur_phys.phys_address()).unwrap();
|
||||
id_set.extend(
|
||||
page.anon_vma()
|
||||
.iter()
|
||||
.map(|vma| vma.id())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
cur_phys = cur_phys.next();
|
||||
}
|
||||
|
||||
// 由于LockedVMA的id是独一无二的,因此有多少个不同的id,就代表着有多少个不同的VMA映射到共享内存段
|
||||
return id_set.len();
|
||||
}
|
||||
|
||||
pub fn copy_from(&mut self, shm_id_ds: PosixShmIdDs) {
|
||||
self.kern_ipc_perm.uid = shm_id_ds.uid() as usize;
|
||||
self.kern_ipc_perm.gid = shm_id_ds.gid() as usize;
|
||||
self.kern_ipc_perm.mode = ShmFlags::from_bits_truncate(shm_id_ds.mode());
|
||||
self.update_ctim();
|
||||
}
|
||||
|
||||
pub fn set_mode(&mut self, shmflg: ShmFlags, set: bool) {
|
||||
if set {
|
||||
self.kern_ipc_perm.mode.insert(shmflg);
|
||||
} else {
|
||||
self.kern_ipc_perm.mode.remove(shmflg);
|
||||
}
|
||||
|
||||
self.update_ctim();
|
||||
}
|
||||
}
|
||||
|
||||
/// 共享内存权限信息
|
||||
#[derive(Debug)]
|
||||
pub struct KernIpcPerm {
|
||||
/// 共享内存id
|
||||
id: ShmId,
|
||||
/// 共享内存键值,由创建共享内存用户指定
|
||||
key: ShmKey,
|
||||
/// 共享内存拥有者用户id
|
||||
uid: usize,
|
||||
/// 共享内存拥有者所在组id
|
||||
gid: usize,
|
||||
/// 共享内存创建者用户id
|
||||
_cuid: usize,
|
||||
/// 共享内存创建者所在组id
|
||||
_cgid: usize,
|
||||
/// 共享内存区权限模式
|
||||
mode: ShmFlags,
|
||||
_seq: usize,
|
||||
}
|
||||
|
||||
/// 共享内存元信息,符合POSIX标准
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PosixShmMetaInfo {
|
||||
/// 最大共享内存段的大小(bytes)
|
||||
shmmax: usize,
|
||||
/// 最小共享内存段的大小(bytes)
|
||||
shmmin: usize,
|
||||
/// 最大共享内存标识符数量
|
||||
shmmni: usize,
|
||||
/// 单个进程可以拥有的最大共享内存段的数量,和最大共享内存标识符数量相同
|
||||
shmseg: usize,
|
||||
/// 所有共享内存段总共可以使用的最大内存量(pages)
|
||||
shmall: usize,
|
||||
_unused1: usize,
|
||||
_unused2: usize,
|
||||
_unused3: usize,
|
||||
_unused4: usize,
|
||||
}
|
||||
|
||||
impl PosixShmMetaInfo {
|
||||
/// 最小共享内存段的大小(bytes)
|
||||
pub const SHMMIN: usize = 1;
|
||||
/// 最大共享内存标识符数量
|
||||
pub const SHMMNI: usize = 4096;
|
||||
/// 最大共享内存段的大小(bytes)
|
||||
pub const SHMMAX: usize = usize::MAX - (1 << 24);
|
||||
/// 所有共享内存段总共可以使用的最大内存量(pages)
|
||||
pub const SHMALL: usize = usize::MAX - (1 << 24);
|
||||
/// 单个进程可以拥有的最大共享内存段的数量,和最大共享内存标识符数量相同
|
||||
pub const SHMSEG: usize = 4096;
|
||||
|
||||
pub fn new() -> Self {
|
||||
PosixShmMetaInfo {
|
||||
shmmax: Self::SHMMAX,
|
||||
shmmin: Self::SHMMIN,
|
||||
shmmni: Self::SHMMNI,
|
||||
shmseg: Self::SHMSEG,
|
||||
shmall: Self::SHMALL,
|
||||
_unused1: 0,
|
||||
_unused2: 0,
|
||||
_unused3: 0,
|
||||
_unused4: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 共享内存信息,符合POSIX标准
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PosixShmInfo {
|
||||
/// 已使用id数
|
||||
used_ids: i32,
|
||||
/// 共享内存总量(pages)
|
||||
shm_tot: usize,
|
||||
/// 保留在内存中的共享内存大小
|
||||
shm_rss: usize,
|
||||
/// 被置换出的共享内存大小
|
||||
shm_swp: usize,
|
||||
/// 尝试置换次数
|
||||
swap_attempts: usize,
|
||||
/// 成功置换次数
|
||||
swap_successes: usize,
|
||||
}
|
||||
|
||||
impl PosixShmInfo {
|
||||
pub fn new(
|
||||
used_ids: i32,
|
||||
shm_tot: usize,
|
||||
shm_rss: usize,
|
||||
shm_swp: usize,
|
||||
swap_attempts: usize,
|
||||
swap_successes: usize,
|
||||
) -> Self {
|
||||
PosixShmInfo {
|
||||
used_ids,
|
||||
shm_tot,
|
||||
shm_rss,
|
||||
shm_swp,
|
||||
swap_attempts,
|
||||
swap_successes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 共享内存段属性信息,符合POSIX标准
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct PosixShmIdDs {
|
||||
/// 共享内存段权限
|
||||
shm_perm: PosixIpcPerm,
|
||||
/// 共享内存大小(bytes)
|
||||
shm_segsz: usize,
|
||||
/// 最后一次连接的时间
|
||||
shm_atime: i64,
|
||||
/// 最后一次断开连接的时间
|
||||
shm_dtime: i64,
|
||||
/// 最后一次更改信息的时间
|
||||
shm_ctime: i64,
|
||||
/// 创建者进程id
|
||||
shm_cpid: u32,
|
||||
/// 最后操作者进程id
|
||||
shm_lpid: u32,
|
||||
/// 链接数
|
||||
shm_map_count: usize,
|
||||
_unused1: usize,
|
||||
_unused2: usize,
|
||||
}
|
||||
|
||||
impl PosixShmIdDs {
|
||||
pub fn uid(&self) -> u32 {
|
||||
self.shm_perm.uid
|
||||
}
|
||||
|
||||
pub fn gid(&self) -> u32 {
|
||||
self.shm_perm.gid
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> u32 {
|
||||
self.shm_perm.mode
|
||||
}
|
||||
}
|
||||
|
||||
/// 共享内存段权限,符合POSIX标准
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct PosixIpcPerm {
|
||||
/// IPC对象键值
|
||||
key: i32,
|
||||
/// 当前用户id
|
||||
uid: u32,
|
||||
/// 当前用户组id
|
||||
gid: u32,
|
||||
/// 创建者用户id
|
||||
cuid: u32,
|
||||
/// 创建者组id
|
||||
cgid: u32,
|
||||
/// 权限
|
||||
mode: u32,
|
||||
/// 序列号
|
||||
seq: i32,
|
||||
_pad1: i32,
|
||||
_unused1: usize,
|
||||
_unused2: usize,
|
||||
}
|
||||
|
||||
impl PosixIpcPerm {
|
||||
pub fn new(key: i32, uid: u32, gid: u32, cuid: u32, cgid: u32, mode: u32) -> Self {
|
||||
PosixIpcPerm {
|
||||
key,
|
||||
uid,
|
||||
gid,
|
||||
cuid,
|
||||
cgid,
|
||||
mode,
|
||||
seq: 0,
|
||||
_pad1: 0,
|
||||
_unused1: 0,
|
||||
_unused2: 0,
|
||||
}
|
||||
}
|
||||
}
|
@ -6,20 +6,35 @@ use core::{
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
arch::ipc::signal::{SigCode, SigFlags, SigSet, Signal},
|
||||
arch::{
|
||||
ipc::signal::{SigCode, SigFlags, SigSet, Signal},
|
||||
MMArch,
|
||||
},
|
||||
filesystem::vfs::{
|
||||
file::{File, FileMode},
|
||||
FilePrivateData,
|
||||
},
|
||||
ipc::shm::{shm_manager_lock, IPC_PRIVATE},
|
||||
kerror, kwarn,
|
||||
libs::align::page_align_up,
|
||||
libs::spinlock::SpinLock,
|
||||
mm::VirtAddr,
|
||||
mm::{
|
||||
allocator::page_frame::{PageFrameCount, PhysPageFrame, VirtPageFrame},
|
||||
page::{page_manager_lock_irqsave, PageFlags, PageFlushAll},
|
||||
syscall::ProtFlags,
|
||||
ucontext::{AddressSpace, VMA},
|
||||
VirtAddr, VmFlags,
|
||||
},
|
||||
process::{Pid, ProcessManager},
|
||||
syscall::{user_access::UserBufferWriter, Syscall},
|
||||
syscall::{
|
||||
user_access::{UserBufferReader, UserBufferWriter},
|
||||
Syscall,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
pipe::{LockedPipeInode, PipeFsPrivateData},
|
||||
shm::{ShmCtlCmd, ShmFlags, ShmId, ShmKey},
|
||||
signal_types::{
|
||||
SaHandlerType, SigInfo, SigType, Sigaction, SigactionType, UserSigaction, USER_SIG_DFL,
|
||||
USER_SIG_ERR, USER_SIG_IGN,
|
||||
@ -230,4 +245,262 @@ impl Syscall {
|
||||
}
|
||||
return retval.map(|_| 0);
|
||||
}
|
||||
|
||||
/// # SYS_SHMGET系统调用函数,用于获取共享内存
|
||||
///
|
||||
/// ## 参数
|
||||
///
|
||||
/// - `key`: 共享内存键值
|
||||
/// - `size`: 共享内存大小(bytes)
|
||||
/// - `shmflg`: 共享内存标志
|
||||
///
|
||||
/// ## 返回值
|
||||
///
|
||||
/// 成功:共享内存id
|
||||
/// 失败:错误码
|
||||
pub fn shmget(key: ShmKey, size: usize, shmflg: ShmFlags) -> Result<usize, SystemError> {
|
||||
// 暂不支持巨页
|
||||
if shmflg.contains(ShmFlags::SHM_HUGETLB) {
|
||||
kerror!("shmget: not support huge page");
|
||||
return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP);
|
||||
}
|
||||
|
||||
let mut shm_manager_guard = shm_manager_lock();
|
||||
match key {
|
||||
// 创建共享内存段
|
||||
IPC_PRIVATE => shm_manager_guard.add(key, size, shmflg),
|
||||
_ => {
|
||||
// 查找key对应的共享内存段是否存在
|
||||
let id = shm_manager_guard.contains_key(&key);
|
||||
if let Some(id) = id {
|
||||
// 不能重复创建
|
||||
if shmflg.contains(ShmFlags::IPC_CREAT | ShmFlags::IPC_EXCL) {
|
||||
return Err(SystemError::EEXIST);
|
||||
}
|
||||
|
||||
// key值存在,说明有对应共享内存,返回该共享内存id
|
||||
return Ok(id.data());
|
||||
} else {
|
||||
// key不存在且shm_flags不包含IPC_CREAT创建IPC对象标志,则返回错误码
|
||||
if !shmflg.contains(ShmFlags::IPC_CREAT) {
|
||||
return Err(SystemError::ENOENT);
|
||||
}
|
||||
|
||||
// 存在创建IPC对象标志
|
||||
return shm_manager_guard.add(key, size, shmflg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # SYS_SHMAT系统调用函数,用于连接共享内存段
|
||||
///
|
||||
/// ## 参数
|
||||
///
|
||||
/// - `id`: 共享内存id
|
||||
/// - `vaddr`: 连接共享内存的进程虚拟内存区域起始地址
|
||||
/// - `shmflg`: 共享内存标志
|
||||
///
|
||||
/// ## 返回值
|
||||
///
|
||||
/// 成功:映射到共享内存的虚拟内存区域起始地址
|
||||
/// 失败:错误码
|
||||
pub fn shmat(id: ShmId, vaddr: VirtAddr, shmflg: ShmFlags) -> Result<usize, SystemError> {
|
||||
let mut shm_manager_guard = shm_manager_lock();
|
||||
let current_address_space = AddressSpace::current()?;
|
||||
let mut address_write_guard = current_address_space.write();
|
||||
|
||||
let kernel_shm = shm_manager_guard.get_mut(&id).ok_or(SystemError::EINVAL)?;
|
||||
let size = page_align_up(kernel_shm.size());
|
||||
let mut phys = PhysPageFrame::new(kernel_shm.start_paddr());
|
||||
let count = PageFrameCount::from_bytes(size).unwrap();
|
||||
let r = match vaddr.data() {
|
||||
// 找到空闲区域并映射到共享内存
|
||||
0 => {
|
||||
// 找到空闲区域
|
||||
let region = address_write_guard
|
||||
.mappings
|
||||
.find_free(vaddr, size)
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
let vm_flags = VmFlags::from(shmflg);
|
||||
let destination = VirtPageFrame::new(region.start());
|
||||
let page_flags: PageFlags<MMArch> =
|
||||
PageFlags::from_prot_flags(ProtFlags::from(vm_flags), true);
|
||||
let flusher: PageFlushAll<MMArch> = PageFlushAll::new();
|
||||
|
||||
// 将共享内存映射到对应虚拟区域
|
||||
let vma = VMA::physmap(
|
||||
phys,
|
||||
destination,
|
||||
count,
|
||||
vm_flags,
|
||||
page_flags,
|
||||
&mut address_write_guard.user_mapper.utable,
|
||||
flusher,
|
||||
)?;
|
||||
|
||||
// 将VMA加入到当前进程的VMA列表中
|
||||
address_write_guard.mappings.insert_vma(vma);
|
||||
|
||||
region.start().data()
|
||||
}
|
||||
// 指定虚拟地址
|
||||
_ => {
|
||||
// 获取对应vma
|
||||
let vma = address_write_guard
|
||||
.mappings
|
||||
.contains(vaddr)
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
if vma.lock().region().start() != vaddr {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
// 验证用户虚拟内存区域是否有效
|
||||
let _ = UserBufferReader::new(vaddr.data() as *const u8, size, true)?;
|
||||
|
||||
// 必须在取消映射前获取到PageFlags
|
||||
let page_flags = address_write_guard
|
||||
.user_mapper
|
||||
.utable
|
||||
.translate(vaddr)
|
||||
.ok_or(SystemError::EINVAL)?
|
||||
.1;
|
||||
|
||||
// 取消原映射
|
||||
let flusher: PageFlushAll<MMArch> = PageFlushAll::new();
|
||||
vma.unmap(&mut address_write_guard.user_mapper.utable, flusher);
|
||||
|
||||
// 将该虚拟内存区域映射到共享内存区域
|
||||
let mut page_manager_guard = page_manager_lock_irqsave();
|
||||
let mut virt = VirtPageFrame::new(vaddr);
|
||||
for _ in 0..count.data() {
|
||||
let r = unsafe {
|
||||
address_write_guard.user_mapper.utable.map_phys(
|
||||
virt.virt_address(),
|
||||
phys.phys_address(),
|
||||
page_flags,
|
||||
)
|
||||
}
|
||||
.expect("Failed to map zero, may be OOM error");
|
||||
r.flush();
|
||||
|
||||
// 将vma加入到对应Page的anon_vma
|
||||
page_manager_guard
|
||||
.get_mut(&phys.phys_address())
|
||||
.insert_vma(vma.clone());
|
||||
|
||||
phys = phys.next();
|
||||
virt = virt.next();
|
||||
}
|
||||
|
||||
// 更新vma的映射状态
|
||||
vma.lock().set_mapped(true);
|
||||
|
||||
vaddr.data()
|
||||
}
|
||||
};
|
||||
|
||||
// 更新最后一次连接时间
|
||||
kernel_shm.update_atim();
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// # SYS_SHMDT系统调用函数,用于取消对共享内存的连接
|
||||
///
|
||||
/// ## 参数
|
||||
///
|
||||
/// - `vaddr`: 需要取消映射的虚拟内存区域起始地址
|
||||
///
|
||||
/// ## 返回值
|
||||
///
|
||||
/// 成功:0
|
||||
/// 失败:错误码
|
||||
pub fn shmdt(vaddr: VirtAddr) -> Result<usize, SystemError> {
|
||||
let current_address_space = AddressSpace::current()?;
|
||||
let mut address_write_guard = current_address_space.write();
|
||||
|
||||
// 获取vma
|
||||
let vma = address_write_guard
|
||||
.mappings
|
||||
.contains(vaddr)
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
|
||||
// 判断vaddr是否为起始地址
|
||||
if vma.lock().region().start() != vaddr {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
// 获取映射的物理地址
|
||||
let paddr = address_write_guard
|
||||
.user_mapper
|
||||
.utable
|
||||
.translate(vaddr)
|
||||
.ok_or(SystemError::EINVAL)?
|
||||
.0;
|
||||
|
||||
// 如果物理页的shm_id为None,代表不是共享页
|
||||
let page_manager_guard = page_manager_lock_irqsave();
|
||||
let page = page_manager_guard.get(&paddr).ok_or(SystemError::EINVAL)?;
|
||||
let shm_id = page.shm_id().ok_or(SystemError::EINVAL)?;
|
||||
drop(page_manager_guard);
|
||||
|
||||
// 获取对应共享页管理信息
|
||||
let mut shm_manager_guard = shm_manager_lock();
|
||||
let kernel_shm = shm_manager_guard
|
||||
.get_mut(&shm_id)
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
// 更新最后一次断开连接时间
|
||||
kernel_shm.update_dtim();
|
||||
drop(shm_manager_guard);
|
||||
|
||||
// 取消映射
|
||||
let flusher: PageFlushAll<MMArch> = PageFlushAll::new();
|
||||
vma.unmap(&mut address_write_guard.user_mapper.utable, flusher);
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
/// # SYS_SHMCTL系统调用函数,用于管理共享内存段
|
||||
///
|
||||
/// ## 参数
|
||||
///
|
||||
/// - `id`: 共享内存id
|
||||
/// - `cmd`: 操作码
|
||||
/// - `user_buf`: 用户缓冲区
|
||||
/// - `from_user`: buf_vaddr是否来自用户地址空间
|
||||
///
|
||||
/// ## 返回值
|
||||
///
|
||||
/// 成功:0
|
||||
/// 失败:错误码
|
||||
pub fn shmctl(
|
||||
id: ShmId,
|
||||
cmd: ShmCtlCmd,
|
||||
user_buf: *const u8,
|
||||
from_user: bool,
|
||||
) -> Result<usize, SystemError> {
|
||||
let mut shm_manager_guard = shm_manager_lock();
|
||||
|
||||
match cmd {
|
||||
// 查看共享内存元信息
|
||||
ShmCtlCmd::IpcInfo => shm_manager_guard.ipc_info(user_buf, from_user),
|
||||
// 查看共享内存使用信息
|
||||
ShmCtlCmd::ShmInfo => shm_manager_guard.shm_info(user_buf, from_user),
|
||||
// 查看id对应的共享内存信息
|
||||
ShmCtlCmd::ShmStat | ShmCtlCmd::ShmtStatAny | ShmCtlCmd::IpcStat => {
|
||||
shm_manager_guard.shm_stat(id, cmd, user_buf, from_user)
|
||||
}
|
||||
// 设置KernIpcPerm
|
||||
ShmCtlCmd::IpcSet => shm_manager_guard.ipc_set(id, user_buf, from_user),
|
||||
// 将共享内存段设置为可回收状态
|
||||
ShmCtlCmd::IpcRmid => shm_manager_guard.ipc_rmid(id),
|
||||
// 锁住共享内存段,不允许内存置换
|
||||
ShmCtlCmd::ShmLock => shm_manager_guard.shm_lock(id),
|
||||
// 解锁共享内存段,允许内存置换
|
||||
ShmCtlCmd::ShmUnlock => shm_manager_guard.shm_unlock(id),
|
||||
// 无效操作码
|
||||
ShmCtlCmd::Default => Err(SystemError::EINVAL),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user