uname and ps
rustcは知っている
本家の解説を見てたら、さりげなく紹介されてたので、
sakae@pen:/tmp/rust-psutil$ rustc --print=cfg debug_assertions target_arch="x86_64" target_endian="little" target_env="gnu" target_family="unix" target_feature="fxsr" target_feature="sse" target_feature="sse2" target_os="linux" target_pointer_width="64" target_vendor="unknown" unix
デビアンの64Bitと言う本流
vbox$ rustc --print=cfg debug_assertions target_arch="x86" target_endian="little" target_env="" target_family="unix" target_os="openbsd" target_pointer_width="32" target_vendor="unknown" unix
オイラーが大好きなやつ
uname
前回、psutilを縮退運転した時、OSの名前と石が、そんなの知らないと言うつれない返答だった。rustは、OSや石の組み合わせで、勝手に3つのグループに格付けしてるんだ。
最上位は、Tier one,常に正しく動く亊を保証してるやつ。Linux,macOS,Windowsが該当。次の下の階層は、一応コンパイル出来るよってレベル。該当するやつはFreeBSD等だ。そして最下位はレベル3のグループ。まあ、そんなのあったねって、ほとんど爪弾きのやつ。該当するのは、OpenBSD、石は過去になりつつあるi386みたいな奴ね。
格付けのリストは、 Registered Platforms に掲載されている。
悔しいんで、OpenBSDかつ古い石を使ってみる。
vbox$ uname -a OpenBSD vbox.local.jp 7.0 GENERIC.MP#3 i386
rust流に言うと、文句なく映す価値無しっていう代物。正月番組でしつこく浜ちゃんが司会してた番組のあれだ。
vbox$ man -s 3 uname : SYNOPSIS #include <sys/utsname.h> int uname(struct utsname *name); sysname Name of the operating system implementation. nodename Network name of this machine. release Release level of the operating system. version Version level of the operating system. machine Machine hardware platform.
3流品以下でも、マニュアルはどこかのリナと違って、超一流品だ。これが分れば、簡単にコードに落せる。
#include <sys/utsname.h> #include <stdio.h> int main(int argc, char *argv[]) { struct utsname u; uname(&u); printf("%s\n", u.sysname); printf("%s\n", u.machine); }
パクッてきた証拠が、そこはかとなく残っているな。
vbox$ ./a.out OpenBSD i386
面倒だから、rustでUNknownと言われた部分だけをだしてみた。
ついでに、unameがやってる亊を確認しとく。/usr/src/lib/libc/gen/uname.c
uname(struct utsname *name) { int mib[2], rval; size_t len; rval = 0; mib[0] = CTL_KERN; mib[1] = KERN_OSTYPE; len = sizeof(name->sysname); if (sysctl(mib, 2, &name->sysname, &len, NULL, 0) == -1) rval = -1;
ふむ、カーネルの変数エリアを丁寧に呼び出しているんだな。
ob$ sysctl -a | grep OpenBSD kern.ostype=OpenBSD kern.version=OpenBSD 7.0 (GENERIC.MP) #3: Wed Dec 15 13:14:26 MST 2021
port from psutil
psutilからInfoを表示する部分だけを移植してみた。
// from psutil use nix::sys; #[derive(Clone, Debug)] pub struct Info { pub(crate) operating_system: String, pub(crate) release: String, pub(crate) version: String, pub(crate) hostname: String, pub(crate) architecture: String, } impl Info { pub fn operating_system(&self) -> &str { &self.operating_system } pub fn release(&self) -> &str { &self.release } pub fn version(&self) -> &str { &self.version } pub fn hostname(&self) -> &str { &self.hostname } pub fn architecture(&self) -> &str { &self.architecture } } pub fn info() -> Info { let utsname = sys::utsname::uname(); let operating_system = utsname.sysname().to_string(); let release = utsname.release().to_string(); let version = utsname.version().to_string(); let hostname = utsname.nodename().to_string(); let architecture = utsname.machine().to_string(); Info { operating_system, release, version, hostname, architecture, } } fn main() { println!("From psutil"); let myinfo = info(); dbg!(myinfo); }
必要な木箱は、これだけだ。 Cargo.toml に追加しとく。
[dependencies] nix = "0.23.1"
まずは、一流プラットフォームで確認
sakae@deb:/tmp/myinfo$ cargo r Finished dev [unoptimized + debuginfo] target(s) in 0.02s Running `target/debug/myinfo` From psutil [src/main.rs:51] myinfo = Info { operating_system: "Linux", release: "5.10.0-10-686-pae", version: "#1 SMP Debian 5.10.84-1 (2021-12-08)", hostname: "deb", architecture: "i686", } # OSが報告したやつ。 sakae@deb:/tmp/myinfo$ uname -a Linux deb 5.10.0-10-686-pae #1 SMP Debian 5.10.84-1 (2021-12-08) i686 GNU/Linux
構造体の一部を出そうとして、貸借り問題が発生した。で、エラーの対処法が出て来た。
error[E0382]: borrow of moved value: `myinfo` --> src/main.rs:54:20 | 51 | let myinfo = info(); | ------ move occurs because `myinfo` has type `Info`, which does not implement the `Copy` trait 52 | let info = myinfo.clone(); 53 | dbg!(myinfo); | ------------- value moved here 54 | println!("{}", myinfo.operating_system); | ^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move For more information about this error, try `rustc --explain E0382`. error: could not compile `myinfo` due to previous error
言われた通りに、rustc –explain E0382 する。
fn main() { println!("From psutil"); let myinfo = info(); let info = myinfo.clone(); dbg!(myinfo); println!("{}", info.operating_system); println!("{}", info.hostname); }
そして、こうなった。
[src/main.rs:53] myinfo = Info { operating_system: "OpenBSD", release: "7.0", version: "GENERIC.MP#3", hostname: "vbox.local.jp", architecture: "i386", } OpenBSD vbox.local.jp
ps
次なるトライは、rssの表示だな。psutilを使って、軽くやってたけど、OpenBSDは3流以下なんで、それをコンパイル出来無いのよ。で、別の方法を模索してみる。
メモリーの使用具合をどうやって取得してるか、psコマンドを覗いてみる。-oで、好きなデータだけを表示出来るので、rssとcommandだけを選んでみた。
(gdb) b ps.c:370 Breakpoint 2 at 0x15776c95: file ps.c, line 370. (gdb) r -orss,command Starting program: /tmp/ps/ps -orss,command : (gdb) c Continuing. 23256 emacs (emacs-27.2)
それはいいんだけど、特別なlibkvmなんてライブラリィーを使ってるな。
kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); : kp = kvm_getprocs(kd, what, flag, sizeof(*kp), &nentries); for (i = 0; i < nentries; i++) kinfo[i] = &kp[i]; for (i = lineno = 0; i < nentries; i++) { : for (vent = vhead; vent; vent = vent->next) { (vent->var->oproc)(kinfo[i], vent); if (vent->next != NULL) (void)putchar(' '); } (void)putchar('\n');
何を欲しいかwhatに指定してからgetprocsする。すると目当てな個数がnentriesに入って來るんで、それだけの回数を回してあげる。ventのループ内で、rssとかの個別のデータを出力してくって寸法。
rustに移植しようとすると、超大変そう。
別解
問題部分はこれ
println!( "プロセスの使用メモリ量(参考):{:?}MB", psutil::process::Process::new(std::process::id()) .unwrap() .memory_info() .unwrap() .rss() as f64 / 1e6 );
これを置き換えたい。pidを得て、それのrssを取得、表示だ。まずは、pidを得る部分。
use std::process; fn main() { println!("My pid is {}", process::id()); }
stdなソースを検索したら、ずばりな例が出て来た。
sakae@deb:~$ ps --no-headers -orss -p 2286 41668
そしてこれは、指定したpidのrssを取得するやつ。例はあらかじめ調べておいたemacsのセッション使用状況。OpenBSDは –no-headersが無い代わりに、下記のようにすれば良い。
ob$ ps -o rss='',command -p 95873 COMMAND 34360 emacs src/main.rs (emacs-27.2) ob$ ps -o rss='' -p 95873 34360
pidとpsの合わせ技を簡単に組込めるようにした。
fn rss() { let pid = std::process::id().to_string(); let output = std::process::Command::new("ps") .args(&["-o rss= ", "-p", &pid]) .output().unwrap_or_else(|e| { panic!("failed to execute process: {}", e) }); if output.status.success() { let s = String::from_utf8_lossy(&output.stdout); print!("RSS = {}", s); } else { let s = String::from_utf8_lossy(&output.stderr); print!("ps failed and stderr was:\n{}", s); } }
これを作るきっかけとなったrustとpythonの力比べがOpenBSDではrustの年式違いで動かないので、簡単な答合わせコードを作った。
fn main(){ let mut v = Vec::new(); for i in 1 .. 10_000_000 { v.push(i); } println!("Wait until RET key"); let mut dummy = String::new(); std::io::stdin().read_line(&mut dummy) .expect("Failed to read line"); rss(); }
走り始めたら、大きなサイズのベクターを作成。そこでKEY入力待ちになる。その時、別端末からpsでサイズ確認。その後、rss()での結果を確認。
ob$ ps -o rss=,command -a COMMAND 41544 target/debug/rss
ob$ cargo r Finished dev [unoptimized + debuginfo] target(s) in 0.03s Running `target/debug/rss` Wait until RET key RSS = 41556
合ってて良かったな。今迄、天下り的にrssって言ってたけど、その正体は? ps(1)に答があった。
rss The real memory (resident set) size of the process (in 1024 byte units).
本番の試験
巷では、共通試験の真っ盛りみたいだけど、オイラーもやってみる。ってんで、元ネタに組込む。アプリがリナでしか動かないので、それ用にちょいと引数の部分を修整。
fn rss() { let pid = std::process::id().to_string(); let output = std::process::Command::new("ps") .args(&["-orss", "--no-headers", "-p", &pid]) :
いざ試験。結果の関係部分だけを抜き出し。
RSS = 161172 プロセスの使用メモリ量(参考):165.040128MB : RSS = 237576 プロセスの使用メモリ量(参考):243.277824MB :
微妙に答が違うぞ。
sakae@deb:~$ bc scale=5 165.040128 * 1000 / 1024 161.17200
と、焦る亊なかれ。元の表示は、素人さんにも分かり易いように、1000の単位で表示してる。それに対してrss()の方はIT屋さんが好きな単位で表示。こういうのが、詐欺に使われる。
そもそも、こういうデータの観察機構は、自分の所に取り込んでしまうのって、どうよ。 何か事故が起きた時、事故調査委員会が設置されるけど、その委員が自己組織から選出してて、世間の袋叩きにあう亊がある。で、第三者に依頼して鎮静化される。
今回の事例では、rssを取って來るのに、外部アプリのpsを使った。これが正しい道だろう。 そうすれば、psutilを駆逐出来るしね。結局、これが言いたかった訳ね。
組み込んでみるかな
psutilの置き換えって亊で、今回は容易に別ルートが取れたけど、そうそう上手く行く亊も無い場合があろう。そんな時は、どうしてもFFIを直接利用するはめになるはずだ。下調べしとく。
MicroSoft 学園
なんだかマイクロソフトもrustに注目しているみたいで、コースが公開されてた。 WSLでリナを導入する才覚が有るぐらいだから、rustもこれから伸ると踏んで、先行投資してるんだだ。
Windowsでrustしたい人は、見ておくと吉かも知れない。