取得執行環境的作業系統資訊是開發軟體時經常會需要被實作的功能,Rust作為一個系統級的程式語言,對於這部份自然會有許多解決方案。不過也由於Rust程式語言跨作業系統,因此許多取得系統資訊的套件也會以跨平台支援為主,這就導致取得到的資訊可能過於低階、不夠充份或是不統一,使得開發者還需要自行撰寫程式將零散的資訊處理為一般人習慣閱讀的格式。又或者取得系統資訊的方式是依靠系統中提供的指令工具,使得程式還得建立新的行程(process),呼叫外部執行檔來取得結果。這些都會導致程式為了取得系統資訊而用了許多硬體資源,有點本末倒置的感覺。



在Linux作業系統上,就算不使用lscpufreeps等查看系統資訊的指令工具,有些系統資訊還是可以直接透過呼叫libc內的函數來取得,而libc無法取得的部份,則大多可以依靠讀取/proc或是/sys目錄中的檔案來取得。

原則上,能夠用libc取得的資料,為求穩定的介面,就會優先使用libc來取得。例如主機名稱(hostname),要在Rust使用libc這個crate來取得主機名稱,程式碼可以這樣寫:

use std::io;

pub fn get_hostname() -> Result<String, io::Error> {
    let buffer_size = unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) } as usize;

    let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);

    unsafe {
        buffer.set_len(buffer_size);
    }

    let c = unsafe { libc::gethostname(buffer.as_mut_ptr() as *mut libc::c_char, buffer_size) } as usize;

    if c != 0 {
        return Err(io::Error::last_os_error());
    }

    if let Some(end) = buffer.iter().copied().position(|e| e == b'\0') {
        unsafe {
            buffer.set_len(end);
        }
    }

    Ok(unsafe { String::from_utf8_unchecked(buffer) })
}

使用Rust程式語言中透過libc或是/proc/sys檔案讀取系統資訊時,大部份的情況下都可以直接使用Rust的不安全模式(unsafe)來將字串類型的資料轉成字串(String)或是字串切片(& str),省去UTF-8的檢查,增加程式執行的效能。

而當遇到在/proc或是/sys目錄下皆可以取得同樣的系統資訊的情況時,選擇選擇讀取/proc目錄下的檔案會比較好。根據筆者的測試,/proc目錄的讀取速度比/sys目錄的讀取速度還要快,甚至可以差到兩倍左右。

筆者其實不太建議使用其它比較高階的套件來抓取Linux系統資訊,因為那些套件大多是提供比較通用解決方案,總是會去抓到根本用不到的資訊。所以依照自己的需求去抓資訊才是最好的。

M Prober Lib

「M Prober Lib」是筆者開發的套件,專門用來蒐集Linux作業系統的系統資訊,像是主機名稱、Linux核心名稱、開機時間點(btime)、已開機時間(uptime)、系統RTC時間、CPU、記憶體、網路、硬碟(或分割區、邏輯捲軸)和行程。原先是直接做在M Prober上的功能,後來將它們獨立成這個套件,注重效能,不會去抓取(筆者自己認為)用不太到的資訊。

如果是在開機之後就幾乎不能被改變的資訊(例如開機時間點、記憶體的分頁大小等),這個套件或是這個套件用到的相依套件,都會自動對其做快取的動作。

Crates.io

Cargo.toml

mprober-lib = "*"

使用方法

mprober_lib這個crate底下根據系統資訊的類型,區分了不同的模組,每個模組底下都有函數可以調用,抓取系統資訊並回傳特定的結構實體。

use mprober_lib::*;

println!("{}", hostname::get_hostname().unwrap());
println!("{}", kernel::get_kernel_version().unwrap());
println!("{}", btime::get_btime());
println!("{}", rtc_time::get_rtc_date_time().unwrap());
println!("{:#?}", uptime::get_uptime().unwrap());
println!("{:#?}", load_average::get_load_average().unwrap());
println!("{:#?}", cpu::get_cpus().unwrap());
println!("{:#?}", memory::free().unwrap());
println!("{:#?}", volume::get_volumes().unwrap());
println!("{:#?}", network::get_networks().unwrap());
println!("{:#?}", process::get_processes_with_stat(&process::ProcessFilter::default()).unwrap().into_iter().map(|(process, _)| process).collect::<Vec<process::Process>>());