Add system-wide file descriptor limits support (/proc/sys/fs)#71
Add system-wide file descriptor limits support (/proc/sys/fs)#71
Conversation
Co-authored-by: Nugine <30099658+Nugine@users.noreply.github.com>
… write function Co-authored-by: Nugine <30099658+Nugine@users.noreply.github.com>
|
@copilot Fix the CI failures |
Co-authored-by: Nugine <30099658+Nugine@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds comprehensive support for reading and writing system-wide file descriptor limits from /proc/sys/fs/ on Linux and Android systems. The implementation creates a new module parallel to the existing proc_limits module, providing access to system-wide resource limits rather than per-process limits.
Key changes:
- New
SysLimitsstruct and API for reading system-wide file descriptor limits (file-max,file-nr,nr_open) - Write operations for
file-maxandnr_open(requires root privileges) - Comprehensive example demonstrating the differences between system-wide and per-process limits
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| src/sys_limits.rs | New module implementing system-wide limits with read/write operations, helper functions, and unit tests |
| src/lib.rs | Module integration to export SysLimits types under Linux/Android platform guards |
| examples/sys_limits.rs | Example demonstrating system-wide vs per-process limits with practical use cases |
| tests/it/linux.rs | Integration test validating SysLimits functionality and data consistency |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/sys_limits.rs
Outdated
|
|
||
| /// Reads file descriptor statistics from `/proc/sys/fs/file-nr`. | ||
| /// | ||
| /// The file contains three tab-separated values: allocated, free, and maximum. |
There was a problem hiding this comment.
The comment states the values are "tab-separated", but the implementation on line 145 uses split_whitespace() which handles any whitespace (tabs, spaces, etc.). While this works correctly since /proc/sys/fs/file-nr typically uses tabs, the documentation should either be updated to say "whitespace-separated" for accuracy or the implementation should explicitly use split('\t') if strictly tab-separated parsing is required.
| /// The file contains three tab-separated values: allocated, free, and maximum. | |
| /// The file contains three whitespace-separated values: allocated, free, and maximum. |
src/sys_limits.rs
Outdated
| use std::io::Write; | ||
|
|
||
| let temp_dir = std::env::temp_dir(); | ||
| let test_file = temp_dir.join("test_file_nr.txt"); |
There was a problem hiding this comment.
The test file name "test_file_nr.txt" in temp_dir could potentially collide with parallel test execution. Consider using a more unique name, such as including the thread ID or using a randomly generated suffix, to prevent race conditions in test environments where tests may run concurrently.
| let test_file = temp_dir.join("test_file_nr.txt"); | |
| let test_file = temp_dir.join(format!( | |
| "test_file_nr_{}.txt", | |
| std::thread::current().id().as_u64() | |
| )); |
|
|
||
| let temp_dir = std::env::temp_dir(); | ||
| let test_file = temp_dir.join("test_u64.txt"); | ||
|
|
||
| // Write test data | ||
| let mut file = fs::File::create(&test_file).unwrap(); | ||
| writeln!(file, "1048576").unwrap(); | ||
| file.flush().unwrap(); | ||
| drop(file); | ||
|
|
||
| // Read and verify | ||
| let result = read_u64_from_file(&test_file).unwrap(); | ||
| assert_eq!(result, 1_048_576); | ||
|
|
||
| // Clean up | ||
| fs::remove_file(&test_file).ok(); |
There was a problem hiding this comment.
Similar to the test_file_nr.txt case, this test file name could collide in parallel test execution. Consider using a unique name to prevent race conditions in concurrent test environments.
| let temp_dir = std::env::temp_dir(); | |
| let test_file = temp_dir.join("test_u64.txt"); | |
| // Write test data | |
| let mut file = fs::File::create(&test_file).unwrap(); | |
| writeln!(file, "1048576").unwrap(); | |
| file.flush().unwrap(); | |
| drop(file); | |
| // Read and verify | |
| let result = read_u64_from_file(&test_file).unwrap(); | |
| assert_eq!(result, 1_048_576); | |
| // Clean up | |
| fs::remove_file(&test_file).ok(); | |
| use tempfile::NamedTempFile; | |
| // Create a uniquely named temporary file | |
| let mut temp_file = NamedTempFile::new().unwrap(); | |
| // Write test data | |
| writeln!(temp_file, "1048576").unwrap(); | |
| temp_file.flush().unwrap(); | |
| // Read and verify | |
| let result = read_u64_from_file(temp_file.path()).unwrap(); | |
| assert_eq!(result, 1_048_576); | |
| // No need to manually clean up; NamedTempFile will remove the file when dropped |
tests/it/linux.rs
Outdated
| let file_max = limits.file_max.unwrap(); | ||
| println!("file_max: {}", file_max); |
There was a problem hiding this comment.
This code unwraps the same optional value twice. After checking that file_max is Some on line 105, the code unwraps it on line 106 and stores it in the file_max variable. However, line 107 then prints this value. The redundant unwrap on line 106 shadows the outer scope and should be removed or the variable should be reused directly.
tests/it/linux.rs
Outdated
| let nr_open = limits.nr_open.unwrap(); | ||
| println!("nr_open: {}", nr_open); |
There was a problem hiding this comment.
Similar to the file_max pattern above, this code unwraps nr_open after verifying it exists. However, unlike file_max which is used in an assertion on line 119, this unwrapped value is only used for printing. Consider directly printing without the intermediate variable or ensuring consistency with the file_max pattern.
| /// File descriptor usage statistics from `/proc/sys/fs/file-nr`. | ||
| #[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))] | ||
| #[derive(Debug, Clone, Default, PartialEq, Eq)] | ||
| pub struct FileNr { | ||
| /// Number of allocated file descriptors. | ||
| pub allocated: u64, | ||
| /// Number of free file descriptors. |
There was a problem hiding this comment.
The "free" field in /proc/sys/fs/file-nr has been deprecated since Linux 2.6 and is always 0 in modern kernels. The documentation should mention that this field is deprecated and always returns 0 to avoid confusion for users who might expect it to contain meaningful data.
| /// File descriptor usage statistics from `/proc/sys/fs/file-nr`. | |
| #[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))] | |
| #[derive(Debug, Clone, Default, PartialEq, Eq)] | |
| pub struct FileNr { | |
| /// Number of allocated file descriptors. | |
| pub allocated: u64, | |
| /// Number of free file descriptors. | |
| /// File descriptor usage statistics from `/proc/sys/fs/file-nr`. | |
| /// | |
| /// **Note:** The "free" field in `/proc/sys/fs/file-nr` has been deprecated since Linux 2.6 and is always 0 in modern kernels. | |
| #[cfg_attr(docsrs, doc(cfg(any(target_os = "linux", target_os = "android"))))] | |
| #[derive(Debug, Clone, Default, PartialEq, Eq)] | |
| pub struct FileNr { | |
| /// Number of allocated file descriptors. | |
| pub allocated: u64, | |
| /// Number of free file descriptors. | |
| /// | |
| /// **Deprecated:** This field in `/proc/sys/fs/file-nr` has been deprecated since Linux 2.6 and is always 0 in modern kernels. | |
| /// It does not contain meaningful data. |
src/sys_limits.rs
Outdated
| /// | ||
| /// This corresponds to `/proc/sys/fs/file-nr` and contains three values: | ||
| /// - `allocated`: Number of allocated file descriptors | ||
| /// - `free`: Number of free file descriptors |
There was a problem hiding this comment.
The documentation states that "free" represents the number of free file descriptors, but this field has been deprecated since Linux 2.6 and is always 0 in modern kernels. Consider clarifying this in the documentation to avoid misleading users.
|
@copilot apply changes based on the comments in this thread |
…precated fields Co-authored-by: Nugine <30099658+Nugine@users.noreply.github.com>
Applied all changes from the review in 12c30eb:
|
This PR adds comprehensive support for reading and writing system-wide file descriptor limits from
/proc/sys/fs/on Linux and Android systems, addressing the distinction between system-wide limits (file-max) and per-process limits (NOFILE).Changes
New module
sys_limitsprovides:SysLimits::read()- readsfile-max,file-nr, andnr_openfrom/proc/sys/fs/SysLimits::set_file_max(u64)- sets system-wide file descriptor maximum (requires root)SysLimits::set_nr_open(u64)- sets per-process NOFILE ceiling (requires root)New types:
SysLimits- container for system-wide limitsFileNr- current file descriptor usage stats (allocated, free, maximum)Example usage:
See
examples/sys_limits.rsfor a comparison between system-wide and per-process limits.Documentation
freefield inFileNrhas been deprecated since Linux 2.6 and always returns 0 in modern kernelsTesting
Security
No security vulnerabilities were introduced. The implementation:
#![deny(unsafe_code)])By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
Original prompt
/proc/sys/fs/file-max#50💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.