Repository: GuillaumeGomez/sysinfo
Pull request #1309 (2024-07-09 12:30:33)

Add support for cgroup memory.stat
This PR aims at reading RSS memory information of cgroups via the `memory.stat` file.

The logic of this PR is inspired by the implementation in https://github.com/google/cadvisor/blob/1f17a6cfb8af89df396fe97c97cb5c1ec8ae6a3a/container/libcontainer/handler.go#L808.


Patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index f279573..4230c63 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -18,6 +18,10 @@ task:
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,disk
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features component
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,component
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features network
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,network
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features user
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,user
     - cargo clippy -- -D warnings
     - cargo check --example simple
     - FREEBSD_CI=1 cargo test -- --test-threads=1
@@ -43,6 +47,10 @@ task:
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,disk
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features component
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,component
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features network
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,network
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features user
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,user
     - cargo clippy -- -D warnings
     - cargo check --example simple
     - FREEBSD_CI=1 cargo test -- --test-threads=1
@@ -70,6 +78,10 @@ task:
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,disk
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features component
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,component
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features network
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,network
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features user
+    - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features debug,serde,user
     - RUSTFLAGS=-Dwarnings cargo check --no-default-features --features apple-sandbox
     - cargo clippy -- -D warnings
     - cargo check --example simple
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 7352e51..f4b69a3 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -133,6 +133,20 @@ jobs:
           args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features --features=debug,serde,component
           use-cross: ${{ matrix.triple.cross }}
 
+      - name: Check debug/serde feature (network)
+        uses: ClementTsang/cargo-action@v0.0.6
+        with:
+          command: rustc
+          args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features --features=debug,serde,network
+          use-cross: ${{ matrix.triple.cross }}
+
+      - name: Check debug/serde feature (user)
+        uses: ClementTsang/cargo-action@v0.0.6
+        with:
+          command: rustc
+          args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features --features=debug,serde,user
+          use-cross: ${{ matrix.triple.cross }}
+
       - name: Check without multithreading
         uses: ClementTsang/cargo-action@v0.0.6
         with:
@@ -140,27 +154,41 @@ jobs:
           args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features
           use-cross: ${{ matrix.triple.cross }}
 
-      - name: Check features
+      - name: Check features (system)
         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 features
+      - name: Check features (disk)
         uses: ClementTsang/cargo-action@v0.0.6
         with:
           command: check
           args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features --features disk
           use-cross: ${{ matrix.triple.cross }}
 
-      - name: Check features
+      - name: Check features (component)
         uses: ClementTsang/cargo-action@v0.0.6
         with:
           command: check
           args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features --features component
           use-cross: ${{ matrix.triple.cross }}
 
+      - name: Check features (network)
+        uses: ClementTsang/cargo-action@v0.0.6
+        with:
+          command: check
+          args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features --features network
+          use-cross: ${{ matrix.triple.cross }}
+
+      - name: Check features (user)
+        uses: ClementTsang/cargo-action@v0.0.6
+        with:
+          command: check
+          args: --target=${{ matrix.triple.target }} --manifest-path=Cargo.toml --no-default-features --features user
+          use-cross: ${{ matrix.triple.cross }}
+
       - name: Check example
         uses: ClementTsang/cargo-action@v0.0.6
         with:
diff --git a/Cargo.lock b/Cargo.lock
index 849347b..98dcc8c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
 
 [[package]]
 name = "bitflags"
-version = "2.5.0"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 [[package]]
 name = "bstr"
@@ -58,9 +58,9 @@ checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
 
 [[package]]
 name = "either"
-version = "1.12.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
 
 [[package]]
 name = "errno"
