implement posix thread

This commit is contained in:
Jianfeng Jiang 2022-12-20 14:12:22 +08:00
parent 7e6591aab2
commit 1e89933a99
73 changed files with 2579 additions and 893 deletions

3
.gitattributes vendored
View File

@ -5,4 +5,5 @@ src/apps/execve/execve filter=lfs diff=lfs merge=lfs -text
src/apps/execve/hello filter=lfs diff=lfs merge=lfs -text
src/apps/fork_c/fork filter=lfs diff=lfs merge=lfs -text
src/apps/signal_c/signal_test filter=lfs diff=lfs merge=lfs -text
src/apps/busybox/busybox filter=lfs diff=lfs merge=lfs -text
src/apps/busybox/busybox filter=lfs diff=lfs merge=lfs -text
src/apps/pthread/pthread_test filter=lfs diff=lfs merge=lfs -text

View File

@ -0,0 +1,7 @@
.PHONY: build clean run
build: pthread_test.c
@gcc -static pthread_test.c -lpthread -o pthread_test
clean:
@rm pthread_test
run: build
@./pthread_test

3
src/apps/pthread/pthread_test Executable file
View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d40bfc16dbf95aba3afa1934fa4efc0147f0a0a12533362a8d95e105d4d2af21
size 1693840

View File

@ -0,0 +1,280 @@
// This test file is from occlum pthread test.
#include <sys/types.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#ifndef SYS_gettid
#error "SYS_gettid unavailable on this system"
#endif
#define gettid() ((pid_t)syscall(SYS_gettid))
// ============================================================================
// Helper functions
// ============================================================================
#define THROW_ERROR(fmt, ...) do { \
printf("\t\tERROR:" fmt " in func %s at line %d of file %s with errno %d: %s\n", \
##__VA_ARGS__, __func__, __LINE__, __FILE__, errno, strerror(errno)); \
return -1; \
} while (0)
// ============================================================================
// Helper macros
// ============================================================================
#define NTHREADS (3)
#define STACK_SIZE (8 * 1024)
// ============================================================================
// The test case of concurrent counter
// ============================================================================
#define LOCAL_COUNT (1000UL)
#define EXPECTED_GLOBAL_COUNT (LOCAL_COUNT * NTHREADS)
struct thread_arg {
int ti;
long local_count;
volatile unsigned long *global_count;
pthread_mutex_t *mutex;
};
static void *thread_func(void *_arg) {
struct thread_arg *arg = _arg;
for (long i = 0; i < arg->local_count; i++) {
pthread_mutex_lock(arg->mutex);
(*arg->global_count)++;
pthread_mutex_unlock(arg->mutex);
}
return NULL;
}
static int test_mutex_with_concurrent_counter(void) {
/*
* Multiple threads are to increase a global counter concurrently
*/
volatile unsigned long global_count = 0;
pthread_t threads[NTHREADS];
struct thread_arg thread_args[NTHREADS];
/*
* Protect the counter with a mutex
*/
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
/*
* Start the threads
*/
for (int ti = 0; ti < NTHREADS; ti++) {
struct thread_arg *thread_arg = &thread_args[ti];
thread_arg->ti = ti;
thread_arg->local_count = LOCAL_COUNT;
thread_arg->global_count = &global_count;
thread_arg->mutex = &mutex;
if (pthread_create(&threads[ti], NULL, thread_func, thread_arg) < 0) {
printf("ERROR: pthread_create failed (ti = %d)\n", ti);
return -1;
}
}
/*
* Wait for the threads to finish
*/
for (int ti = 0; ti < NTHREADS; ti++) {
if (pthread_join(threads[ti], NULL) < 0) {
printf("ERROR: pthread_join failed (ti = %d)\n", ti);
return -1;
}
}
/*
* Check the correctness of the concurrent counter
*/
if (global_count != EXPECTED_GLOBAL_COUNT) {
printf("ERROR: incorrect global_count (actual = %ld, expected = %ld)\n",
global_count, EXPECTED_GLOBAL_COUNT);
return -1;
}
pthread_mutex_destroy(&mutex);
return 0;
}
// ============================================================================
// The test case of robust mutex
// ============================================================================
struct thread_robust_arg {
int ti;
volatile int *global_count;
pthread_mutex_t *mutex;
};
int ret_err = -1;
static void *thread_worker(void *_arg) {
struct thread_robust_arg *arg = _arg;
int err = pthread_mutex_lock(arg->mutex);
if (err == EOWNERDEAD) {
// The mutex is locked by the thread here, but the state is marked as
// inconsistent, the thread should call 'pthread_mutex_consistent' to
// make the mutex consistent again.
if (pthread_mutex_consistent(arg->mutex) != 0) {
printf("ERROR: failed to recover the mutex\n");
return &ret_err;
}
} else if (err != 0) {
printf("ERROR: failed to lock the mutex with error: %d\n", err);
return &ret_err;
}
// Mutex is locked
(*arg->global_count)++;
// Wait for other threads to acquire the lock
sleep(1);
// Exit without unlocking the mutex, this will makes the mutex in an
// inconsistent state.
return NULL;
}
static int test_robust_mutex_with_concurrent_counter(void) {
volatile int global_count = 0;
pthread_t threads[NTHREADS];
struct thread_robust_arg thread_args[NTHREADS];
// Init robust mutex
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(&mutex, &attr);
// Start the threads
for (int ti = 0; ti < NTHREADS; ti++) {
struct thread_robust_arg *thread_arg = &thread_args[ti];
thread_arg->ti = ti;
thread_arg->global_count = &global_count;
thread_arg->mutex = &mutex;
if (pthread_create(&threads[ti], NULL, thread_worker, thread_arg) < 0) {
THROW_ERROR("pthread_create failed (ti = %d)", ti);
}
}
// Wait for the threads to finish
for (int ti = 0; ti < NTHREADS; ti++) {
int *ret_val;
if (pthread_join(threads[ti], (void **)&ret_val) < 0) {
THROW_ERROR("pthread_join failed (ti = %d)", ti);
}
// printf("Thread %d joined\n", ti);
// fflush(stdout);
if (ret_val && *ret_val != 0) {
THROW_ERROR("run thread failed (ti = %d) with return val: %d", ti, *ret_val);
}
}
// printf("Thread all exited.\n");
// fflush(stdout);
// Check the result
if (global_count != NTHREADS) {
THROW_ERROR("incorrect global_count (actual = %d, expected = %d)", global_count,
NTHREADS);
}
pthread_mutex_destroy(&mutex);
return 0;
}
// ============================================================================
// The test case of waiting condition variable
// ============================================================================
#define WAIT_ROUND (10)
struct thread_cond_arg {
int ti;
volatile unsigned int *val;
volatile int *exit_thread_count;
pthread_cond_t *cond_val;
pthread_mutex_t *mutex;
};
static void *thread_cond_wait(void *_arg) {
struct thread_cond_arg *arg = _arg;
printf("Thread #%d: start to wait on condition variable.\n", arg->ti);
fflush(stdout);
for (unsigned int i = 0; i < WAIT_ROUND; ++i) {
int tid = gettid();
printf("WAIT ROUND: %d, tid = %d\n", i, tid);
fflush(stdout);
pthread_mutex_lock(arg->mutex);
printf("pthread mutex lock: tid = %d\n", tid);
fflush(stdout);
while (*(arg->val) == 0) {
pthread_cond_wait(arg->cond_val, arg->mutex);
printf("pthread cond wait: tid = %d\n", tid);
fflush(stdout);
}
pthread_mutex_unlock(arg->mutex);
}
(*arg->exit_thread_count)++;
printf("Thread #%d: exited.\n", arg->ti);
fflush(stdout);
return NULL;
}
static int test_mutex_with_cond_wait(void) {
volatile unsigned int val = 0;
volatile int exit_thread_count = 0;
pthread_t threads[NTHREADS];
struct thread_cond_arg thread_args[NTHREADS];
pthread_cond_t cond_val = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/*
* Start the threads waiting on the condition variable
*/
for (int ti = 0; ti < NTHREADS; ti++) {
struct thread_cond_arg *thread_arg = &thread_args[ti];
thread_arg->ti = ti;
thread_arg->val = &val;
thread_arg->exit_thread_count = &exit_thread_count;
thread_arg->cond_val = &cond_val;
thread_arg->mutex = &mutex;
if (pthread_create(&threads[ti], NULL, thread_cond_wait, thread_arg) < 0) {
printf("ERROR: pthread_create failed (ti = %d)\n", ti);
return -1;
}
}
/*
* Unblock all threads currently waiting on the condition variable
*/
while (exit_thread_count < NTHREADS) {
pthread_mutex_lock(&mutex);
val = 1;
pthread_cond_broadcast(&cond_val);
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
val = 0;
pthread_mutex_unlock(&mutex);
}
/*
* Wait for the threads to finish
*/
for (int ti = 0; ti < NTHREADS; ti++) {
if (pthread_join(threads[ti], NULL) < 0) {
printf("ERROR: pthread_join failed (ti = %d)\n", ti);
return -1;
}
}
return 0;
}
int main() {
test_mutex_with_concurrent_counter();
test_robust_mutex_with_concurrent_counter();
// test_mutex_with_cond_wait();
return 0;
}

View File

@ -12,7 +12,6 @@
#include <string.h>
#include <spawn.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <pthread.h>

View File

@ -4,7 +4,7 @@ use crate::log::LogLevel;
pub const USER_STACK_SIZE: usize = PAGE_SIZE * 4;
pub const KERNEL_STACK_SIZE: usize = PAGE_SIZE * 64;
pub const KERNEL_HEAP_SIZE: usize = 0x1_000_000;
pub const KERNEL_HEAP_SIZE: usize = 0x2_000_000;
pub const KERNEL_OFFSET: usize = 0xffffff00_00000000;
pub const PHYS_OFFSET: usize = 0xFFFF800000000000;

View File

@ -51,6 +51,10 @@ impl CpuContext {
pub fn set_rip(&mut self, rip: u64) {
self.gp_regs.rip = rip;
}
pub fn set_fsbase(&mut self, fs_base: u64) {
self.fs_base = fs_base;
}
}
#[derive(Clone, Default, Copy, Debug)]

View File

@ -146,7 +146,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! {
}
pub fn panic_handler() {
println!("[panic]: cr3:{:x}", x86_64_util::get_cr3());
// println!("[panic]: cr3:{:x}", x86_64_util::get_cr3());
// let mut fp: usize;
// let stop = unsafe{
// Task::current().kstack.get_top()

View File

@ -3,7 +3,7 @@ use crate::prelude::*;
use crate::{
config::PAGE_SIZE,
mm::address::is_aligned,
vm::{VmFrame, VmFrameVec},
vm::{VmFrame, VmFrameVec, VmPerm},
*,
};
use alloc::collections::{btree_map::Entry, BTreeMap};
@ -264,6 +264,11 @@ impl MemorySet {
}
Err(Error::PageFault)
}
pub fn protect(&mut self, addr: Vaddr, flags: PTFlags) {
let va = VirtAddr(addr);
self.pt.protect(va, flags)
}
}
impl Clone for MemorySet {

View File

@ -34,6 +34,8 @@ bitflags::bitflags! {
/// Indicates that the mapping is present in all address spaces, so it isn't flushed from
/// the TLB on an address space switch.
const GLOBAL = 1 << 8;
/// Forbid execute codes on the page. The NXE bits in EFER msr must be set.
const NO_EXECUTE = 1 << 63;
}
}

View File

