Repository: GuillaumeGomez/sysinfo
Pull request #1291 (2024-06-22 22:53:46)

Add `system` cargo feature
Part of https://github.com/GuillaumeGomez/sysinfo/issues/965.

Remains to be done:

* Remove features in `windows-rs` crate that are linked to `system`.
* Add CI to check for this feature in particular.


Patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index ac50b60..4483e58 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -11,11 +11,12 @@ task:
   test_script:
     - . $HOME/.cargo/env
     - cargo check
-    - cargo check --no-default-features
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features system
     - cargo clippy -- -D warnings
     - cargo check --example simple
-    - FREEBSD_CI=1 cargo test -j1
-    - FREEBSD_CI=1 cargo test --lib -j1 -- --ignored
+    - FREEBSD_CI=1 cargo test -- --test-threads=1
+    - FREEBSD_CI=1 cargo test --lib -- --ignored --test-threads=1
 
 task:
   name: rust nightly on freebsd 13
@@ -30,11 +31,12 @@ task:
   test_script:
     - . $HOME/.cargo/env
     - cargo check
-    - cargo check --no-default-features
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features system
     - cargo clippy -- -D warnings
     - cargo check --example simple
-    - FREEBSD_CI=1 cargo test -j1
-    - FREEBSD_CI=1 cargo test --lib -j1 -- --ignored
+    - FREEBSD_CI=1 cargo test -- --test-threads=1
+    - FREEBSD_CI=1 cargo test --lib -- --test-threads=1
 
 task:
   name: rust 1.74 on mac m1
@@ -51,9 +53,10 @@ task:
   test_script:
     - source $HOME/.cargo/env
     - cargo check
-    - cargo check --no-default-features
-    - cargo check --no-default-features --features apple-sandbox
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features system
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features apple-sandbox
     - cargo clippy -- -D warnings
     - cargo check --example simple
-    - APPLE_CI=1 cargo test -j1
-    - APPLE_CI=1 cargo test --lib -j1 -- --ignored
+    - APPLE_CI=1 cargo test -- --test-threads=1
+    - APPLE_CI=1 cargo test --lib -- --ignored --test-threads=1
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index d1553eb..df5d5f9 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -81,14 +81,14 @@ jobs:
         uses: ClementTsang/cargo-action@v0.0.6
         with:
           command: rustc
-          args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml -- -D warnings
+          args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml
           use-cross: ${{ matrix.triple.cross }}
 
       - name: Check debug feature
         uses: ClementTsang/cargo-action@v0.0.6
         with:
           command: rustc
-          args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --features=debug -- -D warnings
+          args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --features=debug
           use-cross: ${{ matrix.triple.cross }}
 
       - name: Check without multithreading
@@ -98,6 +98,13 @@ jobs:
           args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features
           use-cross: ${{ matrix.triple.cross }}
 
+      - name: Check features
+        uses: ClementTsang/cargo-action@v0.0.6
+        with:
+          command: check
+          args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features --features system
+          use-cross: ${{ matrix.triple.cross }}
+
       - name: Check example
         uses: ClementTsang/cargo-action@v0.0.6
         with:
diff --git a/Cargo.toml b/Cargo.toml
index ac19fd2..0c48c6c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,10 +15,27 @@ edition = "2021"
 name = "sysinfo"
 
 [features]
-default = ["multithread"]
+default = ["system", "multithread"]
+system = [
+    "windows/Wdk_System_SystemInformation",
+    "windows/Wdk_System_SystemServices",
+    "windows/Wdk_System_Threading",
+    "windows/Win32_System_Diagnostics_Debug",
+    "windows/Win32_System_Kernel",
+    "windows/Win32_System_Memory",
+    "windows/Win32_System_Performance",
+    "windows/Win32_System_Power",
+    "windows/Win32_System_ProcessStatus",
+    "windows/Win32_System_Registry",
+    "windows/Win32_System_RemoteDesktop",
+    "windows/Win32_System_SystemInformation",
+    "windows/Win32_System_SystemServices",
+    "windows/Win32_System_Threading",
+    "windows/Win32_UI_Shell",
+]
 apple-sandbox = []
 apple-app-store = ["apple-sandbox"]
-c-interface = []
+c-interface = ["default"]
 multithread = ["dep:rayon"]
 linux-netdevs = []
 linux-tmpfs = []