@@ -176,18 +176,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "serde"
-version = "1.0.203"
+version = "1.0.204"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
+checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.203"
+version = "1.0.204"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
+checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -196,9 +196,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.117"
+version = "1.0.120"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
 dependencies = [
  "itoa",
  "ryu",
@@ -207,9 +207,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.67"
+version = "2.0.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90"
+checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -336,9 +336,9 @@ dependencies = [
 
 [[package]]
 name = "windows-targets"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
  "windows_aarch64_gnullvm",
  "windows_aarch64_msvc",
@@ -352,48 +352,48 @@ dependencies = [
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
 name = "windows_i686_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/Cargo.toml b/Cargo.toml
index ed5a7b8..ff4c014 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,23 +15,29 @@ edition = "2021"
 name = "sysinfo"
 
 [features]
-default = ["component", "disk", "system", "multithread"]
+default = ["component", "disk", "network", "system", "user", "multithread"]
 component = [
+    "windows/Win32_Foundation",
+    "windows/Win32_Security",
     "windows/Win32_System_Com",
     "windows/Win32_System_Rpc",
     "windows/Win32_System_Variant",
     "windows/Win32_System_Wmi",
 ]
 disk = [
+    "windows/Win32_Foundation",
     "windows/Win32_Storage_FileSystem",
+    "windows/Win32_Security", # For `windows::Win32::Storage::FileSystem::CreateFileW`.
     "windows/Win32_System_IO",
     "windows/Win32_System_Ioctl",
     "windows/Win32_System_WindowsProgramming",
 ]
 system = [
+    "windows/Win32_Foundation",
     "windows/Wdk_System_SystemInformation",
     "windows/Wdk_System_SystemServices",
     "windows/Wdk_System_Threading",
+    "windows/Win32_Security_Authorization",
     "windows/Win32_System_Diagnostics_Debug",
     "windows/Win32_System_Kernel",
     "windows/Win32_System_Memory",
@@ -44,7 +50,21 @@ system = [
     "windows/Win32_System_SystemServices",
     "windows/Win32_System_Threading",
     "windows/Win32_UI_Shell",
-    "ntapi",
+    "dep:ntapi",
+    "dep:memchr",
+]
+network = [
+    "windows/Win32_Foundation",
+    "windows/Win32_NetworkManagement_IpHelper",
+    "windows/Win32_NetworkManagement_Ndis",
+    "windows/Win32_Networking_WinSock",
+]
+user = [
+    "windows/Win32_Foundation",
+    "windows/Win32_NetworkManagement_NetManagement",
+    "windows/Win32_Security",
+    "windows/Win32_Security_Authentication_Identity",
+    "windows/Win32_Security_Authorization",
 ]
 apple-sandbox = []
 apple-app-store = ["apple-sandbox"]
@@ -71,23 +91,16 @@ cargo-args = ["-Zbuild-std"]
 rustdoc-args = ["--generate-link-to-definition"]
 
 [dependencies]
-bstr = "1.9.0"
-memchr = "2.7.1"
+memchr = { version = "2.7.1", optional = true }
 rayon = { version = "^1.8", optional = true }
 serde = { version = "^1.0.190", optional = true }
 
 [target.'cfg(windows)'.dependencies]
 ntapi = { version = "0.4", optional = true }
-windows = { version = "0.56", features = [
-    "Win32_Foundation",
-    "Win32_NetworkManagement_IpHelper",
-    "Win32_NetworkManagement_NetManagement",
-    "Win32_NetworkManagement_Ndis",
-    "Win32_Networking_WinSock",
-    "Win32_Security",
-    "Win32_Security_Authentication_Identity",
-    "Win32_Security_Authorization",
-]}
+windows = { version = "0.56", optional = true }
+
+[target.'cfg(any(windows, target_os = "linux", target_os = "android"))'.dependencies]
+bstr = "1.9.0"
 
 [target.'cfg(not(any(target_os = "unknown", target_arch = "wasm32")))'.dependencies]
 libc = "^0.2.153"
@@ -100,6 +113,7 @@ tempfile = "3.9"
 
 [dev-dependencies]
 serde_json = "1.0" # Used in documentation tests.
+bstr = "1.9.0"
 
 [[example]]
 name = "simple"
diff --git a/benches/basic.rs b/benches/basic.rs
index eff86b5..05a2a9c 100644
--- a/benches/basic.rs
+++ b/benches/basic.rs
@@ -90,6 +90,7 @@ fn bench_refresh_disks_list(b: &mut test::Bencher) {
     });
 }
 
+#[cfg(feature = "network")]
 #[bench]
 fn bench_refresh_networks(b: &mut test::Bencher) {
     let mut n = sysinfo::Networks::new_with_refreshed_list();
@@ -99,6 +100,7 @@ fn bench_refresh_networks(b: &mut test::Bencher) {
     });
 }
 
+#[cfg(feature = "network")]
 #[bench]
 fn bench_refresh_networks_list(b: &mut test::Bencher) {
     let mut n = sysinfo::Networks::new_with_refreshed_list();
diff --git a/src/common/mod.rs b/src/common/mod.rs
index 1284403..acc9601 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -4,18 +4,22 @@
 pub(crate) mod component;
 #[cfg(feature = "disk")]
 pub(crate) mod disk;
+#[cfg(feature = "network")]
 pub(crate) mod network;
 #[cfg(feature = "system")]
 pub(crate) mod system;
+#[cfg(feature = "user")]
 pub(crate) mod user;
 
 macro_rules! xid {
     ($(#[$outer:meta])+ $name:ident, $type:ty $(, $trait:ty)?) => {
+        #[cfg(any(feature = "system", feature = "user"))]
         $(#[$outer])+
         #[repr(transparent)]
         #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
         pub struct $name(pub(crate) $type);
 
+        #[cfg(any(feature = "system", feature = "user"))]
         impl std::ops::Deref for $name {
             type Target = $type;
 
@@ -25,6 +29,7 @@ macro_rules! xid {
         }
 
         $(
+        #[cfg(any(feature = "system", feature = "user"))]
         impl TryFrom<usize> for $name {
             type Error = <$type as TryFrom<usize>>::Error;
 
@@ -33,6 +38,7 @@ macro_rules! xid {
             }
         }
 
+        #[cfg(any(feature = "system", feature = "user"))]
         impl $trait for $name {
             type Err = <$type as $trait>::Err;
 
@@ -84,6 +90,7 @@ cfg_if! {
         uid!(crate::windows::Sid);
         gid!(u32);
         // Manual implementation outside of the macro...
+        #[cfg(any(feature = "system", feature = "user"))]
         impl std::str::FromStr for Uid {
             type Err = <crate::windows::Sid as std::str::FromStr>::Err;
 
diff --git a/src/common/system.rs b/src/common/system.rs
index 8485487..219d988 100644
--- a/src/common/system.rs
+++ b/src/common/system.rs
@@ -987,6 +987,8 @@ pub struct CGroupLimits {
     pub free_memory: u64,
     /// Free swap (in bytes) for the current cgroup.
     pub free_swap: u64,
+    /// Resident Set Size (RSS) (in bytes) for the current cgroup.
+    pub rss: u64,
 }
 
 /// Type containing read and written bytes.
@@ -1242,6 +1244,11 @@ impl Process {
 
     /// Returns the command line.
     ///
+    ///  **⚠️ Important ⚠️**
+    ///
+    /// On **Windows**, you might need to use `administrator` privileges when running your program  
+    /// to have access to this information.  
+    ///
     /// ```no_run
     /// use sysinfo::{Pid, System};
     ///
diff --git a/src/debug.rs b/src/debug.rs
index 46318a7..46a620f 100644
--- a/src/debug.rs
+++ b/src/debug.rs
@@ -1,12 +1,8 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use crate::{NetworkData, Networks, User, Users};
-
-use std::fmt;
-
 #[cfg(feature = "system")]
-impl fmt::Debug for crate::Cpu {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl std::fmt::Debug for crate::Cpu {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("Cpu")
             .field("name", &self.name())
             .field("CPU usage", &self.cpu_usage())
@@ -18,8 +14,8 @@ impl fmt::Debug for crate::Cpu {
 }
 
 #[cfg(feature = "system")]
-impl fmt::Debug for crate::System {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl std::fmt::Debug for crate::System {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("System")
             .field("global CPU usage", &self.global_cpu_usage())
             .field("load average", &Self::load_average())
@@ -34,8 +30,8 @@ impl fmt::Debug for crate::System {
 }
 
 #[cfg(feature = "disk")]
-impl fmt::Debug for crate::Disk {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl std::fmt::Debug for crate::Disk {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(
             fmt,
             "Disk({:?})[FS: {:?}][Type: {:?}][removable: {}] mounted on {:?}: {}/{} B",
@@ -51,15 +47,15 @@ impl fmt::Debug for crate::Disk {
 }
 
 #[cfg(feature = "disk")]
-impl fmt::Debug for crate::Disks {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl std::fmt::Debug for crate::Disks {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_list().entries(self.iter()).finish()
     }
 }
 
 #[cfg(feature = "system")]
-impl fmt::Debug for crate::Process {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl std::fmt::Debug for crate::Process {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("Process")
             .field("pid", &self.pid())
             .field("parent", &self.parent())
@@ -81,15 +77,15 @@ impl fmt::Debug for crate::Process {
 }
 
 #[cfg(feature = "component")]
-impl fmt::Debug for crate::Components {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl std::fmt::Debug for crate::Components {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_list().entries(self.iter()).finish()
     }
 }
 
 #[cfg(feature = "component")]
-impl fmt::Debug for crate::Component {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl std::fmt::Debug for crate::Component {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         if let Some(critical) = self.critical() {
             write!(
                 f,
@@ -111,14 +107,16 @@ impl fmt::Debug for crate::Component {
     }
 }
 
-impl fmt::Debug for Networks {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+#[cfg(feature = "network")]
+impl std::fmt::Debug for crate::Networks {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_list().entries(self.iter()).finish()
     }
 }
 
-impl fmt::Debug for NetworkData {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+#[cfg(feature = "network")]
+impl std::fmt::Debug for crate::NetworkData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("NetworkData")
             .field("income", &self.received())
             .field("total income", &self.total_received())
@@ -136,14 +134,16 @@ impl fmt::Debug for NetworkData {
     }
 }
 
-impl fmt::Debug for Users {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+#[cfg(feature = "user")]
+impl std::fmt::Debug for crate::Users {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_list().entries(self.iter()).finish()
     }
 }
 
-impl fmt::Debug for User {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+#[cfg(feature = "user")]
+impl std::fmt::Debug for crate::User {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("User")
             .field("uid", &self.id())
             .field("gid", &self.group_id())
diff --git a/src/lib.rs b/src/lib.rs
index d53b7e6..6a49986 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,8 +1,16 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-#![cfg_attr(any(feature = "system", feature = "disk", feature = "component"), doc = include_str!("../README.md"))]
 #![cfg_attr(
-    not(any(feature = "system", feature = "disk", feature = "component")),
+    all(feature = "system", feature = "disk", feature = "component", feature = "system"),
+    doc = include_str!("../README.md")
+)]
+#![cfg_attr(
+    not(all(
+        feature = "system",
+        feature = "disk",
+        feature = "component",
+        feature = "system"
+    )),
     doc = "For crate-level documentation, all features need to be enabled."
 )]
 #![cfg_attr(feature = "serde", doc = include_str!("../md_doc/serde.md"))]
@@ -31,8 +39,11 @@ cfg_if! {
         target_os = "freebsd"))]
     {
         mod unix;
-        mod network;
         use crate::unix::sys as sys;
+
+        #[cfg(feature = "network")]
+        mod network;
+        #[cfg(feature = "network")]
         use crate::unix::network_helper;
 
         #[cfg(test)]
@@ -40,8 +51,11 @@ cfg_if! {
     } else if #[cfg(windows)] {
         mod windows;
         use crate::windows as sys;
-        use crate::windows::network_helper;
+
+        #[cfg(feature = "network")]
         mod network;
+        #[cfg(feature = "network")]
+        use crate::windows::network_helper;
 
         #[cfg(test)]
         pub(crate) const MIN_USERS: usize = 1;
@@ -54,37 +68,39 @@ cfg_if! {
     }
 }
 
-pub use crate::common::{
-    network::{IpNetwork, MacAddr, NetworkData, Networks},
-    user::{Group, Groups, User, Users},
-    Gid, Uid,
-};
-
+#[cfg(feature = "component")]
+pub use crate::common::component::{Component, Components};
 #[cfg(feature = "disk")]
 pub use crate::common::disk::{Disk, DiskKind, Disks};
-#[cfg(feature = "disk")]
-pub(crate) use crate::sys::{DiskInner, DisksInner};
-
+#[cfg(feature = "network")]
+pub use crate::common::network::{IpNetwork, MacAddr, NetworkData, Networks};
 #[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,
 };
-#[cfg(feature = "system")]
-pub(crate) use crate::sys::{CpuInner, ProcessInner, SystemInner};
+#[cfg(feature = "user")]
+pub use crate::common::user::{Group, Groups, User, Users};
+#[cfg(any(feature = "user", feature = "system"))]
+pub use crate::common::{Gid, Uid};
 #[cfg(feature = "system")]
 pub use crate::sys::{MINIMUM_CPU_UPDATE_INTERVAL, SUPPORTED_SIGNALS};
 
+#[cfg(feature = "user")]
 pub(crate) use crate::common::user::GroupInner;
-
-#[cfg(feature = "component")]
-pub use crate::common::component::{Component, Components};
+#[cfg(feature = "user")]
+pub(crate) use crate::sys::UserInner;
 #[cfg(feature = "component")]
 pub(crate) use crate::sys::{ComponentInner, ComponentsInner};
+#[cfg(feature = "system")]
+pub(crate) use crate::sys::{CpuInner, ProcessInner, SystemInner};
+#[cfg(feature = "disk")]
+pub(crate) use crate::sys::{DiskInner, DisksInner};
+#[cfg(feature = "network")]
+pub(crate) use crate::sys::{NetworkDataInner, NetworksInner};
 
 pub use crate::sys::IS_SUPPORTED_SYSTEM;
-pub(crate) use crate::sys::{NetworkDataInner, NetworksInner, UserInner};
 
 #[cfg(feature = "c-interface")]
 pub use crate::c_interface::*;
@@ -191,6 +207,31 @@ use sysinfo::", stringify!($imports), r";
         Disks,
         DiskKind,
     );
+
+    #[cfg(not(feature = "component"))]
+    compile_fail_import!(
+        no_component_feature =>
+        Component,
+        Components,
+    );
+
+    #[cfg(not(feature = "network"))]
+    compile_fail_import!(
+        no_network_feature =>
+        IpNetwork,
+        MacAddr,
+        NetworkData,
+        Networks,
+    );
+
+    #[cfg(not(feature = "user"))]
+    compile_fail_import!(
+        no_user_feature =>
+        Group,
+        Groups,
+        User,
+        Users,
+    );
 }
 
 #[cfg(test)]
@@ -222,6 +263,7 @@ mod test {
         check_minimum_cpu_update_interval(MINIMUM_CPU_UPDATE_INTERVAL);
     }
 
+    #[cfg(feature = "user")]
     #[test]
     fn check_uid_gid() {
         let mut users = Users::new();
diff --git a/src/macros.rs b/src/macros.rs
index 2b85587..bbd74e4 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -59,6 +59,7 @@ macro_rules! declare_signals {
 }
 
 #[cfg(all(unix, not(feature = "unknown-ci")))]
+#[allow(unused_macros)]
 macro_rules! retry_eintr {
     (set_to_0 => $($t:tt)+) => {{
         let errno = crate::unix::libc_errno();
diff --git a/src/serde.rs b/src/serde.rs
index d178cb1..f112ef0 100644
--- a/src/serde.rs
+++ b/src/serde.rs
@@ -1,5 +1,12 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
+#[cfg(any(
+    feature = "component",
+    feature = "disk",
+    feature = "network",
+    feature = "system",
+    feature = "user"
+))]
 use serde::{ser::SerializeStruct, Serialize, Serializer};
 
 #[cfg(feature = "disk")]
@@ -55,24 +62,6 @@ impl Serialize for crate::DiskKind {
     }
 }
 
-impl Serialize for crate::Gid {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        serializer.serialize_newtype_struct("Gid", &self.to_string())
-    }
-}
-
-impl Serialize for crate::Uid {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        serializer.serialize_newtype_struct("Uid", &self.to_string())
-    }
-}
-
 #[cfg(feature = "system")]
 impl Serialize for crate::Pid {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -186,15 +175,6 @@ impl Serialize for crate::CGroupLimits {
     }
 }
 
-impl Serialize for crate::Networks {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        serializer.collect_seq(self.iter())
-    }
-}
-
 #[cfg(feature = "component")]
 impl Serialize for crate::Components {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -223,15 +203,6 @@ impl Serialize for crate::Component {
     }
 }
 
-impl Serialize for crate::Users {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        serializer.collect_seq(self.iter())
-    }
-}
-
 #[cfg(feature = "system")]
 impl Serialize for crate::ThreadKind {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -308,6 +279,17 @@ impl Serialize for crate::LoadAvg {
     }
 }
 
+#[cfg(feature = "network")]
+impl Serialize for crate::Networks {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.collect_seq(self.iter())
+    }
+}
+
+#[cfg(feature = "network")]
 impl Serialize for crate::NetworkData {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
@@ -341,6 +323,42 @@ impl Serialize for crate::NetworkData {
     }
 }
 
+#[cfg(feature = "network")]
+impl Serialize for crate::MacAddr {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_newtype_struct("MacAddr", &self.0)
+    }
+}
+
+#[cfg(feature = "network")]
+impl Serialize for crate::IpNetwork {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let mut state = serializer.serialize_struct("IpNetwork", 2)?;
+
+        state.serialize_field("addr", &self.addr)?;
+        state.serialize_field("prefix", &self.prefix)?;
+
+        state.end()
+    }
+}
+
+#[cfg(feature = "user")]
+impl Serialize for crate::Users {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.collect_seq(self.iter())
+    }
+}
+
+#[cfg(feature = "user")]
 impl Serialize for crate::User {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
@@ -358,6 +376,7 @@ impl Serialize for crate::User {
     }
 }
 
+#[cfg(feature = "user")]
 impl Serialize for crate::Group {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
@@ -373,6 +392,26 @@ impl Serialize for crate::Group {
     }
 }
 
+#[cfg(any(feature = "user", feature = "system"))]
+impl Serialize for crate::Gid {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_newtype_struct("Gid", &self.to_string())
+    }
+}
+
+#[cfg(any(feature = "user", feature = "system"))]
+impl Serialize for crate::Uid {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_newtype_struct("Uid", &self.to_string())
+    }
+}
+
 #[cfg(feature = "system")]
 impl Serialize for crate::ProcessStatus {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -420,26 +459,3 @@ impl Serialize for crate::DiskUsage {
         state.end()
     }
 }
-
-impl Serialize for crate::MacAddr {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        serializer.serialize_newtype_struct("MacAddr", &self.0)
-    }
-}
-
-impl Serialize for crate::IpNetwork {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        let mut state = serializer.serialize_struct("IpNetwork", 2)?;
-
-        state.serialize_field("addr", &self.addr)?;
-        state.serialize_field("prefix", &self.prefix)?;
-
-        state.end()
-    }
-}
diff --git a/src/unix/apple/mod.rs b/src/unix/apple/mod.rs
index ab8961b..5238b54 100644
--- a/src/unix/apple/mod.rs
+++ b/src/unix/apple/mod.rs
@@ -4,9 +4,6 @@
 pub(crate) mod app_store;
 
 mod ffi;
-pub mod groups;
-pub mod network;
-pub mod users;
 mod utils;
 
 cfg_if! {
@@ -42,11 +39,21 @@ cfg_if! {
 
         pub(crate) use self::component::{ComponentInner, ComponentsInner};
     }
-}
 
-pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use crate::unix::groups::get_groups;
-pub(crate) use crate::unix::users::{get_users, UserInner};
+    if #[cfg(feature = "network")] {
+        pub mod network;
+
+        pub(crate) use self::network::{NetworkDataInner, NetworksInner};
+    }
+
+    if #[cfg(feature = "user")] {
+        pub mod groups;
+        pub mod users;
+
+        pub(crate) use crate::unix::groups::get_groups;
+        pub(crate) use crate::unix::users::{get_users, UserInner};
+    }
+}
 
 #[doc = include_str!("../../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = true;
diff --git a/src/unix/freebsd/mod.rs b/src/unix/freebsd/mod.rs
index 4c48277..3dabf51 100644
--- a/src/unix/freebsd/mod.rs
+++ b/src/unix/freebsd/mod.rs
@@ -1,7 +1,6 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-pub mod network;
-mod utils;
+pub(crate) mod utils;
 
 cfg_if! {
     if #[cfg(feature = "system")] {
@@ -26,11 +25,18 @@ cfg_if! {
 
         pub(crate) use self::component::{ComponentInner, ComponentsInner};
     }
-}
 
-pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use crate::unix::groups::get_groups;
-pub(crate) use crate::unix::users::{get_users, UserInner};
+    if #[cfg(feature = "network")] {
+        pub mod network;
+
+        pub(crate) use self::network::{NetworkDataInner, NetworksInner};
+    }
+
+    if #[cfg(feature = "user")] {
+        pub(crate) use crate::unix::groups::get_groups;
+        pub(crate) use crate::unix::users::{get_users, UserInner};
+    }
+}
 
 #[doc = include_str!("../../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = true;
