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を直接利用するはめになるはずだ。下調べしとく。

他言語関数インターフェイス(bsic)

Rustと少しのC (rust std book)

Rust の Foreign Function Interface (FFI)

Rustのツール周りがモダンだって話

RustでCのライブラリを頑張って呼ぶ

MicroSoft 学園

なんだかマイクロソフトもrustに注目しているみたいで、コースが公開されてた。 WSLでリナを導入する才覚が有るぐらいだから、rustもこれから伸ると踏んで、先行投資してるんだだ。

Windowsでrustしたい人は、見ておくと吉かも知れない。

Rust の最初のステップ


This year's Index

Home