「オレの名前を言ってみろ…!! オレは誰なんだよ」
…というのはスラムダンクの三井寿の山王工業戦での名セリフです.
自分で作成したシェルスクリプトやC/C++のプログラムの中で実行中の環境が何なのか、知りたい場合があります.
CPU の情報を知る lscpu と, そのソースコードからどのように CPU 情報等を得ているかを調べて、unameシステムコール、そしてCPUのByteorderの取得方法について書いてみます.
Table of Contents
lscpu – display information about the CPU architecture
lscpu コマンドはCPUアーキテクチャ等の情報を表示するコマンドです.
以下は私の Chromebook での実行結果です.
$ lscpu Architecture: aarch64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 Thread(s) per core: 1 Core(s) per socket: 2 Socket(s): 2 Vendor ID: ARM Model: 2 Model name: Cortex-A53 Stepping: r0p2 BogoMIPS: 26.00 Vulnerability Itlb multihit: Not affected Vulnerability L1tf: Not affected Vulnerability Mds: Not affected Vulnerability Meltdown: Not affected Vulnerability Mmio stale data: Not affected Vulnerability Retbleed: Not affected Vulnerability Spec store bypass: Vulnerable Vulnerability Spectre v1: Mitigation; __user pointer sanitization Vulnerability Spectre v2: Mitigation; Branch predictor hardening, BHB Vulnerability Srbds: Not affected Vulnerability Tsx async abort: Not affected Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid
CPU アーキテクチャ、バイトオーダー、CPUコア数などの情報を表示します.
では、lscpu はどうやってこれらの情報を得ているのか、ソースコードを読んで調べてみました.
uname システムコール
lcpu は ulil-linux の一つとして開発されており, 2023年5月11日現在の最新バージョンは 2.39.
ソースコード はここから取得できます.
ソースコードを斜め読みしてみると、どうやらこの lscpu_read_architecture() でCPUアーキテクチャを取得しているようです.
struct lscpu_arch *lscpu_read_architecture(struct lscpu_cxt *cxt)
{
struct utsname utsbuf;
struct lscpu_arch *ar;
struct lscpu_cputype *ct;
assert(cxt);
DBG(GATHER, ul_debug("reading architecture"));
if (uname(&utsbuf) == -1)
err(EXIT_FAILURE, _("error: uname failed"));
ar = xcalloc(1, sizeof(*cxt->arch));
ar->name = xstrdup(utsbuf.machine);
if (cxt->noalive)
/* reading info from any /{sys,proc} dump, don't mix it with
* information about our real CPU */
;
else {
#if defined(__alpha__) || defined(__ia64__)
ar->bit64 = 1; /* 64bit platforms only */
#endif
この関数の冒頭、uname() という関数が実行されてます.
uname コマンドは昔から使っておりましたが, uname() という関数があることは知りませんでした…
man 2 uname に掲載されているところを見るとシステムコールの様です.
NAME
uname - get name and information about current kernel
SYNOPSIS
#include <sys/utsname.h>
int uname(struct utsname *buf);
uname() システムコールを実行すると, struct utsname *buf に示されるアドレスに情報が格納されるようです.
struct utsname は sys/utsname.h に以下の様に定義されてます.
/* Structure describing the system and machine. */
struct utsname
{
/* Name of the implementation of the operating system. */
char sysname[_UTSNAME_SYSNAME_LENGTH];
/* Name of this node on the network. */
char nodename[_UTSNAME_NODENAME_LENGTH];
/* Current release level of this implementation. */
char release[_UTSNAME_RELEASE_LENGTH];
/* Current version level of this release. */
char version[_UTSNAME_VERSION_LENGTH];
/* Name of the hardware type the system is running on. */
char machine[_UTSNAME_MACHINE_LENGTH];
#if _UTSNAME_DOMAIN_LENGTH - 0
/* Name of the domain of this node on the network. */
# ifdef __USE_GNU
char domainname[_UTSNAME_DOMAIN_LENGTH];
# else
char __domainname[_UTSNAME_DOMAIN_LENGTH];
# endif
#endif
};
システムコール uname() を使った簡単なサンプルプログラムを書いてみました.
#include <stdio.h>
#include <sys/utsname.h>
int main ()
{
struct utsname buf;
int st = uname (&buf);
if (st < 0)
{
perror ("uname");
return -1;
}
printf ("sysname: %s\n", buf.sysname);
printf ("nodename: %s\n", buf.nodename);
printf ("release: %s\n", buf.release);
printf ("version: %s\n", buf.version);
printf ("machine: %s\n", buf.machine);
return 0;
}
これを先ほどの 私の Chromebook で実行した結果です.
$ ./uname_sample sysname: Linux nodename: penguin release: 5.10.136-19394-g7a24dee39fa0 version: #1 SMP PREEMPT Wed Oct 12 18:46:02 PDT 2022 machine: aarch64
uname() システムコールを使えば、かなり簡単にC/C++プログラムの中から実行中のCPU情報を得ることができます.
/proc/cpuinfo をオープンして読んでみる、という手もありますが、C/C++での文字列操作は目銅ですからね…
CPUのバイトオーダーを知る
さて、lscpu ではCPUのバイトオーダーも表示していましたが、その情報はどこから来るのか調べてみました.
どうやら以下に示す sysfs_get_byteorder() の中で取得しているようです.
enum sysfs_byteorder sysfs_get_byteorder(struct path_cxt *pc)
{
int rc;
char buf[BUFSIZ];
enum sysfs_byteorder ret;
rc = ul_path_read_buffer(pc, buf, sizeof(buf), _PATH_SYS_CPU_BYTEORDER);
if (rc < 0)
goto unknown;
if (strncmp(buf, "little", sizeof(buf)) == 0) {
ret = SYSFS_BYTEORDER_LITTLE;
goto out;
} else if (strncmp(buf, "big", sizeof(buf)) == 0) {
ret = SYSFS_BYTEORDER_BIG;
goto out;
}
unknown:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
ret = SYSFS_BYTEORDER_LITTLE;
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
ret = SYSFS_BYTEORDER_BIG;
#else
#error Unknown byte order
#endif
out:
return ret;
}
おや? 少し前の util-linux から少しコードが追加されているようだ…
私の記憶に間違いがなければ、少し前のバージョンではCPUバイトオーダーの判定は処理系で定義されている __BYTE_ORDER__ マクロによって行われる、と思っていました.
__BYTE_ORDER__ が __ORDER_LIttLE_ENDIAN__ であれば Little Endian, __ORDER_BIG_ENDIAN__ であれば Big Endian と判断されます.
すなわちプログラムはコンパイルされた時点でエンディアンは決まってしまう、そう思ってました.
ですが、最新版の util-linux を見ると _PATH_SYS_CPU_BYTEORDER で定義されているパスから何やら読みだして CPU のバイトオーダーを判断している様子.
_PATH_SYS_CPU_BYTEORDER は /sys/kernel/cpu_byteorder を指してます.
#define _PATH_SYS_BLOCK "/sys/block"
#define _PATH_SYS_DEVBLOCK "/sys/dev/block"
#define _PATH_SYS_DEVCHAR "/sys/dev/char"
#define _PATH_SYS_CLASS "/sys/class"
#define _PATH_SYS_SCSI "/sys/bus/scsi"
#define _PATH_SYS_CPU_BYTEORDER "/sys/kernel/cpu_byteorder"
#define _PATH_SYS_ADDRESS_BITS "/sys/kernel/address_bits"
ただ私のマシンには /sys/kernel/cpu_byteorder がありません. そのため, sysfs_get_byteorder() の中の ul_path_read_buffer() は失敗し, unknown タグの行に飛んで、従来通り __BYTE_ORDER__ マクロによる判定処理が行われている、と推測します.
/sys/kernel/cpu_byteorder とは?
/sys/kernel/cpu_byteorder とはなんだろ? としらべてみたところ、この様な記事が見つかりました.
Certain files in procfs are formatted in byteorder-dependent formats. For example the IP addresses in /proc/net/udp. When using emulation like qemu-user, applications are not guaranteed to be using the same byteorder as the kernel. Therefore the kernel needs to provide a way for applications to discover the byteorder used in API-filesystems. Using systemcalls is not enough because these are intercepted and translated by the emulation. Also this makes it easier for non-compiled applications like shellscripts to discover the byteorder.
qemu-user のようなエミュレーションを使用する際にカーネルのバイトオーダーを取得したい、という要望があって、機能が追加されたようです.
そのカーネルの機能追加に対応したのが先の sysfs_get_byteorder() の様です. なかなか興味深いことです.
この手の話題はなんだかちょっと興奮します(笑)
気が付いたら 2023年でテックネタを書いたのはこれが初の様で…ちょっと反省しております.
ともかく、この手のハード・ソフト境界ネタは大好物で、コードを読むととても興奮します(笑)
この記事を書くにあたって参考にしたのがこれ, ソフトウェアデザイン.
技術雑誌が少なくなった今、とても貴重な存在です.
では