diff --git a/src/unix/freebsd/utils.rs b/src/unix/freebsd/utils.rs
index 5d17675..7e6d00b 100644
--- a/src/unix/freebsd/utils.rs
+++ b/src/unix/freebsd/utils.rs
@@ -1,15 +1,13 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use libc::c_int;
 #[cfg(feature = "system")]
 use std::ffi::{CStr, OsStr, OsString};
-use std::mem;
 #[cfg(feature = "system")]
 use std::os::unix::ffi::OsStrExt;
 
 #[cfg(feature = "system")]
 #[inline]
-pub unsafe fn init_mib(name: &[u8], mib: &mut [c_int]) {
+pub unsafe fn init_mib(name: &[u8], mib: &mut [libc::c_int]) {
     let mut len = mib.len();
     libc::sysctlnametomib(name.as_ptr() as _, mib.as_mut_ptr(), &mut len);
 }
@@ -21,7 +19,7 @@ pub(crate) fn boot_time() -> u64 {
         tv_usec: 0,
     };
     let mut len = std::mem::size_of::<libc::timeval>();
-    let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME];
+    let mut mib: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME];
     unsafe {
         if libc::sysctl(
             mib.as_mut_ptr(),
@@ -39,8 +37,9 @@ pub(crate) fn boot_time() -> u64 {
     }
 }
 
-pub(crate) unsafe fn get_sys_value<T: Sized>(mib: &[c_int], value: &mut T) -> bool {
-    let mut len = mem::size_of::<T>() as libc::size_t;
+#[cfg(any(feature = "system", feature = "network"))]
+pub(crate) unsafe fn get_sys_value<T: Sized>(mib: &[libc::c_int], value: &mut T) -> bool {
+    let mut len = std::mem::size_of::<T>() as libc::size_t;
     libc::sysctl(
         mib.as_ptr(),
         mib.len() as _,
@@ -52,8 +51,8 @@ pub(crate) unsafe fn get_sys_value<T: Sized>(mib: &[c_int], value: &mut T) -> bo
 }
 
 #[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;
+pub(crate) unsafe fn get_sys_value_array<T: Sized>(mib: &[libc::c_int], value: &mut [T]) -> bool {
+    let mut len = std::mem::size_of_val(value) as libc::size_t;
     libc::sysctl(
         mib.as_ptr(),
         mib.len() as _,
@@ -64,6 +63,7 @@ pub(crate) unsafe fn get_sys_value_array<T: Sized>(mib: &[c_int], value: &mut [T
     ) == 0
 }
 
+#[cfg(any(feature = "disk", feature = "system", feature = "network"))]
 pub(crate) fn c_buf_to_utf8_str(buf: &[libc::c_char]) -> Option<&str> {
     unsafe {
         let buf: &[u8] = std::slice::from_raw_parts(buf.as_ptr() as _, buf.len());
@@ -77,6 +77,7 @@ pub(crate) fn c_buf_to_utf8_str(buf: &[libc::c_char]) -> Option<&str> {
     }
 }
 
+#[cfg(any(feature = "system", feature = "network"))]
 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())
 }
@@ -101,10 +102,10 @@ pub(crate) fn c_buf_to_os_string(buf: &[libc::c_char]) -> OsString {
 
 #[cfg(feature = "system")]
 pub(crate) unsafe fn get_sys_value_str(
-    mib: &[c_int],
+    mib: &[libc::c_int],
     buf: &mut [libc::c_char],
 ) -> Option<OsString> {
-    let mut len = mem::size_of_val(buf) as libc::size_t;
+    let mut len = std::mem::size_of_val(buf) as libc::size_t;
     if libc::sysctl(
         mib.as_ptr(),
         mib.len() as _,
@@ -117,13 +118,13 @@ pub(crate) unsafe fn get_sys_value_str(
         return None;
     }
     Some(c_buf_to_os_string(
-        &buf[..len / mem::size_of::<libc::c_char>()],
+        &buf[..len / std::mem::size_of::<libc::c_char>()],
     ))
 }
 
 #[cfg(any(feature = "system", feature = "component"))]
 pub(crate) unsafe fn get_sys_value_by_name<T: Sized>(name: &[u8], value: &mut T) -> bool {
-    let mut len = mem::size_of::<T>() as libc::size_t;
+    let mut len = std::mem::size_of::<T>() as libc::size_t;
     let original = len;
 
     libc::sysctlbyname(
@@ -200,8 +201,8 @@ pub(crate) unsafe fn from_cstr_array(ptr: *const *const libc::c_char) -> Vec<OsS
 
 #[cfg(any(feature = "system", feature = "component"))]
 pub(crate) unsafe fn get_nb_cpus() -> usize {
-    let mut smp: c_int = 0;
-    let mut nb_cpus: c_int = 1;
+    let mut smp: libc::c_int = 0;
+    let mut nb_cpus: libc::c_int = 1;
 
     if !get_sys_value_by_name(b"kern.smp.active\0", &mut smp) {
         smp = 0;
diff --git a/src/unix/linux/mod.rs b/src/unix/linux/mod.rs
index ff5c784..3dabf51 100644
--- a/src/unix/linux/mod.rs
+++ b/src/unix/linux/mod.rs
@@ -1,6 +1,5 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-pub mod network;
 pub(crate) mod utils;
 
 cfg_if! {
@@ -26,11 +25,18 @@ cfg_if! {
 
         pub(crate) use self::component::{ComponentInner, ComponentsInner};
     }
-}
 
-pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use crate::unix::groups::get_groups;
-pub(crate) use crate::unix::users::{get_users, UserInner};
+    if #[cfg(feature = "network")] {
+        pub mod network;
+
+        pub(crate) use self::network::{NetworkDataInner, NetworksInner};
+    }
+
+    if #[cfg(feature = "user")] {
+        pub(crate) use crate::unix::groups::get_groups;
+        pub(crate) use crate::unix::users::{get_users, UserInner};
+    }
+}
 
 #[doc = include_str!("../../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = true;
diff --git a/src/unix/linux/process.rs b/src/unix/linux/process.rs
index 1f88f03..04d174a 100644
--- a/src/unix/linux/process.rs
+++ b/src/unix/linux/process.rs
@@ -892,7 +892,7 @@ fn parse_stat_file(data: &[u8]) -> Option<Parts<'_>> {
     str_parts.extend(data.split_whitespace());
     Some(Parts {
         str_parts,
-        short_exe: short_exe.strip_prefix(&[b'(']).unwrap_or(short_exe),
+        short_exe: short_exe.strip_prefix(b"(").unwrap_or(short_exe),
     })
 }
 
diff --git a/src/unix/linux/system.rs b/src/unix/linux/system.rs
index afab18e..83eca3c 100644
--- a/src/unix/linux/system.rs
+++ b/src/unix/linux/system.rs
@@ -580,22 +580,43 @@ where
     }
 }
 
+fn read_table_key(filename: &str, target_key: &str, colsep: char) -> Option<u64> {
+    if let Ok(content) = get_all_utf8_data(filename, 16_635) {
+        return content
+            .split('\n')
+            .find_map(|line| {
+                let mut split = line.split(colsep);
+                let key = split.next()?;
+                if key != target_key {
+                    return None;
+                }
+
+                let value = split.next()?;
+                let value0 = value.trim_start().split(' ').next()?;
+                u64::from_str(value0).ok()
+            });
+    }
+
+    None
+}
+
 impl crate::CGroupLimits {
     fn new(sys: &SystemInner) -> Option<Self> {
         assert!(
             sys.mem_total != 0,
             "You need to call System::refresh_memory before trying to get cgroup limits!",
         );
-        if let (Some(mem_cur), Some(mem_max)) = (
+        if let (Some(mem_cur), Some(mem_max), Some(mem_rss)) = (
+            // cgroups v2
             read_u64("/sys/fs/cgroup/memory.current"),
             read_u64("/sys/fs/cgroup/memory.max"),
+            read_table_key("/sys/fs/cgroup/memory.stat", "anon", ' ')
         ) {
-            // cgroups v2
-
             let mut limits = Self {
                 total_memory: sys.mem_total,
                 free_memory: sys.mem_free,
                 free_swap: sys.swap_free,
+                rss: mem_rss
             };
 
             limits.total_memory = min(mem_max, sys.mem_total);
@@ -606,15 +627,17 @@ impl crate::CGroupLimits {
             }
 
             Some(limits)
-        } else if let (Some(mem_cur), Some(mem_max)) = (
+        } else if let (Some(mem_cur), Some(mem_max), Some(mem_rss)) = (
             // cgroups v1
             read_u64("/sys/fs/cgroup/memory/memory.usage_in_bytes"),
             read_u64("/sys/fs/cgroup/memory/memory.limit_in_bytes"),
+            read_table_key("/sys/fs/cgroup/memory/memory.stat", "total_rss", ' ')
         ) {
             let mut limits = Self {
                 total_memory: sys.mem_total,
                 free_memory: sys.mem_free,
                 free_swap: sys.swap_free,
+                rss: mem_rss
             };
 
             limits.total_memory = min(mem_max, sys.mem_total);
@@ -724,6 +747,102 @@ mod test {
     #[cfg(not(target_os = "android"))]
     use super::get_system_info_linux;
     use super::InfoType;
+    use super::read_table;
+    use super::read_table_key;
+    use std::collections::HashMap;
+    use std::io::Write;
+    use tempfile::NamedTempFile;
+
+    #[test]
+    fn test_read_table() {
+        // Create a temporary file with test content
+        let mut file = NamedTempFile::new().unwrap();
+        writeln!(file, "KEY1:100 kB").unwrap();
+        writeln!(file, "KEY2:200 kB").unwrap();
+        writeln!(file, "KEY3:300 kB").unwrap();
+        writeln!(file, "KEY4:invalid").unwrap();
+
+        let file_path = file.path().to_str().unwrap();
+
+        // Test reading the table
+        let mut result = HashMap::new();
+        read_table(file_path, ':', |key, value| {
+            result.insert(key.to_string(), value);
+        });
+
+        assert_eq!(result.get("KEY1"), Some(&100));
+        assert_eq!(result.get("KEY2"), Some(&200));
+        assert_eq!(result.get("KEY3"), Some(&300));
+        assert_eq!(result.get("KEY4"), None);
+
+        // Test with different separator and units
+        let mut file = NamedTempFile::new().unwrap();
+        writeln!(file, "KEY1 400 MB").unwrap();
+        writeln!(file, "KEY2 500 GB").unwrap();
+        writeln!(file, "KEY3 600").unwrap();
+
+        let file_path = file.path().to_str().unwrap();
+
+        let mut result = HashMap::new();
+        read_table(file_path, ' ', |key, value| {
+            result.insert(key.to_string(), value);
+        });
+
+        assert_eq!(result.get("KEY1"), Some(&400));
+        assert_eq!(result.get("KEY2"), Some(&500));
+        assert_eq!(result.get("KEY3"), Some(&600));
+
+        // Test with empty file
+        let file = NamedTempFile::new().unwrap();
+        let file_path = file.path().to_str().unwrap();
+
+        let mut result = HashMap::new();
+        read_table(file_path, ':', |key, value| {
+            result.insert(key.to_string(), value);
+        });
+
+        assert!(result.is_empty());
+
+        // Test with non-existent file
+        let mut result = HashMap::new();
+        read_table("/nonexistent/file", ':', |key, value| {
+            result.insert(key.to_string(), value);
+        });
+
+        assert!(result.is_empty());
+    }
+
+    #[test]
+    fn test_read_table_key() {
+        // Create a temporary file with test content
+        let mut file = NamedTempFile::new().unwrap();
+        writeln!(file, "KEY1:100 kB").unwrap();
+        writeln!(file, "KEY2:200 kB").unwrap();
+        writeln!(file, "KEY3:300 kB").unwrap();
+
+        let file_path = file.path().to_str().unwrap();
+
+        // Test existing keys
+        assert_eq!(read_table_key(file_path, "KEY1", ':'), Some(100));
+        assert_eq!(read_table_key(file_path, "KEY2", ':'), Some(200));
+        assert_eq!(read_table_key(file_path, "KEY3", ':'), Some(300));
+
+        // Test non-existent key
+        assert_eq!(read_table_key(file_path, "KEY4", ':'), None);
+
+        // Test with different separator
+        let mut file = NamedTempFile::new().unwrap();
+        writeln!(file, "KEY1 400 kB").unwrap();
+        writeln!(file, "KEY2 500 kB").unwrap();
+
+        let file_path = file.path().to_str().unwrap();
+
+        assert_eq!(read_table_key(file_path, "KEY1", ' '), Some(400));
+        assert_eq!(read_table_key(file_path, "KEY2", ' '), Some(500));
+
+        // Test with invalid file
+        assert_eq!(read_table_key("/nonexistent/file", "KEY1", ':'), None);
+    }
 
     #[test]
     #[cfg(target_os = "android")]
diff --git a/src/unix/mod.rs b/src/unix/mod.rs
index e712b15..0d6845c 100644
--- a/src/unix/mod.rs
+++ b/src/unix/mod.rs
@@ -5,19 +5,23 @@ cfg_if! {
         pub(crate) mod apple;
         pub(crate) use apple as sys;
 
+        #[allow(unused_imports)]
         pub(crate) use libc::__error as libc_errno;
     } else if #[cfg(any(target_os = "linux", target_os = "android"))] {
         pub(crate) mod linux;
         pub(crate) use linux as sys;
 
         #[cfg(target_os = "linux")]
+        #[allow(unused_imports)]
         pub(crate) use libc::__errno_location as libc_errno;
         #[cfg(target_os = "android")]
+        #[allow(unused_imports)]
         pub(crate) use libc::__errno as libc_errno;
     } else if #[cfg(target_os = "freebsd")] {
         pub(crate) mod freebsd;
         pub(crate) use freebsd as sys;
 
+        #[allow(unused_imports)]
         pub(crate) use libc::__error as libc_errno;
     } else {
         compile_error!("Invalid cfg!");
@@ -38,9 +42,15 @@ cfg_if! {
             }
         }
     }
+
+    if #[cfg(feature = "network")] {
+        pub(crate) mod network_helper;
+    }
+
+    if #[cfg(feature = "user")] {
+        pub(crate) mod users;
+        pub(crate) mod groups;
+    }
 }
 
-pub(crate) mod groups;
-pub(crate) mod network_helper;
-pub(crate) mod users;
 pub(crate) mod utils;
diff --git a/src/unix/utils.rs b/src/unix/utils.rs
index 6c5bb36..66f83a7 100644
--- a/src/unix/utils.rs
+++ b/src/unix/utils.rs
@@ -1,12 +1,16 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use libc::c_char;
-
-pub(crate) fn cstr_to_rust(c: *const c_char) -> Option<String> {
+#[cfg(feature = "user")]
+pub(crate) fn cstr_to_rust(c: *const libc::c_char) -> Option<String> {
     cstr_to_rust_with_size(c, None)
 }
 
-pub(crate) fn cstr_to_rust_with_size(c: *const c_char, size: Option<usize>) -> Option<String> {
+#[cfg(any(feature = "disk", feature = "system", feature = "user"))]
+#[allow(dead_code)]
+pub(crate) fn cstr_to_rust_with_size(
+    c: *const libc::c_char,
+    size: Option<usize>,
+) -> Option<String> {
     if c.is_null() {
         return None;
     }
diff --git a/src/unknown/mod.rs b/src/unknown/mod.rs
index 5ac6e1d..1074b1b 100644
--- a/src/unknown/mod.rs
+++ b/src/unknown/mod.rs
@@ -1,9 +1,5 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-pub mod groups;
-pub mod network;
-pub mod users;
-
 cfg_if! {
     if #[cfg(feature = "system")] {
         pub mod cpu;
@@ -27,11 +23,21 @@ cfg_if! {
 
         pub(crate) use self::component::{ComponentInner, ComponentsInner};
     }
-}
 
-pub(crate) use self::groups::get_groups;
-pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use self::users::{get_users, UserInner};
+    if #[cfg(feature = "network")] {
+        pub mod network;
+
+        pub(crate) use self::network::{NetworkDataInner, NetworksInner};
+    }
+
+    if #[cfg(feature = "user")] {
+        pub mod groups;
+        pub mod users;
+
+        pub(crate) use self::groups::get_groups;
+        pub(crate) use self::users::{get_users, UserInner};
+    }
+}
 
 #[doc = include_str!("../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = false;
diff --git a/src/utils.rs b/src/utils.rs
index 8700462..67e8596 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,41 +1,26 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-/// Converts the value into a parallel iterator if the `multithread` feature is enabled.
-/// Uses the `rayon::iter::IntoParallelIterator` trait.
-#[cfg(all(
-    feature = "multithread",
-    not(feature = "unknown-ci"),
-    not(all(target_os = "macos", feature = "apple-sandbox")),
-))]
-#[allow(dead_code)]
-pub(crate) fn into_iter<T>(val: T) -> T::Iter
-where
-    T: rayon::iter::IntoParallelIterator,
-{
-    val.into_par_iter()
-}
-
-/// Converts the value into a sequential iterator if the `multithread` feature is disabled.
-/// Uses the `std::iter::IntoIterator` trait.
-#[cfg(any(
-    not(feature = "multithread"),
-    feature = "unknown-ci",
-    all(target_os = "macos", feature = "apple-sandbox")
-))]
-#[allow(dead_code)]
-pub(crate) fn into_iter<T>(val: T) -> T::IntoIter
-where
-    T: IntoIterator,
-{
-    val.into_iter()
-}
-
 cfg_if! {
     if #[cfg(all(
         feature = "multithread",
         not(feature = "unknown-ci"),
         not(all(target_os = "macos", feature = "apple-sandbox")),
     ))] {
+        /// Converts the value into a parallel iterator if the `multithread` feature is enabled.
+        /// Uses the `rayon::iter::IntoParallelIterator` trait.
+        #[cfg(all(
+            feature = "multithread",
+            not(feature = "unknown-ci"),
+            not(all(target_os = "macos", feature = "apple-sandbox")),
+        ))]
+        #[allow(dead_code)]
+        pub(crate) fn into_iter<T>(val: T) -> T::Iter
+        where
+            T: rayon::iter::IntoParallelIterator,
+        {
+            val.into_par_iter()
+        }
+
         /// Converts the value into a parallel mutable iterator if the `multithread` feature is
         /// enabled. Uses the `rayon::iter::IntoParallelRefMutIterator` trait.
         #[cfg(feature = "component")]
