LoginSignup
4
1

More than 5 years have passed since last update.

[Linux] Delphi で proc ディレクトリからハードウェア情報を取得する

Last updated at Posted at 2018-09-29

ハードウェア情報の取得

OS からハードウェア情報を取得する方法は OS 毎に様々です。

例えば Windows だったら Win32 API を呼んで取ったり WMI (Windows Management Instrumentation) を使って取ったりできます。

同様に Linux もいくつか取り方があるようですが一番簡単なのが proc ディレクトリから情報を読み出す、という方法です。
UNIX 系 OS はデバイスなどもファイルシステムにマッピングされるため、ハードウェアの情報もファイルとして取り出せます。それらの情報があるのが proc ディレクトリです。

ためしに Ubuntu (Windows Store 版) で、proc ディレクトリを見てみるとこんな感じです。

xxx@XXX:/$ ls -alF /proc/
total 0
dr-xr-xr-x 9 root root    0 Sep 29 17:24 ./
drwxr-xr-x 1 root root 4096 Sep 29 17:23 ../
dr-xr-xr-x 7 root root    0 Sep 29 17:24 1/
dr-xr-xr-x 7 root root    0 Sep 29 18:05 3/
dr-xr-xr-x 7 jun  xxx     0 Sep 29 18:05 4/
dr-xr-xr-x 7 jun  xxx     0 Sep 29 18:05 6298/
dr-xr-xr-x 2 root root    0 Sep 29 17:24 bus/
-r--r--r-- 1 root root    0 Sep 29 17:24 cgroups
-r--r--r-- 1 root root    0 Sep 29 17:24 cmdline
-r--r--r-- 1 root root    0 Sep 29 17:24 cpuinfo
-r--r--r-- 1 root root    0 Sep 29 17:24 filesystems
-r--r--r-- 1 root root    0 Sep 29 17:24 interrupts
-r--r--r-- 1 root root    0 Sep 29 17:24 loadavg
-r--r--r-- 1 root root    0 Sep 29 17:24 meminfo
lrwxrwxrwx 1 root root    0 Sep 29 17:24 mounts -> self/mounts
lrwxrwxrwx 1 root root    0 Sep 29 17:24 net -> self/net/
lrwxrwxrwx 1 root root    0 Sep 29 17:24 self -> 6298/
-r--r--r-- 1 root root    0 Sep 29 17:24 stat
dr-xr-xr-x 6 root root    0 Sep 29 17:24 sys/
dr-xr-xr-x 2 root root    0 Sep 29 17:24 tty/
-r--r--r-- 1 root root    0 Sep 29 17:24 uptime
-r--r--r-- 1 root root    0 Sep 29 17:24 version
-r--r--r-- 1 root root    0 Sep 29 17:24 version_signature

色々なファイル・ディレクトリがあります。
それらが、どのような意味を持つのかは検索してみて下さい

ここでは、この中の1つ "cpuinfo" を見てみます。
cat で中身を表示すると

xxx@XXX:/$ cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 94
model name      : Intel(R) Core(TM) i7-6700T CPU @ 2.80GHz
stepping        : 3
(以下略)

こんな風に CPU の情報が取得できました。
つまり cpuinfo はその名の通り CPU の情報を返すファイル、ということです。

さて、では proc ディレクトリ配下のファイルを プログラムから、しかも Delphi から取得するには、どのようにするのでしょうか?

Delphi で読み出す

proc 配下のファイルも当然ファイルなので、普通にファイルとして扱えます。

…しかし!そこに罠が!

例えば、次のように…

procedure ReadCPUInfo;
var
  SL: TStringList;
begin
  SL := TStringList.Create;
  try
    SL.LoadFromFile('/proc/cpuinfo');
    Writeln('----------');
    Writeln(SL.Text);
    Writeln('----------');
  finally
    SL.DisposeOf;
  end;
end;

とすると、表示されるのは…

----------

----------

上記のようになり、SL.Text の部分は空行になっています。

何故空行になるかは、先ほどの ls proc の内容を見ると判ります。

cpuinfo
-r--r--r-- 1 root root    0 Sep 29 17:24 cpuinfo

となっていて、ファイルサイズが 0 になっているのです!
そして、TStringList.LoadFromFile の内部から呼ばれる TStrings.LoadFromStream は、ファイルサイズを見てバッファを確保するため 0 バイトを返すファイルは読み込めません!

すごく当たり前の実装なのですが、今回のような場合は使えません。

罠の回避

では、どのように読み込むかというと、

  1. File Stream を 1 バイト読み込む。
  2. 読み出したバイト数が 1 の場合 1 に戻り、それ以外の場合は終了。

とするだけです。

それを実装すると下記の様になります。

function ReadCPUInfo: String;
var
  Ch: Char;
  FS: TFileStream;
begin
  Result := '';
  FS := TFileStream.Create('/proc/cpuinfo', fmOpenRead);
  try
    while FS.Read(Ch, 1) = 1 do // 1バイト読み込んだ戻値が 1 の間処理を継続
      Write(Ch);
    Writeln;
  finally
    FS.DisposeOf;
  end;
end;

これを実行すると下記の出力が得られ、先に実行した cat /proc/cpuinfo と同じ結果になりました。

processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 94
model name      : Intel(R) Core(TM) i7-6700T CPU @ 2.80GHz
stepping        : 3
(以下略)

ここでは cpuinfo だけ読むようにしてみましたが、他にも色々取得できます。
ですので、汎用化してみます。

汎用化

汎用化したソースが以下です。
短いので全文載せて起きます。

(*
 * Hardware Information reader from proc dicrectory.
 *
 * PLATFORMS
 *   Linux
 *
 * LICENSE
 *   Copyright (c) 2018 HOSOKAWA Jun
 *   Released under the MIT license
 *   http://opensource.org/licenses/mit-license.php
 *
 * USAGE
 *   Call ReadHardInfo with information filename.
 *
 * SAMPLE
 *   Info := TProcDirectory.ReadInfo('cpuinfo');
 *
 * 2018/09/28 Version 1.0.0
 * Programmed by HOSOKAWA Jun (twitter: @pik)
 *)

 unit PK.HardInfo.ProcDirectory.Linux;

interface

type
  TProcDirectory = record
  private const
    PROC_PATH = '/proc';
  public
    class function ReadInfo(const iFilename: String): String; static;
  end;

implementation

uses
  System.Classes,
  System.SysUtils,
  System.IOUtils;

class function TProcDirectory.ReadInfo(const iFilename: String): String;
var
  Ch: Char;
  Path: String;
  FS: TFileStream;
  SB: TStringBuilder;
begin
  Path := TPath.Combine(PROC_PATH, iFilename);
  if not TFile.Exists(Path) then
    Exit('');

  FS := TFileStream.Create(Path, fmOpenRead);
  try
    SB := TStringBuilder.Create;
    try
      // The file in the proc directory, it's size return -1,
      // Therefore, only one byte can be read.
      while FS.Read(Ch, 1) = 1 do
        SB.Append(Ch);

      Result := SB.ToString;
    finally
      SB.DisposeOf;
    end;
  finally
    FS.DisposeOf;
  end;
end;

end.

使う時は、こんな風にします。

Writeln(TProcDirectory.ReadInfo('cpuinfo'));

まとめ

なお、この罠は当然 Delphi に限ったことではないです。
やはり、各 OS について詳しく知っていないと、マルチプラットフォームアプリケーションの開発は難しいですね!

4
1
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
4
1