@ -5,7 +5,7 @@ use crate::{
*,
};
use alloc::{collections::BTreeMap, vec, vec::Vec};
use core::fmt;
use core::{fmt, panic};
use lazy_static::lazy_static;
lazy_static! {
@ -94,6 +94,18 @@ impl PageTable {
entry.0 = 0;
}
pub fn protect(&mut self, va: VirtAddr, flags: PTFlags) {
let entry = self.get_entry_or_create(va).unwrap();
if entry.is_unused() || !entry.is_present() {
panic!("{:#x?} is invalid before protect", va);
}
// clear old mask
let clear_flags_mask = !PTFlags::all().bits;
entry.0 &= clear_flags_mask;
// set new mask
entry.0 |= flags.bits;
}
pub fn map_area(&mut self, area: &MapArea) {
for (va, pa) in area.mapper.iter() {
assert!(pa.start_pa().0 < PHYS_OFFSET);

View File

@ -47,13 +47,7 @@ impl VmSpace {
///
/// For more information, see `VmMapOptions`.
pub fn map(&self, frames: VmFrameVec, options: &VmMapOptions) -> Result<Vaddr> {
let mut flags = PTFlags::PRESENT;
if options.perm.contains(VmPerm::W) {
flags.insert(PTFlags::WRITABLE);
}
// if options.perm.contains(VmPerm::U) {
flags.insert(PTFlags::USER);
// }
let flags = PTFlags::from(options.perm);
if options.addr.is_none() {
return Err(Error::InvalidArgs);
}
@ -101,7 +95,16 @@ impl VmSpace {
/// The entire specified VM range must have been mapped with physical
/// memory pages.
pub fn protect(&self, range: &Range<Vaddr>, perm: VmPerm) -> Result<()> {
todo!()
debug_assert!(range.start % PAGE_SIZE == 0);
debug_assert!(range.end % PAGE_SIZE == 0);
let start_page = range.start / PAGE_SIZE;
let end_page = range.end / PAGE_SIZE;
let flags = PTFlags::from(perm);
for page_idx in start_page..end_page {
let addr = page_idx * PAGE_SIZE;
self.memory_set.lock().protect(addr, flags)
}
Ok(())
}
}
@ -227,3 +230,17 @@ impl TryFrom<u64> for VmPerm {
VmPerm::from_bits(value as u8).ok_or(Error::InvalidVmpermBits)
}
}
impl From<VmPerm> for PTFlags {
fn from(vm_perm: VmPerm) -> Self {
let mut flags = PTFlags::PRESENT | PTFlags::USER;
if vm_perm.contains(VmPerm::W) {
flags |= PTFlags::WRITABLE;
}
// FIXME: how to respect executable flags?
if !vm_perm.contains(VmPerm::X) {
flags |= PTFlags::NO_EXECUTE;
}
flags
}
}

View File

@ -1,7 +1,9 @@
//! util for x86_64, it will rename to x86_64 when depend x86_64 isn't necessary
use core::arch::{asm, x86_64::CpuidResult};
use x86_64::registers::{control::Cr4Flags, segmentation::Segment64, xcontrol::XCr0Flags};
use x86_64::registers::{
control::Cr4Flags, model_specific::EferFlags, segmentation::Segment64, xcontrol::XCr0Flags,
};
#[inline(always)]
pub fn read_rsp() -> usize {
@ -237,6 +239,13 @@ pub fn enable_common_cpu_features() {
unsafe {
x86_64::registers::xcontrol::XCr0::write(xcr0);
}
unsafe {
// enable non-executable page protection
x86_64::registers::model_specific::Efer::update(|efer| {
*efer |= EferFlags::NO_EXECUTE_ENABLE;
});
}
}
pub fn flush_tlb() {

View File

@ -93,8 +93,8 @@ fn create_fs_image(path: &Path) -> anyhow::Result<String> {
.write(true)
.create(true)
.open(fs_img_path.as_str())?;
// 16MiB
f.set_len(16 * 1024 * 1024).unwrap();
// 32MiB
f.set_len(64 * 1024 * 1024).unwrap();
Ok(format!(
"file={},if=none,format=raw,id=x0",
fs_img_path.as_str()
@ -120,6 +120,9 @@ pub fn create_disk_images(kernel_binary_path: &Path) -> PathBuf {
.arg(kernel_binary_path.parent().unwrap());
build_cmd.arg("--quiet");
// println!("current dir = {:?}", build_cmd.get_current_dir());
// println!("args = {:?}", build_cmd.get_args());
if !build_cmd.status().unwrap().success() {
panic!("build failed");
}
@ -142,3 +145,4 @@ fn run_test_command(mut cmd: Command) -> anyhow::Result<ExitStatus> {
let status = runner_utils::run_with_timeout(&mut cmd, Duration::from_secs(TEST_TIMEOUT_SECS))?;
Ok(status)
}

View File

@ -2,9 +2,9 @@
//! When use this crate, typeflags and typeflags-util should also be added as dependency.
//!
//! The require macro are used to ensure that an object has the enough capability to call the function.
//! The **require** macro can accept constrait [SomeRightSet] > [SomeRight],
//! The **require** macro can accept constraint [SomeRightSet] > [SomeRight],
//! which means the SomeRightSet should **contain** the SomeRight.
//! The **require** macro can also accept constrait [SomeRightSet] > [AnotherRightSet],
//! The **require** macro can also accept constraint [SomeRightSet] > [AnotherRightSet],
//! which means the SomeRightSet should **include** the AnotherRightSet. In this case, AnotherRightSet should be a **generic parameter**.
//! i.e., AnotherRightSet should occur the the generic param list of the function.
//!

View File

@ -140,6 +140,7 @@ fn set_contain_where_clause(
}
}
/// generate a where clause to constraint the type set with another type set
fn set_include_where_clause(
require_attr: &RequireAttr,
required_type_set: Ident,

View File

@ -100,7 +100,7 @@ fn inner_block_device_test() {
}
#[allow(unused)]
pub fn block_device_test() {
let test_process = Process::spawn_kernel_process(|| {
// inner_block_device_test();
let _ = Process::spawn_kernel_process(|| {
inner_block_device_test();
});
}

View File

@ -19,16 +19,13 @@
use crate::{
prelude::*,
user_apps::{get_busybox_app, UserApp},
user_apps::{get_all_apps, get_busybox_app, UserApp},
};
use process::Process;
use crate::{
process::{
process_filter::ProcessFilter,
wait::{wait_child_exit, WaitOptions},
},
user_apps::get_all_apps,
use crate::process::{
process_filter::ProcessFilter,
wait::{wait_child_exit, WaitOptions},
};
extern crate alloc;
@ -42,6 +39,7 @@ mod process;
pub mod rights;
pub mod syscall;
pub mod thread;
pub mod time;
pub mod tty;
mod user_apps;
mod util;
@ -56,19 +54,20 @@ pub fn init() {
jinux_frame::enable_interrupts();
}
pub fn init_process() {
println!("[kernel] Spawn init process!, pid = {}", current!().pid());
driver::pci::virtio::block::block_device_test();
pub fn init_thread() {
println!(
"[kernel] Spawn init thread, tid = {}",
current_thread!().tid()
);
// driver::pci::virtio::block::block_device_test();
let process = Process::spawn_kernel_process(|| {
println!("[kernel] Hello world from kernel!");
let current = current!();
let pid = current.pid();
let current = current_thread!();
let pid = current.tid();
debug!("current pid = {}", pid);
let ppid = current.parent().unwrap().pid();
debug!("current ppid = {}", ppid);
});
info!(
"[jinux-std/lib.rs] spawn kernel process, pid = {}",
"[jinux-std/lib.rs] spawn kernel thread, tid = {}",
process.pid()
);
@ -79,18 +78,18 @@ pub fn init_process() {
// Run test apps
for app in get_all_apps().into_iter() {
let UserApp {
app_name,
elf_path: app_name,
app_content,
argv,
envp,
} = app;
info!("[jinux-std/lib.rs] spwan {:?} process", app_name);
println!("[jinux-std/lib.rs] spwan {:?} process", app_name);
Process::spawn_user_process(app_name.clone(), app_content, argv, Vec::new());
}
// Run busybox ash
let UserApp {
app_name,
elf_path: app_name,
app_content,
argv,
envp,
@ -102,13 +101,12 @@ pub fn init_process() {
loop {
// We don't have preemptive scheduler now.
// The long running init process should yield its own execution to allow other tasks to go on.
// The init process should wait and reap all children.
let _ = wait_child_exit(ProcessFilter::Any, WaitOptions::empty());
}
}
/// first process never return
pub fn run_first_process() -> ! {
Process::spawn_kernel_process(init_process);
Process::spawn_kernel_process(init_thread);
unreachable!()
}

View File

@ -17,6 +17,7 @@ pub(crate) use jinux_frame::vm::Vaddr;
pub(crate) use jinux_frame::{debug, error, info, print, println, trace, warn};
pub(crate) use spin::{Mutex, RwLock};
/// return current process
#[macro_export]
macro_rules! current {
() => {
@ -24,7 +25,16 @@ macro_rules! current {
};
}
/// return current thread
#[macro_export]
macro_rules! current_thread {
() => {
crate::thread::Thread::current()
};
}
pub(crate) use crate::current;
pub(crate) use crate::current_thread;
pub(crate) use crate::error::{Errno, Error};
pub(crate) use lazy_static::lazy_static;
pub(crate) type Result<T> = core::result::Result<T, Error>;

View File

@ -5,12 +5,22 @@ use jinux_frame::{
};
use crate::{
current_thread,
fs::file_table::FileTable,
prelude::*,
process::{new_pid, signal::sig_queues::SigQueues, table},
thread::Thread,
process::{
posix_thread::{
builder::PosixThreadBuilder, name::ThreadName, posix_thread_ext::PosixThreadExt,
},
process_table,
},
rights::Full,
thread::{allocate_tid, thread_table, Thread, Tid},
util::write_val_to_user,
vm::vmar::Vmar,
};
use super::Process;
use super::{posix_thread::PosixThread, signal::sig_disposition::SigDispositions, Process};
bitflags! {
pub struct CloneFlags: u32 {
@ -43,10 +53,10 @@ bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct CloneArgs {
new_sp: Vaddr,
new_sp: u64,
parent_tidptr: Vaddr,
child_tidptr: Vaddr,
tls: usize,
tls: u64,
clone_flags: CloneFlags,
}
@ -62,10 +72,10 @@ impl CloneArgs {
}
pub const fn new(
new_sp: Vaddr,
new_sp: u64,
parent_tidptr: Vaddr,
child_tidptr: Vaddr,
tls: usize,
tls: u64,
clone_flags: CloneFlags,
) -> Self {
CloneArgs {
@ -87,62 +97,150 @@ impl From<u64> for CloneFlags {
}
impl CloneFlags {
fn contains_unsupported_flags(&self) -> bool {
self.intersects(!(CloneFlags::CLONE_CHILD_SETTID | CloneFlags::CLONE_CHILD_CLEARTID))
fn check_unsupported_flags(&self) -> Result<()> {
let supported_flags = CloneFlags::CLONE_VM
| CloneFlags::CLONE_FS
| CloneFlags::CLONE_FILES
| CloneFlags::CLONE_SIGHAND
| CloneFlags::CLONE_THREAD
| CloneFlags::CLONE_SYSVSEM
| CloneFlags::CLONE_SETTLS
| CloneFlags::CLONE_PARENT_SETTID
| CloneFlags::CLONE_CHILD_SETTID
| CloneFlags::CLONE_CHILD_CLEARTID;
let unsupported_flags = *self - supported_flags;
if !unsupported_flags.is_empty() {
panic!("contains unsupported clone flags: {:?}", unsupported_flags);
}
Ok(())
}
}
/// Clone a child process. Without schedule it to run.
pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result<Arc<Process>> {
let child_pid = new_pid();
let current = Process::current();
/// Clone a child thread. Without schedule it to run.
pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result<Tid> {
clone_args.clone_flags.check_unsupported_flags()?;
if clone_args.clone_flags.contains(CloneFlags::CLONE_THREAD) {
let child_thread = clone_child_thread(parent_context, clone_args)?;
let child_tid = child_thread.tid();
debug!(
"*********schedule child thread, child pid = {}**********",
child_tid
);
child_thread.run();
debug!(
"*********return to parent thread, child pid = {}*********",
child_tid
);
Ok(child_tid)
} else {
let child_process = clone_child_process(parent_context, clone_args)?;
let child_pid = child_process.pid();
debug!(
"*********schedule child process, child pid = {}**********",
child_pid
);
child_process.run();
debug!(
"*********return to parent process, child pid = {}*********",
child_pid
);
Ok(child_pid)
}
}
// child process vmar
let parent_root_vmar = current.root_vmar().unwrap();
let child_root_vmar = current.root_vmar().unwrap().fork_vmar()?;
fn clone_child_thread(parent_context: CpuContext, clone_args: CloneArgs) -> Result<Arc<Thread>> {
let clone_flags = clone_args.clone_flags;
let current = current!();
debug_assert!(clone_flags.contains(CloneFlags::CLONE_VM));
debug_assert!(clone_flags.contains(CloneFlags::CLONE_FILES));
debug_assert!(clone_flags.contains(CloneFlags::CLONE_SIGHAND));
let child_root_vmar = current.root_vmar();
let child_vm_space = child_root_vmar.vm_space().clone();
let child_cpu_context = clone_cpu_context(
parent_context,
clone_args.new_sp,
clone_args.tls,
clone_flags,
);
let child_user_space = Arc::new(UserSpace::new(child_vm_space, child_cpu_context));
clone_sysvsem(clone_flags)?;
// child process user_vm
let child_user_vm = match current.user_vm() {
None => None,
Some(user_vm) => Some(user_vm.clone()),
};
let child_tid = allocate_tid();
// inherit sigmask from current thread
let current_thread = current_thread!();
let current_posix_thread = current_thread.posix_thread();
let sig_mask = current_posix_thread.sig_mask().lock().clone();
let is_main_thread = child_tid == current.pid();
let thread_builder = PosixThreadBuilder::new(child_tid, child_user_space)
.process(Arc::downgrade(&current))
.is_main_thread(is_main_thread);
let child_thread = thread_builder.build();
current.threads.lock().push(child_thread.clone());
let child_posix_thread = child_thread.posix_thread();
clone_parent_settid(child_tid, clone_args.parent_tidptr, clone_flags)?;
clone_child_cleartid(child_posix_thread, clone_args.child_tidptr, clone_flags)?;
clone_child_settid(
child_root_vmar,
child_tid,
clone_args.child_tidptr,
clone_flags,
)?;
Ok(child_thread)
}
// child process user space
let mut child_cpu_context = parent_context.clone();
child_cpu_context.gp_regs.rax = 0; // Set return value of child process
fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Result<Arc<Process>> {
let current = current!();
let clone_flags = clone_args.clone_flags;
// clone vm
let parent_root_vmar = current.root_vmar();
let child_root_vmar = clone_vm(parent_root_vmar, clone_flags)?;
let child_user_vm = Some(current.user_vm().unwrap().clone());
// clone user space
let child_cpu_context = clone_cpu_context(
parent_context,
clone_args.new_sp,
clone_args.tls,
clone_flags,
);
let child_vm_space = child_root_vmar.vm_space().clone();
let child_user_space = Arc::new(UserSpace::new(child_vm_space, child_cpu_context));
let child_file_name = match current.filename() {
None => None,
Some(filename) => Some(filename.clone()),
};
let child_file_table = current.file_table.lock().clone();
// clone file table
let child_file_table = clone_files(current.file_table(), clone_flags);
// clone sig dispositions
let child_sig_dispositions = clone_sighand(current.sig_dispositions(), clone_flags);
// clone system V semaphore
clone_sysvsem(clone_flags)?;
let child_elf_path = current.filename().unwrap().clone();
let child_thread_name = ThreadName::new_from_elf_path(&child_elf_path)?;
// inherit parent's sig disposition
let child_sig_dispositions = current.sig_dispositions().lock().clone();
// sig queue is set empty
let child_sig_queues = SigQueues::new();
// inherit parent's sig mask
let child_sig_mask = current.sig_mask().lock().clone();
let current_thread = current_thread!();
let posix_thread = current_thread.posix_thread();
let child_sig_mask = posix_thread.sig_mask().lock().clone();
let child_tid = allocate_tid();
let mut child_thread_builder = PosixThreadBuilder::new(child_tid, child_user_space)
.thread_name(Some(child_thread_name))
.sig_mask(child_sig_mask);
let child = Arc::new_cyclic(|child_process_ref| {
let weak_child_process = child_process_ref.clone();
let tid = child_pid;
let child_thread =
Thread::new_user_thread(tid, child_user_space.clone(), weak_child_process);
let child_pid = child_tid;
child_thread_builder = child_thread_builder.process(weak_child_process);
let child_thread = child_thread_builder.build();
Process::new(
child_pid,
vec![child_thread],
child_file_name,
Some(child_elf_path),
child_user_vm,
// Some(child_user_space),
Some(child_root_vmar),
child_root_vmar.clone(),
Weak::new(),
child_file_table,
child_sig_dispositions,
child_sig_queues,
child_sig_mask,
)
});
// Inherit parent's process group
@ -151,36 +249,131 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result<
child.set_process_group(Arc::downgrade(&parent_process_group));
current!().add_child(child.clone());
table::add_process(child.clone());
deal_with_clone_args(clone_args, &child)?;
process_table::add_process(child.clone());
let child_thread = thread_table::tid_to_thread(child_tid).unwrap();
let child_posix_thread = child_thread.posix_thread();
clone_parent_settid(child_tid, clone_args.parent_tidptr, clone_flags)?;
clone_child_cleartid(child_posix_thread, clone_args.child_tidptr, clone_flags)?;
clone_child_settid(
&child_root_vmar,
child_tid,
clone_args.child_tidptr,
clone_flags,
)?;
Ok(child)
}
fn deal_with_clone_args(clone_args: CloneArgs, child_process: &Arc<Process>) -> Result<()> {
let clone_flags = clone_args.clone_flags;
if clone_flags.contains_unsupported_flags() {
panic!("Found unsupported clone flags: {:?}", clone_flags);
}
fn clone_child_cleartid(
child_posix_thread: &PosixThread,
child_tidptr: Vaddr,
clone_flags: CloneFlags,
) -> Result<()> {
if clone_flags.contains(CloneFlags::CLONE_CHILD_CLEARTID) {
clone_child_clear_tid(child_process)?;
let mut clear_tid = child_posix_thread.clear_child_tid().lock();
*clear_tid = child_tidptr;
}
Ok(())
}
fn clone_child_settid(
child_root_vmar: &Vmar<Full>,
child_tid: Tid,
child_tidptr: Vaddr,
clone_flags: CloneFlags,
) -> Result<()> {
if clone_flags.contains(CloneFlags::CLONE_CHILD_SETTID) {
clone_child_set_tid(child_process, clone_args)?;
child_root_vmar.write_val(child_tidptr, &child_tid)?;
}
Ok(())
}
fn clone_child_clear_tid(child_process: &Arc<Process>) -> Result<()> {
// TODO: clone_child_clear_tid does nothing now
fn clone_parent_settid(
child_tid: Tid,
parent_tidptr: Vaddr,
clone_flags: CloneFlags,
) -> Result<()> {
if clone_flags.contains(CloneFlags::CLONE_PARENT_SETTID) {
write_val_to_user(parent_tidptr, &child_tid)?;
}
Ok(())
}
fn clone_child_set_tid(child_process: &Arc<Process>, clone_args: CloneArgs) -> Result<()> {
let child_pid = child_process.pid();
let child_vmar = child_process
.root_vmar()
.ok_or_else(|| Error::new(Errno::ECHILD))?;
child_vmar.write_val(clone_args.child_tidptr, &child_pid)?;
/// clone child vmar. If CLONE_VM is set, both threads share the same root vmar.
/// Otherwise, fork a new copy-on-write vmar.
fn clone_vm(
parent_root_vmar: &Arc<Vmar<Full>>,
clone_flags: CloneFlags,
) -> Result<Arc<Vmar<Full>>> {
if clone_flags.contains(CloneFlags::CLONE_VM) {
Ok(parent_root_vmar.clone())
} else {
Ok(Arc::new(parent_root_vmar.fork_vmar()?))
}
}
fn clone_cpu_context(
parent_context: CpuContext,
new_sp: u64,
tls: u64,
clone_flags: CloneFlags,
) -> CpuContext {
let mut child_context = parent_context.clone();
// The return value of child thread is zero
child_context.set_rax(0);
if clone_flags.contains(CloneFlags::CLONE_VM) {
// if parent and child shares the same address space, a new stack must be specified.
debug_assert!(new_sp != 0);
}
if new_sp != 0 {
child_context.set_rsp(new_sp as u64);
}
if clone_flags.contains(CloneFlags::CLONE_SETTLS) {
// x86_64 specific: TLS is the fsbase register
child_context.set_fsbase(tls as u64);
}
child_context
}
fn clone_fs(clone_flags: CloneFlags) -> Result<()> {
if clone_flags.contains(CloneFlags::CLONE_FS) {
warn!("CLONE_FS is not supported now")
}
Ok(())
}
fn clone_files(
parent_file_table: &Arc<Mutex<FileTable>>,
clone_flags: CloneFlags,
) -> Arc<Mutex<FileTable>> {
// if CLONE_FILES is set, the child and parent shares the same file table
// Otherwise, the child will deep copy a new file table.
// FIXME: the clone may not be deep copy.
if clone_flags.contains(CloneFlags::CLONE_FILES) {
parent_file_table.clone()
} else {
Arc::new(Mutex::new(parent_file_table.lock().clone()))
}
}
fn clone_sighand(
parent_sig_dispositions: &Arc<Mutex<SigDispositions>>,
clone_flags: CloneFlags,
) -> Arc<Mutex<SigDispositions>> {
// similer to CLONE_FILES
if clone_flags.contains(CloneFlags::CLONE_SIGHAND) {
parent_sig_dispositions.clone()
} else {
Arc::new(Mutex::new(parent_sig_dispositions.lock().clone()))
}
}
fn clone_sysvsem(clone_flags: CloneFlags) -> Result<()> {
if clone_flags.contains(CloneFlags::CLONE_SYSVSEM) {
warn!("CLONE_SYSVSEM is not supported now");
}
Ok(())
}

View File

@ -12,7 +12,6 @@ use crate::{prelude::*, rights::Full, vm::vmar::Vmar};
/// 2. create a vmo for each elf segment, create a backup pager for each segment. Then map the vmo to the root vmar.
/// 3. write proper content to the init stack.
pub fn load_elf_to_root_vmar(
filename: CString,
elf_file_content: &'static [u8],
root_vmar: &Vmar<Full>,
argv: Vec<CString>,

View File

@ -1,19 +1,20 @@
use core::sync::atomic::{AtomicI32, Ordering};
use self::name::ProcessName;
use self::posix_thread::posix_thread_ext::PosixThreadExt;
use self::process_group::ProcessGroup;
use self::process_vm::user_heap::UserHeap;
use self::process_vm::UserVm;
use self::rlimit::ResourceLimits;
use self::signal::constants::SIGCHLD;
use self::signal::sig_disposition::SigDispositions;
use self::signal::sig_mask::SigMask;
use self::signal::sig_queues::SigQueues;
use self::signal::signals::kernel::KernelSignal;
use self::status::ProcessStatus;
use crate::fs::file_table::FileTable;
use crate::prelude::*;
use crate::rights::Full;
use crate::thread::Thread;
use crate::thread::kernel_thread::KernelThreadExt;
use crate::thread::{thread_table, Thread};
use crate::tty::get_console;
use crate::vm::vmar::Vmar;
use jinux_frame::sync::WaitQueue;
@ -21,39 +22,37 @@ use jinux_frame::task::Task;
pub mod clone;
pub mod elf;
pub mod exception;
pub mod fifo_scheduler;
pub mod name;
pub mod posix_thread;
pub mod process_filter;
pub mod process_group;
pub mod process_table;
pub mod process_vm;
pub mod rlimit;
pub mod signal;
pub mod status;
pub mod table;
pub mod wait;
static PID_ALLOCATOR: AtomicI32 = AtomicI32::new(0);
pub type Pid = i32;
pub type Pgid = i32;
pub type ExitCode = i32;
/// Process stands for a set of tasks that shares the same userspace.
/// Currently, we only support one task inside a process.
/// Process stands for a set of threads that shares the same userspace.
/// Currently, we only support one thread inside a process.
pub struct Process {
// Immutable Part
pid: Pid,
threads: Vec<Arc<Thread>>,
filename: Option<CString>,
// user_space: Option<Arc<UserSpace>>,
elf_path: Option<CString>,
user_vm: Option<UserVm>,
root_vmar: Option<Vmar<Full>>,
root_vmar: Arc<Vmar<Full>>,
/// wait for child status changed
waiting_children: WaitQueue,
/// wait for io events
poll_queue: WaitQueue,
// Mutable Part
/// The threads
threads: Mutex<Vec<Arc<Thread>>>,
/// The exit code
exit_code: AtomicI32,
/// Process status
@ -64,39 +63,43 @@ pub struct Process {
children: Mutex<BTreeMap<Pid, Arc<Process>>>,
/// Process group
process_group: Mutex<Weak<ProcessGroup>>,
/// Process name
process_name: Mutex<Option<ProcessName>>,
/// File table
file_table: Mutex<FileTable>,
file_table: Arc<Mutex<FileTable>>,
/// resource limits
resource_limits: Mutex<ResourceLimits>,
// Signal
sig_dispositions: Mutex<SigDispositions>,
/// sig dispositions
sig_dispositions: Arc<Mutex<SigDispositions>>,
/// Process-level signal queues
sig_queues: Mutex<SigQueues>,
/// Process-level sigmask
sig_mask: Mutex<SigMask>,
/// Signal handler ucontext address
sig_context: Mutex<VecDeque<Vaddr>>,
}
impl Process {
/// returns the current process
pub fn current() -> Arc<Process> {
let current_thread = Thread::current();
current_thread.process()
if current_thread.is_posix_thread() {
let posix_thread = current_thread.posix_thread();
posix_thread.process()
} else if current_thread.is_kernel_thread() {
let kernel_thread = current_thread.kernel_thread();
kernel_thread.process()
} else {
panic!("[Internal error]The process is neither kernel process or user process");
}
}
/// create a new process(not schedule it)
pub fn new(
pid: Pid,
threads: Vec<Arc<Thread>>,
exec_filename: Option<CString>,
elf_path: Option<CString>,
user_vm: Option<UserVm>,
root_vmar: Option<Vmar<Full>>,
root_vmar: Arc<Vmar<Full>>,
process_group: Weak<ProcessGroup>,
file_table: FileTable,
sig_dispositions: SigDispositions,
sig_queues: SigQueues,
sig_mask: SigMask,
file_table: Arc<Mutex<FileTable>>,
sig_dispositions: Arc<Mutex<SigDispositions>>,
) -> Self {
let parent = if pid == 0 {
Weak::new()
@ -107,15 +110,11 @@ impl Process {
let children = BTreeMap::new();
let waiting_children = WaitQueue::new();
let poll_queue = WaitQueue::new();
let process_name = exec_filename.as_ref().map(|filename| {
let mut process_name = ProcessName::new();
process_name.set_name(filename).unwrap();
process_name
});
let resource_limits = ResourceLimits::default();
Self {
pid,
threads,
filename: exec_filename,
threads: Mutex::new(threads),
elf_path,
user_vm,
root_vmar,
waiting_children,
@ -125,12 +124,10 @@ impl Process {
parent: Mutex::new(parent),
children: Mutex::new(children),
process_group: Mutex::new(process_group),
process_name: Mutex::new(process_name),
file_table: Mutex::new(file_table),
sig_dispositions: Mutex::new(sig_dispositions),
sig_queues: Mutex::new(sig_queues),
sig_mask: Mutex::new(sig_mask),
sig_context: Mutex::new(VecDeque::new()),
file_table,
sig_dispositions,
sig_queues: Mutex::new(SigQueues::new()),
resource_limits: Mutex::new(resource_limits),
}
}
@ -142,7 +139,7 @@ impl Process {
&self.poll_queue
}
/// init a user process and send the process to scheduler
/// init a user process and run the process
pub fn spawn_user_process(
filename: CString,
elf_file_content: &'static [u8],
@ -157,14 +154,14 @@ impl Process {
process
}
/// init a kernel process and send the process to scheduler
/// init a kernel process and run the process
pub fn spawn_kernel_process<F>(task_fn: F) -> Arc<Self>
where
F: Fn() + Send + Sync + 'static,
{
let process_fn = move || {
task_fn();
current!().exit(0);
current!().exit_group(0);
};
let process = Process::create_kernel_process(process_fn);
process.run();
@ -172,48 +169,43 @@ impl Process {
}
fn create_user_process(
filename: CString,
elf_path: CString,
elf_file_content: &'static [u8],
argv: Vec<CString>,
envp: Vec<CString>,
) -> Arc<Self> {
let pid = new_pid();
let user_process = Arc::new_cyclic(|weak_process_ref| {
let weak_process = weak_process_ref.clone();
let cloned_filename = Some(filename.clone());
let cloned_filename = Some(elf_path.clone());
let root_vmar = Vmar::<Full>::new_root().unwrap();
let tid = pid;
let thread = Thread::new_user_thread_from_elf(
let thread = Thread::new_posix_thread_from_elf(
&root_vmar,
filename,
elf_path,
elf_file_content,
weak_process,
tid,
argv,
envp,
);
let pid = thread.tid();
let user_vm = UserVm::new();
let file_table = FileTable::new_with_stdio();
let sig_dispositions = SigDispositions::new();
let sig_queues = SigQueues::new();
let sig_mask = SigMask::new_empty();
Process::new(
let process = Process::new(
pid,
vec![thread],
cloned_filename,
Some(user_vm),
Some(root_vmar),
Arc::new(root_vmar),
Weak::new(),
file_table,
sig_dispositions,
sig_queues,
sig_mask,
)
Arc::new(Mutex::new(file_table)),
Arc::new(Mutex::new(sig_dispositions)),
);
process
});
// Set process group
user_process.create_and_set_process_group();
table::add_process(user_process.clone());
process_table::add_process(user_process.clone());
let parent = user_process
.parent()
.expect("[Internel error] User process should always have parent");
@ -225,30 +217,27 @@ impl Process {
where
F: Fn() + Send + Sync + 'static,
{
let pid = new_pid();
let kernel_process = Arc::new_cyclic(|weak_process_ref| {
let weak_process = weak_process_ref.clone();
let tid = pid;
let thread = Thread::new_kernel_thread(tid, task_fn, weak_process);
let thread = Thread::new_kernel_thread(task_fn, weak_process_ref.clone());
let pid = thread.tid();
let file_table = FileTable::new();
let sig_dispositions = SigDispositions::new();
let sig_queues = SigQueues::new();
let sig_mask = SigMask::new_empty();
// FIXME: kernel process does not need root vmar
let root_vmar = Vmar::<Full>::new_root().unwrap();
Process::new(
pid,
vec![thread],
None,
None,
None,
Arc::new(root_vmar),
Weak::new(),
file_table,
sig_dispositions,
sig_queues,
sig_mask,
Arc::new(Mutex::new(file_table)),
Arc::new(Mutex::new(sig_dispositions)),
)
});
kernel_process.create_and_set_process_group();
table::add_process(kernel_process.clone());
process_table::add_process(kernel_process.clone());
if let Some(parent) = kernel_process.parent() {
parent.add_child(kernel_process.clone());
}
@ -269,25 +258,17 @@ impl Process {
}
}
pub fn process_name(&self) -> &Mutex<Option<ProcessName>> {
&self.process_name
}
pub fn process_group(&self) -> &Mutex<Weak<ProcessGroup>> {
&self.process_group
}
pub fn sig_context(&self) -> &Mutex<VecDeque<Vaddr>> {
&self.sig_context
}
/// add a child process
pub fn add_child(&self, child: Arc<Process>) {
let child_pid = child.pid();
self.children.lock().insert(child_pid, child);
}
fn set_parent(&self, parent: Weak<Process>) {
pub fn set_parent(&self, parent: Weak<Process>) {
*self.parent.lock() = parent;
}
@ -300,7 +281,7 @@ impl Process {
*self.process_group.lock() = process_group;
}
pub fn file_table(&self) -> &Mutex<FileTable> {
pub fn file_table(&self) -> &Arc<Mutex<FileTable>> {
&self.file_table
}
@ -310,20 +291,24 @@ impl Process {
let process_group = Arc::new(ProcessGroup::new(self.clone()));
let pgid = process_group.pgid();
self.set_process_group(Arc::downgrade(&process_group));
table::add_process_group(process_group);
process_table::add_process_group(process_group);
}
pub fn parent(&self) -> Option<Arc<Process>> {
self.parent.lock().upgrade()
}
/// Exit process.
/// Exit thread group(the process).
/// Set the status of the process as Zombie and set exit code.
/// Move all children to init process.
/// Wake up the parent wait queue if parent is waiting for self.
pub fn exit(&self, exit_code: i32) {
pub fn exit_group(&self, exit_code: i32) {
debug!("exit group was called");
self.status.lock().set_zombie();
self.exit_code.store(exit_code, Ordering::Relaxed);
for thread in &*self.threads.lock() {
thread.exit();
}
// move children to the init process
if !self.is_init_process() {
let init_process = get_init_process();
@ -349,9 +334,17 @@ impl Process {
/// start to run current process
pub fn run(&self) {
for thread in &self.threads {
thread.run()
}
let threads = self.threads.lock();
// when run the process, the process should has only one thread
debug_assert!(threads.len() == 1);
let thread = threads[0].clone();
// should not hold the lock when run thread
drop(threads);
thread.run();
}
pub fn threads(&self) -> &Mutex<Vec<Arc<Thread>>> {
&self.threads
}
/// yield the current process to allow other processes to run
@ -365,8 +358,8 @@ impl Process {
}
/// returns the root vmar
pub fn root_vmar(&self) -> Option<&Vmar<Full>> {
self.root_vmar.as_ref()
pub fn root_vmar(&self) -> &Arc<Vmar<Full>> {
&self.root_vmar
}
/// returns the user heap if the process does have, otherwise None
@ -382,19 +375,22 @@ impl Process {
pub fn reap_zombie_child(&self, pid: Pid) -> i32 {
let child_process = self.children.lock().remove(&pid).unwrap();
assert!(child_process.status().lock().is_zombie());
table::remove_process(child_process.pid());
for thread in &*child_process.threads.lock() {
thread_table::remove_thread(thread.tid());
}
process_table::remove_process(child_process.pid());
if let Some(process_group) = child_process.process_group().lock().upgrade() {
process_group.remove_process(child_process.pid);
}
child_process.exit_code()
child_process.exit_code().load(Ordering::SeqCst)
}
pub fn children(&self) -> &Mutex<BTreeMap<Pid, Arc<Process>>> {
&self.children
}
pub fn exit_code(&self) -> i32 {
self.exit_code.load(Ordering::Relaxed)
pub fn exit_code(&self) -> &AtomicI32 {
&self.exit_code
}
/// whether the process has child process
@ -403,24 +399,24 @@ impl Process {
}
pub fn filename(&self) -> Option<&CString> {
self.filename.as_ref()
self.elf_path.as_ref()
}
pub fn status(&self) -> &Mutex<ProcessStatus> {
&self.status
}
pub fn sig_dispositions(&self) -> &Mutex<SigDispositions> {
pub fn resource_limits(&self) -> &Mutex<ResourceLimits> {
&self.resource_limits
}
pub fn sig_dispositions(&self) -> &Arc<Mutex<SigDispositions>> {
&self.sig_dispositions
}
pub fn sig_queues(&self) -> &Mutex<SigQueues> {
&self.sig_queues
}
pub fn sig_mask(&self) -> &Mutex<SigMask> {
&self.sig_mask
}
}
/// Get the init process
@ -436,8 +432,3 @@ pub fn get_init_process() -> Arc<Process> {
}
current_process
}
/// allocate a new pid for new process
pub fn new_pid() -> Pid {
PID_ALLOCATOR.fetch_add(1, Ordering::Release)
}

View File

@ -1,31 +0,0 @@
use crate::prelude::*;
pub const MAX_PROCESS_NAME_LEN: usize = 128;
pub struct ProcessName {
inner: [u8; MAX_PROCESS_NAME_LEN],
count: usize,
}
impl ProcessName {
pub fn new() -> Self {
ProcessName {
inner: [0; MAX_PROCESS_NAME_LEN],
count: 0,
}
}
pub fn set_name(&mut self, name: &CStr) -> Result<()> {
let bytes = name.to_bytes_with_nul();
let bytes_len = bytes.len();
if bytes_len > MAX_PROCESS_NAME_LEN {
return_errno_with_message!(Errno::E2BIG, "process name is too long");
}
self.count = bytes_len;
self.inner[..bytes_len].clone_from_slice(bytes);
Ok(())
}
pub fn get_name(&self) -> Result<Option<&CStr>> {
Ok(Some(&(CStr::from_bytes_with_nul(&self.inner)?)))
}
}

View File

@ -0,0 +1,109 @@
use jinux_frame::user::UserSpace;
use crate::{
prelude::*,
process::{
posix_thread::name::ThreadName,
signal::{sig_mask::SigMask, sig_queues::SigQueues},
Process,
},
thread::{status::ThreadStatus, task::create_new_user_task, thread_table, Thread, Tid},
};
use super::PosixThread;
/// The builder to build a posix thread
pub struct PosixThreadBuilder {
// The essential part
tid: Tid,
user_space: Arc<UserSpace>,
process: Weak<Process>,
// Optional part
thread_name: Option<ThreadName>,
set_child_tid: Vaddr,
clear_child_tid: Vaddr,
sig_mask: SigMask,
sig_queues: SigQueues,
is_main_thread: bool,
}
impl PosixThreadBuilder {
pub fn new(tid: Tid, user_space: Arc<UserSpace>) -> Self {
Self {
tid,
user_space,
process: Weak::new(),
thread_name: None,
set_child_tid: 0,
clear_child_tid: 0,
sig_mask: SigMask::new_empty(),
sig_queues: SigQueues::new(),
is_main_thread: true,
}
}
pub fn process(mut self, process: Weak<Process>) -> Self {
self.process = process;
self
}
pub fn thread_name(mut self, thread_name: Option<ThreadName>) -> Self {
self.thread_name = thread_name;
self
}
pub fn set_child_tid(mut self, set_child_tid: Vaddr) -> Self {
self.set_child_tid = set_child_tid;
self
}
pub fn clear_child_tid(mut self, clear_child_tid: Vaddr) -> Self {
self.clear_child_tid = clear_child_tid;
self
}
pub fn is_main_thread(mut self, is_main_thread: bool) -> Self {
self.is_main_thread = is_main_thread;
self
}
pub fn sig_mask(mut self, sig_mask: SigMask) -> Self {
self.sig_mask = sig_mask;
self
}
pub fn build(self) -> Arc<Thread> {
let Self {
tid,
user_space,
process,
thread_name,
set_child_tid,
clear_child_tid,
sig_mask,
sig_queues,
is_main_thread,
} = self;
let thread = Arc::new_cyclic(|thread_ref| {
let task = create_new_user_task(user_space, thread_ref.clone());
let status = ThreadStatus::Init;
let sig_context = Mutex::new(None);
let posix_thread = PosixThread {
process,
is_main_thread,
name: Mutex::new(thread_name),
set_child_tid: Mutex::new(set_child_tid),
clear_child_tid: Mutex::new(clear_child_tid),
sig_mask: Mutex::new(sig_mask),
sig_queues: Mutex::new(sig_queues),
sig_context,
robust_list: Mutex::new(None),
};
Thread::new(tid, task, posix_thread, status)
});
thread_table::add_thread(thread.clone());
thread
}
}

View File

@ -0,0 +1,438 @@
use core::sync::atomic::{AtomicBool, Ordering};
use jinux_frame::cpu::num_cpus;
use crate::{
prelude::*,
thread::{Thread, Tid},
util::read_val_from_user,
};
type FutexBitSet = u32;
type FutexBucketRef = Arc<Mutex<FutexBucket>>;
const FUTEX_OP_MASK: u32 = 0x0000_000F;
const FUTEX_FLAGS_MASK: u32 = 0xFFFF_FFF0;
const FUTEX_BITSET_MATCH_ANY: FutexBitSet = 0xFFFF_FFFF;
/// do futex wait
pub fn futex_wait(futex_addr: u64, futex_val: i32, timeout: &Option<FutexTimeout>) -> Result<()> {
futex_wait_bitset(futex_addr as _, futex_val, timeout, FUTEX_BITSET_MATCH_ANY)
}
/// do futex wait bitset
pub fn futex_wait_bitset(
futex_addr: Vaddr,
futex_val: i32,
timeout: &Option<FutexTimeout>,
bitset: FutexBitSet,
) -> Result<()> {
debug!(
"futex_wait_bitset addr: {:#x}, val: {}, timeout: {:?}, bitset: {:#x}",
futex_addr, futex_val, timeout, bitset
);
let futex_key = FutexKey::new(futex_addr);
let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key);
// lock futex bucket ref here to avoid data race
let mut futex_bucket = futex_bucket_ref.lock();
if futex_key.load_val() != futex_val {
return_errno_with_message!(Errno::EINVAL, "futex value does not match");
}
let futex_item = FutexItem::new(futex_key, bitset);
futex_bucket.enqueue_item(futex_item.clone());
// drop lock
drop(futex_bucket);
// Wait on the futex item
futex_item.wait();
Ok(())
}
/// do futex wake
pub fn futex_wake(futex_addr: Vaddr, max_count: usize) -> Result<usize> {
futex_wake_bitset(futex_addr, max_count, FUTEX_BITSET_MATCH_ANY)
}
/// Do futex wake with bitset
pub fn futex_wake_bitset(
futex_addr: Vaddr,
max_count: usize,
bitset: FutexBitSet,
) -> Result<usize> {
debug!(
"futex_wake_bitset addr: {:#x}, max_count: {}, bitset: {:#x}",
futex_addr as usize, max_count, bitset
);
let futex_key = FutexKey::new(futex_addr);
let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key);
let mut futex_bucket = futex_bucket_ref.lock();
let res = futex_bucket.dequeue_and_wake_items(futex_key, max_count, bitset);
// debug!("futex wake bitset succeeds, res = {}", res);
drop(futex_bucket);
// for _ in 0..res {
// Thread::yield_now();
// }
Ok(res)
}
/// Do futex requeue
pub fn futex_requeue(
futex_addr: Vaddr,
max_nwakes: usize,
max_nrequeues: usize,
futex_new_addr: Vaddr,
) -> Result<usize> {
if futex_new_addr == futex_addr {
return futex_wake(futex_addr, max_nwakes);
}
let futex_key = FutexKey::new(futex_addr);
let futex_new_key = FutexKey::new(futex_new_addr);
let (bucket_idx, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key);
let (new_bucket_idx, futex_new_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_new_key);
let nwakes = {
if bucket_idx == new_bucket_idx {
let mut futex_bucket = futex_bucket_ref.lock();
let nwakes =
futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY);
futex_bucket.update_item_keys(futex_key, futex_new_key, max_nrequeues);
drop(futex_bucket);
nwakes
} else {
let (mut futex_bucket, mut futex_new_bucket) = {
if bucket_idx < new_bucket_idx {
let futex_bucket = futex_bucket_ref.lock();
let futext_new_bucket = futex_new_bucket_ref.lock();
(futex_bucket, futext_new_bucket)
} else {
// bucket_idx > new_bucket_idx
let futex_new_bucket = futex_new_bucket_ref.lock();
let futex_bucket = futex_bucket_ref.lock();
(futex_bucket, futex_new_bucket)
}
};
let nwakes =
futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY);
futex_bucket.requeue_items_to_another_bucket(
futex_key,
&mut futex_new_bucket,
futex_new_key,
max_nrequeues,
);
nwakes
}
};
Ok(nwakes)
}
lazy_static! {
// Use the same count as linux kernel to keep the same performance
static ref BUCKET_COUNT: usize = ((1<<8)* num_cpus()).next_power_of_two() as _;
static ref BUCKET_MASK: usize = *BUCKET_COUNT - 1;
static ref FUTEX_BUCKETS: FutexBucketVec = FutexBucketVec::new(*BUCKET_COUNT);
}
#[derive(Debug, Clone)]
pub struct FutexTimeout {}
impl FutexTimeout {
pub fn new() -> Self {
todo!()
}
}
struct FutexBucketVec {
vec: Vec<FutexBucketRef>,
}
impl FutexBucketVec {
pub fn new(size: usize) -> FutexBucketVec {
let mut buckets = FutexBucketVec {
vec: Vec::with_capacity(size),
};
for _ in 0..size {
let bucket = Arc::new(Mutex::new(FutexBucket::new()));
buckets.vec.push(bucket);
}
buckets
}
pub fn get_bucket(&self, key: FutexKey) -> (usize, FutexBucketRef) {
let index = *BUCKET_MASK & {
// The addr is the multiples of 4, so we ignore the last 2 bits
let addr = key.addr() >> 2;
// simple hash
addr / self.size()
};
(index, self.vec[index].clone())
}
fn size(&self) -> usize {
self.vec.len()
}
}
struct FutexBucket {
queue: VecDeque<FutexItem>,
}
impl FutexBucket {
pub fn new() -> FutexBucket {
FutexBucket {
queue: VecDeque::new(),
}
}
pub fn enqueue_item(&mut self, item: FutexItem) {
self.queue.push_back(item);
}
pub fn dequeue_item(&mut self, item: &FutexItem) {
let item_i = self
.queue
.iter()
.position(|futex_item| *futex_item == *item);
if let Some(item_i) = item_i {
self.queue.remove(item_i).unwrap();
}
}
pub fn dequeue_and_wake_items(
&mut self,
key: FutexKey,
max_count: usize,
bitset: FutexBitSet,
) -> usize {
let mut count = 0;
let mut items_to_wake = Vec::new();
self.queue.retain(|item| {
if count >= max_count || key != item.key || (bitset & item.bitset) == 0 {
true
} else {
items_to_wake.push(item.clone());
count += 1;
false
}
});
// debug!("items to wake len: {}", items_to_wake.len());
FutexItem::batch_wake(&items_to_wake);
count
}
pub fn update_item_keys(&mut self, key: FutexKey, new_key: FutexKey, max_count: usize) {
let mut count = 0;
for item in self.queue.iter_mut() {
if count == max_count {
break;
}
if (*item).key == key {
(*item).key = new_key;
count += 1;
}
}
}
pub fn requeue_items_to_another_bucket(
&mut self,
key: FutexKey,
another: &mut Self,
new_key: FutexKey,
max_nrequeues: usize,
) {
let mut count = 0;
self.queue.retain(|item| {
if count >= max_nrequeues || key != item.key {
true
} else {
let mut new_item = item.clone();
new_item.key = new_key;
another.enqueue_item(new_item);
count += 1;
false
}
});
}
}
#[derive(Debug, PartialEq, Clone)]
struct FutexItem {
key: FutexKey,
bitset: FutexBitSet,
waiter: FutexWaiterRef,
}
impl FutexItem {
pub fn new(key: FutexKey, bitset: FutexBitSet) -> Self {
FutexItem {
key,
bitset,
waiter: Arc::new(FutexWaiter::new()),
}
}
pub fn wake(&self) {
// debug!("wake futex item, key = {:?}", self.key);
self.waiter.wake();
}
pub fn wait(&self) {
// debug!("wait on futex item, key = {:?}", self.key);
self.waiter.wait();
// debug!("wait finished, key = {:?}", self.key);
}
pub fn waiter(&self) -> &FutexWaiterRef {
&self.waiter
}
pub fn batch_wake(items: &[FutexItem]) {
let waiters = items.iter().map(|item| item.waiter()).collect::<Vec<_>>();
FutexWaiter::batch_wake(&waiters);
}
}
// The addr of a futex, it should be used to mark different futex word
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct FutexKey(Vaddr);
impl FutexKey {
pub fn new(futex_addr: Vaddr) -> Self {
FutexKey(futex_addr as _)
}
pub fn load_val(&self) -> i32 {
// FIXME: how to implement a atomic load?
warn!("implement an atomic load");
read_val_from_user(self.0).unwrap()
}
pub fn addr(&self) -> Vaddr {
self.0
}
}
// The implementation is from occlum
#[derive(PartialEq, Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum FutexOp {
FUTEX_WAIT = 0,
FUTEX_WAKE = 1,
FUTEX_FD = 2,
FUTEX_REQUEUE = 3,
FUTEX_CMP_REQUEUE = 4,
FUTEX_WAKE_OP = 5,
FUTEX_LOCK_PI = 6,
FUTEX_UNLOCK_PI = 7,
FUTEX_TRYLOCK_PI = 8,
FUTEX_WAIT_BITSET = 9,
FUTEX_WAKE_BITSET = 10,
}
impl FutexOp {
pub fn from_u32(bits: u32) -> Result<FutexOp> {
match bits {
0 => Ok(FutexOp::FUTEX_WAIT),
1 => Ok(FutexOp::FUTEX_WAKE),
2 => Ok(FutexOp::FUTEX_FD),
3 => Ok(FutexOp::FUTEX_REQUEUE),
4 => Ok(FutexOp::FUTEX_CMP_REQUEUE),
5 => Ok(FutexOp::FUTEX_WAKE_OP),
6 => Ok(FutexOp::FUTEX_LOCK_PI),
7 => Ok(FutexOp::FUTEX_UNLOCK_PI),
8 => Ok(FutexOp::FUTEX_TRYLOCK_PI),
9 => Ok(FutexOp::FUTEX_WAIT_BITSET),
10 => Ok(FutexOp::FUTEX_WAKE_BITSET),
_ => return_errno_with_message!(Errno::EINVAL, "Unknown futex op"),
}
}
}
bitflags! {
pub struct FutexFlags : u32 {
const FUTEX_PRIVATE = 128;
const FUTEX_CLOCK_REALTIME = 256;
}
}
impl FutexFlags {
pub fn from_u32(bits: u32) -> Result<FutexFlags> {
FutexFlags::from_bits(bits)
.ok_or_else(|| Error::with_message(Errno::EINVAL, "unknown futex flags"))
}
}
pub fn futex_op_and_flags_from_u32(bits: u32) -> Result<(FutexOp, FutexFlags)> {
let op = {
let op_bits = bits & FUTEX_OP_MASK;
FutexOp::from_u32(op_bits)?
};
let flags = {
let flags_bits = bits & FUTEX_FLAGS_MASK;
FutexFlags::from_u32(flags_bits)?
};
Ok((op, flags))
}
type FutexWaiterRef = Arc<FutexWaiter>;
#[derive(Debug)]
struct FutexWaiter {
is_woken: AtomicBool,
tid: Tid,
}
impl PartialEq for FutexWaiter {
fn eq(&self, other: &Self) -> bool {
self.tid == other.tid
}
}
impl FutexWaiter {
pub fn new() -> Self {
Self {
is_woken: AtomicBool::new(false),
tid: current_thread!().tid(),
}
}
pub fn wait(&self) {
let current_thread = current_thread!();
if current_thread.tid() != self.tid {
return;
}
self.is_woken.store(false, Ordering::SeqCst);
while !self.is_woken() {
// debug!("futex is wait for waken, tid = {}", self.tid);
Thread::yield_now();
}
// debug!("futex is waken, tid = {}", self.tid);
}
pub fn wake(&self) {
if !self.is_woken() {
// debug!("wake up futex, tid = {}", self.tid);
self.is_woken.store(true, Ordering::SeqCst);
}
}
pub fn is_woken(&self) -> bool {
self.is_woken.load(Ordering::SeqCst)
}
pub fn batch_wake(waiters: &[&FutexWaiterRef]) {
waiters.iter().for_each(|waiter| {
waiter.wake();
});
}
}

View File

@ -0,0 +1,145 @@
use crate::{
prelude::*,
process::posix_thread::{futex::futex_wake, robust_list::wake_robust_futex},
thread::{thread_table, Tid},
util::write_val_to_user,
};
use self::{name::ThreadName, robust_list::RobustListHead};
use super::{
signal::{sig_mask::SigMask, sig_queues::SigQueues},
Process,
};
pub mod builder;
pub mod futex;
pub mod name;
pub mod posix_thread_ext;
pub mod robust_list;
pub struct PosixThread {
// Immutable part
process: Weak<Process>,
is_main_thread: bool,
// Mutable part
name: Mutex<Option<ThreadName>>,
// Linux specific attributes.
// https://man7.org/linux/man-pages/man2/set_tid_address.2.html
set_child_tid: Mutex<Vaddr>,
clear_child_tid: Mutex<Vaddr>,
robust_list: Mutex<Option<RobustListHead>>,
// signal
/// blocked signals
sig_mask: Mutex<SigMask>,
/// thread-directed sigqueue
sig_queues: Mutex<SigQueues>,
/// Signal handler ucontext address
/// FIXME: This field may be removed. For glibc applications with RESTORER flag set, the sig_context is always equals with rsp.
sig_context: Mutex<Option<Vaddr>>,
}
impl PosixThread {
pub fn process(&self) -> Arc<Process> {
self.process.upgrade().unwrap()
}
pub fn thread_name(&self) -> &Mutex<Option<ThreadName>> {
&self.name
}
pub fn set_child_tid(&self) -> &Mutex<Vaddr> {
&self.set_child_tid
}
pub fn clear_child_tid(&self) -> &Mutex<Vaddr> {
&self.clear_child_tid
}
pub fn sig_mask(&self) -> &Mutex<SigMask> {
&self.sig_mask
}
pub fn sig_queues(&self) -> &Mutex<SigQueues> {
&self.sig_queues
}
pub fn sig_context(&self) -> &Mutex<Option<Vaddr>> {
&self.sig_context
}
pub fn robust_list(&self) -> &Mutex<Option<RobustListHead>> {
&self.robust_list
}
/// Whether the thread is main thread. For Posix thread, If a thread's tid is equal to pid, it's main thread.
pub fn is_main_thread(&self) -> bool {
self.is_main_thread
}
/// whether the thread is the last running thread in process
pub fn is_last_thread(&self) -> bool {
let process = self.process.upgrade().unwrap();
let threads = process.threads().lock();
threads
.iter()
.filter(|thread| !thread.status().lock().is_exited())
.count()
== 0
}
/// Walks the robust futex list, marking futex dead and wake waiters.
/// It corresponds to Linux's exit_robust_list(), errors are silently ignored.
pub fn wake_robust_list(&self, tid: Tid) {
let mut robust_list = self.robust_list.lock();
let list_head = match *robust_list {
None => {
return;
}
Some(robust_list_head) => robust_list_head,
};
debug!("wake the rubust_list: {:?}", list_head);
for futex_addr in list_head.futexes() {
// debug!("futex addr = 0x{:x}", futex_addr);
wake_robust_futex(futex_addr, tid).unwrap();
}
debug!("wake robust futex success");
*robust_list = None;
}
/// Posix thread does not contains tid info. So we require tid as a parameter.
pub fn exit(&self, tid: Tid, exit_code: i32) -> Result<()> {
let mut clear_ctid = self.clear_child_tid().lock();
// If clear_ctid !=0 ,do a futex wake and write zero to the clear_ctid addr.
debug!("wake up ctid");
if *clear_ctid != 0 {
futex_wake(*clear_ctid, 1)?;
// FIXME: the correct write length?
write_val_to_user(*clear_ctid, &0i32)?;
*clear_ctid = 0;
}
// exit the robust list: walk the robust list; mark futex words as dead and do futex wake
self.wake_robust_list(tid);
if tid != self.process().pid {
// If the thread is not main thread. We don't remove main thread.
// Main thread are removed when the whole process is reaped.
thread_table::remove_thread(tid);
}
if self.is_main_thread() || self.is_last_thread() {
// exit current process.
debug!("self is main thread or last thread");
debug!("main thread: {}", self.is_main_thread());
debug!("last thread: {}", self.is_last_thread());
current!().exit_group(exit_code);
}
debug!("perform futex wake");
futex_wake(Arc::as_ptr(&self.process()) as Vaddr, 1)?;
Ok(())
}
}

View File

@ -0,0 +1,47 @@
use crate::prelude::*;
pub const MAX_THREAD_NAME_LEN: usize = 16;
pub struct ThreadName {
inner: [u8; MAX_THREAD_NAME_LEN],
count: usize,
}
impl ThreadName {
pub fn new() -> Self {
ThreadName {
inner: [0; MAX_THREAD_NAME_LEN],
count: 0,
}
}
pub fn new_from_elf_path(elf_path: &CStr) -> Result<Self> {
let mut thread_name = ThreadName::new();
let elf_file_name = elf_path
.to_str()?
.split('/')
.last()
.ok_or(Error::with_message(Errno::EINVAL, "invalid elf path"))?;
let name = CString::new(elf_file_name)?;
thread_name.set_name(&name)?;
Ok(thread_name)
}
pub fn set_name(&mut self, name: &CStr) -> Result<()> {
let bytes = name.to_bytes_with_nul();
let bytes_len = bytes.len();
if bytes_len > MAX_THREAD_NAME_LEN {
// if len > MAX_THREAD_NAME_LEN, truncate it.
self.count = MAX_THREAD_NAME_LEN;
self.inner[..MAX_THREAD_NAME_LEN].clone_from_slice(&bytes[..MAX_THREAD_NAME_LEN]);
self.inner[MAX_THREAD_NAME_LEN] = 0;
return Ok(());
}
self.count = bytes_len;
self.inner[..bytes_len].clone_from_slice(bytes);
Ok(())
}
pub fn get_name(&self) -> Result<Option<&CStr>> {
Ok(Some(&(CStr::from_bytes_with_nul(&self.inner)?)))
}
}

View File

@ -0,0 +1,57 @@
use jinux_frame::{cpu::CpuContext, user::UserSpace};
use crate::{
prelude::*,
process::{elf::load_elf_to_root_vmar, Process},
rights::Full,
thread::{allocate_tid, Thread},
vm::vmar::Vmar,
};
use super::{builder::PosixThreadBuilder, name::ThreadName, PosixThread};
pub trait PosixThreadExt {
fn is_posix_thread(&self) -> bool;
fn posix_thread(&self) -> &PosixThread;
fn new_posix_thread_from_elf(
root_vmar: &Vmar<Full>,
elf_path: CString,
elf_file_content: &'static [u8],
process: Weak<Process>,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Arc<Self>;
}
impl PosixThreadExt for Thread {
/// This function should only be called when launch shell()
fn new_posix_thread_from_elf(
root_vmar: &Vmar<Full>,
elf_path: CString,
elf_file_content: &'static [u8],
process: Weak<Process>,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Arc<Self> {
let elf_load_info = load_elf_to_root_vmar(elf_file_content, &root_vmar, argv, envp)
.expect("Load Elf failed");
let vm_space = root_vmar.vm_space().clone();
let mut cpu_ctx = CpuContext::default();
cpu_ctx.set_rip(elf_load_info.entry_point());
cpu_ctx.set_rsp(elf_load_info.user_stack_top());
let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx));
let thread_name = Some(ThreadName::new_from_elf_path(&elf_path).unwrap());
let tid = allocate_tid();
let thread_builder = PosixThreadBuilder::new(tid, user_space)
.thread_name(thread_name)
.process(process);
thread_builder.build()
}
fn is_posix_thread(&self) -> bool {
self.data().downcast_ref::<PosixThread>().is_some()
}
fn posix_thread(&self) -> &PosixThread {
self.data().downcast_ref::<PosixThread>().unwrap()
}
}

View File

@ -0,0 +1,152 @@
//! The implementation of robust list is from occlum.
use crate::{
prelude::*,
process::{posix_thread::futex::futex_wake, Pid},
util::{read_val_from_user, write_val_to_user},
};
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod)]
struct RobustList {
next: Vaddr, // *const Robust list
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod)]
pub struct RobustListHead {
/// Linked list of lock entries
///
/// If it points to the head of the list, then it is the end of the list.
/// If it is an invalid user space pointer or a null pointer, stop iterating
/// the list.
list: RobustList,
/// Specifies the offset from the address of the lock entry to the address
/// of the futex.
futex_offset: isize,
/// Contains transient copy of the address of the lock entry, during list
/// insertion and removal.
list_op_pending: Vaddr, // *const RobustList
}
impl RobustListHead {
/// Return an iterator for all futexes in the robust list.
///
/// The futex refered to by `list_op_pending`, if any, will be returned as
/// the last item.
pub fn futexes<'a>(&'a self) -> FutexIter<'a> {
FutexIter::new(self)
}
/// Return the pending futex address if exist
fn pending_futex_addr(&self) -> Option<Vaddr> {
if self.list_op_pending == 0 {
None
} else {
Some(self.futex_addr(self.list_op_pending))
}
}
/// Get the futex address
fn futex_addr(&self, entry_ptr: Vaddr) -> Vaddr {
(entry_ptr as isize + self.futex_offset) as _
}
}
pub struct FutexIter<'a> {
robust_list: &'a RobustListHead,
entry_ptr: Vaddr,
count: isize,
}
impl<'a> FutexIter<'a> {
pub fn new(robust_list: &'a RobustListHead) -> Self {
Self {
robust_list,
entry_ptr: robust_list.list.next,
count: 0,
}
}
// The `self.count` is normally a positive value used to iterate the list
// to avoid excessively long or circular list, we use a special value -1
// to represent the end of the Iterator.
fn set_end(&mut self) {
self.count = -1;
}
fn is_end(&self) -> bool {
self.count < 0
}
}
const ROBUST_LIST_LIMIT: isize = 2048;
impl<'a> Iterator for FutexIter<'a> {
type Item = Vaddr;
fn next(&mut self) -> Option<Self::Item> {
if self.is_end() {
return None;
}
while self.entry_ptr != &self.robust_list.list as *const _ as _ {
if self.count == ROBUST_LIST_LIMIT {
break;
}
if self.entry_ptr == 0 {
return None;
}
let futex_addr = if self.entry_ptr != self.robust_list.list_op_pending {
Some(self.robust_list.futex_addr(self.entry_ptr))
} else {
None
};
let robust_list = read_val_from_user::<RobustList>(self.entry_ptr).unwrap();
self.entry_ptr = robust_list.next;
self.count += 1;
if futex_addr.is_some() {
return futex_addr;
}
}
self.set_end();
self.robust_list.pending_futex_addr()
}
}
const FUTEX_WAITERS: u32 = 0x8000_0000;
const FUTEX_OWNER_DIED: u32 = 0x4000_0000;
const FUTEX_TID_MASK: u32 = 0x3FFF_FFFF;
/// Wakeup one robust futex owned by the thread
/// FIXME: requires atomic operations here
pub fn wake_robust_futex(futex_addr: Vaddr, tid: Pid) -> Result<()> {
let futex_val = {
if futex_addr == 0 {
return_errno_with_message!(Errno::EINVAL, "invalid futext addr");
}
read_val_from_user::<u32>(futex_addr)?
};
let mut old_val = futex_val;
loop {
// This futex may held by another thread, do nothing
if old_val & FUTEX_TID_MASK != tid as u32 {
break;
}
let new_val = (old_val & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
let cur_val = read_val_from_user(futex_addr)?;
if cur_val != new_val {
// The futex value has changed, let's retry with current value
old_val = cur_val;
write_val_to_user(futex_addr, &new_val)?;
continue;
}
// Wakeup one waiter
if cur_val & FUTEX_WAITERS != 0 {
debug!("wake robust futex addr: {:?}", futex_addr);
futex_wake(futex_addr, 1)?;
}
break;
}
Ok(())
}

View File

@ -1,4 +1,4 @@
use super::{table, Pgid, Pid, Process};
use super::{process_table, Pgid, Pid, Process};
use crate::prelude::*;
pub struct ProcessGroup {
@ -53,11 +53,19 @@ impl ProcessGroup {
// if self contains no process, remove self from table
if len == 0 {
// this must be the last statement
table::remove_process_group(pgid);
process_table::remove_process_group(pgid);
}
}
pub fn pgid(&self) -> Pgid {
self.inner.lock().pgid
}
/// Wake up all processes waiting on polling queue
pub fn wake_all_polling_procs(&self) {
let inner = self.inner.lock();
for (_, process) in &inner.processes {
process.poll_queue().wake_all();
}
}
}

View File

@ -28,7 +28,7 @@ impl UserHeap {
pub fn brk(&self, new_heap_end: Option<Vaddr>) -> Result<Vaddr> {
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let root_vmar = current.root_vmar();
match new_heap_end {
None => {
// create a heap vmo for current process
@ -50,7 +50,8 @@ impl UserHeap {
return Ok(current_heap_end);
}
let new_size = (new_heap_end - self.heap_base).align_up(PAGE_SIZE);
let heap_vmo = root_vmar.get_mapped_vmo(USER_HEAP_BASE)?;
let heap_mapping = root_vmar.get_vm_mapping(USER_HEAP_BASE)?;
let heap_vmo = heap_mapping.vmo();
heap_vmo.resize(new_size)?;
self.current_heap_end.store(new_heap_end, Ordering::Release);
return Ok(new_heap_end);

View File

@ -0,0 +1,119 @@
//! This implementation is from occlum
#![allow(non_camel_case_types)]
use crate::prelude::*;
use super::{elf::init_stack::INIT_STACK_SIZE, process_vm::user_heap::USER_HEAP_SIZE_LIMIT};
pub struct ResourceLimits {
rlimits: [RLimit64; RLIMIT_COUNT],
}
impl ResourceLimits {
pub fn get_rlimit(&self, resource: ResourceType) -> &RLimit64 {
&self.rlimits[resource as usize]
}
pub fn get_rlimit_mut(&mut self, resource: ResourceType) -> &mut RLimit64 {
&mut self.rlimits[resource as usize]
}
}
impl Default for ResourceLimits {
fn default() -> Self {
let stack_size = RLimit64::new(INIT_STACK_SIZE as u64);
let heap_size = RLimit64::new(USER_HEAP_SIZE_LIMIT as u64);
let open_files = RLimit64::new(1024);
let mut rlimits = Self {
rlimits: [RLimit64::default(); RLIMIT_COUNT],
};
*rlimits.get_rlimit_mut(ResourceType::RLIMIT_STACK) = stack_size;
*rlimits.get_rlimit_mut(ResourceType::RLIMIT_DATA) = heap_size;
*rlimits.get_rlimit_mut(ResourceType::RLIMIT_NOFILE) = open_files;
rlimits
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum ResourceType {
RLIMIT_CPU = 0,
RLIMIT_FSIZE = 1,
RLIMIT_DATA = 2,
RLIMIT_STACK = 3,
RLIMIT_CORE = 4,
RLIMIT_RSS = 5,
RLIMIT_NPROC = 6,
RLIMIT_NOFILE = 7,
RLIMIT_MEMLOCK = 8,
RLIMIT_AS = 9,
RLIMIT_LOCKS = 10,
RLIMIT_SIGPENDING = 11,
RLIMIT_MSGQUEUE = 12,
RLIMIT_NICE = 13,
RLIMIT_RTPRIO = 14,
RLIMIT_RTTIME = 15,
}
impl TryFrom<u32> for ResourceType {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
match value {
0 => Ok(ResourceType::RLIMIT_CPU),
1 => Ok(ResourceType::RLIMIT_FSIZE),
2 => Ok(ResourceType::RLIMIT_DATA),
3 => Ok(ResourceType::RLIMIT_STACK),
4 => Ok(ResourceType::RLIMIT_CORE),
5 => Ok(ResourceType::RLIMIT_RSS),
6 => Ok(ResourceType::RLIMIT_NPROC),
7 => Ok(ResourceType::RLIMIT_NOFILE),
8 => Ok(ResourceType::RLIMIT_MEMLOCK),
9 => Ok(ResourceType::RLIMIT_AS),
10 => Ok(ResourceType::RLIMIT_LOCKS),
11 => Ok(ResourceType::RLIMIT_SIGPENDING),
12 => Ok(ResourceType::RLIMIT_MSGQUEUE),
13 => Ok(ResourceType::RLIMIT_NICE),
14 => Ok(ResourceType::RLIMIT_RTPRIO),
15 => Ok(ResourceType::RLIMIT_RTTIME),
_ => return_errno_with_message!(Errno::EINVAL, "invalid resource type"),
}
}
}
pub const RLIMIT_COUNT: usize = 16;
#[derive(Debug, Clone, Copy, Pod)]
#[repr(C)]
pub struct RLimit64 {
cur: u64,
max: u64,
}
impl RLimit64 {
pub fn new(cur: u64) -> Self {
Self {
cur,
max: u64::max_value(),
}
}
pub fn get_cur(&self) -> u64 {
self.cur
}
pub fn get_max(&self) -> u64 {
self.max
}
}
impl Default for RLimit64 {
fn default() -> Self {
Self {
cur: u64::max_value(),
max: u64::max_value(),
}
}
}

View File

@ -15,6 +15,8 @@ use jinux_frame::{cpu::CpuContext, task::Task};
use self::c_types::siginfo_t;
use self::sig_mask::SigMask;
use self::sig_num::SigNum;
use crate::current_thread;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::signal::c_types::ucontext_t;
use crate::process::signal::sig_action::SigActionFlags;
use crate::util::{write_bytes_to_user, write_val_to_user};
@ -26,12 +28,22 @@ use crate::{
/// Handle pending signal for current process
pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> {
let current = current!();
let current_thread = current_thread!();
let posix_thread = current_thread.posix_thread();
let pid = current.pid();
let process_name = current.filename().unwrap();
let sig_queues = current.sig_queues();
let mut sig_queues_guard = sig_queues.lock();
let sig_mask = current.sig_mask().lock().clone();
if let Some(signal) = sig_queues_guard.dequeue(&sig_mask) {
let sig_mask = posix_thread.sig_mask().lock().clone();
let mut thread_sig_queues = posix_thread.sig_queues().lock();
let mut proc_sig_queues = current.sig_queues().lock();
// We first deal with signal in current thread, then signal in current process.
let signal = if let Some(signal) = thread_sig_queues.dequeue(&sig_mask) {
Some(signal)
} else if let Some(signal) = proc_sig_queues.dequeue(&sig_mask) {
Some(signal)
} else {
None
};
if let Some(signal) = signal {
let sig_num = signal.num();
debug!("sig_num = {:?}, sig_name = {}", sig_num, sig_num.sig_name());
let sig_action = current.sig_dispositions().lock().get(sig_num);
@ -65,28 +77,28 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> {
sig_num.sig_name()
);
// FIXME: How to set correct status if process is terminated
current.exit(1);
current.exit_group(1);
// We should exit current here, since we cannot restore a valid status from trap now.
Task::current().exit();
}
SigDefaultAction::Ign => {}
SigDefaultAction::Stop => {
let mut status_guard = current.status().lock();
if status_guard.is_runnable() {
status_guard.set_suspend();
let mut status = current_thread.status().lock();
if status.is_running() {
status.set_stopped();
} else {
panic!("Try to suspend a not running process.")
}
drop(status_guard);
drop(status);
}
SigDefaultAction::Cont => {
let mut status_guard = current.status().lock();
if status_guard.is_suspend() {
status_guard.set_runnable();
let mut status = current_thread.status().lock();
if status.is_stopped() {
status.set_running();
} else {
panic!("Try to continue a not suspended process.")
}
drop(status_guard);
drop(status);
}
}
}
@ -117,9 +129,10 @@ pub fn handle_user_signal(
let current_mask = SigMask::from(sig_num);
mask.block(current_mask.as_u64());
}
let current = current!();
let current_thread = current_thread!();
let posix_thread = current_thread.posix_thread();
// block signals in sigmask when running signal handler
current.sig_mask().lock().block(mask.as_u64());
posix_thread.sig_mask().lock().block(mask.as_u64());
// set up signal stack in user stack. // avoid corrupt user stack, we minus 128 first.
let mut user_rsp = context.gp_regs.rsp;
@ -129,27 +142,35 @@ pub fn handle_user_signal(
user_rsp = user_rsp - mem::size_of::<siginfo_t>() as u64;
write_val_to_user(user_rsp as _, &sig_info)?;
let siginfo_addr = user_rsp;
debug!("siginfo_addr = 0x{:x}", siginfo_addr);
// debug!("siginfo_addr = 0x{:x}", siginfo_addr);
// 2. write ucontext_t.
user_rsp = alloc_aligned_in_user_stack(user_rsp, mem::size_of::<ucontext_t>(), 16)?;
let mut ucontext = ucontext_t::default();
ucontext.uc_sigmask = mask.as_u64();
ucontext.uc_mcontext.inner.gp_regs = context.gp_regs;
let mut sig_context = posix_thread.sig_context().lock();
if let Some(sig_context_addr) = *sig_context {
ucontext.uc_link = sig_context_addr;
} else {
ucontext.uc_link = 0;
}
// TODO: store fp regs in ucontext
write_val_to_user(user_rsp as _, &ucontext)?;
let ucontext_addr = user_rsp;
debug!("ucontext addr = 0x{:x}", ucontext_addr);
// Store the ucontext addr in sig context of current process.
current.sig_context().lock().push_back(ucontext_addr as _);
*sig_context = Some(ucontext_addr as Vaddr);
// current.sig_context().lock().push_back(ucontext_addr as _);
// 3. Set the address of the trampoline code.
if flags.contains(SigActionFlags::SA_RESTORER) {
// If contains SA_RESTORER flag, trampoline code is provided by libc in restorer_addr.
// We just store restorer_addr on user stack to allow user code just to trampoline code.
user_rsp = write_u64_to_user_stack(user_rsp, restorer_addr as u64)?;
debug!("After set restorer addr: user_rsp = 0x{:x}", user_rsp);
} else {
// Otherwise we create
// Otherwise we create a trampoline.
// FIXME: This may cause problems if we read old_context from rsp.
const TRAMPOLINE: &[u8] = &[
0xb8, 0x0f, 0x00, 0x00, 0x00, // mov eax, 15(syscall number of rt_sigreturn)
0x0f, 0x05, // syscall (call rt_sigreturn)

View File

@ -64,4 +64,14 @@ impl SigMask {
fn num_to_idx(num: SigNum) -> usize {
(num.as_u8() - MIN_STD_SIG_NUM) as usize
}
pub fn remove_signal(&mut self, signum: SigNum) {
let idx = Self::num_to_idx(signum);
self.bits &= !(1_u64 << idx);
}
pub fn add_signal(&mut self, signum: SigNum) {
let idx = Self::num_to_idx(signum);
self.bits |= 1_u64 << idx;
}
}

View File

@ -4,8 +4,6 @@
pub enum ProcessStatus {
/// Can be scheduled to run
Runnable,
/// Suspend until be woken by SIGCONT signal
SuspendSignalable,
/// Exit while not reaped by parent
Zombie,
}
@ -18,20 +16,4 @@ impl ProcessStatus {
pub fn is_zombie(&self) -> bool {
*self == ProcessStatus::Zombie
}
pub fn set_suspend(&mut self) {
*self = ProcessStatus::SuspendSignalable;
}
pub fn is_suspend(&self) -> bool {
*self == ProcessStatus::SuspendSignalable
}
pub fn set_runnable(&mut self) {
*self = ProcessStatus::Runnable;
}
pub fn is_runnable(&self) -> bool {
*self == ProcessStatus::Runnable
}
}

View File

@ -1,3 +1,5 @@
use core::sync::atomic::Ordering;
use crate::prelude::*;
use super::{process_filter::ProcessFilter, ExitCode, Pid};
@ -51,7 +53,7 @@ pub fn wait_child_exit(
if let Some(zombie_child) = zombie_child {
let zombie_pid = zombie_child.pid();
let exit_code = zombie_child.exit_code();
let exit_code = zombie_child.exit_code().load(Ordering::SeqCst);
if wait_options.contains(WaitOptions::WNOWAIT) {
// does not reap child, directly return
return Some(Ok((zombie_pid, exit_code)));

View File

@ -0,0 +1,44 @@
use core::time::Duration;
use super::SyscallReturn;
use super::SYS_CLOCK_NANOSLEEP;
use crate::{
log_syscall_entry,
prelude::*,
thread::Thread,
time::{clockid_t, timespec_t, ClockID, TIMER_ABSTIME},
util::{read_val_from_user, write_val_to_user},
};
pub fn sys_clock_nanosleep(
clockid: clockid_t,
flags: i32,
request_timespec_addr: Vaddr,
remain_timespec_addr: Vaddr,
) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_CLOCK_NANOSLEEP);
let clock_id = ClockID::try_from(clockid)?;
let abs_time = if flags == 0 {
false
} else if flags == TIMER_ABSTIME {
true
} else {
unreachable!()
};
let request_timespec = read_val_from_user::<timespec_t>(request_timespec_addr)?;
debug!(
"clockid = {:?}, abs_time = {}, request_timespec = {:?}, remain timespec addr = 0x{:x}",
clock_id, abs_time, request_timespec, remain_timespec_addr
);
// FIXME: do real sleep. Here we simply yield the execution of current thread since we does not have timeout support now.
// If the sleep is interrupted by a signal, this syscall should return error.
Thread::yield_now();
if remain_timespec_addr != 0 {
let remain_duration = Duration::new(0, 0);
let remain_timespec = timespec_t::from(remain_duration);
write_val_to_user(remain_timespec_addr, &remain_timespec)?;
}
Ok(SyscallReturn::Return(0))
}

View File

@ -10,22 +10,16 @@ use super::SyscallReturn;
// This order we use here is the order for x86_64. See https://man7.org/linux/man-pages/man2/clone.2.html.
pub fn sys_clone(
clone_flags: u64,
new_sp: Vaddr,
new_sp: u64,
parent_tidptr: Vaddr,
child_tidptr: Vaddr,
tls: usize,
tls: u64,
parent_context: CpuContext,
) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_CLONE);
let clone_flags = CloneFlags::from(clone_flags);
debug!("flags = {:?}, child_stack_ptr = 0x{:x}, parent_tid_ptr = 0x{:x}, child tid ptr = 0x{:x}, tls = 0x{:x}", clone_flags, new_sp, parent_tidptr, child_tidptr, tls);
let clone_args = CloneArgs::new(new_sp, parent_tidptr, child_tidptr, tls, clone_flags);
let child_process = clone_child(parent_context, clone_args).unwrap();
let child_pid = child_process.pid();
let pid = current!().pid();
debug!("*********schedule child process, pid = {}**********", pid);
child_process.run();
debug!("*********return to parent process, pid = {}*********", pid);
let child_pid = clone_child(parent_context, clone_args).unwrap();
Ok(SyscallReturn::Return(child_pid as _))
}

View File

@ -3,6 +3,8 @@ use jinux_frame::cpu::CpuContext;
use super::{constants::*, SyscallReturn};
use crate::log_syscall_entry;
use crate::process::elf::load_elf_to_root_vmar;
use crate::process::posix_thread::name::ThreadName;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::util::{read_cstring_from_user, read_val_from_user};
use crate::{prelude::*, syscall::SYS_EXECVE};
@ -13,31 +15,35 @@ pub fn sys_execve(
context: &mut CpuContext,
) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_EXECVE);
let filename = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
let elf_path = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
let argv = read_cstring_vec(argv_ptr_ptr, MAX_ARGV_NUMBER, MAX_ARG_LEN)?;
let envp = read_cstring_vec(envp_ptr_ptr, MAX_ENVP_NUMBER, MAX_ENV_LEN)?;
debug!(
"filename: {:?}, argv = {:?}, envp = {:?}",
filename, argv, envp
elf_path, argv, envp
);
if filename != CString::new("./hello").unwrap() {
if elf_path != CString::new("./hello").unwrap() {
panic!("Unknown filename.");
}
// FIXME: should we set thread name in execve?
let current_thread = current_thread!();
let posix_thread = current_thread.posix_thread();
let mut thread_name = posix_thread.thread_name().lock();
let new_thread_name = ThreadName::new_from_elf_path(&elf_path)?;
*thread_name = Some(new_thread_name);
let elf_file_content = crate::user_apps::read_execve_hello_content();
let current = current!();
// destroy root vmars
let root_vmar = current
.root_vmar()
.expect("[Internal Error] User process should have vm space");
let root_vmar = current.root_vmar();
root_vmar.clear()?;
let user_vm = current
.user_vm()
.expect("[Internal Error] User process should have user vm");
user_vm.set_default();
// load elf content to new vm space
let elf_load_info = load_elf_to_root_vmar(filename, elf_file_content, root_vmar, argv, envp)
.expect("load elf failed");
let elf_load_info =
load_elf_to_root_vmar(elf_file_content, root_vmar, argv, envp).expect("load elf failed");
debug!("load elf in execve succeeds");
// set signal disposition to default
current.sig_dispositions().lock().inherit();

View File

@ -1,9 +1,19 @@
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::{log_syscall_entry, prelude::*};
use crate::syscall::{SyscallReturn, SYS_EXIT};
pub fn sys_exit(exit_code: i32) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_EXIT);
current!().exit(exit_code);
debug!("exid code = {}", exit_code);
let current_thread = current_thread!();
let tid = current_thread.tid();
let current = current!();
let pid = current.pid();
debug!("tid = {}, pid = {}", tid, pid);
let posix_thread = current_thread.posix_thread();
current_thread.exit();
posix_thread.exit(tid, exit_code)?;
Ok(SyscallReturn::Return(0))
}

View File

@ -5,6 +5,7 @@ use crate::syscall::{SyscallReturn, SYS_EXIT_GROUP};
/// Exit all thread in a process.
pub fn sys_exit_group(exit_code: u64) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_EXIT_GROUP);
current!().exit(exit_code as _);
// Exit all thread in current process
current!().exit_group(exit_code as _);
Ok(SyscallReturn::Return(0))
}

View File

@ -5,25 +5,15 @@ use crate::{
};
use jinux_frame::cpu::CpuContext;
use crate::{process::Process, syscall::SYS_FORK};
use crate::syscall::SYS_FORK;
use super::SyscallReturn;
pub fn sys_fork(parent_context: CpuContext) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_FORK);
let child_process = fork(parent_context);
Ok(SyscallReturn::Return(child_process.pid() as _))
}
/// Fork a child process
fn fork(parent_context: CpuContext) -> Arc<Process> {
let current = current!();
// FIXME: set correct args for fork
let clone_args = CloneArgs::default();
let child = clone_child(parent_context, clone_args).unwrap();
let pid = current.pid();
debug!("*********schedule child process, pid = {}**********", pid);
child.run();
debug!("*********return to parent process, pid = {}*********", pid);
child
let child_pid = clone_child(parent_context, clone_args).unwrap();
Ok(SyscallReturn::Return(child_pid as _))
}

View File

@ -10,7 +10,7 @@ pub fn sys_fstat(fd: u64, stat_buf_ptr: Vaddr) -> Result<SyscallReturn> {
debug!("fd = {}, stat_buf_addr = 0x{:x}", fd, stat_buf_ptr);
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let root_vmar = current.root_vmar();
if fd == 1 {
let stat = Stat::stdout_stat();
root_vmar.write_val(stat_buf_ptr, &stat)?;

View File

@ -1,24 +1,16 @@
use core::sync::atomic::{AtomicBool, Ordering};
use crate::process::{Pid, Process};
use crate::process::posix_thread::futex::{
futex_op_and_flags_from_u32, futex_requeue, futex_wait, futex_wait_bitset, futex_wake,
futex_wake_bitset, FutexOp, FutexTimeout,
};
use crate::syscall::SyscallReturn;
use crate::syscall::SYS_FUTEX;
use crate::util::read_val_from_user;
use crate::{log_syscall_entry, prelude::*};
use jinux_frame::cpu::num_cpus;
type FutexBitSet = u32;
type FutexBucketRef = Arc<Mutex<FutexBucket>>;
const FUTEX_OP_MASK: u32 = 0x0000_000F;
const FUTEX_FLAGS_MASK: u32 = 0xFFFF_FFF0;
const FUTEX_BITSET_MATCH_ANY: FutexBitSet = 0xFFFF_FFFF;
pub fn sys_futex(
futex_addr: u64,
futex_op: u64,
futex_val: u64,
futex_addr: Vaddr,
futex_op: i32,
futex_val: u32,
utime_addr: u64,
futex_new_addr: u64,
bitset: u64,
@ -26,6 +18,10 @@ pub fn sys_futex(
log_syscall_entry!(SYS_FUTEX);
// FIXME: we current ignore futex flags
let (futex_op, futex_flags) = futex_op_and_flags_from_u32(futex_op as _).unwrap();
debug!(
"futex_op = {:?}, futex_flags = {:?}, futex_addr = 0x{:x}",
futex_op, futex_flags, futex_addr
);
let get_futex_val = |val: i32| -> Result<usize> {
if val < 0 {
@ -74,406 +70,6 @@ pub fn sys_futex(
}
.unwrap();
debug!("futex returns, tid= {} ", current_thread!().tid());
Ok(SyscallReturn::Return(res as _))
}
/// do futex wait
pub fn futex_wait(futex_addr: u64, futex_val: i32, timeout: &Option<FutexTimeout>) -> Result<()> {
futex_wait_bitset(futex_addr as _, futex_val, timeout, FUTEX_BITSET_MATCH_ANY)
}
/// do futex wait bitset
pub fn futex_wait_bitset(
futex_addr: Vaddr,
futex_val: i32,
timeout: &Option<FutexTimeout>,
bitset: FutexBitSet,
) -> Result<()> {
debug!(
"futex_wait_bitset addr: {:#x}, val: {}, timeout: {:?}, bitset: {:#x}",
futex_addr, futex_val, timeout, bitset
);
let futex_key = FutexKey::new(futex_addr);
let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key);
// lock futex bucket ref here to avoid data race
let mut futex_bucket = futex_bucket_ref.lock();
if futex_key.load_val() != futex_val {
return_errno_with_message!(Errno::EINVAL, "futex value does not match");
}
let futex_item = FutexItem::new(futex_key, bitset);
futex_bucket.enqueue_item(futex_item);
// drop lock
drop(futex_bucket);
Ok(())
}
/// do futex wake
pub fn futex_wake(futex_addr: Vaddr, max_count: usize) -> Result<usize> {
futex_wake_bitset(futex_addr, max_count, FUTEX_BITSET_MATCH_ANY)
}
/// Do futex wake with bitset
pub fn futex_wake_bitset(
futex_addr: Vaddr,
max_count: usize,
bitset: FutexBitSet,
) -> Result<usize> {
debug!(
"futex_wake_bitset addr: {:#x}, max_count: {}, bitset: {:#x}",
futex_addr as usize, max_count, bitset
);
let futex_key = FutexKey::new(futex_addr);
let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key);
let mut futex_bucket = futex_bucket_ref.lock();
let res = futex_bucket.dequeue_and_wake_items(futex_key, max_count, bitset);
Ok(res)
}
/// Do futex requeue
pub fn futex_requeue(
futex_addr: Vaddr,
max_nwakes: usize,
max_nrequeues: usize,
futex_new_addr: Vaddr,
) -> Result<usize> {
if futex_new_addr == futex_addr {
return futex_wake(futex_addr, max_nwakes);
}
let futex_key = FutexKey::new(futex_addr);
let futex_new_key = FutexKey::new(futex_new_addr);
let (bucket_idx, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key);
let (new_bucket_idx, futex_new_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_new_key);
let nwakes = {
if bucket_idx == new_bucket_idx {
let mut futex_bucket = futex_bucket_ref.lock();
let nwakes =
futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY);
futex_bucket.update_item_keys(futex_key, futex_new_key, max_nrequeues);
drop(futex_bucket);
nwakes
} else {
let (mut futex_bucket, mut futex_new_bucket) = {
if bucket_idx < new_bucket_idx {
let futex_bucket = futex_bucket_ref.lock();
let futext_new_bucket = futex_new_bucket_ref.lock();
(futex_bucket, futext_new_bucket)
} else {
// bucket_idx > new_bucket_idx
let futex_new_bucket = futex_new_bucket_ref.lock();
let futex_bucket = futex_bucket_ref.lock();
(futex_bucket, futex_new_bucket)
}
};
let nwakes =
futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY);
futex_bucket.requeue_items_to_another_bucket(
futex_key,
&mut futex_new_bucket,
futex_new_key,
max_nrequeues,
);
nwakes
}
};
Ok(nwakes)
}
lazy_static! {
// Use the same count as linux kernel to keep the same performance
static ref BUCKET_COUNT: usize = ((1<<8)* num_cpus()).next_power_of_two() as _;
static ref BUCKET_MASK: usize = *BUCKET_COUNT - 1;
static ref FUTEX_BUCKETS: FutexBucketVec = FutexBucketVec::new(*BUCKET_COUNT);
}
#[derive(Debug, Clone)]
pub struct FutexTimeout {}
impl FutexTimeout {
pub fn new() -> Self {
todo!()
}
}
struct FutexBucketVec {
vec: Vec<FutexBucketRef>,
}
impl FutexBucketVec {
pub fn new(size: usize) -> FutexBucketVec {
let mut buckets = FutexBucketVec {
vec: Vec::with_capacity(size),
};
for _ in 0..size {
let bucket = Arc::new(Mutex::new(FutexBucket::new()));
buckets.vec.push(bucket);
}
buckets
}
pub fn get_bucket(&self, key: FutexKey) -> (usize, FutexBucketRef) {
let index = *BUCKET_MASK & {
// The addr is the multiples of 4, so we ignore the last 2 bits
let addr = key.addr() >> 2;
// simple hash
addr / self.size()
};
(index, self.vec[index].clone())
}
fn size(&self) -> usize {
self.vec.len()
}
}
struct FutexBucket {
queue: VecDeque<FutexItem>,
}
impl FutexBucket {
pub fn new() -> FutexBucket {
FutexBucket {
queue: VecDeque::new(),
}
}
pub fn enqueue_item(&mut self, item: FutexItem) {
self.queue.push_back(item);
}
pub fn dequeue_item(&mut self, item: &FutexItem) {
let item_i = self
.queue
.iter()
.position(|futex_item| *futex_item == *item);
if let Some(item_i) = item_i {
self.queue.remove(item_i).unwrap();
}
}
pub fn dequeue_and_wake_items(
&mut self,
key: FutexKey,
max_count: usize,
bitset: FutexBitSet,
) -> usize {
let mut count = 0;
let mut items_to_wake = Vec::new();
self.queue.retain(|item| {
if count >= max_count || key != item.key || (bitset & item.bitset) == 0 {
true
} else {
items_to_wake.push(item.clone());
count += 1;
false
}
});
FutexItem::batch_wake(&items_to_wake);
count
}
pub fn update_item_keys(&mut self, key: FutexKey, new_key: FutexKey, max_count: usize) {
let mut count = 0;
for item in self.queue.iter_mut() {
if count == max_count {
break;
}
if (*item).key == key {
(*item).key = new_key;
count += 1;
}
}
}
pub fn requeue_items_to_another_bucket(
&mut self,
key: FutexKey,
another: &mut Self,
new_key: FutexKey,
max_nrequeues: usize,
) {
let mut count = 0;
self.queue.retain(|item| {
if count >= max_nrequeues || key != item.key {
true
} else {
let mut new_item = item.clone();
new_item.key = new_key;
another.enqueue_item(new_item);
count += 1;
false
}
});
}
}
#[derive(Debug, PartialEq, Clone)]
struct FutexItem {
key: FutexKey,
bitset: FutexBitSet,
waiter: FutexWaiterRef,
}
impl FutexItem {
pub fn new(key: FutexKey, bitset: FutexBitSet) -> Self {
FutexItem {
key,
bitset,
waiter: Arc::new(FutexWaiter::new()),
}
}
pub fn wake(&self) {
self.waiter.wake();
}
pub fn wait(&self) {
self.waiter.wait();
}
pub fn waiter(&self) -> &FutexWaiterRef {
&self.waiter
}
pub fn batch_wake(items: &[FutexItem]) {
let waiters = items.iter().map(|item| item.waiter()).collect::<Vec<_>>();
FutexWaiter::batch_wake(&waiters);
}
}
// The addr of a futex, it should be used to mark different futex word
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct FutexKey(Vaddr);
impl FutexKey {
pub fn new(futex_addr: Vaddr) -> Self {
FutexKey(futex_addr as _)
}
pub fn load_val(&self) -> i32 {
// FIXME: how to implement a atomic load?
warn!("implement an atomic load");
read_val_from_user(self.0).unwrap()
}
pub fn addr(&self) -> Vaddr {
self.0
}
}
// The implementation is from occlum
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
pub enum FutexOp {
FUTEX_WAIT = 0,
FUTEX_WAKE = 1,
FUTEX_FD = 2,
FUTEX_REQUEUE = 3,
FUTEX_CMP_REQUEUE = 4,
FUTEX_WAKE_OP = 5,
FUTEX_LOCK_PI = 6,
FUTEX_UNLOCK_PI = 7,
FUTEX_TRYLOCK_PI = 8,
FUTEX_WAIT_BITSET = 9,
FUTEX_WAKE_BITSET = 10,
}
impl FutexOp {
pub fn from_u32(bits: u32) -> Result<FutexOp> {
match bits {
0 => Ok(FutexOp::FUTEX_WAIT),
1 => Ok(FutexOp::FUTEX_WAKE),
2 => Ok(FutexOp::FUTEX_FD),
3 => Ok(FutexOp::FUTEX_REQUEUE),
4 => Ok(FutexOp::FUTEX_CMP_REQUEUE),
5 => Ok(FutexOp::FUTEX_WAKE_OP),
6 => Ok(FutexOp::FUTEX_LOCK_PI),
7 => Ok(FutexOp::FUTEX_UNLOCK_PI),
8 => Ok(FutexOp::FUTEX_TRYLOCK_PI),
9 => Ok(FutexOp::FUTEX_WAIT_BITSET),
10 => Ok(FutexOp::FUTEX_WAKE_BITSET),
_ => return_errno_with_message!(Errno::EINVAL, "Unknown futex op"),
}
}
}
bitflags! {
pub struct FutexFlags : u32 {
const FUTEX_PRIVATE = 128;
const FUTEX_CLOCK_REALTIME = 256;
}
}
impl FutexFlags {
pub fn from_u32(bits: u32) -> Result<FutexFlags> {
FutexFlags::from_bits(bits)
.ok_or_else(|| Error::with_message(Errno::EINVAL, "unknown futex flags"))
}
}
pub fn futex_op_and_flags_from_u32(bits: u32) -> Result<(FutexOp, FutexFlags)> {
let op = {
let op_bits = bits & FUTEX_OP_MASK;
FutexOp::from_u32(op_bits)?
};
let flags = {
let flags_bits = bits & FUTEX_FLAGS_MASK;
FutexFlags::from_u32(flags_bits)?
};
Ok((op, flags))
}
type FutexWaiterRef = Arc<FutexWaiter>;
#[derive(Debug)]
struct FutexWaiter {
is_woken: AtomicBool,
pid: Pid,
}
impl PartialEq for FutexWaiter {
fn eq(&self, other: &Self) -> bool {
self.pid == other.pid
}
}
impl FutexWaiter {
pub fn new() -> Self {
Self {
is_woken: AtomicBool::new(false),
pid: current!().pid(),
}
}
pub fn wait(&self) {
self.is_woken.store(false, Ordering::SeqCst);
while !self.is_woken() {
Process::yield_now();
}
}
pub fn wake(&self) {
self.is_woken.store(true, Ordering::SeqCst);
}
pub fn is_woken(&self) -> bool {
self.is_woken.load(Ordering::SeqCst)
}
pub fn batch_wake(waiters: &[&FutexWaiterRef]) {
waiters.iter().for_each(|waiter| {
waiter.wake();
});
}
}

View File

@ -6,7 +6,7 @@ use super::SyscallReturn;
pub fn sys_gettid() -> Result<SyscallReturn> {
log_syscall_entry!(SYS_GETTID);
// For single-thread process, tid is equal to pid
let tid = current!().pid();
let current_thread = current_thread!();
let tid = current_thread.tid();
Ok(SyscallReturn::Return(tid as _))
}

View File

@ -1,7 +1,7 @@
use crate::{log_syscall_entry, prelude::*};
use crate::process::signal::signals::user::{UserSignal, UserSignalKind};
use crate::process::{table, Process};
use crate::process::{process_table, Process};
use crate::{
process::{process_filter::ProcessFilter, signal::sig_num::SigNum},
syscall::SYS_KILL,
@ -42,12 +42,12 @@ pub fn do_sys_kill(process_filter: ProcessFilter, sig_num: SigNum) -> Result<()>
fn get_processes(filter: &ProcessFilter) -> Result<Vec<Arc<Process>>> {
let processes = match filter {
ProcessFilter::Any => {
let mut processes = table::get_all_processes();
let mut processes = process_table::get_all_processes();
processes.retain(|process| process.pid() != 0);
processes
}
ProcessFilter::WithPid(pid) => {
let process = table::pid_to_process(*pid);
let process = process_table::pid_to_process(*pid);
match process {
None => {
return_errno_with_message!(Errno::ESRCH, "No such process in process table")

View File

@ -0,0 +1,95 @@
use crate::util::read_bytes_from_user;
use crate::{log_syscall_entry, prelude::*};
use super::SyscallReturn;
use super::SYS_MADVISE;
pub fn sys_madvise(start: Vaddr, len: usize, behavior: i32) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_MADVISE);
let behavior = MadviseBehavior::try_from(behavior)?;
debug!(
"start = 0x{:x}, len = 0x{:x}, behavior = {:?}",
start, len, behavior
);
match behavior {
MadviseBehavior::MADV_NORMAL
| MadviseBehavior::MADV_SEQUENTIAL
| MadviseBehavior::MADV_WILLNEED => {
// perform a read at first
let mut buffer = vec![0u8; len];
read_bytes_from_user(start, &mut buffer)?;
}
MadviseBehavior::MADV_DONTNEED => madv_dontneed(start, len)?,
_ => todo!(),
}
Ok(SyscallReturn::Return(0))
}
fn madv_dontneed(start: Vaddr, len: usize) -> Result<()> {
debug_assert!(start % PAGE_SIZE == 0);
debug_assert!(len % PAGE_SIZE == 0);
let current = current!();
let root_vmar = current.root_vmar();
let vm_mapping = root_vmar.get_vm_mapping(start)?;
// ensure the range is totally in the mapping
debug_assert!(vm_mapping.map_to_addr() <= start);
debug_assert!(start + len <= vm_mapping.map_to_addr() + vm_mapping.size());
vm_mapping.unmap_and_decommit(start..(start + len))
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, Pod)]
#[allow(non_camel_case_types)]
/// This definition is the same from linux
pub enum MadviseBehavior {
MADV_NORMAL = 0, /* no further special treatment */
MADV_RANDOM = 1, /* expect random page references */
MADV_SEQUENTIAL = 2, /* expect sequential page references */
MADV_WILLNEED = 3, /* will need these pages */
MADV_DONTNEED = 4, /* don't need these pages */
/* common parameters: try to keep these consistent across architectures */
MADV_FREE = 8, /* free pages only if memory pressure */
MADV_REMOVE = 9, /* remove these pages & resources */
MADV_DONTFORK = 10, /* don't inherit across fork */
MADV_DOFORK = 11, /* do inherit across fork */
MADV_HWPOISON = 100, /* poison a page for testing */
MADV_SOFT_OFFLINE = 101, /* soft offline page for testing */
MADV_MERGEABLE = 12, /* KSM may merge identical pages */
MADV_UNMERGEABLE = 13, /* KSM may not merge identical pages */
MADV_HUGEPAGE = 14, /* Worth backing with hugepages */
MADV_NOHUGEPAGE = 15, /* Not worth backing with hugepages */
MADV_DONTDUMP = 16, /* Explicity exclude from the core dump,
overrides the coredump filter bits */
MADV_DODUMP = 17, /* Clear the MADV_DONTDUMP flag */
MADV_WIPEONFORK = 18, /* Zero memory on fork, child only */
MADV_KEEPONFORK = 19, /* Undo MADV_WIPEONFORK */
MADV_COLD = 20, /* deactivate these pages */
MADV_PAGEOUT = 21, /* reclaim these pages */
MADV_POPULATE_READ = 22, /* populate (prefault) page tables readable */
MADV_POPULATE_WRITE = 23, /* populate (prefault) page tables writable */
MADV_DONTNEED_LOCKED = 24, /* like DONTNEED, but drop locked pages too */
}
impl TryFrom<i32> for MadviseBehavior {
type Error = Error;
fn try_from(value: i32) -> Result<Self> {
let behavior = match value {
0 => MadviseBehavior::MADV_NORMAL,
1 => MadviseBehavior::MADV_RANDOM,
2 => MadviseBehavior::MADV_SEQUENTIAL,
3 => MadviseBehavior::MADV_WILLNEED,
4 => MadviseBehavior::MADV_DONTNEED,
_ => return_errno_with_message!(Errno::EINVAL, "invalid madvise behavior"),
};
Ok(behavior)
}
}

View File

@ -1,5 +1,6 @@
//! This mod defines mmap flags and the handler to syscall mmap
use crate::fs::file::FileDescripter;
use crate::process::process_vm::mmap_flags::MMapFlags;
use crate::rights::Rights;
use crate::vm::perms::VmPerms;
@ -27,7 +28,7 @@ pub fn sys_mmap(
len as usize,
perms,
flags,
fd as usize,
fd as _,
offset as usize,
)?;
Ok(SyscallReturn::Return(res as _))
@ -38,10 +39,10 @@ pub fn do_sys_mmap(
len: usize,
vm_perm: VmPerm,
flags: MMapFlags,
fd: usize,
fd: FileDescripter,
offset: usize,
) -> Result<Vaddr> {
info!(
debug!(
"addr = 0x{:x}, len = 0x{:x}, perms = {:?}, flags = {:?}, fd = {}, offset = 0x{:x}",
addr, len, vm_perm, flags, fd, offset
);
@ -76,7 +77,7 @@ pub fn mmap_anonymous_vmo(
let vmo_options: VmoOptions<Rights> = VmoOptions::new(len);
let vmo = vmo_options.alloc()?;
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let root_vmar = current.root_vmar();
let perms = VmPerms::from(vm_perm);
let mut vmar_map_options = root_vmar.new_map(vmo, perms)?;
if flags.contains(MMapFlags::MAP_FIXED) {

View File

@ -4,6 +4,7 @@ use crate::prelude::*;
use crate::syscall::access::sys_access;
use crate::syscall::arch_prctl::sys_arch_prctl;
use crate::syscall::brk::sys_brk;
use crate::syscall::clock_nanosleep::sys_clock_nanosleep;
use crate::syscall::clone::sys_clone;
use crate::syscall::close::sys_close;
use crate::syscall::execve::sys_execve;
@ -26,18 +27,22 @@ use crate::syscall::ioctl::sys_ioctl;
use crate::syscall::kill::sys_kill;
use crate::syscall::lseek::sys_lseek;
use crate::syscall::lstat::sys_lstat;
use crate::syscall::madvise::sys_madvise;
use crate::syscall::mmap::sys_mmap;
use crate::syscall::mprotect::sys_mprotect;
use crate::syscall::munmap::sys_munmap;
use crate::syscall::openat::sys_openat;
use crate::syscall::poll::sys_poll;
use crate::syscall::prctl::sys_prctl;
use crate::syscall::prlimit64::sys_prlimit64;
use crate::syscall::read::sys_read;
use crate::syscall::readlink::sys_readlink;
use crate::syscall::rt_sigaction::sys_rt_sigaction;
use crate::syscall::rt_sigprocmask::sys_rt_sigprocmask;
use crate::syscall::rt_sigreturn::sys_rt_sigreturn;
use crate::syscall::sched_yield::sys_sched_yield;
use crate::syscall::set_robust_list::sys_set_robust_list;
use crate::syscall::set_tid_address::sys_set_tid_address;
use crate::syscall::setpgid::sys_setpgid;
use crate::syscall::tgkill::sys_tgkill;
use crate::syscall::uname::sys_uname;
@ -50,6 +55,7 @@ use jinux_frame::cpu::CpuContext;
mod access;
mod arch_prctl;
mod brk;
mod clock_nanosleep;
mod clone;
mod close;
mod constants;
@ -73,18 +79,22 @@ mod ioctl;
mod kill;
mod lseek;
mod lstat;
mod madvise;
mod mmap;
mod mprotect;
mod munmap;
mod openat;
mod poll;
mod prctl;
mod prlimit64;
mod read;
mod readlink;
mod rt_sigaction;
mod rt_sigprocmask;
mod rt_sigreturn;
mod sched_yield;
mod set_robust_list;
mod set_tid_address;
mod setpgid;
mod tgkill;
mod uname;
@ -142,6 +152,7 @@ define_syscall_nums!(
SYS_WRITEV = 20,
SYS_ACCESS = 21,
SYS_SCHED_YIELD = 24,
SYS_MADVISE = 28,
SYS_GETPID = 39,
SYS_CLONE = 56,
SYS_FORK = 57,
@ -164,10 +175,14 @@ define_syscall_nums!(
SYS_ARCH_PRCTL = 158,
SYS_GETTID = 186,
SYS_FUTEX = 202,
SYS_SET_TID_ADDRESS = 218,
SYS_CLOCK_NANOSLEEP = 230,
SYS_EXIT_GROUP = 231,
SYS_TGKILL = 234,
SYS_WAITID = 247,
SYS_OPENAT = 257
SYS_OPENAT = 257,
SYS_SET_ROBUST_LIST = 273,
SYS_PRLIMIT64 = 302
);
pub struct SyscallArgument {
@ -208,7 +223,6 @@ pub fn handle_syscall(context: &mut CpuContext) {
match syscall_return {
Ok(return_value) => {
debug!("syscall return: {:?}", return_value);
if let SyscallReturn::Return(return_value) = return_value {
context.set_rax(return_value as u64);
}
@ -245,6 +259,7 @@ pub fn syscall_dispatch(
SYS_WRITEV => syscall_handler!(3, sys_writev, args),
SYS_ACCESS => syscall_handler!(2, sys_access, args),
SYS_SCHED_YIELD => syscall_handler!(0, sys_sched_yield),
SYS_MADVISE => syscall_handler!(3, sys_madvise, args),
SYS_GETPID => syscall_handler!(0, sys_getpid),
SYS_CLONE => syscall_handler!(5, sys_clone, args, context.clone()),
SYS_FORK => syscall_handler!(0, sys_fork, context.clone()),
@ -267,11 +282,15 @@ pub fn syscall_dispatch(
SYS_ARCH_PRCTL => syscall_handler!(2, sys_arch_prctl, args, context),
SYS_GETTID => syscall_handler!(0, sys_gettid),
SYS_FUTEX => syscall_handler!(6, sys_futex, args),
SYS_SET_TID_ADDRESS => syscall_handler!(1, sys_set_tid_address, args),
SYS_CLOCK_NANOSLEEP => syscall_handler!(4, sys_clock_nanosleep, args),
SYS_EXIT_GROUP => syscall_handler!(1, sys_exit_group, args),
SYS_TGKILL => syscall_handler!(3, sys_tgkill, args),
SYS_WAITID => syscall_handler!(5, sys_waitid, args),
SYS_OPENAT => syscall_handler!(4, sys_openat, args),
_ => panic!("Unsupported syscall number: {}, args:{:x?}", syscall_number,args),
SYS_SET_ROBUST_LIST => syscall_handler!(2, sys_set_robust_list, args),
SYS_PRLIMIT64 => syscall_handler!(4, sys_prlimit64, args),
_ => panic!("Unsupported syscall number: {}", syscall_number),
}
}

View File

@ -1,23 +1,21 @@
use jinux_frame::vm::VmPerm;
use crate::{log_syscall_entry, prelude::*};
use crate::syscall::SYS_MPROTECT;
use crate::vm::perms::VmPerms;
use super::SyscallReturn;
pub fn sys_mprotect(vaddr: u64, len: u64, perms: u64) -> Result<SyscallReturn> {
pub fn sys_mprotect(addr: Vaddr, len: usize, perms: u64) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_MPROTECT);
let perms = VmPerm::try_from(perms).unwrap();
do_sys_mprotect(vaddr as Vaddr, len as usize, perms);
Ok(SyscallReturn::Return(0))
}
pub fn do_sys_mprotect(addr: Vaddr, len: usize, perms: VmPerm) -> isize {
// let perms = VmPerm::try_from(perms).unwrap();
let vm_perms = VmPerms::from_bits_truncate(perms as u32);
debug!(
"addr = 0x{:x}, len = 0x{:x}, perms = {:?}",
addr, len, perms
addr, len, vm_perms
);
// TODO: mprotect do nothing now
0
let current = current!();
let root_vmar = current.root_vmar();
let range = addr..(addr + len);
root_vmar.protect(vm_perms, range)?;
Ok(SyscallReturn::Return(0))
}

View File

@ -1,6 +1,7 @@
use crate::log_syscall_entry;
use crate::prelude::*;
use crate::process::name::MAX_PROCESS_NAME_LEN;
use crate::process::posix_thread::name::MAX_THREAD_NAME_LEN;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::util::read_cstring_from_user;
use crate::util::write_bytes_to_user;
@ -10,21 +11,22 @@ pub fn sys_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Res
log_syscall_entry!(SYS_PRCTL);
let prctl_cmd = PrctlCmd::from_args(option, arg2, arg3, arg4, arg5)?;
debug!("prctl cmd = {:?}", prctl_cmd);
let current = current!();
let current_thread = current_thread!();
let posix_thread = current_thread.posix_thread();
match prctl_cmd {
PrctlCmd::PR_GET_NAME(write_to_addr) => {
let process_name = current.process_name().lock();
if let Some(process_name) = &*process_name {
if let Some(process_name) = process_name.get_name()? {
write_bytes_to_user(write_to_addr, process_name.to_bytes_with_nul())?;
let thread_name = posix_thread.thread_name().lock();
if let Some(thread_name) = &*thread_name {
if let Some(thread_name) = thread_name.get_name()? {
write_bytes_to_user(write_to_addr, thread_name.to_bytes_with_nul())?;
}
}
}
PrctlCmd::PR_SET_NAME(read_addr) => {
let mut process_name = current.process_name().lock();
if let Some(process_name) = &mut *process_name {
let new_process_name = read_cstring_from_user(read_addr, MAX_PROCESS_NAME_LEN)?;
process_name.set_name(&new_process_name)?;
let mut thread_name = posix_thread.thread_name().lock();
if let Some(thread_name) = &mut *thread_name {
let new_thread_name = read_cstring_from_user(read_addr, MAX_THREAD_NAME_LEN)?;
thread_name.set_name(&new_thread_name)?;
}
}
_ => todo!(),

View File

@ -0,0 +1,31 @@
use crate::process::rlimit::ResourceType;
use crate::util::{read_val_from_user, write_val_to_user};
use crate::{log_syscall_entry, prelude::*, process::Pid};
use super::SyscallReturn;
use super::SYS_PRLIMIT64;
pub fn sys_prlimit64(
pid: Pid,
resource: u32,
new_rlim_addr: Vaddr,
old_rlim_addr: Vaddr,
) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_PRLIMIT64);
let resource = ResourceType::try_from(resource)?;
debug!(
"pid = {}, resource = {:?}, new_rlim_addr = 0x{:x}, old_rlim_addr = 0x{:x}",
pid, resource, new_rlim_addr, old_rlim_addr
);
let current = current!();
let mut resource_limits = current.resource_limits().lock();
if old_rlim_addr != 0 {
let rlimit = resource_limits.get_rlimit(resource);
write_val_to_user(old_rlim_addr, rlimit)?;
}
if new_rlim_addr != 0 {
let new_rlimit = read_val_from_user(new_rlim_addr)?;
*resource_limits.get_rlimit_mut(resource) = new_rlimit;
}
Ok(SyscallReturn::Return(0))
}

View File

@ -10,29 +10,32 @@ use super::SyscallReturn;
pub fn sys_rt_sigaction(
sig_num: u8,
sig_action_ptr: Vaddr,
old_sig_action_ptr: Vaddr,
sig_action_addr: Vaddr,
old_sig_action_addr: Vaddr,
sigset_size: u64,
) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_RT_SIGACTION);
let sig_num = SigNum::try_from(sig_num)?;
let sig_action_c = read_val_from_user::<sigaction_t>(sig_action_ptr)?;
let sig_action = SigAction::try_from(sig_action_c).unwrap();
debug!(
"sig_num = {}, sig_action = {:x?}, old_sig_action_ptr = 0x{:x}, sigset_size = {}",
"signal = {}, sig_action_addr = 0x{:x}, old_sig_action_addr = 0x{:x}, sigset_size = {}",
sig_num.sig_name(),
sig_action,
old_sig_action_ptr,
sig_action_addr,
old_sig_action_addr,
sigset_size
);
let current = current!();
let mut sig_dispositions = current.sig_dispositions().lock();
let old_action = sig_dispositions.get(sig_num);
let old_action_c = old_action.to_c();
sig_dispositions.set(sig_num, sig_action);
if old_sig_action_ptr != 0 {
write_val_to_user(old_sig_action_ptr, &old_action_c)?;
if old_sig_action_addr != 0 {
write_val_to_user(old_sig_action_addr, &old_action_c)?;
}
if sig_action_addr != 0 {
let sig_action_c = read_val_from_user::<sigaction_t>(sig_action_addr)?;
let sig_action = SigAction::try_from(sig_action_c).unwrap();
debug!("sig action = {:?}", sig_action);
sig_dispositions.set(sig_num, sig_action);
}
Ok(SyscallReturn::Return(0))
}

View File

@ -1,8 +1,11 @@
use jinux_frame::vm::VmIo;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::signal::constants::{SIGKILL, SIGSTOP};
use crate::{
log_syscall_entry,
prelude::*,
process::signal::sig_mask::SigMask,
syscall::{SyscallReturn, SYS_RT_SIGPROCMASK},
};
@ -32,8 +35,10 @@ fn do_rt_sigprocmask(
sigset_size: usize,
) -> Result<()> {
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let mut sig_mask = current.sig_mask().lock();
let current_thread = current_thread!();
let posix_thread = current_thread.posix_thread();
let root_vmar = current.root_vmar();
let mut sig_mask = posix_thread.sig_mask().lock();
let old_sig_mask_value = sig_mask.as_u64();
debug!("old sig mask value: 0x{:x}", old_sig_mask_value);
if oldset_ptr != 0 {
@ -41,9 +46,15 @@ fn do_rt_sigprocmask(
}
if set_ptr != 0 {
let new_set = root_vmar.read_val::<u64>(set_ptr)?;
debug!("new set = 0x{:x}", new_set);
match mask_op {
MaskOp::Block => sig_mask.block(new_set),
MaskOp::Block => {
let mut new_sig_mask = SigMask::from(new_set);
// According to man pages, "it is not possible to block SIGKILL or SIGSTOP.
// Attempts to do so are silently ignored."
new_sig_mask.remove_signal(SIGKILL);
new_sig_mask.remove_signal(SIGSTOP);
sig_mask.block(new_sig_mask.as_u64());
}
MaskOp::Unblock => sig_mask.unblock(new_set),
MaskOp::SetMask => sig_mask.set(new_set),
}

View File

@ -1,5 +1,8 @@
use crate::{
log_syscall_entry, prelude::*, process::signal::c_types::ucontext_t, util::read_val_from_user,
log_syscall_entry,
prelude::*,
process::{posix_thread::posix_thread_ext::PosixThreadExt, signal::c_types::ucontext_t},
util::read_val_from_user,
};
use jinux_frame::cpu::CpuContext;
@ -7,17 +10,28 @@ use super::{SyscallReturn, SYS_RT_SIGRETRUN};
pub fn sys_rt_sigreturn(context: &mut CpuContext) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_RT_SIGRETRUN);
let current = current!();
let sig_context_addr = current.sig_context().lock().pop_back().unwrap();
println!("sig context address = 0x{:x}", sig_context_addr);
let stack_value = read_val_from_user::<Vaddr>((context.gp_regs.rsp) as usize)?;
println!("stack value = 0x{:x}", stack_value);
// debug_assert!(sig_context_addr == stack_value);
// println!("stack value = 0x{:x}", sig_context);
let current_thread = current_thread!();
let posix_thread = current_thread.posix_thread();
let mut sig_context = posix_thread.sig_context().lock();
if None == *sig_context {
return_errno_with_message!(Errno::EINVAL, "sigretrun should not been called");
}
let sig_context_addr = sig_context.unwrap();
// FIXME: This assertion is not always true, if RESTORER flag is not presented.
// In this case, we will put restorer code on user stack, then the assertion will fail.
// However, for most glibc applications, the restorer codes is provided by glibc and RESTORER flag is set.
debug_assert!(sig_context_addr == context.gp_regs.rsp as Vaddr);
let ucontext = read_val_from_user::<ucontext_t>(sig_context_addr)?;
// Set previous ucontext address
if ucontext.uc_link == 0 {
*sig_context = None;
} else {
*sig_context = Some(ucontext.uc_link);
};
context.gp_regs = ucontext.uc_mcontext.inner.gp_regs;
// unblock sig mask
let sig_mask = ucontext.uc_sigmask;
current.sig_mask().lock().unblock(sig_mask);
posix_thread.sig_mask().lock().unblock(sig_mask);
Ok(SyscallReturn::NoReturn)
}

View File

@ -0,0 +1,28 @@
use super::{SyscallReturn, SYS_SET_ROBUST_LIST};
use crate::{
log_syscall_entry,
prelude::*,
process::posix_thread::{posix_thread_ext::PosixThreadExt, robust_list::RobustListHead},
util::read_val_from_user,
};
pub fn sys_set_robust_list(robust_list_head_ptr: Vaddr, len: usize) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_SET_ROBUST_LIST);
debug!(
"robust list head ptr: 0x{:x}, len = {}",
robust_list_head_ptr, len
);
if len != core::mem::size_of::<RobustListHead>() {
return_errno_with_message!(
Errno::EINVAL,
"The len is not equal to the size of robust list head"
);
}
let robust_list_head: RobustListHead = read_val_from_user(robust_list_head_ptr)?;
debug!("{:x?}", robust_list_head);
let current_thread = current_thread!();
let posix_thread = current_thread.posix_thread();
let mut robust_list = posix_thread.robust_list().lock();
*robust_list = Some(robust_list_head);
Ok(SyscallReturn::Return(0))
}

View File

@ -0,0 +1,22 @@
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::{log_syscall_entry, prelude::*};
use super::SyscallReturn;
use super::SYS_SET_TID_ADDRESS;
pub fn sys_set_tid_address(tidptr: Vaddr) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_SET_TID_ADDRESS);
debug!("tidptr = 0x{:x}", tidptr);
let current_thread = current_thread!();
let posix_thread = current_thread.posix_thread();
let mut clear_child_tid = posix_thread.clear_child_tid().lock();
if *clear_child_tid != 0 {
// According to manuals at https://man7.org/linux/man-pages/man2/set_tid_address.2.html
// We need to write 0 to clear_child_tid and do futex wake
todo!()
} else {
*clear_child_tid = tidptr;
}
let tid = current_thread.tid();
Ok(SyscallReturn::Return(tid as _))
}

View File

@ -1,7 +1,7 @@
use crate::{
log_syscall_entry,
prelude::*,
process::{process_group::ProcessGroup, table, Pgid, Pid},
process::{process_group::ProcessGroup, process_table, Pgid, Pid},
};
use super::{SyscallReturn, SYS_SETPGID};
@ -23,18 +23,18 @@ pub fn sys_setpgid(pid: Pid, pgid: Pgid) -> Result<SyscallReturn> {
}
// only can move process to an existing group or self
if pgid != pid && table::pgid_to_process_group(pgid).is_none() {
if pgid != pid && process_table::pgid_to_process_group(pgid).is_none() {
return_errno_with_message!(Errno::EPERM, "process group must exist");
}
if let Some(new_process_group) = table::pgid_to_process_group(pgid) {
if let Some(new_process_group) = process_table::pgid_to_process_group(pgid) {
new_process_group.add_process(current.clone());
current.set_process_group(Arc::downgrade(&new_process_group));
} else {
let new_process_group = Arc::new(ProcessGroup::new(current.clone()));
new_process_group.add_process(current.clone());
current.set_process_group(Arc::downgrade(&new_process_group));
table::add_process_group(new_process_group);
process_table::add_process_group(new_process_group);
}
Ok(SyscallReturn::Return(0))

View File

@ -1,8 +1,10 @@
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::thread::{thread_table, Tid};
use crate::{log_syscall_entry, prelude::*};
use crate::process::signal::sig_num::SigNum;
use crate::process::signal::signals::user::{UserSignal, UserSignalKind};
use crate::process::{table, Pgid, Pid};
use crate::process::Pid;
use crate::syscall::SYS_TGKILL;
use super::SyscallReturn;
@ -10,20 +12,21 @@ use super::SyscallReturn;
/// tgkill send a signal to a thread with pid as its thread id, and tgid as its thread group id.
/// Since jinuxx only supports one-thread process now, tgkill will send signal to process with pid as its process id,
/// and tgid as its process group id.
pub fn sys_tgkill(tgid: Pgid, pid: Pid, sig_num: u8) -> Result<SyscallReturn> {
pub fn sys_tgkill(tgid: Pid, tid: Tid, sig_num: u8) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_TGKILL);
let sig_num = SigNum::from_u8(sig_num);
debug!("tgid = {}, pid = {}, sig_num = {:?}", tgid, pid, sig_num);
let target_process =
table::pid_to_process(pid).ok_or(Error::with_message(Errno::EINVAL, "Invalid pid"))?;
let pgid = target_process.pgid();
if pgid != tgid {
info!("tgid = {}, pid = {}, sig_num = {:?}", tgid, tid, sig_num);
let target_thread = thread_table::tid_to_thread(tid)
.ok_or(Error::with_message(Errno::EINVAL, "Invalid pid"))?;
let posix_thread = target_thread.posix_thread();
let pid = posix_thread.process().pid();
if pid != tgid {
return_errno_with_message!(
Errno::EINVAL,
"the combination of tgid and pid is not valid"
);
}
if target_process.status().lock().is_zombie() {
if target_thread.status().lock().is_exited() {
return Ok(SyscallReturn::Return(0));
}
let signal = {
@ -36,7 +39,7 @@ pub fn sys_tgkill(tgid: Pgid, pid: Pid, sig_num: u8) -> Result<SyscallReturn> {
src_uid,
))
};
let mut sig_queue = target_process.sig_queues().lock();
let mut sig_queue = posix_thread.sig_queues().lock();
sig_queue.enqueue(signal);
Ok(SyscallReturn::Return(0))
}

View File

@ -12,7 +12,7 @@ const STDERR: u64 = 2;
pub fn sys_write(
fd: FileDescripter,
user_buf_ptr: Vaddr,
user_buf_len: u64,
user_buf_len: usize,
) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_WRITE);
debug!(
@ -23,7 +23,7 @@ pub fn sys_write(
let current = current!();
let file_table = current.file_table().lock();
let file = file_table.get_file(fd)?;
let mut buffer = vec![0u8; user_buf_len as usize];
let mut buffer = vec![0u8; user_buf_len];
read_bytes_from_user(user_buf_ptr as usize, &mut buffer)?;
let write_len = file.write(&buffer)?;
Ok(SyscallReturn::Return(write_len as _))

View File

@ -12,7 +12,7 @@ pub fn handle_exception(context: &mut CpuContext) {
let trap_info = context.trap_information.clone();
log_trap_info(&trap_info);
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let root_vmar = current.root_vmar();
match trap_info.id {
PAGE_FAULT => handle_page_fault(&trap_info),
@ -31,13 +31,18 @@ fn handle_page_fault(trap_info: &TrapInformation) {
if not_present || write {
// If page is not present or due to write access, we should ask the vmar try to commit this page
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let root_vmar = current.root_vmar();
let page_fault_addr = trap_info.cr2 as Vaddr;
debug!(
trace!(
"Page fault address: 0x{:x}, write access: {}",
page_fault_addr, write
page_fault_addr,
write
);
if let Err(_) = root_vmar.handle_page_fault(page_fault_addr, not_present, write) {
if let Err(e) = root_vmar.handle_page_fault(page_fault_addr, not_present, write) {
error!(
"page fault handler failed: addr: 0x{:x}, err: {:?}",
page_fault_addr, e
);
generate_fault_signal(trap_info);
} else {
// ensure page fault is successfully handled
@ -60,7 +65,7 @@ fn generate_fault_signal(trap_info: &TrapInformation) {
macro_rules! log_trap_common {
($exception_name: ident, $trap_info: ident) => {
debug!(
trace!(
"[Trap][{}][err = {}]",
stringify!($exception_name),
$trap_info.err
@ -85,7 +90,7 @@ fn log_trap_info(trap_info: &TrapInformation) {
STACK_SEGMENT_FAULT => log_trap_common!(STACK_SEGMENT_FAULT, trap_info),
GENERAL_PROTECTION_FAULT => log_trap_common!(GENERAL_PROTECTION_FAULT, trap_info),
PAGE_FAULT => {
debug!(
trace!(
"[Trap][{}][page fault addr = 0x{:x}, err = {}]",
stringify!(PAGE_FAULT),
trap_info.cr2,

View File

@ -0,0 +1,52 @@
use jinux_frame::task::Task;
use crate::{prelude::*, process::Process};
use super::{allocate_tid, status::ThreadStatus, thread_table, Thread};
pub struct KernelThread {
process: Weak<Process>,
}
impl KernelThread {
pub fn new(process: Weak<Process>) -> Self {
Self { process }
}
pub fn process(&self) -> Arc<Process> {
self.process.upgrade().unwrap()
}
}
pub trait KernelThreadExt {
fn is_kernel_thread(&self) -> bool;
fn kernel_thread(&self) -> &KernelThread;
fn new_kernel_thread<F>(task_fn: F, process: Weak<Process>) -> Arc<Self>
where
F: Fn() + Send + Sync + 'static;
}
impl KernelThreadExt for Thread {
fn is_kernel_thread(&self) -> bool {
self.data().downcast_ref::<KernelThread>().is_some()
}
fn kernel_thread(&self) -> &KernelThread {
self.data().downcast_ref::<KernelThread>().unwrap()
}
fn new_kernel_thread<F>(task_fn: F, process: Weak<Process>) -> Arc<Self>
where
F: Fn() + Send + Sync + 'static,
{
let tid = allocate_tid();
let thread = Arc::new_cyclic(|thread_ref| {
let weal_thread = thread_ref.clone();
let task = Task::new(task_fn, weal_thread, None).unwrap();
let status = ThreadStatus::Init;
let kernel_thread = KernelThread::new(process);
Thread::new(tid, task, kernel_thread, status)
});
thread_table::add_thread(thread.clone());
thread
}
}

View File

@ -1,70 +1,54 @@
//! Posix thread implementation
use crate::{
prelude::*,
process::{elf::load_elf_to_root_vmar, Process},
rights::Full,
vm::vmar::Vmar,
use core::{
any::Any,
sync::atomic::{AtomicI32, Ordering},
};
use jinux_frame::{cpu::CpuContext, task::Task, user::UserSpace};
use self::task::create_new_user_task;
use jinux_frame::task::Task;
use crate::prelude::*;
use self::status::ThreadStatus;
pub mod exception;
pub mod kernel_thread;
pub mod status;
pub mod task;
pub mod thread_table;
pub type Tid = i32;
static TID_ALLOCATOR: AtomicI32 = AtomicI32::new(0);
/// A thread is a wrapper on top of task.
pub struct Thread {
// immutable part
/// Thread id
tid: Tid,
/// Low-level info
task: Arc<Task>,
/// The process. FIXME: should we store the process info here?
process: Weak<Process>,
/// Data: Posix thread info/Kernel thread Info
data: Box<dyn Send + Sync + Any>,
// mutable part
status: Mutex<ThreadStatus>,
}
impl Thread {
pub fn new_user_thread_from_elf(
root_vmar: &Vmar<Full>,
filename: CString,
elf_file_content: &'static [u8],
process: Weak<Process>,
/// Never call these function directly
pub fn new(
tid: Tid,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Arc<Self> {
let elf_load_info =
load_elf_to_root_vmar(filename, elf_file_content, &root_vmar, argv, envp)
.expect("Load Elf failed");
let vm_space = root_vmar.vm_space().clone();
let mut cpu_ctx = CpuContext::default();
cpu_ctx.set_rip(elf_load_info.entry_point());
cpu_ctx.set_rsp(elf_load_info.user_stack_top());
let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx));
Thread::new_user_thread(tid, user_space, process)
}
pub fn new_user_thread(
tid: Tid,
user_space: Arc<UserSpace>,
process: Weak<Process>,
) -> Arc<Self> {
Arc::new_cyclic(|thread_ref| {
let task = create_new_user_task(user_space, thread_ref.clone());
Thread { tid, task, process }
})
}
pub fn new_kernel_thread<F>(tid: Tid, task_fn: F, process: Weak<Process>) -> Arc<Self>
where
F: Fn() + Send + Sync + 'static,
{
Arc::new_cyclic(|thread_ref| {
let weal_thread = thread_ref.clone();
let task = Task::new(task_fn, weal_thread, None).unwrap();
Thread { tid, task, process }
})
task: Arc<Task>,
data: impl Send + Sync + Any,
status: ThreadStatus,
) -> Self {
Thread {
tid,
task,
data: Box::new(data),
status: Mutex::new(status),
}
}
pub fn current() -> Arc<Self> {
@ -72,22 +56,43 @@ impl Thread {
let thread = task
.data()
.downcast_ref::<Weak<Thread>>()
.expect("[Internal Error] task data should points to weak<process>");
.expect("[Internal Error] task data should points to weak<thread>");
thread
.upgrade()
.expect("[Internal Error] current process cannot be None")
}
pub fn process(&self) -> Arc<Process> {
self.process.upgrade().unwrap()
.expect("[Internal Error] current thread cannot be None")
}
/// Add inner task to the run queue of scheduler. Note this does not means the thread will run at once.
pub fn run(&self) {
self.status.lock().set_running();
self.task.run();
}
pub fn exit(&self) {
let mut status = self.status.lock();
if !status.is_exited() {
status.set_exited();
}
}
pub fn status(&self) -> &Mutex<ThreadStatus> {
&self.status
}
pub fn yield_now() {
Task::yield_now()
}
pub fn tid(&self) -> Tid {
self.tid
}
pub fn data(&self) -> &Box<dyn Send + Sync + Any> {
&self.data
}
}
/// allocate a new pid for new process
pub fn allocate_tid() -> Tid {
TID_ALLOCATOR.fetch_add(1, Ordering::SeqCst)
}

View File

@ -0,0 +1,35 @@
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ThreadStatus {
Init,
Running,
Exited,
Stopped,
}
impl ThreadStatus {
pub fn is_running(&self) -> bool {
*self == ThreadStatus::Running
}
pub fn is_exited(&self) -> bool {
*self == ThreadStatus::Exited
}
pub fn is_stopped(&self) -> bool {
*self == ThreadStatus::Stopped
}
pub fn set_running(&mut self) {
debug_assert!(!self.is_exited());
*self = ThreadStatus::Running;
}
pub fn set_stopped(&mut self) {
debug_assert!(!self.is_exited());
*self = ThreadStatus::Stopped;
}
pub fn set_exited(&mut self) {
*self = ThreadStatus::Exited;
}
}

View File

@ -5,9 +5,8 @@ use jinux_frame::{
};
use crate::{
prelude::*,
process::{exception::handle_exception, signal::handle_pending_signal},
syscall::handle_syscall,
prelude::*, process::signal::handle_pending_signal, syscall::handle_syscall,
thread::exception::handle_exception,
};
use super::Thread;
@ -26,20 +25,20 @@ pub fn create_new_user_task(user_space: Arc<UserSpace>, thread_ref: Weak<Thread>
let context = user_mode.context_mut();
// handle user event:
handle_user_event(user_event, context);
let current = current!();
let current_thread = current_thread!();
// should be do this comparison before handle signal?
if current.status().lock().is_zombie() {
if current_thread.status().lock().is_exited() {
break;
}
handle_pending_signal(context).unwrap();
if current.status().lock().is_zombie() {
if current_thread.status().lock().is_exited() {
debug!("exit due to signal");
break;
}
// If current is suspended, wait for a signal to wake up self
while current.status().lock().is_suspend() {
while current_thread.status().lock().is_stopped() {
Thread::yield_now();
debug!("{} is suspended.", current.pid());
debug!("{} is suspended.", current_thread.tid());
handle_pending_signal(context).unwrap();
}
}

View File

@ -0,0 +1,20 @@
use crate::prelude::*;
use super::{Thread, Tid};
lazy_static! {
static ref THREAD_TABLE: Mutex<BTreeMap<Tid, Arc<Thread>>> = Mutex::new(BTreeMap::new());
}
pub fn add_thread(thread: Arc<Thread>) {
let tid = thread.tid();
THREAD_TABLE.lock().insert(tid, thread);
}
pub fn remove_thread(tid: Tid) {
THREAD_TABLE.lock().remove(&tid);
}
pub fn tid_to_thread(tid: Tid) -> Option<Arc<Thread>> {
THREAD_TABLE.lock().get(&tid).map(|thread| thread.clone())
}

View File

@ -0,0 +1,66 @@
#![allow(non_camel_case_types)]
use core::time::Duration;
use crate::prelude::*;
pub type clockid_t = i32;
pub type time_t = i64;
pub type suseconds_t = i64;
pub type clock_t = i64;
#[derive(Debug, Copy, Clone, Pod)]
#[repr(i32)]
pub enum ClockID {
CLOCK_REALTIME = 0,
CLOCK_MONOTONIC = 1,
CLOCK_PROCESS_CPUTIME_ID = 2,
CLOCK_THREAD_CPUTIME_ID = 3,
CLOCK_MONOTONIC_RAW = 4,
CLOCK_REALTIME_COARSE = 5,
CLOCK_MONOTONIC_COARSE = 6,
CLOCK_BOOTTIME = 7,
}
impl TryFrom<clockid_t> for ClockID {
type Error = Error;
fn try_from(value: clockid_t) -> Result<Self> {
Ok(match value as i32 {
0 => ClockID::CLOCK_REALTIME,
1 => ClockID::CLOCK_MONOTONIC,
2 => ClockID::CLOCK_PROCESS_CPUTIME_ID,
3 => ClockID::CLOCK_THREAD_CPUTIME_ID,
4 => ClockID::CLOCK_MONOTONIC_RAW,
5 => ClockID::CLOCK_REALTIME_COARSE,
6 => ClockID::CLOCK_MONOTONIC_COARSE,
7 => ClockID::CLOCK_BOOTTIME,
_ => return_errno_with_message!(Errno::EINVAL, "invalid clockid"),
})
}
}
#[repr(C)]
#[derive(Debug, Default, Copy, Clone, Pod)]
pub struct timespec_t {
sec: time_t,
nsec: i64,
}
impl From<Duration> for timespec_t {
fn from(duration: Duration) -> timespec_t {
let sec = duration.as_secs() as time_t;
let nsec = duration.subsec_nanos() as i64;
debug_assert!(sec >= 0); // nsec >= 0 always holds
timespec_t { sec, nsec }
}
}
impl From<timespec_t> for Duration {
fn from(timespec: timespec_t) -> Self {
Duration::new(timespec.sec as u64, timespec.nsec as u32)
}
}
/// The various flags for setting POSIX.1b interval timers:
pub const TIMER_ABSTIME: i32 = 0x01;

View File

@ -3,7 +3,7 @@ use self::line_discipline::LineDiscipline;
use crate::driver::console::receive_console_char;
use crate::fs::events::IoEvents;
use crate::fs::ioctl::IoctlCmd;
use crate::process::Pgid;
use crate::process::{process_table, Pgid};
use crate::util::{read_val_from_user, write_val_to_user};
use crate::{fs::file::File, prelude::*};
@ -32,9 +32,21 @@ impl Tty {
}
}
/// Set foreground process group
pub fn set_fg(&self, pgid: Pgid) {
self.ldisc.lock().set_fg(pgid);
}
/// Wake up foreground process group that wait on IO events.
/// This function should be called when the interrupt handler of IO events is called.
pub fn wake_fg_proc_grp(&self) {
let ldisc = self.ldisc.lock();
if let Some(fg_pgid) = ldisc.get_fg() {
if let Some(fg_proc_grp) = process_table::pgid_to_process_group(*fg_pgid) {
fg_proc_grp.wake_all_polling_procs();
}
}
}
}
impl File for Tty {
@ -108,6 +120,7 @@ impl File for Tty {
}
}
/// FIXME: should we maintain a static console?
pub fn get_console() -> &'static Arc<Tty> {
&N_TTY
}

View File

@ -1,17 +1,17 @@
use crate::prelude::*;
pub struct UserApp {
pub app_name: CString,
pub elf_path: CString,
pub app_content: &'static [u8],
pub argv: Vec<CString>,
pub envp: Vec<CString>,
}
impl UserApp {
pub fn new(app_name: &str, app_content: &'static [u8]) -> Self {
let app_name = CString::new(app_name).unwrap();
pub fn new(elf_path: &str, app_content: &'static [u8]) -> Self {
let app_name = CString::new(elf_path).unwrap();
UserApp {
app_name,
elf_path: app_name,
app_content,
argv: Vec::new(),
envp: Vec::new(),
@ -35,7 +35,7 @@ pub fn get_all_apps() -> Vec<UserApp> {
res.push(asm_hello_world);
// Hello world, written in C language.
// Since glibc requires the app name starts with "/", and we don't have filesystem now.
// Since glibc requires the elf path starts with "/", and we don't have filesystem now.
// So we manually add a leading "/" for app written in C language.
let hello_c = UserApp::new("/hello_c", read_hello_c_content());
res.push(hello_c);
@ -56,6 +56,10 @@ pub fn get_all_apps() -> Vec<UserApp> {
let signal_test = UserApp::new("/signal_test", read_signal_test_content());
res.push(signal_test);
// pthread test
let pthread_test = UserApp::new("/pthread_test", read_pthread_test_content());
res.push(pthread_test);
res
}
@ -109,6 +113,10 @@ fn read_signal_test_content() -> &'static [u8] {
include_bytes!("../../../../apps/signal_c/signal_test")
}
fn read_pthread_test_content() -> &'static [u8] {
include_bytes!("../../../../apps/pthread/pthread_test")
}
fn read_busybox_content() -> &'static [u8] {
include_bytes!("../../../../apps/busybox/busybox")
}

View File

@ -5,28 +5,28 @@ use pod::Pod;
/// copy bytes from user space of current process. The bytes len is the len of dest.
pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) -> Result<()> {
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let root_vmar = current.root_vmar();
Ok(root_vmar.read_bytes(src, dest)?)
}
/// copy val (Plain of Data type) from user space of current process.
pub fn read_val_from_user<T: Pod>(src: Vaddr) -> Result<T> {
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let root_vmar = current.root_vmar();
Ok(root_vmar.read_val(src)?)
}
/// write bytes from user space of current process. The bytes len is the len of src.
pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) -> Result<()> {
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let root_vmar = current.root_vmar();
Ok(root_vmar.write_bytes(dest, src)?)
}
/// write val (Plain of Data type) to user space of current process.
pub fn write_val_to_user<T: Pod>(dest: Vaddr, val: &T) -> Result<()> {
let current = current!();
let root_vmar = current.root_vmar().unwrap();
let root_vmar = current.root_vmar();
Ok(root_vmar.write_val(dest, val)?)
}

View File

@ -20,7 +20,6 @@ use jinux_frame::AlignExt;
use self::vm_mapping::VmMapping;
use super::page_fault_handler::PageFaultHandler;
use super::vmo::Vmo;
/// Virtual Memory Address Regions (VMARs) are a type of capability that manages
/// user address spaces.
@ -228,7 +227,7 @@ impl Vmar_ {
// FIXME: If multiple vmos are mapped to the addr, should we allow all vmos to handle page fault?
for (vm_mapping_base, vm_mapping) in &inner.vm_mappings {
if *vm_mapping_base <= page_fault_addr
&& page_fault_addr <= *vm_mapping_base + vm_mapping.size()
&& page_fault_addr < *vm_mapping_base + vm_mapping.size()
{
return vm_mapping.handle_page_fault(page_fault_addr, not_present, write);
}
@ -639,10 +638,10 @@ impl Vmar_ {
}
/// get mapped vmo at given offset
pub fn get_mapped_vmo(&self, offset: Vaddr) -> Result<Vmo<Rights>> {
pub fn get_vm_mapping(&self, offset: Vaddr) -> Result<Arc<VmMapping>> {
for (vm_mapping_base, vm_mapping) in &self.inner.lock().vm_mappings {
if *vm_mapping_base <= offset && offset < *vm_mapping_base + vm_mapping.size() {
return Ok(vm_mapping.vmo().dup()?);
return Ok(vm_mapping.clone());
}
}
return_errno_with_message!(Errno::EACCES, "No mapped vmo at this offset");
@ -671,10 +670,10 @@ impl<R> Vmar<R> {
}
/// get a mapped vmo
pub fn get_mapped_vmo(&self, offset: Vaddr) -> Result<Vmo<Rights>> {
pub fn get_vm_mapping(&self, offset: Vaddr) -> Result<Arc<VmMapping>> {
let rights = Rights::all();
self.check_rights(rights)?;
self.0.get_mapped_vmo(offset)
self.0.get_vm_mapping(offset)
}
}

View File

@ -106,7 +106,7 @@ impl VmMapping {
})
}
pub(super) fn vmo(&self) -> &Vmo<Rights> {
pub fn vmo(&self) -> &Vmo<Rights> {
&self.vmo
}
@ -143,11 +143,11 @@ impl VmMapping {
}
/// the mapping's start address
pub(super) fn map_to_addr(&self) -> Vaddr {
pub fn map_to_addr(&self) -> Vaddr {
self.map_to_addr
}
pub(super) fn size(&self) -> usize {
pub fn size(&self) -> usize {
self.map_size
}
@ -176,6 +176,13 @@ impl VmMapping {
Ok(())
}
pub fn unmap_and_decommit(&self, range: Range<usize>) -> Result<()> {
let vmo_range = (range.start - self.map_to_addr)..(range.end - self.map_to_addr);
self.unmap(range, false)?;
self.vmo.decommit(vmo_range)?;
Ok(())
}
pub fn is_destroyed(&self) -> bool {
self.inner.lock().is_destroyed
}
@ -206,11 +213,25 @@ impl VmMapping {
pub(super) fn protect(&self, perms: VmPerms, range: Range<usize>) -> Result<()> {
let rights = Rights::from(perms);
self.vmo().check_rights(rights)?;
// FIXME: should we commit and map these pages before protect vmspace?
debug_assert!(range.start % PAGE_SIZE == 0);
debug_assert!(range.end % PAGE_SIZE == 0);
let start_page = (range.start - self.map_to_addr) / PAGE_SIZE;
let end_page = (range.end - self.map_to_addr) / PAGE_SIZE;
let vmar = self.parent.upgrade().unwrap();
let vm_space = vmar.vm_space();
let perm = VmPerm::from(perms);
vm_space.protect(&range, perm)?;
let mut inner = self.inner.lock();
for page_idx in start_page..end_page {
inner.page_perms.insert(page_idx, perm);
let page_addr = page_idx * PAGE_SIZE + self.map_to_addr;
if vm_space.is_mapped(page_addr) {
// if the page is already mapped, we will modify page table
let perm = VmPerm::from(perms);
let page_range = page_addr..(page_addr + PAGE_SIZE);
vm_space.protect(&page_range, perm)?;
}
}
Ok(())
}