0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

FreeBSD でプログラムからシステムやプロセスの情報を取得する

Last updated at Posted at 2022-11-20

Linux の場合

Linux の場合 procfs があるので、/proc 以下のファイルを開いて読み込んで解釈すればよい。例えば

#include <stdio.h>
#include <string.h>

size_t read_process_size(){
    const size_t SIZE = 2048;
    size_t virtual_size = 0;
    size_t nbytes = 0;
    int read_size = 0;
    char buf[SIZE];

    FILE* fp = fopen("/proc/self/status", "rb");
    nbytes = fread(buf, 1, SIZE-1, fp);
    buf[SIZE] = '\0';
    char *memp = strstr(buf, "VmSize:");
    read_size = sscanf(memp, "%*s %u", &virtual_size);
    fclose(fp);
    return(virtual_size);
}

という感じに、読み取った文字列を処理することになる。
なんでもファイルシステムにしてしまおうという思想のもとに、プログラムとしては結構簡単なものになっている。サンプルなのでエラー処理が一切ないのは赦してほしい。

FreeBSD では

同じことはできる

FreeBSD にも procfs はあるので同様のことはできる。
いや、出力フォーマットも Linux と一緒にしてくれないと、同じコードが使い回せないから困るんだけどという場合は、linprocfs というカーネルモジュールがあるのでそちらを使えば良い。実際、Linuxエミュレーションで Linux のプログラムを動かす時にこちらが必要になることがある。

procfs がいつも使えるとは限らない

しかし、FreeBSD の場合 procfs はオプショナルなもので、どのシステムでも /proc をマウントしてくれているとは限らない。FreeBSD なら動くというプログラムでは、/proc の存在に頼らないようにしなければならない。
ではどうすればよいかというと、答えは /proc がマウントされていなくてもシステムやプロセスの情報を取得してくれる、ベースシステムの ps コマンドや top コマンドのソースを見ればよいのだが、それをちょっと解説してみよう。

システム情報 kvm および sysctl

FreeBSDで ps コマンドや top コマンドがシステムやプロセスの情報を取得するのに使っている、公式のインターフェースは kvm: kernel memory interface である(Linux の KVM とは違うので注意)。これを使う場合は、libkvm をリンクする必要がある。
場合によっては、それと sysctl コマンドで得られる情報を組み合わせる必要があり、こちらは sysctl関数で取得できる。こちらは libc に含まれる。

例として、システム情報のうち swap のサイズ、プロセス情報のうち仮想サイズを取得するプログラムは以下のようになる。

sysctl

後述する kvm で取得できるメモリの量はメモリページの数で示されるので、kB に変換するためには、1ページが何kBか知る必要があり、それは sysctl で取得する。

#include <sys/sysctl.h>

int mib[2];
int pagesize = 0;
size_t len;

mib[0] = CTL_HW;
mib[1] = HW_PAGESIZE;
len = sizeof(pagesize);
sysctl(mib, 2, &pagesize, &len, NULL, 0);

これはコマンドでは sysctl hw.pagesize に相当する。
mib に指定可能な定数(つまりどのような値を取得可能か)と値を取得する変数の型について、代表的なものは sys/sysctl.h に定義されている。また、sysctl 関数のmanページにも載っている。
今回の場合は、pagesize を取得するために指定する2番目の定数は

/*
 * CTL_HW identifiers
 */
#define HW_PAGESIZE      7              /* int: software page size */

となっているので、1番目の定数は CTL_HW で、値を取得する変数の型は int であることが分かる。
なお、5番目と6番目の引数は、値を変更する際に指定するものなので、値を取得する場合はそれぞれ NULL0 を指定しておく。

残念ながら sysctl 変数を網羅したドキュメントはないため、ここに載っていない sysctl 変数を取得したい場合、kernel のソースコードから sysctl 変数を公開している箇所を探す必要がある。hw.pagesize は sys/kern/kern_mib.c で次のように定義されており、型が int であることが分かる。