@@ -48,6 +33,16 @@ cfg_if! {
             val.par_iter_mut()
         }
     } else {
+        /// Converts the value into a sequential iterator if the `multithread` feature is disabled.
+        /// Uses the `std::iter::IntoIterator` trait.
+        #[allow(dead_code)]
+        pub(crate) fn into_iter<T>(val: T) -> T::IntoIter
+        where
+            T: IntoIterator,
+        {
+            val.into_iter()
+        }
+
         // In the multithreaded version of `into_iter_mut` above, the `&mut` on the argument is
         // indicating the parallel iterator is an exclusive reference. In the non-multithreaded
         // case, the `&mut` is already part of `T` and specifying it will result in the argument
diff --git a/src/windows/mod.rs b/src/windows/mod.rs
index 1496f01..d3d9e19 100644
--- a/src/windows/mod.rs
+++ b/src/windows/mod.rs
@@ -1,8 +1,6 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-mod groups;
-mod network;
-pub(crate) mod network_helper;
+mod utils;
 
 cfg_if! {
     if #[cfg(feature = "system")] {
@@ -26,17 +24,29 @@ cfg_if! {
 
         pub(crate) use self::component::{ComponentInner, ComponentsInner};
     }
-}
 
-mod sid;
-mod users;
-mod utils;
+    if #[cfg(feature = "network")] {
+        mod network;
+        pub(crate) mod network_helper;
+
+        pub(crate) use self::network::{NetworkDataInner, NetworksInner};
+    }
+
+    if #[cfg(feature = "user")] {
+        mod groups;
+        mod users;
 
