1//! Execution scheduling 2//! 3//! See Also 4//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html) 5use crate::{Errno, Result}; 6 7#[cfg(any(target_os = "android", target_os = "linux"))] 8pub use self::sched_linux_like::*; 9 10#[cfg(any(target_os = "android", target_os = "linux"))] 11#[cfg_attr(docsrs, doc(cfg(all())))] 12mod sched_linux_like { 13 use crate::errno::Errno; 14 use crate::unistd::Pid; 15 use crate::Result; 16 use libc::{self, c_int, c_void}; 17 use std::mem; 18 use std::option::Option; 19 use std::os::unix::io::RawFd; 20 21 // For some functions taking with a parameter of type CloneFlags, 22 // only a subset of these flags have an effect. 23 libc_bitflags! { 24 /// Options for use with [`clone`] 25 pub struct CloneFlags: c_int { 26 /// The calling process and the child process run in the same 27 /// memory space. 28 CLONE_VM; 29 /// The caller and the child process share the same filesystem 30 /// information. 31 CLONE_FS; 32 /// The calling process and the child process share the same file 33 /// descriptor table. 34 CLONE_FILES; 35 /// The calling process and the child process share the same table 36 /// of signal handlers. 37 CLONE_SIGHAND; 38 /// If the calling process is being traced, then trace the child 39 /// also. 40 CLONE_PTRACE; 41 /// The execution of the calling process is suspended until the 42 /// child releases its virtual memory resources via a call to 43 /// execve(2) or _exit(2) (as with vfork(2)). 44 CLONE_VFORK; 45 /// The parent of the new child (as returned by getppid(2)) 46 /// will be the same as that of the calling process. 47 CLONE_PARENT; 48 /// The child is placed in the same thread group as the calling 49 /// process. 50 CLONE_THREAD; 51 /// The cloned child is started in a new mount namespace. 52 CLONE_NEWNS; 53 /// The child and the calling process share a single list of System 54 /// V semaphore adjustment values 55 CLONE_SYSVSEM; 56 // Not supported by Nix due to lack of varargs support in Rust FFI 57 // CLONE_SETTLS; 58 // Not supported by Nix due to lack of varargs support in Rust FFI 59 // CLONE_PARENT_SETTID; 60 // Not supported by Nix due to lack of varargs support in Rust FFI 61 // CLONE_CHILD_CLEARTID; 62 /// Unused since Linux 2.6.2 63 #[deprecated(since = "0.23.0", note = "Deprecated by Linux 2.6.2")] 64 CLONE_DETACHED; 65 /// A tracing process cannot force `CLONE_PTRACE` on this child 66 /// process. 67 CLONE_UNTRACED; 68 // Not supported by Nix due to lack of varargs support in Rust FFI 69 // CLONE_CHILD_SETTID; 70 /// Create the process in a new cgroup namespace. 71 CLONE_NEWCGROUP; 72 /// Create the process in a new UTS namespace. 73 CLONE_NEWUTS; 74 /// Create the process in a new IPC namespace. 75 CLONE_NEWIPC; 76 /// Create the process in a new user namespace. 77 CLONE_NEWUSER; 78 /// Create the process in a new PID namespace. 79 CLONE_NEWPID; 80 /// Create the process in a new network namespace. 81 CLONE_NEWNET; 82 /// The new process shares an I/O context with the calling process. 83 CLONE_IO; 84 } 85 } 86 87 /// Type for the function executed by [`clone`]. 88 pub type CloneCb<'a> = Box<dyn FnMut() -> isize + 'a>; 89 90 /// `clone` create a child process 91 /// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html)) 92 /// 93 /// `stack` is a reference to an array which will hold the stack of the new 94 /// process. Unlike when calling `clone(2)` from C, the provided stack 95 /// address need not be the highest address of the region. Nix will take 96 /// care of that requirement. The user only needs to provide a reference to 97 /// a normally allocated buffer. 98 pub fn clone( 99 mut cb: CloneCb, 100 stack: &mut [u8], 101 flags: CloneFlags, 102 signal: Option<c_int>, 103 ) -> Result<Pid> { 104 extern "C" fn callback(data: *mut CloneCb) -> c_int { 105 let cb: &mut CloneCb = unsafe { &mut *data }; 106 (*cb)() as c_int 107 } 108 109 let res = unsafe { 110 let combined = flags.bits() | signal.unwrap_or(0); 111 let ptr = stack.as_mut_ptr().add(stack.len()); 112 let ptr_aligned = ptr.sub(ptr as usize % 16); 113 libc::clone( 114 mem::transmute( 115 callback 116 as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32, 117 ), 118 ptr_aligned as *mut c_void, 119 combined, 120 &mut cb as *mut _ as *mut c_void, 121 ) 122 }; 123 124 Errno::result(res).map(Pid::from_raw) 125 } 126 127 /// disassociate parts of the process execution context 128 /// 129 /// See also [unshare(2)](https://man7.org/linux/man-pages/man2/unshare.2.html) 130 pub fn unshare(flags: CloneFlags) -> Result<()> { 131 let res = unsafe { libc::unshare(flags.bits()) }; 132 133 Errno::result(res).map(drop) 134 } 135 136 /// reassociate thread with a namespace 137 /// 138 /// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html) 139 pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> { 140 let res = unsafe { libc::setns(fd, nstype.bits()) }; 141 142 Errno::result(res).map(drop) 143 } 144} 145 146#[cfg(any( 147 target_os = "android", 148 target_os = "dragonfly", 149 target_os = "freebsd", 150 target_os = "linux" 151))] 152pub use self::sched_affinity::*; 153 154#[cfg(any( 155 target_os = "android", 156 target_os = "dragonfly", 157 target_os = "freebsd", 158 target_os = "linux" 159))] 160mod sched_affinity { 161 use crate::errno::Errno; 162 use crate::unistd::Pid; 163 use crate::Result; 164 use std::mem; 165 166 /// CpuSet represent a bit-mask of CPUs. 167 /// CpuSets are used by sched_setaffinity and 168 /// sched_getaffinity for example. 169 /// 170 /// This is a wrapper around `libc::cpu_set_t`. 171 #[repr(transparent)] 172 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 173 pub struct CpuSet { 174 #[cfg(not(target_os = "freebsd"))] 175 cpu_set: libc::cpu_set_t, 176 #[cfg(target_os = "freebsd")] 177 cpu_set: libc::cpuset_t, 178 } 179 180 impl CpuSet { 181 /// Create a new and empty CpuSet. 182 pub fn new() -> CpuSet { 183 CpuSet { 184 cpu_set: unsafe { mem::zeroed() }, 185 } 186 } 187 188 /// Test to see if a CPU is in the CpuSet. 189 /// `field` is the CPU id to test 190 pub fn is_set(&self, field: usize) -> Result<bool> { 191 if field >= CpuSet::count() { 192 Err(Errno::EINVAL) 193 } else { 194 Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) }) 195 } 196 } 197 198 /// Add a CPU to CpuSet. 199 /// `field` is the CPU id to add 200 pub fn set(&mut self, field: usize) -> Result<()> { 201 if field >= CpuSet::count() { 202 Err(Errno::EINVAL) 203 } else { 204 unsafe { 205 libc::CPU_SET(field, &mut self.cpu_set); 206 } 207 Ok(()) 208 } 209 } 210 211 /// Remove a CPU from CpuSet. 212 /// `field` is the CPU id to remove 213 pub fn unset(&mut self, field: usize) -> Result<()> { 214 if field >= CpuSet::count() { 215 Err(Errno::EINVAL) 216 } else { 217 unsafe { 218 libc::CPU_CLR(field, &mut self.cpu_set); 219 } 220 Ok(()) 221 } 222 } 223 224 /// Return the maximum number of CPU in CpuSet 225 pub const fn count() -> usize { 226 #[cfg(not(target_os = "freebsd"))] 227 let bytes = mem::size_of::<libc::cpu_set_t>(); 228 #[cfg(target_os = "freebsd")] 229 let bytes = mem::size_of::<libc::cpuset_t>(); 230 231 8 * bytes 232 } 233 } 234 235 impl Default for CpuSet { 236 fn default() -> Self { 237 Self::new() 238 } 239 } 240 241 /// `sched_setaffinity` set a thread's CPU affinity mask 242 /// ([`sched_setaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html)) 243 /// 244 /// `pid` is the thread ID to update. 245 /// If pid is zero, then the calling thread is updated. 246 /// 247 /// The `cpuset` argument specifies the set of CPUs on which the thread 248 /// will be eligible to run. 249 /// 250 /// # Example 251 /// 252 /// Binding the current thread to CPU 0 can be done as follows: 253 /// 254 /// ```rust,no_run 255 /// use nix::sched::{CpuSet, sched_setaffinity}; 256 /// use nix::unistd::Pid; 257 /// 258 /// let mut cpu_set = CpuSet::new(); 259 /// cpu_set.set(0).unwrap(); 260 /// sched_setaffinity(Pid::from_raw(0), &cpu_set).unwrap(); 261 /// ``` 262 pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> { 263 let res = unsafe { 264 libc::sched_setaffinity( 265 pid.into(), 266 mem::size_of::<CpuSet>() as libc::size_t, 267 &cpuset.cpu_set, 268 ) 269 }; 270 271 Errno::result(res).map(drop) 272 } 273 274 /// `sched_getaffinity` get a thread's CPU affinity mask 275 /// ([`sched_getaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_getaffinity.2.html)) 276 /// 277 /// `pid` is the thread ID to check. 278 /// If pid is zero, then the calling thread is checked. 279 /// 280 /// Returned `cpuset` is the set of CPUs on which the thread 281 /// is eligible to run. 282 /// 283 /// # Example 284 /// 285 /// Checking if the current thread can run on CPU 0 can be done as follows: 286 /// 287 /// ```rust,no_run 288 /// use nix::sched::sched_getaffinity; 289 /// use nix::unistd::Pid; 290 /// 291 /// let cpu_set = sched_getaffinity(Pid::from_raw(0)).unwrap(); 292 /// if cpu_set.is_set(0).unwrap() { 293 /// println!("Current thread can run on CPU 0"); 294 /// } 295 /// ``` 296 pub fn sched_getaffinity(pid: Pid) -> Result<CpuSet> { 297 let mut cpuset = CpuSet::new(); 298 let res = unsafe { 299 libc::sched_getaffinity( 300 pid.into(), 301 mem::size_of::<CpuSet>() as libc::size_t, 302 &mut cpuset.cpu_set, 303 ) 304 }; 305 306 Errno::result(res).and(Ok(cpuset)) 307 } 308 309 /// Determines the CPU on which the calling thread is running. 310 pub fn sched_getcpu() -> Result<usize> { 311 let res = unsafe { libc::sched_getcpu() }; 312 313 Errno::result(res).map(|int| int as usize) 314 } 315} 316 317/// Explicitly yield the processor to other threads. 318/// 319/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html) 320pub fn sched_yield() -> Result<()> { 321 let res = unsafe { libc::sched_yield() }; 322 323 Errno::result(res).map(drop) 324} 325