SYSCTL_INT(_hw, HW_PAGESIZE, pagesize, CTLFLAG_RD|CTLFLAG_CAPRD,
    SYSCTL_NULL_INT_PTR, PAGE_SIZE, "System memory page size");

sys/sysctl.h には sysctl コマンドと同じ形式で取得できる sysctlbyname 関数も定義されており、次の形でも取得できるが、変数の型はいずれにせよ調べる必要がある。

sysctlbyname("hw.pagesize", &pagesize, &len, NULL, 0);

kvm

kvm でのデータ取得は、おおよそ kvm_openfiles 関数で開き、 kvm_getswapinfokvm_getprocs などのデータを読み込む関数で読み込み、kvm_close で閉じるという、ファイルの読み込みと似た形式になっている。

#include <stdio.h>
#include <kvm.h>
#include <fcntl.h>
#include <paths.h>
#include <limits.h>

kvm_t *kd;
char errbuf[_POSIX2_LINE_MAX];
struct kvm_swap ks;

kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf);
kvm_getswapinfo(kd, &ks, 1, 0);
printf("swap used: %lu pages.", ks.ksw_used);
kvm_close(kd);

kvm_getswapinfo でデータを取得する kvm_swap 構造体は kvm.h で定義されているが、 kvm_getprocs でデータを取得する kinfo_procsys/user.h で定義されている。

#include <kvm.h>
#include <unistd.h>
#include <sys/user.h>

struct kinfo_proc *kp;
int nentries = -1;

kp = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &nentries);
printf("process virtual size: %lu bytes", kp->ki_size);

サンプルプログラム

以上をまとめてエラー処理もきちんとつけるとこんな感じになる。ビルド時に -lkvm をお忘れなく。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <kvm.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <unistd.h>

int main()
{
    int mib[2];
    int pagesize = 0;
    size_t len;

    kvm_t *kd;
    const char *nlistf, *memf;
    char errbuf[_POSIX2_LINE_MAX];
    struct kvm_swap ks;
    struct kinfo_proc *kp;
    int nentries = -1;

    mib[0] = CTL_HW;
    mib[1] = HW_PAGESIZE;
    len = sizeof(pagesize);
    if (sysctl(mib, 2, &pagesize, &len, NULL, 0) == -1) {
        fprintf(stderr, "sysctl failed.\n");
        return(1);
    }
    const size_t pagekb = pagesize / 1024;

    nlistf = NULL;
    memf = _PATH_DEVNULL;
    kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
    if (kd == NULL) {
        fprintf(stderr, "%s\n", errbuf);
        return(1);
    }
    if (kvm_getswapinfo(kd, &ks, 1, 0) == -1) {
        fprintf(stderr, "%s\n", kvm_geterr(kd));
    }
    printf("swap used: %lu kb.\n", pagekb * ks.ksw_used);
    printf("swap total: %lu kb.\n", pagekb * ks.ksw_total);

    kp = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &nentries);
    if (kp == NULL || nentries < 0) {
        fprintf(stderr, "%s\n", kvm_geterr(kd));
    }
    if (nentries > 0) {
        printf("process virtual size: %lu kb.\n", kp->ki_size / 1024);
    }

    if (0 != kvm_close(kd)) {
        fprintf(stderr, "%s\n", strerror(errno));
    }

    exit(0);
}

詳しい情報

使い方について上記で足りない場合は、ひとまず前掲の /bin/ps のソースコードを見るのがよいだろう。
その上で、sysctl については sysctl(3) で足りると思うが、kvm については kvm(3) を起点に manpage をいろいろ読む必要があるだろう。

ほかの言語では

C/C++ では libkvm を使って上記のようにプログラムすることになろうが、ほかの言語ではクロスプラットフォームのライブラリがあることが多いので(例えば python では psutil)それを探して利用するのがよいだろう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?