-pub(crate) use self::groups::get_groups;
-pub(crate) use self::network::{NetworkDataInner, NetworksInner};
-pub(crate) use self::sid::Sid;
-pub(crate) use self::users::get_users;
-pub(crate) use self::users::UserInner;
+        pub(crate) use self::groups::get_groups;
+        pub(crate) use self::users::get_users;
+        pub(crate) use self::users::UserInner;
+    }
+
+    if #[cfg(any(feature = "user", feature = "system"))] {
+        mod sid;
+
+        pub(crate) use self::sid::Sid;
+    }
+}
 
 #[doc = include_str!("../../md_doc/is_supported.md")]
 pub const IS_SUPPORTED_SYSTEM: bool = true;
diff --git a/src/windows/utils.rs b/src/windows/utils.rs
index 95ca6e8..cc03b89 100644
--- a/src/windows/utils.rs
+++ b/src/windows/utils.rs
@@ -1,14 +1,12 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use std::ops::Deref;
-use windows::core::PWSTR;
-use windows::Win32::Foundation::{CloseHandle, HANDLE};
 #[cfg(feature = "disk")]
 use windows::Win32::Storage::FileSystem::{
     CreateFileW, FILE_ACCESS_RIGHTS, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
 };
 
-pub(crate) unsafe fn to_utf8_str(p: PWSTR) -> String {
+#[cfg(any(feature = "user", feature = "system"))]
+pub(crate) unsafe fn to_utf8_str(p: windows::core::PWSTR) -> String {
     if p.is_null() {
         return String::new();
     }
@@ -19,48 +17,55 @@ pub(crate) unsafe fn to_utf8_str(p: PWSTR) -> String {
     })
 }
 