@@ -50,9 +67,6 @@ serde = { version = "^1.0.190", optional = true }
 [target.'cfg(windows)'.dependencies]
 ntapi = "0.4"
 windows = { version = "0.56", features = [
-    "Wdk_System_SystemInformation",
-    "Wdk_System_SystemServices",
-    "Wdk_System_Threading",
     "Win32_Foundation",
     "Win32_NetworkManagement_IpHelper",
     "Win32_NetworkManagement_NetManagement",
@@ -63,26 +77,12 @@ windows = { version = "0.56", features = [
     "Win32_Security_Authorization",
     "Win32_Storage_FileSystem",
     "Win32_System_Com",
-    "Win32_System_Diagnostics_Debug",
     "Win32_System_IO",
     "Win32_System_Ioctl",
-    "Win32_System_LibraryLoader",
-    "Win32_System_Kernel",
-    "Win32_System_Memory",
-    "Win32_System_Ole",
-    "Win32_System_Performance",
-    "Win32_System_Power",
-    "Win32_System_ProcessStatus",
-    "Win32_System_Registry",
-    "Win32_System_RemoteDesktop",
     "Win32_System_Rpc",
-    "Win32_System_SystemInformation",
-    "Win32_System_SystemServices",
-    "Win32_System_Threading",
     "Win32_System_Variant",
     "Win32_System_WindowsProgramming",
     "Win32_System_Wmi",
-    "Win32_UI_Shell",
 ]}
 
 [target.'cfg(not(any(target_os = "unknown", target_arch = "wasm32")))'.dependencies]
@@ -96,3 +96,8 @@ tempfile = "3.9"
 
 [dev-dependencies]
 serde_json = "1.0" # Used in documentation tests.
+
+[[example]]
+name = "simple"
+path = "examples/simple.rs"
+required-features = ["default"]
diff --git a/benches/basic.rs b/benches/basic.rs
index 04d1217..21cbd0b 100644
--- a/benches/basic.rs
+++ b/benches/basic.rs
@@ -2,8 +2,7 @@
 
 extern crate test;
 
-use sysinfo::get_current_pid;
-
+#[cfg(feature = "system")]
 #[bench]
 fn bench_new(b: &mut test::Bencher) {
     b.iter(|| {
@@ -11,6 +10,7 @@ fn bench_new(b: &mut test::Bencher) {
     });
 }
 
+#[cfg(feature = "system")]
 #[bench]
 fn bench_new_all(b: &mut test::Bencher) {
     b.iter(|| {
@@ -18,6 +18,7 @@ fn bench_new_all(b: &mut test::Bencher) {
     });
 }
 
+#[cfg(feature = "system")]
 #[bench]
 fn bench_refresh_all(b: &mut test::Bencher) {
     let mut s = sysinfo::System::new_all();
@@ -27,6 +28,7 @@ fn bench_refresh_all(b: &mut test::Bencher) {
     });
 }
 
+#[cfg(feature = "system")]
 #[bench]
 fn bench_refresh_processes(b: &mut test::Bencher) {
     let mut s = sysinfo::System::new();
@@ -37,6 +39,7 @@ fn bench_refresh_processes(b: &mut test::Bencher) {
     });
 }
 
+#[cfg(feature = "system")]
 #[bench]
 fn bench_first_refresh_processes(b: &mut test::Bencher) {
     b.iter(move || {
@@ -45,13 +48,14 @@ fn bench_first_refresh_processes(b: &mut test::Bencher) {
     });
 }
 
+#[cfg(feature = "system")]
 #[bench]
 fn bench_refresh_process(b: &mut test::Bencher) {
     let mut s = sysinfo::System::new();
 
     s.refresh_all();
     // to be sure it'll exist for at least as long as we run
-    let pid = get_current_pid().expect("failed to get current pid");
+    let pid = sysinfo::get_current_pid().expect("failed to get current pid");
     b.iter(move || {
         s.refresh_process(pid);
     });
@@ -104,6 +108,7 @@ fn bench_refresh_networks_list(b: &mut test::Bencher) {
     });
 }
 
+#[cfg(feature = "system")]
 #[bench]
 fn bench_refresh_memory(b: &mut test::Bencher) {
     let mut s = sysinfo::System::new();
@@ -113,6 +118,7 @@ fn bench_refresh_memory(b: &mut test::Bencher) {
     });
 }
 
+#[cfg(feature = "system")]
 #[bench]
 fn bench_refresh_cpu_usage(b: &mut test::Bencher) {
     let mut s = sysinfo::System::new();
diff --git a/src/common/mod.rs b/src/common/mod.rs
index f032b91..12e9867 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -3,6 +3,7 @@
 pub(crate) mod component;
 pub(crate) mod disk;
 pub(crate) mod network;
+#[cfg(feature = "system")]
 pub(crate) mod system;
 pub(crate) mod user;
 
diff --git a/src/common/system.rs b/src/common/system.rs
index 4e7fac1..b490724 100644
--- a/src/common/system.rs
+++ b/src/common/system.rs
@@ -2449,6 +2449,7 @@ impl Cpu {
 #[cfg(test)]
 mod test {
     use crate::*;
+    use std::str::FromStr;
 
     // In case `Process::updated` is misused, `System::refresh_processes` might remove them
     // so this test ensures that it doesn't happen.
@@ -2680,4 +2681,76 @@ mod test {
     fn check_display_impl_process_status() {
         println!("{} {:?}", ProcessStatus::Parked, ProcessStatus::Idle);
     }
+
+    #[test]
+    #[allow(clippy::unnecessary_fallible_conversions)]
+    fn check_pid_from_impls() {
+        assert!(crate::Pid::try_from(0usize).is_ok());
+        // If it doesn't panic, it's fine.
+        let _ = crate::Pid::from(0);
+        assert!(crate::Pid::from_str("0").is_ok());
+    }
+
+    #[test]
+    #[allow(clippy::const_is_empty)]
+    fn check_nb_supported_signals() {
+        if IS_SUPPORTED_SYSTEM {
+            assert!(
+                !SUPPORTED_SIGNALS.is_empty(),
+                "SUPPORTED_SIGNALS shouldn't be empty on supported systems!"
+            );
+        } else {
+            assert!(
+                SUPPORTED_SIGNALS.is_empty(),
+                "SUPPORTED_SIGNALS should be empty on not support systems!"
+            );
+        }
+    }
+}
+
+#[cfg(doctest)]
+mod doctest {
+    // FIXME: Can be removed once negative trait bounds are supported.
+    /// Check that `Process` doesn't implement `Clone`.
+    ///
+    /// First we check that the "basic" code works:
+    ///
+    /// ```no_run
+    /// use sysinfo::{Process, System};
+    ///
+    /// let mut s = System::new_all();
+    /// let p: &Process = s.processes().values().next().unwrap();
+    /// ```
+    ///
+    /// And now we check if it fails when we try to clone it:
+    ///
+    /// ```compile_fail
+    /// use sysinfo::{Process, System};
+    ///
+    /// let mut s = System::new_all();
+    /// let p: &Process = s.processes().values().next().unwrap();
+    /// let p = (*p).clone();
+    /// ```
+    mod process_clone {}
+
+    // FIXME: Can be removed once negative trait bounds are supported.
+    /// Check that `System` doesn't implement `Clone`.
+    ///
+    /// First we check that the "basic" code works:
+    ///
+    /// ```no_run
+    /// use sysinfo::{Process, System};
+    ///
+    /// let s = System::new();
+    /// ```
+    ///
+    /// And now we check if it fails when we try to clone it:
+    ///
+    /// ```compile_fail
+    /// use sysinfo::{Process, System};
+    ///
+    /// let s = System::new();
+    /// let s = s.clone();
+    /// ```
+    mod system_clone {}
 }
diff --git a/src/common/user.rs b/src/common/user.rs
index c4bab17..b2db84a 100644
--- a/src/common/user.rs
+++ b/src/common/user.rs
@@ -333,7 +333,8 @@ impl Users {
     ///
     /// Full example:
     ///
-    /// ```no_run
+    #[cfg_attr(feature = "system", doc = "```no_run")]
+    #[cfg_attr(not(feature = "system"), doc = "```ignore")]
     /// use sysinfo::{Pid, System, Users};
     ///
     /// let mut s = System::new_all();
@@ -523,11 +524,6 @@ mod tests {
 
         assert!(crate::Gid::try_from(0usize).is_ok());
         assert!(crate::Gid::from_str("0").is_ok());
-
-        assert!(crate::Pid::try_from(0usize).is_ok());
-        // If it doesn't panic, it's fine.
-        let _ = crate::Pid::from(0);
-        assert!(crate::Pid::from_str("0").is_ok());
     }
 
     #[test]
diff --git a/src/debug.rs b/src/debug.rs
index a6fadb3..29567ce 100644
--- a/src/debug.rs
+++ b/src/debug.rs
@@ -1,12 +1,11 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use crate::{
-    Component, Components, Cpu, Disk, Disks, NetworkData, Networks, Process, System, User, Users,
-};
+use crate::{Component, Components, Disk, Disks, NetworkData, Networks, User, Users};
 
 use std::fmt;
 
-impl fmt::Debug for Cpu {
+#[cfg(feature = "system")]
+impl fmt::Debug for crate::Cpu {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("Cpu")
             .field("name", &self.name())
@@ -18,7 +17,8 @@ impl fmt::Debug for Cpu {
     }
 }
 
-impl fmt::Debug for System {
+#[cfg(feature = "system")]
+impl fmt::Debug for crate::System {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("System")
             .field("global CPU usage", &self.global_cpu_usage())
@@ -49,7 +49,8 @@ impl fmt::Debug for Disk {
     }
 }
 
-impl fmt::Debug for Process {
+#[cfg(feature = "system")]
+impl fmt::Debug for crate::Process {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("Process")
             .field("pid", &self.pid())
diff --git a/src/lib.rs b/src/lib.rs
index 8e4735c..71f526d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,10 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-#![doc = include_str!("../README.md")]
+#![cfg_attr(feature = "system", doc = include_str!("../README.md"))]
+#![cfg_attr(
+    not(feature = "system"),
+    doc = "For crate-level documentation, all features need to be enabled."
+)]
 #![cfg_attr(feature = "serde", doc = include_str!("../md_doc/serde.md"))]
 #![allow(unknown_lints)]
 #![deny(missing_docs)]
@@ -54,21 +58,27 @@ pub use crate::common::{
     component::{Component, Components},
     disk::{Disk, DiskKind, Disks},
     network::{IpNetwork, MacAddr, NetworkData, Networks},
-    system::{
-        get_current_pid, CGroupLimits, Cpu, CpuRefreshKind, DiskUsage, LoadAvg, MemoryRefreshKind,
-        Pid, Process, ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, System, ThreadKind,
-        UpdateKind,
-    },
     user::{Group, Groups, User, Users},
     Gid, Uid,
 };
 
+#[cfg(feature = "system")]
+pub use crate::common::system::{
+    get_current_pid, CGroupLimits, Cpu, CpuRefreshKind, DiskUsage, LoadAvg, MemoryRefreshKind, Pid,
+    Process, ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, System, ThreadKind,
+    UpdateKind,
+};
+
 pub(crate) use crate::common::user::GroupInner;
+pub use crate::sys::IS_SUPPORTED_SYSTEM;
 pub(crate) use crate::sys::{
-    ComponentInner, ComponentsInner, CpuInner, DiskInner, DisksInner, NetworkDataInner,
-    NetworksInner, ProcessInner, SystemInner, UserInner,
+    ComponentInner, ComponentsInner, DiskInner, DisksInner, NetworkDataInner, NetworksInner,
+    UserInner,
 };
-pub use crate::sys::{IS_SUPPORTED_SYSTEM, MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
+#[cfg(feature = "system")]
+pub(crate) use crate::sys::{CpuInner, ProcessInner, SystemInner};
+#[cfg(feature = "system")]
+pub use crate::sys::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
 
 #[cfg(feature = "c-interface")]
 pub use crate::c_interface::*;
@@ -81,8 +91,8 @@ mod debug;
 mod serde;
 pub(crate) mod utils;
 
-/// This function is only used on Linux targets, on the other platforms it does nothing and returns
-/// `false`.
+/// This function is only used on Linux targets, when the `system` feature is enabled. In other
+/// cases, it does nothing and returns `false`.
 ///
 /// On Linux, to improve performance, we keep a `/proc` file open for each process we index with
 /// a maximum number of files open equivalent to half of the system limit.
@@ -94,7 +104,8 @@ pub(crate) mod utils;
 ///
 /// Returns `true` if the new value has been set.
 ///
-/// ```no_run
+#[cfg_attr(feature = "system", doc = "```no_run")]
+#[cfg_attr(not(feature = "system"), doc = "```ignore")]
 /// use sysinfo::{System, set_open_files_limit};
 ///
 /// // We call the function before any call to the processes update.
@@ -106,7 +117,7 @@ pub(crate) mod utils;
 /// ```
 pub fn set_open_files_limit(mut _new_limit: isize) -> bool {
     cfg_if::cfg_if! {
-        if #[cfg(all(not(feature = "unknown-ci"), any(target_os = "linux", target_os = "android")))]
+        if #[cfg(all(feature = "system", not(feature = "unknown-ci"), any(target_os = "linux", target_os = "android")))]
         {
             use crate::sys::system::remaining_files;
             use std::sync::atomic::Ordering;
@@ -128,57 +139,44 @@ pub fn set_open_files_limit(mut _new_limit: isize) -> bool {
             }).unwrap();
 
             true
-
         } else {
             false
         }
     }
 }
 
-// FIXME: Can be removed once negative trait bounds are supported.
 #[cfg(doctest)]
 mod doctest {
-    /// Check that `Process` doesn't implement `Clone`.
-    ///
-    /// First we check that the "basic" code works:
-    ///
-    /// ```no_run
-    /// use sysinfo::{Process, System};
-    ///
-    /// let mut s = System::new_all();
-    /// let p: &Process = s.processes().values().next().unwrap();
-    /// ```
-    ///
-    /// And now we check if it fails when we try to clone it:
-    ///
-    /// ```compile_fail
-    /// use sysinfo::{Process, System};
-    ///
-    /// let mut s = System::new_all();
-    /// let p: &Process = s.processes().values().next().unwrap();
-    /// let p = (*p).clone();
-    /// ```
-    mod process_clone {}
+    macro_rules! compile_fail_import {
+        ($mod_name:ident => $($imports:ident),+ $(,)?) => {
+            $(#[doc = concat!(r"```compile_fail
+use sysinfo::", stringify!($imports), r";
+```
+")])+
+            mod $mod_name {}
+        };
+    }
 
-    /// Check that `System` doesn't implement `Clone`.
-    ///
-    /// First we check that the "basic" code works:
-    ///
-    /// ```no_run
-    /// use sysinfo::{Process, System};
-    ///
-    /// let s = System::new();
-    /// ```
-    ///
-    /// And now we check if it fails when we try to clone it:
-    ///
-    /// ```compile_fail
-    /// use sysinfo::{Process, System};
-    ///
-    /// let s = System::new();
-    /// let s = s.clone();
-    /// ```
-    mod system_clone {}
+    #[cfg(not(feature = "system"))]
+    compile_fail_import!(
+        no_system_feature =>
+        get_current_pid,
+        CGroupLimits,
+        Cpu,
+        CpuRefreshKind,
+        DiskUsage,
+        LoadAvg,
+        MemoryRefreshKind,
+        Pid,
+        Process,
+        ProcessRefreshKind,
+        ProcessStatus,
+        RefreshKind,
+        Signal,
+        System,
+        ThreadKind,
+        UpdateKind,
+    );
 }
 
 #[cfg(test)]
@@ -195,10 +193,17 @@ mod test {
     #[test]
     fn check_macro_types() {
         fn check_is_supported(_: bool) {}
+
+        check_is_supported(IS_SUPPORTED_SYSTEM);
+    }
+
+    // If this test doesn't compile, it means the current OS doesn't implement them correctly.
+    #[cfg(feature = "system")]
+    #[test]
+    fn check_macro_types2() {
         fn check_supported_signals(_: &'static [Signal]) {}
         fn check_minimum_cpu_update_interval(_: std::time::Duration) {}
 
-        check_is_supported(IS_SUPPORTED_SYSTEM);
         check_supported_signals(SUPPORTED_SIGNALS);
         check_minimum_cpu_update_interval(MINIMUM_CPU_UPDATE_INTERVAL);
     }
@@ -227,19 +232,23 @@ mod test {
                 assert!(user_list.iter().filter(|u| **u.id() > 0).count() > 0);
             }
 
-            // And now check that our `get_user_by_id` method works.
-            let s = System::new_with_specifics(
-                RefreshKind::new()
-                    .with_processes(ProcessRefreshKind::new().with_user(UpdateKind::Always)),
-            );
-            assert!(s
-                .processes()
-                .iter()
-                .filter_map(|(_, p)| p.user_id())
-                .any(|uid| users.get_user_by_id(uid).is_some()));
+            #[cfg(feature = "system")]
+            {
+                // And now check that our `get_user_by_id` method works.
+                let s = System::new_with_specifics(
+                    RefreshKind::new()
+                        .with_processes(ProcessRefreshKind::new().with_user(UpdateKind::Always)),
+                );
+                assert!(s
+                    .processes()
+                    .iter()
+                    .filter_map(|(_, p)| p.user_id())
+                    .any(|uid| users.get_user_by_id(uid).is_some()));
+            }
         }
     }
 
+    #[cfg(feature = "system")]
     #[test]
     fn check_all_process_uids_resolvable() {
         // On linux, some user IDs don't have an associated user (no idea why though).
@@ -273,20 +282,4 @@ mod test {
             assert!(!IS_SUPPORTED_SYSTEM);
         }
     }
-
-    #[test]
-    #[allow(clippy::const_is_empty)]
-    fn check_nb_supported_signals() {
-        if IS_SUPPORTED_SYSTEM {
-            assert!(
-                !SUPPORTED_SIGNALS.is_empty(),
-                "SUPPORTED_SIGNALS shouldn't be empty on supported systems!"
-            );
-        } else {
-            assert!(
-                SUPPORTED_SIGNALS.is_empty(),
-                "SUPPORTED_SIGNALS should be empty on not support systems!"
-            );
-        }
-    }
 }
diff --git a/src/macros.rs b/src/macros.rs
index eba86b2..c2b856a 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -16,6 +16,7 @@ macro_rules! sysinfo_debug {
     ($($x:tt)*) => {{}};
 }
 
+#[cfg(feature = "system")]
 macro_rules! declare_signals {
     ($kind:ty, _ => None,) => (
         use crate::Signal;
diff --git a/src/serde.rs b/src/serde.rs
index c1cbbb7..daef35f 100644
--- a/src/serde.rs
+++ b/src/serde.rs
@@ -42,6 +42,7 @@ impl Serialize for crate::Uid {
     }
 }
 
+#[cfg(feature = "system")]
 impl Serialize for crate::Pid {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
@@ -51,6 +52,7 @@ impl Serialize for crate::Pid {
     }
 }
 
+#[cfg(feature = "system")]
 impl Serialize for crate::Process {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
@@ -82,6 +84,7 @@ impl Serialize for crate::Process {
     }
 }
 
+#[cfg(feature = "system")]
 impl Serialize for crate::Cpu {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
@@ -100,6 +103,7 @@ impl Serialize for crate::Cpu {
     }
 }
 
+#[cfg(feature = "system")]
 impl serde::Serialize for crate::System {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
diff --git a/src/unix/apple/app_store/mod.rs b/src/unix/apple/app_store/mod.rs
index 0df24ca..286e6ec 100644
--- a/src/unix/apple/app_store/mod.rs
+++ b/src/unix/apple/app_store/mod.rs
@@ -1,4 +1,5 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
 pub mod component;
+#[cfg(feature = "system")]
 pub mod process;
diff --git a/src/unix/apple/ios.rs b/src/unix/apple/ios.rs
index 0393c5e..456340e 100644
--- a/src/unix/apple/ios.rs
+++ b/src/unix/apple/ios.rs
@@ -2,4 +2,5 @@
 
 pub mod ffi {}
 pub use crate::sys::app_store::component;
+#[cfg(feature = "system")]
 pub use crate::sys::app_store::process;
diff --git a/src/unix/apple/macos/mod.rs b/src/unix/apple/macos/mod.rs
index 0c82fc8..694bf4f 100644
--- a/src/unix/apple/macos/mod.rs
+++ b/src/unix/apple/macos/mod.rs
@@ -4,20 +4,20 @@ pub mod disk;
 pub mod ffi;
 pub(crate) mod utils;
 
-#[cfg(not(feature = "apple-sandbox"))]
+#[cfg(all(feature = "system", not(feature = "apple-sandbox")))]
 pub(crate) mod cpu;
 
-#[cfg(not(feature = "apple-sandbox"))]
+#[cfg(all(feature = "system", not(feature = "apple-sandbox")))]
 pub mod system;
 
 #[cfg(not(feature = "apple-sandbox"))]
 pub mod component;
 
-#[cfg(not(feature = "apple-sandbox"))]
+#[cfg(all(feature = "system", not(feature = "apple-sandbox")))]
 pub mod process;
 
 #[cfg(feature = "apple-sandbox")]
 pub use crate::sys::app_store::component;
 
-#[cfg(feature = "apple-sandbox")]
+#[cfg(all(feature = "system", feature = "apple-sandbox"))]
 pub use crate::sys::app_store::process;
diff --git a/src/unix/apple/macos/process.rs b/src/unix/apple/macos/process.rs
index 9b06b33..27d1655 100644
--- a/src/unix/apple/macos/process.rs
+++ b/src/unix/apple/macos/process.rs
@@ -110,7 +110,7 @@ impl ProcessInner {
     }
 
     pub(crate) fn kill_with(&self, signal: Signal) -> Option<bool> {
-        let c_signal = crate::sys::convert_signal(signal)?;
+        let c_signal = crate::sys::system::convert_signal(signal)?;
         unsafe { Some(kill(self.pid.0, c_signal) == 0) }
     }
 
diff --git a/src/unix/apple/mod.rs b/src/unix/apple/mod.rs
index 9fae44c..1678237 100644
--- a/src/unix/apple/mod.rs
+++ b/src/unix/apple/mod.rs
@@ -1,89 +1,45 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-#[cfg(target_os = "macos")]
-pub(crate) mod macos;
-
-#[cfg(target_os = "macos")]
-pub(crate) use self::macos as inner;
-
-#[cfg(target_os = "ios")]
-pub(crate) mod ios;
-#[cfg(target_os = "ios")]
-pub(crate) use self::ios as inner;
-
 #[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
 pub(crate) mod app_store;
 
 pub mod component;
-pub mod cpu;
 pub mod disk;
 mod ffi;
 pub mod groups;
 pub mod network;
-pub mod process;
-pub mod system;
 pub mod users;
 mod utils;
 
+cfg_if::cfg_if! {
+    if #[cfg(target_os = "macos")] {
+        pub(crate) mod macos;
+        pub(crate) use self::macos as inner;
+    } else if #[cfg(target_os = "ios")] {
+        pub(crate) mod ios;
+        pub(crate) use self::ios as inner;
+    }
+}
+
+cfg_if::cfg_if! {
+    if #[cfg(feature = "system")] {
+        pub mod cpu;
+        pub mod process;
+        pub mod system;
+
+        pub(crate) use self::cpu::CpuInner;
+        pub(crate) use self::process::ProcessInner;
+        pub(crate) use self::system::SystemInner;
+        pub use self::system::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
+    }
+}
+
 pub(crate) use self::component::{ComponentInner, ComponentsInner};
-pub(crate) use self::cpu::CpuInner;
 pub(crate) use self::disk::DiskInner;
 pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use self::process::ProcessInner;
-pub(crate) use self::system::SystemInner;
 pub(crate) use crate::unix::groups::get_groups;
 pub(crate) use crate::unix::users::{get_users, UserInner};
 pub(crate) use crate::unix::DisksInner;
 
-use std::time::Duration;
-
-#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
-declare_signals! {
-    libc::c_int,
-    Signal::Hangup => libc::SIGHUP,
-    Signal::Interrupt => libc::SIGINT,
-    Signal::Quit => libc::SIGQUIT,
-    Signal::Illegal => libc::SIGILL,
-    Signal::Trap => libc::SIGTRAP,
-    Signal::Abort => libc::SIGABRT,
-    Signal::IOT => libc::SIGIOT,
-    Signal::Bus => libc::SIGBUS,
-    Signal::FloatingPointException => libc::SIGFPE,
-    Signal::Kill => libc::SIGKILL,
-    Signal::User1 => libc::SIGUSR1,
-    Signal::Segv => libc::SIGSEGV,
-    Signal::User2 => libc::SIGUSR2,
-    Signal::Pipe => libc::SIGPIPE,
-    Signal::Alarm => libc::SIGALRM,
-    Signal::Term => libc::SIGTERM,
-    Signal::Child => libc::SIGCHLD,
-    Signal::Continue => libc::SIGCONT,
-    Signal::Stop => libc::SIGSTOP,
-    Signal::TSTP => libc::SIGTSTP,
-    Signal::TTIN => libc::SIGTTIN,
-    Signal::TTOU => libc::SIGTTOU,
-    Signal::Urgent => libc::SIGURG,
-    Signal::XCPU => libc::SIGXCPU,
-    Signal::XFSZ => libc::SIGXFSZ,
-    Signal::VirtualAlarm => libc::SIGVTALRM,
-    Signal::Profiling => libc::SIGPROF,
-    Signal::Winch => libc::SIGWINCH,
-    Signal::IO => libc::SIGIO,
-    // SIGPOLL doesn't exist on apple targets but since it's an equivalent of SIGIO on unix,
-    // we simply use the SIGIO constant.
-    Signal::Poll => libc::SIGIO,
-    Signal::Sys => libc::SIGSYS,
-    _ => None,
-}
-#[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
-declare_signals! {
-    libc::c_int,
-    _ => None,
-}
-
 #[doc = include_str!("../../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = true;
-#[doc = include_str!("../../../md_doc/supported_signals.md")]
-pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
-#[doc = include_str!("../../../md_doc/minimum_cpu_update_interval.md")]
-pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200);
diff --git a/src/unix/apple/system.rs b/src/unix/apple/system.rs
index 3e1f3d3..140d17e 100644
--- a/src/unix/apple/system.rs
+++ b/src/unix/apple/system.rs
@@ -12,6 +12,7 @@ use std::cell::UnsafeCell;
 use std::collections::HashMap;
 use std::ffi::CStr;
 use std::mem;
+use std::time::Duration;
 #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
 use std::time::SystemTime;
 
@@ -20,6 +21,55 @@ use libc::{
     _SC_PAGESIZE,
 };
 
+#[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
+declare_signals! {
+    libc::c_int,
+    Signal::Hangup => libc::SIGHUP,
+    Signal::Interrupt => libc::SIGINT,
+    Signal::Quit => libc::SIGQUIT,
+    Signal::Illegal => libc::SIGILL,
+    Signal::Trap => libc::SIGTRAP,
+    Signal::Abort => libc::SIGABRT,
+    Signal::IOT => libc::SIGIOT,
+    Signal::Bus => libc::SIGBUS,
+    Signal::FloatingPointException => libc::SIGFPE,
+    Signal::Kill => libc::SIGKILL,
+    Signal::User1 => libc::SIGUSR1,
+    Signal::Segv => libc::SIGSEGV,
+    Signal::User2 => libc::SIGUSR2,
+    Signal::Pipe => libc::SIGPIPE,
+    Signal::Alarm => libc::SIGALRM,
+    Signal::Term => libc::SIGTERM,
+    Signal::Child => libc::SIGCHLD,
+    Signal::Continue => libc::SIGCONT,
+    Signal::Stop => libc::SIGSTOP,
+    Signal::TSTP => libc::SIGTSTP,
+    Signal::TTIN => libc::SIGTTIN,
+    Signal::TTOU => libc::SIGTTOU,
+    Signal::Urgent => libc::SIGURG,
+    Signal::XCPU => libc::SIGXCPU,
+    Signal::XFSZ => libc::SIGXFSZ,
+    Signal::VirtualAlarm => libc::SIGVTALRM,
+    Signal::Profiling => libc::SIGPROF,
+    Signal::Winch => libc::SIGWINCH,
+    Signal::IO => libc::SIGIO,
+    // SIGPOLL doesn't exist on apple targets but since it's an equivalent of SIGIO on unix,
+    // we simply use the SIGIO constant.
+    Signal::Poll => libc::SIGIO,
+    Signal::Sys => libc::SIGSYS,
+    _ => None,
+}
+#[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
+declare_signals! {
+    libc::c_int,
+    _ => None,
+}
+
+#[doc = include_str!("../../../md_doc/supported_signals.md")]
+pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
+#[doc = include_str!("../../../md_doc/minimum_cpu_update_interval.md")]
+pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200);
+
 pub(crate) struct SystemInner {
     process_list: HashMap<Pid, Process>,
     mem_total: u64,
diff --git a/src/unix/apple/utils.rs b/src/unix/apple/utils.rs
index c784c0d..72e8499 100644
--- a/src/unix/apple/utils.rs
+++ b/src/unix/apple/utils.rs
@@ -1,7 +1,6 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
 use core_foundation_sys::base::CFRelease;
-use libc::{c_void, sysctl, sysctlbyname};
 use std::ptr::NonNull;
 
 // A helper using to auto release the resource got from CoreFoundation.
@@ -44,8 +43,13 @@ pub(crate) fn vec_to_rust(buf: Vec<i8>) -> Option<String> {
     .ok()
 }
 
-pub(crate) unsafe fn get_sys_value(mut len: usize, value: *mut c_void, mib: &mut [i32]) -> bool {
-    sysctl(
+#[cfg(feature = "system")]
+pub(crate) unsafe fn get_sys_value(
+    mut len: usize,
+    value: *mut libc::c_void,
+    mib: &mut [i32],
+) -> bool {
+    libc::sysctl(
         mib.as_mut_ptr(),
         mib.len() as _,
         value,
@@ -55,12 +59,13 @@ pub(crate) unsafe fn get_sys_value(mut len: usize, value: *mut c_void, mib: &mut
     ) == 0
 }
 
+#[cfg(feature = "system")]
 pub(crate) unsafe fn get_sys_value_by_name(
     name: &[u8],
     len: &mut usize,
-    value: *mut c_void,
+    value: *mut libc::c_void,
 ) -> bool {
-    sysctlbyname(
+    libc::sysctlbyname(
         name.as_ptr() as *const _,
         value,
         len,
diff --git a/src/unix/freebsd/component.rs b/src/unix/freebsd/component.rs
index bd816f8..ffaae25 100644
--- a/src/unix/freebsd/component.rs
+++ b/src/unix/freebsd/component.rs
@@ -56,7 +56,7 @@ pub(crate) struct ComponentsInner {
 
 impl ComponentsInner {
     pub(crate) fn new() -> Self {
-        let nb_cpus = unsafe { super::cpu::get_nb_cpus() };
+        let nb_cpus = unsafe { super::utils::get_nb_cpus() };
         Self {
             nb_cpus,
             components: Vec::with_capacity(nb_cpus),
@@ -65,7 +65,7 @@ impl ComponentsInner {
 
     pub(crate) fn from_vec(components: Vec<Component>) -> Self {
         Self {
-            nb_cpus: unsafe { super::cpu::get_nb_cpus() },
+            nb_cpus: unsafe { super::utils::get_nb_cpus() },
             components,
         }
     }
diff --git a/src/unix/freebsd/cpu.rs b/src/unix/freebsd/cpu.rs
index a8eb5d1..346fb3a 100644
--- a/src/unix/freebsd/cpu.rs
+++ b/src/unix/freebsd/cpu.rs
@@ -1,28 +1,12 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
 use crate::sys::utils::{
-    get_sys_value_array, get_sys_value_by_name, get_sys_value_str_by_name, init_mib, VecSwitcher,
+    get_sys_value_array, get_sys_value_by_name, get_sys_value_str_by_name, init_mib,
 };
 use crate::{Cpu, CpuRefreshKind};
 
 use libc::{c_int, c_ulong};
 
-pub(crate) unsafe fn get_nb_cpus() -> usize {
-    let mut smp: c_int = 0;
-    let mut nb_cpus: c_int = 1;
-
-    if !get_sys_value_by_name(b"kern.smp.active\0", &mut smp) {
-        smp = 0;
-    }
-    #[allow(clippy::collapsible_if)] // I keep as is for readability reasons.
-    if smp != 0 {
-        if !get_sys_value_by_name(b"kern.smp.cpus\0", &mut nb_cpus) || nb_cpus < 1 {
-            nb_cpus = 1;
-        }
-    }
-    nb_cpus as usize
-}
-
 pub(crate) struct CpusWrapper {
     pub(crate) global_cpu_usage: f32,
     pub(crate) cpus: Vec<Cpu>,
@@ -42,7 +26,7 @@ impl CpusWrapper {
         let mut mib_cp_times = [0; 2];
 
         unsafe {
-            let nb_cpus = get_nb_cpus();
+            let nb_cpus = super::utils::get_nb_cpus();
             init_mib(b"kern.cp_time\0", &mut mib_cp_time);
             init_mib(b"kern.cp_times\0", &mut mib_cp_times);
             Self {
@@ -192,3 +176,50 @@ unsafe fn get_frequency_for_cpu(cpu_nb: usize) -> u64 {
     }
     frequency as _
 }
+
+/// This struct is used to switch between the "old" and "new" every time you use "get_mut".
+#[derive(Debug)]
+pub(crate) struct VecSwitcher<T> {
+    v1: Vec<T>,
+    v2: Vec<T>,
+    first: bool,
+}
+
+impl<T: Clone> VecSwitcher<T> {
+    pub fn new(v1: Vec<T>) -> Self {
+        let v2 = v1.clone();
+
+        Self {
+            v1,
+            v2,
+            first: true,
+        }
+    }
+
+    pub fn get_mut(&mut self) -> &mut [T] {
+        self.first = !self.first;
+        if self.first {
+            // It means that `v2` will be the "new".
+            &mut self.v2
+        } else {
+            // It means that `v1` will be the "new".
+            &mut self.v1
+        }
+    }
+
+    pub fn get_old(&self) -> &[T] {
+        if self.first {
+            &self.v1
+        } else {
+            &self.v2
+        }
+    }
+
+    pub fn get_new(&self) -> &[T] {
+        if self.first {
+            &self.v2
+        } else {
+            &self.v1
+        }
+    }
+}
diff --git a/src/unix/freebsd/mod.rs b/src/unix/freebsd/mod.rs
index 9386071..8ab692f 100644
--- a/src/unix/freebsd/mod.rs
+++ b/src/unix/freebsd/mod.rs
@@ -1,64 +1,29 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
 pub mod component;
-pub mod cpu;
 pub mod disk;
 pub mod network;
-pub mod process;
-pub mod system;
 mod utils;
 
+cfg_if::cfg_if! {
+    if #[cfg(feature = "system")] {
+        pub mod cpu;
+        pub mod process;
+        pub mod system;
+
+        pub(crate) use self::cpu::CpuInner;
+        pub(crate) use self::process::ProcessInner;
+        pub(crate) use self::system::SystemInner;
+        pub use self::system::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
+    }
+}
+
 pub(crate) use self::component::{ComponentInner, ComponentsInner};
-pub(crate) use self::cpu::CpuInner;
 pub(crate) use self::disk::DiskInner;
 pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use self::process::ProcessInner;
-pub(crate) use self::system::SystemInner;
 pub(crate) use crate::unix::groups::get_groups;
 pub(crate) use crate::unix::users::{get_users, UserInner};
 pub(crate) use crate::unix::DisksInner;
 
-use libc::c_int;
-use std::time::Duration;
-
-declare_signals! {
-    c_int,
-    Signal::Hangup => libc::SIGHUP,
-    Signal::Interrupt => libc::SIGINT,
-    Signal::Quit => libc::SIGQUIT,
-    Signal::Illegal => libc::SIGILL,
-    Signal::Trap => libc::SIGTRAP,
-    Signal::Abort => libc::SIGABRT,
-    Signal::IOT => libc::SIGIOT,
-    Signal::Bus => libc::SIGBUS,
-    Signal::FloatingPointException => libc::SIGFPE,
-    Signal::Kill => libc::SIGKILL,
-    Signal::User1 => libc::SIGUSR1,
-    Signal::Segv => libc::SIGSEGV,
-    Signal::User2 => libc::SIGUSR2,
-    Signal::Pipe => libc::SIGPIPE,
-    Signal::Alarm => libc::SIGALRM,
-    Signal::Term => libc::SIGTERM,
-    Signal::Child => libc::SIGCHLD,
-    Signal::Continue => libc::SIGCONT,
-    Signal::Stop => libc::SIGSTOP,
-    Signal::TSTP => libc::SIGTSTP,
-    Signal::TTIN => libc::SIGTTIN,
-    Signal::TTOU => libc::SIGTTOU,
-    Signal::Urgent => libc::SIGURG,
-    Signal::XCPU => libc::SIGXCPU,
-    Signal::XFSZ => libc::SIGXFSZ,
-    Signal::VirtualAlarm => libc::SIGVTALRM,
-    Signal::Profiling => libc::SIGPROF,
-    Signal::Winch => libc::SIGWINCH,
-    Signal::IO => libc::SIGIO,
-    Signal::Sys => libc::SIGSYS,
-    _ => None,
-}
-
 #[doc = include_str!("../../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = true;
-#[doc = include_str!("../../../md_doc/supported_signals.md")]
-pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
-#[doc = include_str!("../../../md_doc/minimum_cpu_update_interval.md")]
-pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(100);
diff --git a/src/unix/freebsd/process.rs b/src/unix/freebsd/process.rs
index 9f213ca..3abc348 100644
--- a/src/unix/freebsd/process.rs
+++ b/src/unix/freebsd/process.rs
@@ -69,7 +69,7 @@ pub(crate) struct ProcessInner {
 
 impl ProcessInner {
     pub(crate) fn kill_with(&self, signal: Signal) -> Option<bool> {
-        let c_signal = crate::sys::convert_signal(signal)?;
+        let c_signal = crate::sys::system::convert_signal(signal)?;
         unsafe { Some(libc::kill(self.pid.0, c_signal) == 0) }
     }
 
diff --git a/src/unix/freebsd/system.rs b/src/unix/freebsd/system.rs
index 8c439a6..09e4d61 100644
--- a/src/unix/freebsd/system.rs
+++ b/src/unix/freebsd/system.rs
@@ -10,16 +10,57 @@ use std::ffi::CStr;
 use std::mem::MaybeUninit;
 use std::path::{Path, PathBuf};
 use std::ptr::NonNull;
+use std::time::{Duration, SystemTime};
 
 use crate::sys::cpu::{physical_core_count, CpusWrapper};
 use crate::sys::process::get_exe;
 use crate::sys::utils::{
-    self, boot_time, c_buf_to_os_string, from_cstr_array, get_sys_value, get_sys_value_by_name,
-    get_system_info, init_mib,
+    self, boot_time, c_buf_to_os_string, c_buf_to_utf8_string, from_cstr_array, get_sys_value,
+    get_sys_value_by_name, init_mib,
 };
 
 use libc::c_int;
 
+declare_signals! {
+    c_int,
+    Signal::Hangup => libc::SIGHUP,
+    Signal::Interrupt => libc::SIGINT,
+    Signal::Quit => libc::SIGQUIT,
+    Signal::Illegal => libc::SIGILL,
+    Signal::Trap => libc::SIGTRAP,
+    Signal::Abort => libc::SIGABRT,
+    Signal::IOT => libc::SIGIOT,
+    Signal::Bus => libc::SIGBUS,
+    Signal::FloatingPointException => libc::SIGFPE,
+    Signal::Kill => libc::SIGKILL,
+    Signal::User1 => libc::SIGUSR1,
+    Signal::Segv => libc::SIGSEGV,
+    Signal::User2 => libc::SIGUSR2,
+    Signal::Pipe => libc::SIGPIPE,
+    Signal::Alarm => libc::SIGALRM,
+    Signal::Term => libc::SIGTERM,
+    Signal::Child => libc::SIGCHLD,
+    Signal::Continue => libc::SIGCONT,
+    Signal::Stop => libc::SIGSTOP,
+    Signal::TSTP => libc::SIGTSTP,
+    Signal::TTIN => libc::SIGTTIN,
+    Signal::TTOU => libc::SIGTTOU,
+    Signal::Urgent => libc::SIGURG,
+    Signal::XCPU => libc::SIGXCPU,
+    Signal::XFSZ => libc::SIGXFSZ,
+    Signal::VirtualAlarm => libc::SIGVTALRM,
+    Signal::Profiling => libc::SIGPROF,
+    Signal::Winch => libc::SIGWINCH,
+    Signal::IO => libc::SIGIO,
+    Signal::Sys => libc::SIGSYS,
+    _ => None,
+}
+
+#[doc = include_str!("../../../md_doc/supported_signals.md")]
+pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
+#[doc = include_str!("../../../md_doc/minimum_cpu_update_interval.md")]
+pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(100);
+
 pub(crate) struct SystemInner {
     process_list: HashMap<Pid, Process>,
     mem_total: u64,
@@ -94,7 +135,7 @@ impl SystemInner {
                 sysinfo_debug!("kvm_getprocs returned nothing...");
                 return false;
             }
-            let now = super::utils::get_now();
+            let now = get_now();
 
             let fscale = self.system_info.fscale;
             let page_size = self.system_info.page_size as isize;
@@ -329,7 +370,7 @@ impl SystemInner {
 
             let fscale = self.system_info.fscale;
             let page_size = self.system_info.page_size as isize;
-            let now = super::utils::get_now();
+            let now = get_now();
             let proc_list = utils::WrapMap(UnsafeCell::new(&mut self.process_list));
 
             IterTrait::filter_map(crate::utils::into_iter(kvm_procs), |kproc| {
@@ -680,3 +721,49 @@ impl Drop for SystemInfo {
         }
     }
 }
+
+fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option<String> {
+    let mut size = 0;
+
+    unsafe {
+        // Call first to get size
+        libc::sysctl(
+            mib.as_ptr(),
+            mib.len() as _,
+            std::ptr::null_mut(),
+            &mut size,
+            std::ptr::null_mut(),
+            0,
+        );
+
+        // exit early if we did not update the size
+        if size == 0 {
+            default.map(|s| s.to_owned())
+        } else {
+            // set the buffer to the correct size
+            let mut buf: Vec<libc::c_char> = vec![0; size as _];
+
+            if libc::sysctl(
+                mib.as_ptr(),
+                mib.len() as _,
+                buf.as_mut_ptr() as _,
+                &mut size,
+                std::ptr::null_mut(),
+                0,
+            ) == -1
+            {
+                // If command fails return default
+                default.map(|s| s.to_owned())
+            } else {
+                c_buf_to_utf8_string(&buf)
+            }
+        }
+    }
+}
+
+fn get_now() -> u64 {
+    SystemTime::now()
+        .duration_since(SystemTime::UNIX_EPOCH)
+        .map(|n| n.as_secs())
+        .unwrap_or(0)
+}
diff --git a/src/unix/freebsd/utils.rs b/src/unix/freebsd/utils.rs
index 04cf8d2..a6170d8 100644
--- a/src/unix/freebsd/utils.rs
+++ b/src/unix/freebsd/utils.rs
@@ -1,79 +1,32 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use crate::{Pid, Process};
-use libc::{c_char, c_int, timeval};
-use std::cell::UnsafeCell;
-use std::collections::HashMap;
+use libc::{c_char, c_int};
+#[cfg(feature = "system")]
 use std::ffi::{CStr, OsStr, OsString};
 use std::mem;
+#[cfg(feature = "system")]
 use std::os::unix::ffi::OsStrExt;
-use std::time::SystemTime;
-
-/// This struct is used to switch between the "old" and "new" every time you use "get_mut".
-#[derive(Debug)]
-pub(crate) struct VecSwitcher<T> {
-    v1: Vec<T>,
-    v2: Vec<T>,
-    first: bool,
-}
-
-impl<T: Clone> VecSwitcher<T> {
-    pub fn new(v1: Vec<T>) -> Self {
-        let v2 = v1.clone();
-
-        Self {
-            v1,
-            v2,
-            first: true,
-        }
-    }
-
-    pub fn get_mut(&mut self) -> &mut [T] {
-        self.first = !self.first;
-        if self.first {
-            // It means that `v2` will be the "new".
-            &mut self.v2
-        } else {
-            // It means that `v1` will be the "new".
-            &mut self.v1
-        }
-    }
-
-    pub fn get_old(&self) -> &[T] {
-        if self.first {
-            &self.v1
-        } else {
-            &self.v2
-        }
-    }
-
-    pub fn get_new(&self) -> &[T] {
-        if self.first {
-            &self.v2
-        } else {
-            &self.v1
-        }
-    }
-}
 
+#[cfg(feature = "system")]
 #[inline]
 pub unsafe fn init_mib(name: &[u8], mib: &mut [c_int]) {
     let mut len = mib.len();
     libc::sysctlnametomib(name.as_ptr() as _, mib.as_mut_ptr(), &mut len);
 }
 
+#[cfg(feature = "system")]
 pub(crate) fn boot_time() -> u64 {
-    let mut boot_time = timeval {
+    let mut boot_time = libc::timeval {
         tv_sec: 0,
         tv_usec: 0,
     };
-    let mut len = std::mem::size_of::<timeval>();
+    let mut len = std::mem::size_of::<libc::timeval>();
     let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME];
     unsafe {
         if libc::sysctl(
             mib.as_mut_ptr(),
             mib.len() as _,
-            &mut boot_time as *mut timeval as *mut _,
+            &mut boot_time as *mut libc::timeval as *mut _,
             &mut len,
             std::ptr::null_mut(),
             0,
@@ -98,6 +51,7 @@ pub(crate) unsafe fn get_sys_value<T: Sized>(mib: &[c_int], value: &mut T) -> bo
     ) == 0
 }
 
+#[cfg(feature = "system")]
 pub(crate) unsafe fn get_sys_value_array<T: Sized>(mib: &[c_int], value: &mut [T]) -> bool {
     let mut len = mem::size_of_val(value) as libc::size_t;
     libc::sysctl(
@@ -127,6 +81,7 @@ pub(crate) fn c_buf_to_utf8_string(buf: &[libc::c_char]) -> Option<String> {
     c_buf_to_utf8_str(buf).map(|s| s.to_owned())
 }
 
+#[cfg(feature = "system")]
 pub(crate) fn c_buf_to_os_str(buf: &[libc::c_char]) -> &OsStr {
     unsafe {
         let buf: &[u8] = std::slice::from_raw_parts(buf.as_ptr() as _, buf.len());
@@ -139,10 +94,12 @@ pub(crate) fn c_buf_to_os_str(buf: &[libc::c_char]) -> &OsStr {
     }
 }
 
+#[cfg(feature = "system")]
 pub(crate) fn c_buf_to_os_string(buf: &[libc::c_char]) -> OsString {
     c_buf_to_os_str(buf).to_owned()
 }
 
+#[cfg(feature = "system")]
 pub(crate) unsafe fn get_sys_value_str(
     mib: &[c_int],
     buf: &mut [libc::c_char],
@@ -178,6 +135,7 @@ pub(crate) unsafe fn get_sys_value_by_name<T: Sized>(name: &[u8], value: &mut T)
         && original == len
 }
 
+#[cfg(feature = "system")]
 pub(crate) fn get_sys_value_str_by_name(name: &[u8]) -> Option<String> {
     let mut size = 0;
 
@@ -214,45 +172,7 @@ pub(crate) fn get_sys_value_str_by_name(name: &[u8]) -> Option<String> {
     }
 }
 
-pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option<String> {
-    let mut size = 0;
-
-    unsafe {
-        // Call first to get size
-        libc::sysctl(
-            mib.as_ptr(),
-            mib.len() as _,
-            std::ptr::null_mut(),
-            &mut size,
-            std::ptr::null_mut(),
-            0,
-        );
-
-        // exit early if we did not update the size
-        if size == 0 {
-            default.map(|s| s.to_owned())
-        } else {
-            // set the buffer to the correct size
-            let mut buf: Vec<libc::c_char> = vec![0; size as _];
-
-            if libc::sysctl(
-                mib.as_ptr(),
-                mib.len() as _,
-                buf.as_mut_ptr() as _,
-                &mut size,
-                std::ptr::null_mut(),
-                0,
-            ) == -1
-            {
-                // If command fails return default
-                default.map(|s| s.to_owned())
-            } else {
-                c_buf_to_utf8_string(&buf)
-            }
-        }
-    }
-}
-
+#[cfg(feature = "system")]
 pub(crate) unsafe fn from_cstr_array(ptr: *const *const c_char) -> Vec<OsString> {
     if ptr.is_null() {
         return Vec::new();
@@ -277,24 +197,43 @@ pub(crate) unsafe fn from_cstr_array(ptr: *const *const c_char) -> Vec<OsString>
     ret
 }
 
-pub(crate) fn get_now() -> u64 {
-    SystemTime::now()
-        .duration_since(SystemTime::UNIX_EPOCH)
-        .map(|n| n.as_secs())
-        .unwrap_or(0)
+pub(crate) unsafe fn get_nb_cpus() -> usize {
+    let mut smp: c_int = 0;
+    let mut nb_cpus: c_int = 1;
+
+    if !get_sys_value_by_name(b"kern.smp.active\0", &mut smp) {
+        smp = 0;
+    }
+    #[allow(clippy::collapsible_if)] // I keep as is for readability reasons.
+    if smp != 0 {
+        if !get_sys_value_by_name(b"kern.smp.cpus\0", &mut nb_cpus) || nb_cpus < 1 {
+            nb_cpus = 1;
+        }
+    }
+    nb_cpus as usize
 }
 
 // All this is needed because `kinfo_proc` doesn't implement `Send` (because it contains pointers).
-pub(crate) struct WrapMap<'a>(pub UnsafeCell<&'a mut HashMap<Pid, Process>>);
+#[cfg(feature = "system")]
+pub(crate) struct WrapMap<'a>(
+    pub std::cell::UnsafeCell<&'a mut std::collections::HashMap<crate::Pid, crate::Process>>,
+);
 
+#[cfg(feature = "system")]
 unsafe impl<'a> Send for WrapMap<'a> {}
+#[cfg(feature = "system")]
 unsafe impl<'a> Sync for WrapMap<'a> {}
 
+#[cfg(feature = "system")]
 #[repr(transparent)]
 pub(crate) struct KInfoProc(libc::kinfo_proc);
+
+#[cfg(feature = "system")]
 unsafe impl Send for KInfoProc {}
+#[cfg(feature = "system")]
 unsafe impl Sync for KInfoProc {}
 
+#[cfg(feature = "system")]
 impl std::ops::Deref for KInfoProc {
     type Target = libc::kinfo_proc;
 
diff --git a/src/unix/linux/mod.rs b/src/unix/linux/mod.rs
index 375264b..6eabccc 100644
--- a/src/unix/linux/mod.rs
+++ b/src/unix/linux/mod.rs
@@ -1,64 +1,29 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
 pub mod component;
-pub mod cpu;
 pub mod disk;
 pub mod network;
-pub mod process;
-pub mod system;
 pub(crate) mod utils;
 
+cfg_if::cfg_if! {
+    if #[cfg(feature = "system")] {
+        pub mod cpu;
+        pub mod process;
+        pub mod system;
+
+        pub(crate) use self::cpu::CpuInner;
+        pub(crate) use self::process::ProcessInner;
+        pub(crate) use self::system::SystemInner;
+        pub use self::system::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
+    }
+}
+
 pub(crate) use self::component::{ComponentInner, ComponentsInner};
-pub(crate) use self::cpu::CpuInner;
 pub(crate) use self::disk::DiskInner;
 pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use self::process::ProcessInner;
-pub(crate) use self::system::SystemInner;
 pub(crate) use crate::unix::groups::get_groups;
 pub(crate) use crate::unix::users::{get_users, UserInner};
 pub(crate) use crate::unix::DisksInner;
 
-use std::time::Duration;
-
-declare_signals! {
-    libc::c_int,
-    Signal::Hangup => libc::SIGHUP,
-    Signal::Interrupt => libc::SIGINT,
-    Signal::Quit => libc::SIGQUIT,
-    Signal::Illegal => libc::SIGILL,
-    Signal::Trap => libc::SIGTRAP,
-    Signal::Abort => libc::SIGABRT,
-    Signal::IOT => libc::SIGIOT,
-    Signal::Bus => libc::SIGBUS,
-    Signal::FloatingPointException => libc::SIGFPE,
-    Signal::Kill => libc::SIGKILL,
-    Signal::User1 => libc::SIGUSR1,
-    Signal::Segv => libc::SIGSEGV,
-    Signal::User2 => libc::SIGUSR2,
-    Signal::Pipe => libc::SIGPIPE,
-    Signal::Alarm => libc::SIGALRM,
-    Signal::Term => libc::SIGTERM,
-    Signal::Child => libc::SIGCHLD,
-    Signal::Continue => libc::SIGCONT,
-    Signal::Stop => libc::SIGSTOP,
-    Signal::TSTP => libc::SIGTSTP,
-    Signal::TTIN => libc::SIGTTIN,
-    Signal::TTOU => libc::SIGTTOU,
-    Signal::Urgent => libc::SIGURG,
-    Signal::XCPU => libc::SIGXCPU,
-    Signal::XFSZ => libc::SIGXFSZ,
-    Signal::VirtualAlarm => libc::SIGVTALRM,
-    Signal::Profiling => libc::SIGPROF,
-    Signal::Winch => libc::SIGWINCH,
-    Signal::IO => libc::SIGIO,
-    Signal::Poll => libc::SIGPOLL,
-    Signal::Power => libc::SIGPWR,
-    Signal::Sys => libc::SIGSYS,
-}
-
 #[doc = include_str!("../../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = true;
-#[doc = include_str!("../../../md_doc/supported_signals.md")]
-pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
-#[doc = include_str!("../../../md_doc/minimum_cpu_update_interval.md")]
-pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200);
diff --git a/src/unix/linux/process.rs b/src/unix/linux/process.rs
index bd6c80c..1f88f03 100644
--- a/src/unix/linux/process.rs
+++ b/src/unix/linux/process.rs
@@ -9,18 +9,21 @@ use std::io::Read;
 use std::os::unix::ffi::OsStrExt;
 use std::path::{Path, PathBuf};
 use std::str::{self, FromStr};
+use std::sync::atomic::Ordering;
 
 use bstr::ByteSlice;
 use libc::{c_ulong, gid_t, kill, uid_t};
 
 use crate::sys::system::SystemInfo;
 use crate::sys::utils::{
-    get_all_data_from_file, get_all_utf8_data, realpath, FileCounter, PathHandler, PathPush,
+    get_all_data_from_file, get_all_utf8_data, realpath, PathHandler, PathPush,
 };
 use crate::{
     DiskUsage, Gid, Pid, Process, ProcessRefreshKind, ProcessStatus, Signal, ThreadKind, Uid,
 };
 
+use crate::sys::system::remaining_files;
+
 #[doc(hidden)]
 impl From<char> for ProcessStatus {
     fn from(status: char) -> ProcessStatus {
@@ -115,7 +118,7 @@ pub(crate) struct ProcessInner {
     effective_group_id: Option<Gid>,
     pub(crate) status: ProcessStatus,
     pub(crate) tasks: Option<HashSet<Pid>>,
-    pub(crate) stat_file: Option<FileCounter>,
+    stat_file: Option<FileCounter>,
     old_read_bytes: u64,
     old_written_bytes: u64,
     read_bytes: u64,
@@ -163,7 +166,7 @@ impl ProcessInner {
     }
 
     pub(crate) fn kill_with(&self, signal: Signal) -> Option<bool> {
-        let c_signal = crate::sys::convert_signal(signal)?;
+        let c_signal = crate::sys::system::convert_signal(signal)?;
         unsafe { Some(kill(self.pid.0, c_signal) == 0) }
     }
 
@@ -892,3 +895,41 @@ fn parse_stat_file(data: &[u8]) -> Option<Parts<'_>> {
         short_exe: short_exe.strip_prefix(&[b'(']).unwrap_or(short_exe),
     })
 }
+
+/// Type used to correctly handle the `REMAINING_FILES` global.
+struct FileCounter(File);
+
+impl FileCounter {
+    fn new(f: File) -> Option<Self> {
+        let any_remaining =
+            remaining_files().fetch_update(Ordering::SeqCst, Ordering::SeqCst, |remaining| {
+                if remaining > 0 {
+                    Some(remaining - 1)
+                } else {
+                    // All file descriptors we were allowed are being used.
+                    None
+                }
+            });
+
+        any_remaining.ok().map(|_| Self(f))
+    }
+}
+
+impl std::ops::Deref for FileCounter {
+    type Target = File;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+impl std::ops::DerefMut for FileCounter {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+impl Drop for FileCounter {
+    fn drop(&mut self) {
+        remaining_files().fetch_add(1, Ordering::Relaxed);
+    }
+}
diff --git a/src/unix/linux/system.rs b/src/unix/linux/system.rs
index 5109401..afab18e 100644
--- a/src/unix/linux/system.rs
+++ b/src/unix/linux/system.rs
@@ -6,6 +6,7 @@ use crate::sys::utils::{get_all_utf8_data, to_u64};
 use crate::{Cpu, CpuRefreshKind, LoadAvg, MemoryRefreshKind, Pid, Process, ProcessRefreshKind};
 
 use libc::{self, c_char, sysconf, _SC_CLK_TCK, _SC_HOST_NAME_MAX, _SC_PAGESIZE};
+
 use std::cmp::min;
 use std::collections::HashMap;
 use std::ffi::CStr;
@@ -14,6 +15,7 @@ use std::io::Read;
 use std::path::Path;
 use std::str::FromStr;
 use std::sync::{atomic::AtomicIsize, OnceLock};
+use std::time::Duration;
 
 // This whole thing is to prevent having too many files open at once. It could be problematic
 // for processes using a lot of files and using sysinfo at the same time.
@@ -43,6 +45,47 @@ pub(crate) fn remaining_files() -> &'static AtomicIsize {
     })
 }
 
+declare_signals! {
+    libc::c_int,
+    Signal::Hangup => libc::SIGHUP,
+    Signal::Interrupt => libc::SIGINT,
+    Signal::Quit => libc::SIGQUIT,
+    Signal::Illegal => libc::SIGILL,
+    Signal::Trap => libc::SIGTRAP,
+    Signal::Abort => libc::SIGABRT,
+    Signal::IOT => libc::SIGIOT,
+    Signal::Bus => libc::SIGBUS,
+    Signal::FloatingPointException => libc::SIGFPE,
+    Signal::Kill => libc::SIGKILL,
+    Signal::User1 => libc::SIGUSR1,
+    Signal::Segv => libc::SIGSEGV,
+    Signal::User2 => libc::SIGUSR2,
+    Signal::Pipe => libc::SIGPIPE,
+    Signal::Alarm => libc::SIGALRM,
+    Signal::Term => libc::SIGTERM,
+    Signal::Child => libc::SIGCHLD,
+    Signal::Continue => libc::SIGCONT,
+    Signal::Stop => libc::SIGSTOP,
+    Signal::TSTP => libc::SIGTSTP,
+    Signal::TTIN => libc::SIGTTIN,
+    Signal::TTOU => libc::SIGTTOU,
+    Signal::Urgent => libc::SIGURG,
+    Signal::XCPU => libc::SIGXCPU,
+    Signal::XFSZ => libc::SIGXFSZ,
+    Signal::VirtualAlarm => libc::SIGVTALRM,
+    Signal::Profiling => libc::SIGPROF,
+    Signal::Winch => libc::SIGWINCH,
+    Signal::IO => libc::SIGIO,
+    Signal::Poll => libc::SIGPOLL,
+    Signal::Power => libc::SIGPWR,
+    Signal::Sys => libc::SIGSYS,
+}
+
+#[doc = include_str!("../../../md_doc/supported_signals.md")]
+pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
+#[doc = include_str!("../../../md_doc/minimum_cpu_update_interval.md")]
+pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200);
+
 pub(crate) fn get_max_nb_fds() -> isize {
     unsafe {
         let mut limits = libc::rlimit {
diff --git a/src/unix/linux/utils.rs b/src/unix/linux/utils.rs
index 9c3d076..7b30e25 100644
--- a/src/unix/linux/utils.rs
+++ b/src/unix/linux/utils.rs
@@ -2,11 +2,9 @@
 
 use std::fs::File;
 use std::io::{self, Read, Seek};
-use std::path::{Path, PathBuf};
-use std::sync::atomic::Ordering;
-
-use crate::sys::system::remaining_files;
+use std::path::Path;
 
+#[cfg(feature = "system")]
 pub(crate) fn get_all_data_from_file(file: &mut File, size: usize) -> io::Result<Vec<u8>> {
     let mut buf = Vec::with_capacity(size);
     file.rewind()?;
@@ -26,6 +24,7 @@ pub(crate) fn get_all_utf8_data<P: AsRef<Path>>(file_path: P, size: usize) -> io
     get_all_utf8_data_from_file(&mut file, size)
 }
 
+#[cfg(feature = "system")]
 #[allow(clippy::useless_conversion)]
 pub(crate) fn realpath(path: &Path) -> Option<std::path::PathBuf> {
     match std::fs::read_link(path) {
@@ -37,48 +36,12 @@ pub(crate) fn realpath(path: &Path) -> Option<std::path::PathBuf> {
     }
 }
 
-/// Type used to correctly handle the `REMAINING_FILES` global.
-pub(crate) struct FileCounter(File);
-
-impl FileCounter {
-    pub(crate) fn new(f: File) -> Option<Self> {
-        let any_remaining =
-            remaining_files().fetch_update(Ordering::SeqCst, Ordering::SeqCst, |remaining| {
-                if remaining > 0 {
-                    Some(remaining - 1)
-                } else {
-                    // All file descriptors we were allowed are being used.
-                    None
-                }
-            });
-
-        any_remaining.ok().map(|_| Self(f))
-    }
-}
-
-impl std::ops::Deref for FileCounter {
-    type Target = File;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-impl std::ops::DerefMut for FileCounter {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
-    }
-}
-
-impl Drop for FileCounter {
-    fn drop(&mut self) {
-        remaining_files().fetch_add(1, Ordering::Relaxed);
-    }
-}
-
 /// This type is used in `retrieve_all_new_process_info` because we have a "parent" path and
 /// from it, we `pop`/`join` every time because it's more memory efficient than using `Path::join`.
-pub(crate) struct PathHandler(PathBuf);
+#[cfg(feature = "system")]
+pub(crate) struct PathHandler(std::path::PathBuf);
 
+#[cfg(feature = "system")]
 impl PathHandler {
     pub(crate) fn new(path: &Path) -> Self {
         // `path` is the "parent" for all paths which will follow so we add a fake element at
@@ -87,10 +50,12 @@ impl PathHandler {
     }
 }
 
+#[cfg(feature = "system")]
 pub(crate) trait PathPush {
     fn join(&mut self, p: &str) -> &Path;
 }
 
+#[cfg(feature = "system")]
 impl PathPush for PathHandler {
     fn join(&mut self, p: &str) -> &Path {
         self.0.pop();
@@ -100,13 +65,15 @@ impl PathPush for PathHandler {
 }
 
 // This implementation allows to skip one allocation that is done in `PathHandler`.
-impl PathPush for PathBuf {
+#[cfg(feature = "system")]
+impl PathPush for std::path::PathBuf {
     fn join(&mut self, p: &str) -> &Path {
         self.push(p);
         self.as_path()
     }
 }
 
+#[cfg(feature = "system")]
 pub(crate) fn to_u64(v: &[u8]) -> u64 {
     let mut x = 0;
 
diff --git a/src/unknown/mod.rs b/src/unknown/mod.rs
index b62c841..f8fe1f8 100644
--- a/src/unknown/mod.rs
+++ b/src/unknown/mod.rs
@@ -1,33 +1,29 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
 pub mod component;
-pub mod cpu;
 pub mod disk;
 pub mod groups;
 pub mod network;
-pub mod process;
-pub mod system;
 pub mod users;
 
+cfg_if::cfg_if! {
+    if #[cfg(feature = "system")] {
+        pub mod cpu;
+        pub mod process;
+        pub mod system;
+
+        pub(crate) use self::cpu::CpuInner;
+        pub(crate) use self::process::ProcessInner;
+        pub(crate) use self::system::SystemInner;
+        pub use self::system::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
+    }
+}
+
 pub(crate) use self::component::{ComponentInner, ComponentsInner};
-pub(crate) use self::cpu::CpuInner;
 pub(crate) use self::disk::{DiskInner, DisksInner};
 pub(crate) use self::groups::get_groups;
 pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use self::process::ProcessInner;
-pub(crate) use self::system::SystemInner;
 pub(crate) use self::users::{get_users, UserInner};
 
-use std::time::Duration;
-
-declare_signals! {
-    (),
-    _ => None,
-}
-
 #[doc = include_str!("../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = false;
-#[doc = include_str!("../../md_doc/supported_signals.md")]
-pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
-#[doc = include_str!("../../md_doc/minimum_cpu_update_interval.md")]
-pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(0);
diff --git a/src/unknown/system.rs b/src/unknown/system.rs
index 8c92395..4528972 100644
--- a/src/unknown/system.rs
+++ b/src/unknown/system.rs
@@ -3,6 +3,17 @@
 use crate::{Cpu, CpuRefreshKind, LoadAvg, MemoryRefreshKind, Pid, Process, ProcessRefreshKind};
 
 use std::collections::HashMap;
+use std::time::Duration;
+
+declare_signals! {
+    (),
+    _ => None,
+}
+
+#[doc = include_str!("../../md_doc/supported_signals.md")]
+pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
+#[doc = include_str!("../../md_doc/minimum_cpu_update_interval.md")]
+pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(0);
 
 pub(crate) struct SystemInner {
     processes_list: HashMap<Pid, Process>,
diff --git a/src/windows/cpu.rs b/src/windows/cpu.rs
index 751d92a..1507d26 100644
--- a/src/windows/cpu.rs
+++ b/src/windows/cpu.rs
@@ -1,6 +1,5 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use crate::sys::tools::KeyHandler;
 use crate::{Cpu, CpuRefreshKind, LoadAvg};
 
 use std::collections::HashMap;
@@ -606,3 +605,13 @@ fn init_cpus(refresh_kind: CpuRefreshKind) -> Vec<Cpu> {
         ret
     }
 }
+
+pub(crate) struct KeyHandler {
+    pub unique_id: String,
+}
+
+impl KeyHandler {
+    pub fn new(unique_id: String) -> Self {
+        Self { unique_id }
+    }
+}
diff --git a/src/windows/mod.rs b/src/windows/mod.rs
index b544695..aba1736 100644
--- a/src/windows/mod.rs
+++ b/src/windows/mod.rs
@@ -1,40 +1,35 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
 mod component;
-mod cpu;
 mod disk;
 mod groups;
 mod network;
 pub(crate) mod network_helper;
-mod process;
+
+cfg_if::cfg_if! {
+    if #[cfg(feature = "system")] {
+        mod process;
+        mod cpu;
+        mod system;
+
+        pub(crate) use self::cpu::CpuInner;
+        pub(crate) use self::process::ProcessInner;
+        pub(crate) use self::system::SystemInner;
+        pub use self::system::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
+    }
+}
+
 mod sid;
-mod system;
-mod tools;
 mod users;
 mod utils;
 
 pub(crate) use self::component::{ComponentInner, ComponentsInner};
-pub(crate) use self::cpu::CpuInner;
 pub(crate) use self::disk::{DiskInner, DisksInner};
 pub(crate) use self::groups::get_groups;
 pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use self::process::ProcessInner;
-pub use self::sid::Sid;
-pub(crate) use self::system::SystemInner;
+pub(crate) use self::sid::Sid;
 pub(crate) use self::users::get_users;
 pub(crate) use self::users::UserInner;
 
-use std::time::Duration;
-
-declare_signals! {
-    (),
-    Signal::Kill => (),
-    _ => None,
-}
-
 #[doc = include_str!("../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = true;
-#[doc = include_str!("../../md_doc/supported_signals.md")]
-pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
-#[doc = include_str!("../../md_doc/minimum_cpu_update_interval.md")]
-pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200);
diff --git a/src/windows/process.rs b/src/windows/process.rs
index 68ee6f4..fe0ff97 100644
--- a/src/windows/process.rs
+++ b/src/windows/process.rs
@@ -440,7 +440,7 @@ impl ProcessInner {
     }
 
     pub(crate) fn kill_with(&self, signal: Signal) -> Option<bool> {
-        crate::sys::convert_signal(signal)?;
+        crate::sys::system::convert_signal(signal)?;
         let mut kill = process::Command::new("taskkill.exe");
         kill.arg("/PID").arg(self.pid.to_string()).arg("/F");
         kill.creation_flags(CREATE_NO_WINDOW.0);
@@ -573,7 +573,7 @@ unsafe fn get_process_times(handle: HANDLE) -> u64 {
         &mut x as *mut FILETIME,
         &mut x as *mut FILETIME,
     );
-    super::utils::filetime_to_u64(fstart)
+    filetime_to_u64(fstart)
 }
 
 // On Windows, the root folder is always the current drive. So we get it from its `cwd`.
@@ -1139,5 +1139,5 @@ pub(crate) fn update_memory(p: &mut ProcessInner) {
 
 #[inline(always)]
 const fn filetime_to_u64(ft: FILETIME) -> u64 {
-    ((ft.dwHighDateTime as u64) << 32) + ft.dwLowDateTime as u64
+    (ft.dwHighDateTime as u64) << 32 | (ft.dwLowDateTime as u64)
 }
diff --git a/src/windows/system.rs b/src/windows/system.rs
index fe9245e..d593837 100644
--- a/src/windows/system.rs
+++ b/src/windows/system.rs
@@ -4,26 +4,26 @@ use crate::{Cpu, CpuRefreshKind, LoadAvg, MemoryRefreshKind, Pid, ProcessRefresh
 
 use crate::sys::cpu::*;
 use crate::sys::process::get_start_time;
-use crate::sys::tools::*;
-use crate::sys::utils::{get_now, get_reg_string_value, get_reg_value_u32};
 use crate::{Process, ProcessInner};
 
 use crate::utils::into_iter;
 
 use std::cell::UnsafeCell;
 use std::collections::HashMap;
-use std::ffi::OsString;
+use std::ffi::{OsStr, OsString};
 use std::mem::{size_of, zeroed};
-use std::os::windows::ffi::OsStringExt;
+use std::os::windows::ffi::{OsStrExt, OsStringExt};
 use std::ptr;
-use std::time::SystemTime;
+use std::time::{Duration, SystemTime};
 
 use ntapi::ntexapi::SYSTEM_PROCESS_INFORMATION;
-use windows::core::PWSTR;
+use windows::core::{PCWSTR, PWSTR};
 use windows::Wdk::System::SystemInformation::{NtQuerySystemInformation, SystemProcessInformation};
-use windows::Win32::Foundation::{HANDLE, STATUS_INFO_LENGTH_MISMATCH, STILL_ACTIVE};
+use windows::Win32::Foundation::{self, HANDLE, STATUS_INFO_LENGTH_MISMATCH, STILL_ACTIVE};
 use windows::Win32::System::ProcessStatus::{K32GetPerformanceInfo, PERFORMANCE_INFORMATION};
-use windows::Win32::System::Registry::HKEY_LOCAL_MACHINE;
+use windows::Win32::System::Registry::{
+    RegCloseKey, RegOpenKeyExW, RegQueryValueExW, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, REG_NONE,
+};
 use windows::Win32::System::SystemInformation;
 use windows::Win32::System::SystemInformation::{
     ComputerNamePhysicalDnsHostname, GetComputerNameExW, GetTickCount64, GlobalMemoryStatusEx,
@@ -31,6 +31,17 @@ use windows::Win32::System::SystemInformation::{
 };
 use windows::Win32::System::Threading::GetExitCodeProcess;
 
+declare_signals! {
+    (),
+    Signal::Kill => (),
+    _ => None,
+}
+
+#[doc = include_str!("../../md_doc/supported_signals.md")]
+pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
+#[doc = include_str!("../../md_doc/minimum_cpu_update_interval.md")]
+pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200);
+
 const WINDOWS_ELEVEN_BUILD_NUMBER: u32 = 22000;
 
 impl SystemInner {
@@ -594,3 +605,123 @@ fn get_dns_hostname() -> Option<String> {
     sysinfo_debug!("Failed to get computer hostname");
     None
 }
+
+fn add_english_counter(
+    s: String,
+    query: &mut super::cpu::Query,
+    keys: &mut Option<KeyHandler>,
+    counter_name: String,
+) {
+    let mut full = s.encode_utf16().collect::<Vec<_>>();
+    full.push(0);
+    if query.add_english_counter(&counter_name, full) {
+        *keys = Some(KeyHandler::new(counter_name));
+    }
+}
+
+fn get_now() -> u64 {
+    SystemTime::now()
+        .duration_since(SystemTime::UNIX_EPOCH)
+        .map(|n| n.as_secs())
+        .unwrap_or(0)
+}
+
+fn utf16_str<S: AsRef<OsStr> + ?Sized>(text: &S) -> Vec<u16> {
+    OsStr::new(text)
+        .encode_wide()
+        .chain(Some(0))
+        .collect::<Vec<_>>()
+}
+
+struct RegKey(HKEY);
+
+impl RegKey {
+    unsafe fn open(hkey: HKEY, path: &[u16]) -> Option<Self> {
+        let mut new_hkey = Default::default();
+        if RegOpenKeyExW(
+            hkey,
+            PCWSTR::from_raw(path.as_ptr()),
+            0,
+            KEY_READ,
+            &mut new_hkey,
+        )
+        .is_err()
+        {
+            return None;
+        }
+        Some(Self(new_hkey))
+    }
+
+    unsafe fn get_value(
+        &self,
+        field_name: &[u16],
+        buf: &mut [u8],
+        buf_len: &mut u32,
+    ) -> windows::core::Result<()> {
+        let mut buf_type = REG_NONE;
+
+        RegQueryValueExW(
+            self.0,
+            PCWSTR::from_raw(field_name.as_ptr()),
+            None,
+            Some(&mut buf_type),
+            Some(buf.as_mut_ptr()),
+            Some(buf_len),
+        )
+        .ok()
+    }
+}
+
+impl Drop for RegKey {
+    fn drop(&mut self) {
+        let _err = unsafe { RegCloseKey(self.0) };
+    }
+}
+
+pub(crate) fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option<String> {
+    let c_path = utf16_str(path);
+    let c_field_name = utf16_str(field_name);
+
+    unsafe {
+        let new_key = RegKey::open(hkey, &c_path)?;
+        let mut buf_len: u32 = 2048;
+        let mut buf: Vec<u8> = Vec::with_capacity(buf_len as usize);
+
+        loop {
+            match new_key.get_value(&c_field_name, &mut buf, &mut buf_len) {
+                Ok(()) => break,
+                Err(err) if err.code() == Foundation::ERROR_MORE_DATA.to_hresult() => {
+                    // Needs to be updated for `Vec::reserve` to actually add additional capacity.
+                    buf.set_len(buf.capacity());
+                    buf.reserve(buf_len as _);
+                }
+                _ => return None,
+            }
+        }
+
+        buf.set_len(buf_len as _);
+
+        let words = std::slice::from_raw_parts(buf.as_ptr() as *const u16, buf.len() / 2);
+        let mut s = String::from_utf16_lossy(words);
+        while s.ends_with('\u{0}') {
+            s.pop();
+        }
+        Some(s)
+    }
+}
+
+pub(crate) fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Option<[u8; 4]> {
+    let c_path = utf16_str(path);
+    let c_field_name = utf16_str(field_name);
+
+    unsafe {
+        let new_key = RegKey::open(hkey, &c_path)?;
+        let mut buf_len: u32 = 4;
+        let mut buf = [0u8; 4];
+
+        new_key
+            .get_value(&c_field_name, &mut buf, &mut buf_len)
+            .map(|_| buf)
+            .ok()
+    }
+}
diff --git a/src/windows/tools.rs b/src/windows/tools.rs
deleted file mode 100644
index 842d555..0000000
--- a/src/windows/tools.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Take a look at the license at the top of the repository in the LICENSE file.
-
-use crate::sys::cpu::Query;
-
-pub(crate) struct KeyHandler {
-    pub unique_id: String,
-}
-
-impl KeyHandler {
-    pub fn new(unique_id: String) -> KeyHandler {
-        KeyHandler { unique_id }
-    }
-}
-
-pub(crate) fn add_english_counter(
-    s: String,
-    query: &mut Query,
-    keys: &mut Option<KeyHandler>,
-    counter_name: String,
-) {
-    let mut full = s.encode_utf16().collect::<Vec<_>>();
-    full.push(0);
-    if query.add_english_counter(&counter_name, full) {
-        *keys = Some(KeyHandler::new(counter_name));
-    }
-}
diff --git a/src/windows/utils.rs b/src/windows/utils.rs
index 6c1c066..e45db59 100644
--- a/src/windows/utils.rs
+++ b/src/windows/utils.rs
@@ -1,31 +1,11 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
+use std::ops::Deref;
 use windows::core::{PCWSTR, PWSTR};
-use windows::Win32::Foundation::{self, CloseHandle, FILETIME, HANDLE};
+use windows::Win32::Foundation::{CloseHandle, HANDLE};
 use windows::Win32::Storage::FileSystem::{
     CreateFileW, FILE_ACCESS_RIGHTS, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
 };
-use windows::Win32::System::Registry::{
-    RegCloseKey, RegOpenKeyExW, RegQueryValueExW, HKEY, KEY_READ, REG_NONE,
-};
-
-use std::ffi::OsStr;
-use std::ops::Deref;
-use std::os::windows::ffi::OsStrExt;
-use std::time::SystemTime;
-
-#[inline]
-pub(crate) fn filetime_to_u64(f: FILETIME) -> u64 {
-    (f.dwHighDateTime as u64) << 32 | (f.dwLowDateTime as u64)
-}
-
-#[inline]
-pub(crate) fn get_now() -> u64 {
-    SystemTime::now()
-        .duration_since(SystemTime::UNIX_EPOCH)
-        .map(|n| n.as_secs())
-        .unwrap_or(0)
-}
 
 pub(crate) unsafe fn to_utf8_str(p: PWSTR) -> String {
     if p.is_null() {
@@ -38,109 +18,10 @@ pub(crate) unsafe fn to_utf8_str(p: PWSTR) -> String {
     })
 }
 
-fn utf16_str<S: AsRef<OsStr> + ?Sized>(text: &S) -> Vec<u16> {
-    OsStr::new(text)
-        .encode_wide()
-        .chain(Some(0))
-        .collect::<Vec<_>>()
-}
-
-struct RegKey(HKEY);
-
-impl RegKey {
-    unsafe fn open(hkey: HKEY, path: &[u16]) -> Option<Self> {
-        let mut new_hkey = Default::default();
-        if RegOpenKeyExW(
-            hkey,
-            PCWSTR::from_raw(path.as_ptr()),
-            0,
-            KEY_READ,
-            &mut new_hkey,
-        )
-        .is_err()
-        {
-            return None;
-        }
-        Some(Self(new_hkey))
-    }
-
-    unsafe fn get_value(
-        &self,
-        field_name: &[u16],
-        buf: &mut [u8],
-        buf_len: &mut u32,
-    ) -> windows::core::Result<()> {
-        let mut buf_type = REG_NONE;
-
-        RegQueryValueExW(
-            self.0,
-            PCWSTR::from_raw(field_name.as_ptr()),
-            None,
-            Some(&mut buf_type),
-            Some(buf.as_mut_ptr()),
-            Some(buf_len),
-        )
-        .ok()
-    }
-}
-
-impl Drop for RegKey {
-    fn drop(&mut self) {
-        let _err = unsafe { RegCloseKey(self.0) };
-    }
-}
-
-pub(crate) fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option<String> {
-    let c_path = utf16_str(path);
-    let c_field_name = utf16_str(field_name);
-
-    unsafe {
-        let new_key = RegKey::open(hkey, &c_path)?;
-        let mut buf_len: u32 = 2048;
-        let mut buf: Vec<u8> = Vec::with_capacity(buf_len as usize);
-
-        loop {
-            match new_key.get_value(&c_field_name, &mut buf, &mut buf_len) {
-                Ok(()) => break,
-                Err(err) if err.code() == Foundation::ERROR_MORE_DATA.to_hresult() => {
-                    // Needs to be updated for `Vec::reserve` to actually add additional capacity.
-                    buf.set_len(buf.capacity());
-                    buf.reserve(buf_len as _);
-                }
-                _ => return None,
-            }
-        }
-
-        buf.set_len(buf_len as _);
-
-        let words = std::slice::from_raw_parts(buf.as_ptr() as *const u16, buf.len() / 2);
-        let mut s = String::from_utf16_lossy(words);
-        while s.ends_with('\u{0}') {
-            s.pop();
-        }
-        Some(s)
-    }
-}
-
-pub(crate) fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Option<[u8; 4]> {
-    let c_path = utf16_str(path);
-    let c_field_name = utf16_str(field_name);
-
-    unsafe {
-        let new_key = RegKey::open(hkey, &c_path)?;
-        let mut buf_len: u32 = 4;
-        let mut buf = [0u8; 4];
-
-        new_key
-            .get_value(&c_field_name, &mut buf, &mut buf_len)
-            .map(|_| buf)
-            .ok()
-    }
-}
-
 pub(crate) struct HandleWrapper(pub(crate) HANDLE);
 
 impl HandleWrapper {
+    #[cfg(feature = "system")]
     pub(crate) fn new(handle: HANDLE) -> Option<Self> {
         if handle.is_invalid() {
             None


Test patch:
diff --git a/tests/cpu.rs b/tests/cpu.rs
index 77e6d40..4fb73c1 100644
--- a/tests/cpu.rs
+++ b/tests/cpu.rs
@@ -1,7 +1,8 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-// This test is used to ensure that the CPUs are not loaded by default.
+#![cfg(feature = "system")]
 
+// This test is used to ensure that the CPUs are not loaded by default.
 #[test]
 fn test_cpu() {
     let mut s = sysinfo::System::new();
diff --git a/tests/disk_list.rs b/tests/disk_list.rs
index 09c2da3..f0649f5 100644
--- a/tests/disk_list.rs
+++ b/tests/disk_list.rs
@@ -1,6 +1,7 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
 #[test]
+#[cfg(feature = "system")]
 fn test_disks() {
     if sysinfo::IS_SUPPORTED_SYSTEM {
         let s = sysinfo::System::new_all();
diff --git a/tests/process.rs b/tests/process.rs
index f4dfe57..b03f837 100644
--- a/tests/process.rs
+++ b/tests/process.rs
@@ -1,5 +1,7 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
+#![cfg(feature = "system")]
+
 use bstr::ByteSlice;
 use sysinfo::{Pid, ProcessRefreshKind, System, UpdateKind};
 
diff --git a/tests/send_sync.rs b/tests/send_sync.rs
index 435b23a..af52161 100644
--- a/tests/send_sync.rs
+++ b/tests/send_sync.rs
@@ -2,6 +2,7 @@
 
 #[test]
 #[allow(clippy::extra_unused_type_parameters)]
+#[cfg(feature = "system")]
 fn test_send_sync() {
     fn is_send<T: Send>() {}
     fn is_sync<T: Sync>() {}
diff --git a/tests/system.rs b/tests/system.rs
index 08b18f9..279f063 100644
--- a/tests/system.rs
+++ b/tests/system.rs
@@ -1,5 +1,6 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
+#![cfg(feature = "system")]
 #![allow(clippy::assertions_on_constants)]
 
 use sysinfo::System;