os_core/unix/
mod.rs

1//! Operating system calls that work on OSX and Linux
2/// Allocates physical memory for a file, device, or anonymous-file (ie, a file that is only stored in-memory; just a memory allocation for the running process).
3/// 
4/// Typically, the data in the device (eg, disk) is loaded when the page of memory is accessed. I use the word "typically" because 
5/// the implementation of the device determines how the data is populated in the physical-page, for instance you could implement the mmap functions 
6/// for a device that calls out over the network to populate the physical-page when the first-access to the memory occurs (per page). 
7/// 
8/// For anonomyous files: the page of memory being accessed is allocated a physical-page in RAM whenever it is accessed. 
9///
10/// *NOTE*: This is the best function for allocating program memory, and is the most effecient function for loading files
11/// when the file-contents are accessed randomly; otherwise, if read sequentially once or written sequentially once, use direct-io (see open(2))
12///  
13/// *NOTE*: Accessing outside of the logically-allocated-range will not result in a physical-page being allocated, to clarify.
14/// 
15/// # Example
16/// mapping memory (not based on device or file). give it an address you want the mapping to be at, or just leave it blank (99% of the time)
17/// then set the memory protection flags and allocation flags, use ANON for in-memory only, use -1 for the file-descriptor, and 0 as the offset.
18/// 
19/// the minimum flags for `map_flags` is one of either SHARED or PRIVATE.
20/// ```
21/// use os_core::{unix::{sys_mmap, sys_munmap}, constants::{map, mprot}};
22/// 
23/// let mut mapping = sys_mmap(0 as *mut (), 4096, mprot::READ | mprot::WRITE, map::ANON | map::SHARED, -1, 0);
24/// assert!(mapping != map::FAILED);
25/// 
26/// let mut data = mapping as *mut u8;
27/// unsafe {*(data.offset(4095)) = 0xFF;}
28/// assert_eq!(unsafe {*(data.offset(4095))}, 0xFF);
29/// sys_munmap(mapping, 4096);
30/// // sanity: this would also work -> sys_munmap(data, 4096); <- since it's the same address (data and mapping)
31/// ```
32pub fn sys_mmap(addr: *mut (), len: usize, mprot_flags: i32, map_flags: i32, fd: i32, offset: i64) -> *mut () {
33    unsafe {
34        libc::mmap(addr as *mut libc::c_void, len, mprot_flags, map_flags, fd, offset) as *mut ()
35    }
36}
37
38/// Unmap program memory and memory-mapped devices/files
39pub fn sys_munmap(addr: *mut (), len: usize) -> i32 {
40    unsafe {
41        libc::munmap(addr as *mut libc::c_void, len)
42    }
43}
44
45/// Create a new process by copying the memory-mappings in the current process into a new address space and get a separate
46/// scheduling-context in the OS.
47pub fn sys_fork() -> i32 {
48    unsafe {
49        libc::fork()
50    }
51}
52
53/// Wait for any of the caller's child-process's scheduling-statuses to change (not including going from idle to runnable, 
54/// more signal related statuses; see manpage wait(2))
55/// 
56/// Returns a 2-tuple with the PID first, then the status of the child process.
57pub fn sys_wait() -> (i32, i32) {
58    unsafe {
59        let mut status = 0;
60        let pid = libc::wait(&mut status);
61        (pid, status)
62    }
63}
64
65/// Create a pipe
66/// 
67/// The first i32 is the read-end of the pipe, the second i32 is the write-end
68pub fn sys_pipe (pfds: &mut [i32; 2]) -> i32 {
69    unsafe {
70        libc::pipe(pfds as *mut i32)
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use crate::constants::{map, mprot};
78
79    #[test]
80    fn test_sys_mmap_munmap() {
81        use crate::constants::{map, mprot};
82        let mut mem = 0 as *mut ();
83
84        mem = sys_mmap(0 as *mut (), 4096, mprot::READ | mprot::WRITE, map::ANON | map::SHARED, -1, 0);
85        
86        assert!(map::FAILED != mem);
87        assert!(mem as usize > 0);
88
89        let mut data = mem as *mut u8;
90        for i in 0..4096 {
91            unsafe {*(data.offset(i)) = (i % 256) as u8; }
92        }
93
94        for i in 0..4096 {
95            assert_eq!(unsafe {*(data.offset(i))}, (i % 256) as u8);
96        }
97
98        assert_eq!(sys_munmap(mem, 4096), 0);
99    }
100    
101    #[test]
102    fn test_sys_fork() {
103        let pid = unsafe {libc::getpid()};
104        let mut ch_pid = sys_fork();
105
106        if ch_pid == 0 {
107        // child
108            unsafe {assert_eq!(libc::getppid(), pid);}
109            std::process::exit(0);
110        }
111
112        let mut status = -1;
113        unsafe {
114            assert_eq!(libc::wait(&mut status as *mut i32), ch_pid);
115            assert!(libc::WIFEXITED(status));
116        }
117    }
118}