-pub(crate) struct HandleWrapper(pub(crate) HANDLE);
+cfg_if! {
+    if #[cfg(any(feature = "disk", feature = "system"))] {
+        use windows::Win32::Foundation::{CloseHandle, HANDLE};
+        use std::ops::Deref;
 
-impl HandleWrapper {
-    #[cfg(feature = "system")]
-    pub(crate) fn new(handle: HANDLE) -> Option<Self> {
-        if handle.is_invalid() {
-            None
-        } else {
-            Some(Self(handle))
-        }
-    }
+        pub(crate) struct HandleWrapper(pub(crate) HANDLE);
 
-    #[cfg(feature = "disk")]
-    pub(crate) unsafe fn new_from_file(
-        drive_name: &[u16],
-        open_rights: FILE_ACCESS_RIGHTS,
-    ) -> Option<Self> {
-        let lpfilename = windows::core::PCWSTR::from_raw(drive_name.as_ptr());
-        let handle = CreateFileW(
-            lpfilename,
-            open_rights.0,
-            FILE_SHARE_READ | FILE_SHARE_WRITE,
-            None,
-            OPEN_EXISTING,
-            Default::default(),
-            HANDLE::default(),
-        )
-        .ok()?;
-        Some(Self(handle))
-    }
-}
+        impl HandleWrapper {
+            #[cfg(feature = "system")]
+            pub(crate) fn new(handle: HANDLE) -> Option<Self> {
+                if handle.is_invalid() {
+                    None
+                } else {
+                    Some(Self(handle))
+                }
+            }
+
+            #[cfg(feature = "disk")]
+            pub(crate) unsafe fn new_from_file(
+                drive_name: &[u16],
+                open_rights: FILE_ACCESS_RIGHTS,
+            ) -> Option<Self> {
+                let lpfilename = windows::core::PCWSTR::from_raw(drive_name.as_ptr());
+                let handle = CreateFileW(
+                    lpfilename,
+                    open_rights.0,
+                    FILE_SHARE_READ | FILE_SHARE_WRITE,
+                    None,
+                    OPEN_EXISTING,
+                    Default::default(),
+                    HANDLE::default(),
+                )
+                .ok()?;
+                Some(Self(handle))
+            }
+        }
 
-impl Deref for HandleWrapper {
-    type Target = HANDLE;
+        impl Deref for HandleWrapper {
+            type Target = HANDLE;
 
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
+            fn deref(&self) -> &Self::Target {
+                &self.0
+            }
+        }
 
-impl Drop for HandleWrapper {
-    fn drop(&mut self) {
-        let _err = unsafe { CloseHandle(self.0) };
+        impl Drop for HandleWrapper {
+            fn drop(&mut self) {
+                let _err = unsafe { CloseHandle(self.0) };
+            }
+        }
     }
 }


Test patch:
diff --git a/tests/network.rs b/tests/network.rs
index fafd434..3e753d7 100644
--- a/tests/network.rs
+++ b/tests/network.rs
@@ -2,6 +2,7 @@
 
 // This test is used to ensure that the networks are not loaded by default.
 
+#[cfg(feature = "network")]
 #[test]
 fn test_networks() {
     use sysinfo::Networks;