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}