gradleでのテスト関係のオプション例

ビルドのみ(テストしない)

./gradlew build -x test

指定のテストを実行

./gradlew test --tests "SomeTest.*"

gitで特定の行範囲の変更を追跡する

-L開始行,終了行:ファイル名オプションを使えばよい 例
git log -L 4,11:.screenrc

シリアルコンソールで、ターミナルサイズを変更する方法

シリアルコンソールを使うとデフォルトでは、ターミナルサイズが80x25になっていることが多い。
# stty size
25 80
これを実際の仮想端末のサイズにフィットするには以下のコマンドを実行する
# setterm --resize
すると、ターミナルサイズが変更される
# stty size
51 204

Ubuntu上で新しいバージョンのDebianのLXCコンテナを作る方法

例えば、Ubuntu 20.04上でDebian12のLXCコンテナを作成すると、'signed by unknown key'(知らない鍵で署名されている)と表示されエラーとなる。
# lxc-create -t debian -n deb12 -- -r bookworm
debootstrap is /usr/sbin/debootstrap
Checking cache download in /var/cache/lxc/debian/rootfs-bookworm-amd64 ...
gpg: key 7638D0442B90D010: 4 signatures not checked due to missing keys
gpg: key 7638D0442B90D010: "Debian Archive Automatic Signing Key (8/jessie) " not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
Downloading debian minimal ...
I: Retrieving InRelease
I: Checking Release signature
E: Release signed by unknown key (key id F8D2585B8783D481)
   The specified keyring /var/cache/lxc/debian/archive-key.gpg may be incorrect or out of date.
   You can find the latest Debian release key at https://ftp-master.debian.org/keys.html
Failed to download the rootfs, aborting.
Failed to download 'debian base'
failed to install debian
lxc-create: deb12: lxccontainer.c: create_run_template: 1627 Failed to create container from template
lxc-create: deb12: tools/lxc_create.c: main: 317 Failed to create container deb12
次のように鍵をダウンロードして、ホストOSに登録してから、再度lxc-createを実行すればよい。
wget https://ftp-master.debian.org/keys/archive-key-12.asc -O - --quiet \
| gpg --import --no-default-keyring --keyring=/usr/share/keyrings/debian-archive-keyring.gpg
青字の部分は使用するDebianのバージョンに適宜変更する。鍵の一覧は以下のサイトにある。 https://ftp-master.debian.org/keys.html

Xvfbにxrandrで解像度を追加する

追加したい解像度を引数にして、cvtコマンドを実行する。
$ cvt 1280 800
# 1280x800 59.81 Hz (CVT 1.02MA) hsync: 49.70 kHz; pclk: 83.50 MHz
Modeline "1280x800_60.00"   83.50  1280 1352 1480 1680  800 803 809 831 -hsync +vsync
上記の出力を引数にxrandr --newmode、--admode、-sを実行する。
xrandr --newmode 1280x800 83.50  1280 1352 1480 1680  800 803 809 831 -hsync +vsync
xrandr --addmode screen 1280x800
xrandr -s 1280x800

LXCコンテナの中でDockerを使用するための設定

/var/lib/lxc/[container名]/configに以下の設定を追加し、コンテナを(再)起動する。

なるべく安全な方法

lxc.apparmor.profile = generated
lxc.apparmor.allow_nesting = 1

とにかく権限を与える方法

上記で駄目な場合、以下ならほとんどのケースで起動可能。上記にlxc.cap.drop =を加えて、Dockerを起動できる場合もあり。
lxc.apparmor.profile = unconfined
lxc.cgroup.devices.allow = a
lxc.cap.drop =

Ethernet、UDP、TCPパケットの構造

Ethernetパケット

ヘッダサイズ: 14B (802.1Q VLANタグがある場合18B)、チェックサムサイズ: 4B
サイズ (B)説明
6送信先MAC
6送信元MAC
[4]802.1Qタグ (VLANタグ)
2Ethernet type/size. IPv4:0x0800, IPv6:0x86dd
-ペイロード(IPなど)
4FCS (Frame Check Sequence; CRC)
参考: Wikipedia: イーサーネットフレーム

IPv4

ヘッダサイズ: 20B (拡張情報を除く)
サイズ (B)説明
1バージョン(MSB 4bit)、ヘッダ長(LSB 4bit) 4オクテット単位
1サービス種別
2全長(IPヘッダを含む)
2識別子
2フラグ(MSB 3bit)、フラグメントオフセット(LSB 13bit)
1TTL (Time to Live)
1上位プロトコル. ICMP: 0x01, TCP:0x06, UDP:0x11
2IPヘッダのチェックサム
4送信元アドレス
4送信先アドレス
[2]拡張情報
-データ
参考: Wikipedia: IPv4

UDP

ヘッダサイズ: 8B
サイズ (B)説明
2送信元ポート
2送信先ポート
2データ長
2チェックサム
-ペイロード(IPなど)
参考: Wikipedia: UDP

TCP

ヘッダサイズ: 20B >
サイズ (B)説明
2送信元ポート
2送信先ポート
4シーケンス番号
4確認応答番号
2ヘッダ長(MSB 4bit)[32bit単位]、予約(3bit)、フラグ(9bit)
2Windowサイズ
2チェックサム
2緊急ポインタ
[4-40]オプション
-ペイロード
参考: Wikipedia: TCP

QEMUでLinuxカーネルをGDBでデバッグする

QEMUの起動

以下の2つのオプションを使う
オプション説明
-sTCP 1234番でGDBのリモートコクションをオープンします
-SGDBにアタッチされるまでゲストの実行を待機します。カーネルの起動後にアタッチしても問題ない場合、不要です。

Linuxの起動オプション

QEMUで起動されるLinuxカーネルのコマンドラインオプションにnokaslrを付与する。通常、GRUBなどのブートローダーに指定する。このオプションは、カーネルの関数の配置アドレスのランダム化を抑止する。このオプションを指定しない場合、カーネルのデバッグ情報に記載されているアドレスと、実際のアドレスがことなり、GDBの操作が期待どおりに行われない。

デバッグ情報の入手

ターゲットのカーネルに対応するデバッグ情報ファイルを取得する。

Debianの場合の例

sudo apt install linux-image-5.10.0-22-amd64-dbg
あるいは、APT repositoryから直接取得する。
wget http://ftp.debian.org/debian/pool/main/l/linux/linux-image-5.10.0-22-amd64-dbg_5.10.178-3_amd64.deb
sudo dpkg -i linux-image-5.10.0-22-amd64-dbg_5.10.178-3_amd64.deb

自分でビルドしたカーネルの場合

カーネルのビルドディレクトリにvmlinuxがあるのでそれを使う。ただし、ビルド時、以下の点に留意
  • 以下のCONFIGを有効化
    • CONFIG_DEBUG_INFO_DWARF5、または、CONFIG_DEBUG_INFO_DWARF4
    • CONFIG_GDB_SCRIPTS
    • CONFIG_FRAME_POINTER
  • 以下のCONFIGを無効化
    • CONFIG_DEBUG_INFO_REDUCED
参考: https://docs.kernel.org/dev-tools/gdb-kernel-debugging.html

GDBの起動

デバッグ情報付きのvmlinuxを引数にしてGDBを起動する
gdb /usr/lib/debug/vmlinux-5.10.0-22-amd64
GDB起動後、以下のコマンドを実行する
(gdb) target remote [QEMU起動マシンのアドレス]:1234

グローバル変数の表示例

(gdb) p jiffies
$1 = 4295462764

ブレークポイントの例

do_sys_openにブレークポイントを仕掛けて、ゲストOSでファイルを開く操作をすると以下のようにブレークする
(gdb) b do_sys_open
Breakpoint 2 at 0xffffffff812d6ff0: do_sys_open. (11 locations)
(gdb) c
Continuing.

Breakpoint 2.4, do_sys_open (mode=0, flags=557056, filename=0x7f153b8cabe7 "/etc/ld.so.cache", dfd=-100) at fs/open.c:1201
1201    fs/open.c: No such file or directory.
(gdb) bt
#0  do_sys_open (mode=0, flags=557056, filename=0x7f153b8cabe7 "/etc/ld.so.cache", dfd=-100) at fs/open.c:1201
#1  __do_sys_openat (mode=0, flags=557056, filename=0x7f153b8cabe7 "/etc/ld.so.cache", dfd=-100) at fs/open.c:1218
#2  __se_sys_openat (mode=0, flags=524288, filename=139729170115559, dfd=4294967196) at fs/open.c:1213
#3  __x64_sys_openat (regs=<optimized out>) at fs/open.c:1213
#4  0xffffffff818f7e00 in do_syscall_64 (nr=<optimized out>, regs=0xffffc9000027bf58) at arch/x86/entry/common.c:46
#5  0xffffffff81a000a9 in entry_SYSCALL_64 () at /build/linux-ts3hOX/linux-5.10.178/arch/x86/entry/entry_64.S:125

実行中のプロセスのtask_structの表示例

上記のブレーク例は、lsを実行したときのものである。このときのプロセスが確かにlsであることと、そのプロセスIDを確認する。
(gdb) set $curr = (struct task_struct **)($gs_base + (void *)&current_task)
(gdb) p $curr
$2 = (struct task_struct **) 0xffff88803d01fbc0
(gdb) p $curr->comm
$3 = "ls\000h\000\000\000)\000\000\000\000\000\000\000"
(gdb) p $curr->pid
$4 = 552
上記について補足する。実行中のプロセスのtask_struct構造体へのポインタは、CPUごとの変数領域の中にあり、グローバル変数current_taskの値は、その領域におけるオフセットである。また、CPUごとの変数領域の開始位置はGSセグメントレジスタに格納されている。そのため、両者を加算したアドレス位置に実行中のプロセスに関するtask_struct領域へのポインタが格納されている。

参考

ふるさと納税でおおすすめの返礼品: バンバーグ編

ハンバーグ大好きな私が、ふるさと納税の返礼品でおすすめのハンバーグを紹介します。一概に優劣はつけにくいが、好きなものから順に紹介する。

兵庫県淡路市: 淡路島玉ねぎのBIG生ハンバーグ 200g×10個

玉ねぎ入り。けど玉ねぎが強すぎず、いい意味で肉感が減少して、すこしあっさり目で飽きない。

岩手県一関市:《格之進》金格ハンバーグ120g×6個

やや小ぶりだが、その分、美味しさが凝縮されている。高いレベルでバランスが良い。

佐賀県吉野ヶ里町: 佐賀牛入り 黒毛和牛 ハンバーグ 12個 大容量 1.8kg (150g×12個)

肉感が高く、肉の香り、肉汁が溢れ出している。とにかく肉好きにはおすすめ。

福岡県新宮町: どーんと3㎏!4種ハンバーグセット【150g×20個】

レンジでできるのありがたい。

QEMUでホストのディレクトリをゲストに9pで共有する

ホスト側

QEMUに起動時に次のオプションを付与する。青字のIDは適宜変更可能。紫のタグについても適宜変更。
-fsdev local,path=$(pwd),security_model=mapped-xattr,id=fsdev1 \
-device virtio-9p-pci,fsdev=fsdev1,mount_tag=tag1

参考

パラメータの詳細

QEMU起動の全体のパラメータ

ゲスト側

tag1の部分は、QEMU起動時にホスト側で指定した文字列と同じものを指定します。
mount -t 9p -o trans=virtio tag1 /mnt/dir_tag1 -oversion=9p2000.L

プログラミング言語の実行速度比較(2023/5)

はじめに

先月、プログラミング言語の実行速度比較(2023/4)という記事を投稿した。 思ったより多くの方に見ていただき、有用なコメントやPull Requestをいただいたので、それらを踏まえ以下の更新を行い結果を再投稿する。
  • 明示的な型をもつ言語では、それぞれ32bit、64bit長型の変数を使った場合についてそれぞれ測定
  • C言語については前回の結果で固定長配列とrealloc()を使った場合で大きな違いがなかったのでrealloc()版のみエントリ
  • C/C++では、実用上ほぼ使用されない最適化なしをドロップ
  • Go版では、@kaoriyaさんから、数値の型(int)をint32に変えると性能が向上する旨のコメントを頂いた。Go版もint32とint64で測定した
  • Scala版では、@windymeltさんから、GraalVMを使った場合の性能向上について情報を頂いた。JVM上で動作するプログラムについては、実行環境としてGraalVMを使った場合の結果も追加した
  • PHP版では、sj-iさんからJITを有効にするPull Requestをいただいたのでそれも追加
  • Ruby版を追加
  • 前回、低消費電力が特徴のIntel Core i7-8559Uで測定を行った。これに加えて、比較的能力の高いIntel Core i9-12900Kでも測定を行った
また、当然だがこの投稿で使った素数の算出は様々なワークロードの中のひとつ。実用上、CPUだけぶん回しているタスクはむしろ少数派かも。

速度比較

方法

  • シングルスレッドで、逐次既知の素数で割ることで素数を100,000,000まで求める(ソースはここ)。いわゆる試し割り法
  • その際の実行時間と最大物理メモリ使用量を計測する
  • 素数の数は5,761,455個なので、数値が32bitの場合、それらを格納するには少なくとも約22MiBのメモリが必要(64bitの場合、約44MiB)
  • 言語の比較なので、なるべくユーザモードで動作するコードとして素数の算出を選んだ。カーネルが関係するのはメモリのアロケーション時が中心だが、実行時間の1%もない

評価環境

CPU1 (C1)

CPU2 (C2)

OS

  • Debian 12 (sid)

結果

表の横幅が画面に収まりきらない場合、横スクロールしてご覧ください。
Category 言語 数値型 時間 (sec) 最大使用
物理メモリ
(MiB)
算出素数格納
コンテナ
備考
C1 C2 C1 C2
AOT
Compile
C int 12.6 7.5 23.6 23.6 realloc() gcc 12.2.0 最適化-O3
C long 41.6 12.5 45.5 45.6 realloc()
C++ int 12.7 7.7 36.5 36.5 std::vector
C++ long 41.9 12.5 67.6 68.7 std::vector
Rust i32 14.2 7.6 25.8 24.0 Vec rust 1.63.0
Rust i64 14.1 7.6 46.2 46.0 Vec
Golang int32 19.2 7.7 105.8 92.4 slice C1: Go 1.19.6, C2: Go 1.19.8
Golang int64 48.9 12.7 171.5 202.0 slice
JVM Java int (Integer) 19.5 8.2 230.4 248.0 ArrayList
Java long (Long) 53.0 13.3 269.1 294.1 ArrayList
Java (GraalVM) int 21.8 8.2 247.2 260.1 ArrayList GraalVM CE 22.3.2
Java (GraalVM) long 49.3 13.2 281.2 306.4 ArrayList
Kotlin Int 19.6 8.1 256.1 276.6 List Kotlin 1.8.20
Kotlin Long 51.8 13.1 294.1 323.0 List
Kotlin (GraalVM) Int 17.3 8.1 272.0 294.8 List Kotlin 1.8.20, GraalVM CE 22.3.2
Kotlin (GraalVM) Long 46.6 13.1 310.2 330.9 List
Scala Int 24.9 9.5 1081.8 1449.0 mutable.ArrayBuffer Scala 3.2.2
Scala Long 59.8 14.3 662.7 1430.6 mutable.ArrayBuffer
Scala (GraalVM) Int 19.3 8.2 245.0 321.8 mutable.ArrayBuffer Scala 3.2.2, GraalVM CE 22.3.2
Scala (GraalVM) Long 50.8 13.4 609.8 706.0 mutable.ArrayBuffer
Script JavaScript (Node.js) Number 17.3 7.8 196.1 203.2 Array Node.js 18.13.0
PHP integer 108.7 43.3 149.1 149.2 array PHP 8.2.2
PHP (JIT) integer 71.9 13.4 152.4 152.5 array
Python3 int 289.4 160.0 231.5 232.8 list Python 3.11.2
Ruby Integer 380.0 160.8 57.6 57.7 Array Ruby 3.1.2

グラフ

薄い青: AOT Compile (32bit)  濃い青: AOT Compile (64bit)  薄い赤紫: JVM (32bit)  濃い赤紫: JVM (64bit)  薄い紫: JVM/GraalVM (32bit)  濃い紫: JVM/GraalVM (64bit)  橙色: Script

CPU1 (Core i7-8559U)での実行時間

CPU2 (Core i9-12900K)での実行時間

コメント・感想

全般的な実行時間の傾向

  • AOT (Ahead-Of-Time)コンパイル型の言語は、おおよそ同じオーダーの実行性能で速い
  • 次いでJVM上で実行される言語が続く。多少の差はあるが、これらの実行時間もだいたい同じ
  • GraalVM上での実行では、OpenJDKより若干よいケースもある
  • Script系の言語は、Node.jsが群を抜いて速い
  • PHPのJITも一定の効果はある(約1.5〜3倍)

32bit長と64bit長の整数型について

  • 概して64bit長の数値型を使った場合、32bit長の数値型を使った場合の1.5〜4倍程度、実行時間を要した
  • ただし、Rustでは、i32とi64を使った場合でほぼ同じ。最大メモリ使用量は期待に近いが、実行時間が32bitとほぼ同じなのはなぜ?(コードの誤り?)
  • Core i9-12900Kのほうが32bit長と64bit長整数の乖離が小さい傾向にある
  • メモリ使用量は、32bit長と64bit長整数で大きな差はない。JVM系に関しては64bit長のほうがやや使用量が多いように思える。これは実行環境の搭載メモリ量が多いで単に多くヒープを使用しているだけかもしれない

Template/Generics

  • C++, Rust, Goでは、32bit長と64bit長版をなるべく共通のコードとするためにTemplate/Genericsを用いた。同じ条件なら前回の結果と大きく変わらなかったので、Template/Genericsによる性能のオーバーヘッドもほぼないと考えられる
  • Java, Kotlinに関しては、そもそもGenericsで実現する方法が分からなかった。ベースクラスで基本的な計算をして、型に固有の処理をサブクラスでオーバーライドするような設計も考えたが、他の言語と実装が乖離しそうなのでそのアプローチも諦めた。単純に同じコードで数値型の異なるクラスを2つ用意した。
  • Scalaに関しては、Integral traitで実現しようとしたが、かなり(3〜4倍)実行時間が増加しそうだったので、Genericsの使用を諦めた

Xvfbを使って、LinuxのGUI環境(デスクトップ)をコンテナ内に作成し、Webブラウザでアクセス

Webブラウザで試験をしたり、GUIによるインタラクティブな処理をするなど、Linuxデスクトップを使いたい場合があります。一般的にLinuxデスクトップ(やベースシステムであるX11)はそのPCに装着されているビデオカードを使用します。そのため、コンテナのようにホストOSのビデオカードにアクセスできない環境ではGUIが基本的に使えません

この記事では、Linuxマシンのコンテナ内にXvfbという仮想的なグラフィックカードドライバを使ってLinuxデスクトップを作成する方法を紹介します。以下では、コンテナ内での設定・実行方法を前提に説明しますが、もちろんホストOS上でも「ソフトウェアのインストール・設定」項目以降の操作を行うことで同様に実行することが可能です。

全体構成

下図のような構成を構築します。Xvfbは、仮想ビデオドライバを使用するX11サーバです。x11vncは、Xvfbの画面をVNCプロトコルに変換して、外部からVNCでアクセスできるようにするVNCサーバです。外部にVNCクライアントがあれば、この2つのソフトウェアでアクセスできます。

下図ではさらに、WebSockifyというソフトウェアを使用しています。これは、VNCをHTTPに変換するサーバです。つまり、VNCクライアントの変わりにWebブラウザでVNCサーバにアクセスできるようになります。

コンテナの作成

ここではコンテナイメージとして、Debian 11 (bullseye)を使用します。 また、LXCと使う場合と、Dockerを使う場合を紹介します。 LXCは、一度作成した環境を引き続き利用する場合に便利です。 Dockerはその環境を使い捨てで使用する場合に便利です。 コンテナの作成は、両者で若干異なりますが、ソフトのインストール方法や設定は同じです。

以下のコマンドをリモートマシンのホスト上で実行します。

LXCコンテナの場合

sudo lxc-create -n mycontainer -t debian -- -r bullseye

作成したLXCコンテナのシェル取得方法

sudo lxc-attach -n mycontainer bash

Dockerコンテナの場合

docker run --net host -it debian:11 bash
上記では、ポートフォワードなどの設定を省略するために`--net host`を指定し、ホストOSのネットワークをそのままコンテナで使用します。セキュリティなどの理由で、ブリッジなどを使いたい場合、適宜変更ください。

ソフトウェアのインストール・設定

続いて、コンテナ内で以下のコマンドを実行します。

コンテナ内にユーザを作成

ユーザを作成するのが面倒な場合、rootで続行してもかまいません。中長期的に使用する場合、ユーザを作成するほうが安全で便利です。

パッケージのインストール

apt update && apt install xvfb openbox x11vnc websockify novnc

Xvfbと関連サーバの実行

以下のコマンドをコンテナ内で実行します。青字の部分は他と重複しないように適宜変更ください。以下のコマンドはフォアグランドで実行されるので、続くコマンドは別の端末で実行します。

Xvfb

xvfb-run -f ~/.Xauthority -n 10 -s "-screen 0 800x600x24" openbox
上記の例では、openboxというWindow Managerを起動しています。他に使用したいWindows Managerやデスクトップ環境のプログラムがある場合、それを指定します。上記の紫色のパラメータがルートウィンドウの解像度と色深度です。これも必要に応じて変更ください。

直接Xvfbを実行する場合

Xvfb :10 -screen 0 800x600x24

x11vnc

x11vnc -display :10 -rfbport 5910 -forever -localhost
上記の`-localhost`オプションは、localhostのみからVNC接続を許可するオプションです。WebSockifyを使わず、VNCクライアントでx11vncに直接アクセスする場合は、このオプションを削除ください。

パスワード

パスワードをつける場合、vncpasswdコマンドでパスワードを作成します。それを-rfbauth ~/.vnc/passwdのように上記のコマンドに追加します。

WebSockify

websockify --web=/usr/share/novnc 8010 localhost:5910

Webブラウザから接続

以下のURLにWebブラウザでアクセスするとXの画面が表示されます。
  • http://[コンテナのIP]:8010/vnc.html
起動直後は真っ黒です。マウスを右クリックをすると以下のキャプチャ画像のようにopenboxのコンテキストメニューが表示されます。
Terminal emulatorを選択すると、次のようにxtermが起動します。

プログラミング言語の実行速度比較(2023/4)

お知らせ

この記事へのフィードバックを受けて、以下に更新版「プログラミング言語の実行速度比較(2023/5)」を公開しています。コメントをお寄せくださった皆様ありがとうございます。

はじめに

2019年に「言語間の速度比較」という記事を投稿した。 それから4年近く経過したので、それぞれの言語の最新版で再度検証する。また、サポートが終了したPython2の代わりに近年人気が高いRustを追加する。さらにC言語で算出した素数の格納をrealloc()で確保した領域にする評価も追加した。評価には、シングルスレッドで順次既知の素数で割ることで1億までの素数を求めるプログラムを使用(ソースはここ
言語 時間 (sec) 最大使用物理メモリ(MiB) 算出素数格納コンテナ 補足情報 感想
C 12.6 25.1 固定長配列 gcc 12.2.0
最適化-O3
-
C 12.6 23.9 realloc() gcc 12.2.0
最適化-O3
固定長配列よりメモリ使用量が少ないが、処理時間は同じ。ある程度、数が大きくなると新しい素数の追加が少なくなるからか。
C++ 66.5 36.5 std::vector gcc 12.2.0
最適化なし(-O0でビルド)
-
C++ 12.7 35.6 std::vector gcc 12.2.0
最適化(-O3でビルド)
最適化するとやはりかなり速くなる。メモリ使用量もC言語とRust以外の言語と比べて圧倒的に少ない
Rust 14.2 23.9 Vec 1.63.0 C++に匹敵する速さ。メモリ消費はむしろ少ない。それでいて安全性が高いとくれば次世代の主役の可能性あり
JAVA 19.5 228.9 ArrayList OpenJDK 17.0.6 C/C++の約1.5倍の実行時間。これまでの経験ともだいたい整合する
Kotlin 19.6 256.2 List Kotlin 1.8.20 Javaと同等。コードの書きやすさを考えると悪くない選択肢
Scala 24.9 1078.4 mutable.ArrayBuffer Scala 3.2.2 速度面ではJavaと比較してやや劣る程度だが、メモリ使用量がJavaの約4倍、C++の30倍近い。
Golang 48.9 168.1 slice go1.19.6 予想より速くない。メモリ使用量もJavaの半分程度で、この結果だけだと使いみちが中途半端となるかも。
Node.js 17.3 196.1 Array v18.13.0 JavaやScalaを凌駕。メモリ使用量もそれらよりやや少ない。
PHP 109.5 149.4 array PHP 8.2.2 特に良いところもないような。無理に使うような言語ではないと思う。
Python3 289.4 231.5 list 3.11.2 圧倒的に遅い。メモリ使用量は他のスクリプトと同程度。直接アルゴリズムを組んでごりごりやる言語ではない。今回の評価観点は一番不向きな用途だろう。

評価環境

  • Intel Core i7 8559U (2.7GHz)
  • DDR4 2400MHz 32GiB
  • Debian 12 (sid)

リモートマシンのファイル群のtarアーカイブをリモートマシンに一時ファイルを作成しないでsshで取得する

ありがちな方法(一時ファイル作成)

リモートマシンのファイル群をtarアーカイブとして取得する場合、以下のようのリモートマシンで一度tarアーカイブを作成することは、よく見る状況である。
  1. リモートマシンにSSHでログイン
  2. リモートマシンで、tarコマンドを実行してtarアーカイブを作成する
  3. ローカルマシンからscpで、リモートマシンのtarアーカイブをコピー
  4. リモートマシンで、tarアーカイブを削除
この方法の欠点は、リモートマシンに一時的にtarアーカイブを保存する空き容量が必要なことである。 大量のファイルをローカルにコピーするときは、これがネックになる。

一時ファイルを作らない方法

リモートマシンremoteの/home/foo以下をすべてtarアーカイブfoo.tar.gzとしてバックアップする場合、 ローカルマシンで以下のように入力する。
ssh remote tar czf - /home/foo > foo.tar.gz
上記の青字の部分は、リモートマシンで実行されるコマンドである。リモートで実行したコマンドの標準出力は、ローカルのsshコマンドそのものの標準出力に出力される。上記でtarの出力ファイルとして-(標準出力)を指定しているので、結果としてローカルのsshの標準出力にtarアーカイブの内容が出力される。そこで> foo.tar.gzでローカルのファイルにリダイレクトすれば、そのままtarアーカイブとなる。これを図にすると次のイメージである。
なお、標準入出力については下記を参照。 また、tarをカレントディレクトリが/home/fooで実行させるには以下のようにする。
ssh remote "(cd /home/foo; tar czf - .)" > foo.tar.gz

多段SSHごしでのtarアーカイブ取得

local -> remote -> remote2のようにremoteを介してremote2へ多段SSHして、remote2のファイル群のtarアーカイブを直接取得することもできる。
ssh -J remote remote2 tar czf - /home/goo > goo.tar.gz

lxc-createでバージョンを指定してDebianコンテナを作成する

例:Debian 12 (bookworm)をインストール

# lxc-create -n name -t debian -- -r bookworm
他のバージョンをインストールする場合、上記の青字の部分を下のcode nameに置換する。
versioncode name
10buster
11bullseye
12bookworm

[付録] 初期状態のでプロセス

Debain 10

UID          PID    PPID  C STIME TTY      TIME     CMD
root           1       0  0 18:53 ?        00:00:00 /sbin/init
root          39       1  0 18:53 ?        00:00:00   /lib/systemd/systemd-journald
root          63       1  0 18:53 ?        00:00:00   /sbin/dhclient -4 -v -i -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases -I -df /var/lib/dhcp/dhclient6.eth0.leases eth0
root          85       1  0 18:54 ?        00:00:00   /usr/sbin/sshd -D
root          86       1  0 18:54 pts/0    00:00:00   /sbin/agetty -o -p -- \u --noclear --keep-baud console 115200,38400,9600 vt220

Debian 11

UID          PID    PPID  C STIME TTY      TIME     CMD
root           1       0  0 18:55 ?        00:00:00 /sbin/init
root          38       1  0 18:55 ?        00:00:00   /lib/systemd/systemd-journald
root          63       1  0 18:55 ?        00:00:00   /sbin/dhclient -4 -v -i -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases -I -df /var/lib/dhcp/dhclient6.eth0.leases eth0
root          89       1  0 18:55 pts/0    00:00:00   /sbin/agetty -o -p -- \u --noclear --keep-baud console 115200,38400,9600 vt220
root          90       1  0 18:55 ?        00:00:00   sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

Debian 12

UID          PID    PPID  C STIME TTY      TIME     CMD
root           1       0  0 18:59 ?        00:00:00 /sbin/init
root          38       1  0 18:59 ?        00:00:00   /lib/systemd/systemd-journald
root          70       1  0 18:59 ?        00:00:00   dhclient -4 -v -i -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.le
root          97       1  0 18:59 pts/0    00:00:00   /sbin/agetty -o -p -- \u --noclear --keep-baud - 115200,38400,9600 vt220
root          98       1  0 18:59 ?        00:00:00   sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

参考

LXCの記事一覧

make入門 #4 組み込みルール

組み込みルールの概要

前回(make入門 #3 自動変数とパターンルール)は以下のようなパターンルールについて説明しました。
prog2: main.o sub.o
        gcc -o $@ $^

%.o: %.c
        gcc -c -o $@ $<

main.o: main.c sub.h
sub.o: sub.c sub.h
ここで青字で示されるパターンルールをMakefileから削除してみます。
prog2: main.o sub.o
        gcc -o $@ $^

main.o: main.c sub.h
sub.o: sub.c sub.h
このMakefileをカレントディレクトリに配置してmakeを実行します。
$ make
cc    -c -o main.o main.c
cc    -c -o sub.o sub.c
gcc -o prog2 main.o sub.o
なんと、このMakefileでもビルドが成功します。 とくに驚くべきことは、*.cから*.oを作成する方法を指定しなていにもかかわらず、コンパイルが実行されています。 実は、makeはデフォルトのパータンルールを持っており、パターンルールが明示されていない場合、そのデフォルトのルールが使用されます。 このパターンルールを組み込みルールと呼びます。

C言語用の組み込みルール

以下のGNU Makeの公式マニュアルに組み込みルールが列挙されています。 この中で*.cから*.oを作成する組み込みルールは以下のように記載されています。
$(CC) $(CPPFLAGS) $(CFLAGS) -c
さらに、上記の変数の$(CC)なども組み込みのデフォルト値があります。以下に記載されています。 上記のサイトに記載されている、それぞれの変数のデフォルト値を以下に示します。
変数デフォルト値
CC cc
CPPFLAGS (なし)
CFLAGS (なし)
これらの変数をMakefile内で以下のように上書きできます。
CFLAGS = -g3 -O2

prog2: main.o sub.o
        gcc -o $@ $^

main.o: main.c sub.h
sub.o: sub.c sub.h
このMakefileを使ってmakeを実行すると以下のようにCFLAGSに設定したオプションが付与されています。
cc -g3 -O2   -c -o main.o main.c
cc -g3 -O2   -c -o sub.o sub.c
gcc -o prog2 main.o sub.o

まとめ

本記事では、組み込みルールについて説明しました。

以下には、make入門の全記事がリストアップされています。

私の.screenrcを紹介します

エスケープキーを^Tに設定

escape ^Tt

スタートアップメッセージを非表示

startup_message off

キャプション(ステータスライン)の設定

# %`   : バクチクコマンドの結果
# %-w  : カレントウィンドウより前のウィンドウリスト
# %{= wk} : w: 白, k: 黒 (1つ目文字と2つ目の文字は、それぞれ前景色と背景食)
# %n   : ウィンドウ番号
# %{-} : 色をリセット
# %n   : ウィンドウタイトル
# %+w  : カレントウィンドウより後のウィンドウリスト
caption always "%?%F%{= Wk}%:%{= wK}%? %`%-w%?%P%{= b.}%:%{= k.}%?%?%F%{= .C}%:%{= wk}%?%50> %n %?%F%{= .W}%:%{= wk}%?%?%t %?%?%F%{= Wk}%:%{= wK}%?%+w %<"
上記の設定によるステータスラインの例。白い背景色のラインで、カレントウィンドウは白黒を反転。ウィンドウ番号は水色。

ハードステータスを無視

hardstatus ignore

文字コードをUTF8に

encoding utf8

スクロールバック行数を設定

defscrollback 1024

画面を分割したときの、ウィンドウの移動やサイズ変更のためのキーバインド

bind r eval 'echo Resize window' 'command -c resize'
bind -c resize ^] command
bind -c resize k eval 'resize +1' 'command -c resize'
bind -c resize j eval 'resize -1' 'command -c resize'
bind -c resize l eval 'resize +1' 'command -c resize'
bind -c resize h eval 'resize -1' 'command -c resize'

bind _ resize max
bind = resize =

bind w focus
bind j focus down
bind l focus right
bind k focus up
bind h focus left
bind K kill

誤ってフローコントロール操作(XON/XOFF)をしないよう、それらのキーバインドを無効化

通常、フローコントロールを必要とすることはほぼないが、端末でフローコントロールを有効にしてしまうと 文字が入力できなくなり、すこしビックリする。そのため、徹底的にフローコントロールが発生しないようにキーバインドを設定。

フローコントロール開始・終了のキーバインドを無効化

bind s
bind ^s
bind q
bind ^q

フローコントロールモード変更のキーバインドを無効化

bind f
bind ^f

デフォルトのフローコントロールをOFFにして、現在設定もOFFに

defflow off
flow off

レジスタをペーストするためのキーバインド

bind P paste

レジスタaの内容をペーストするキー操作

  • ^t P a

レジスタaに文字列を登録

  • ^t :register a [登録する文字列]

ウィンドウのタイトルを設定

shelltitle "$ |bash"

ヴィジュアルベルをOFFに

vbell off

256色表示設定

term screen-256color

ログを有効化

deflog on
logfile .screen-logs/%Y%m%d.%S.%n.log
logtstamp on
logtstamp string "\n-- SCREEN [%Y/%m/%d %02c:%s] %n:%t --\n"

make入門 #3 自動変数とパターンルール

前回(make入門 #2 複数のルール)は、複数のルールからなるMakefileについて説明しました。 その際に気づいた方もいるかもしれませんが、ファイル名などが反復して使用されています。 今回は、反復した記述を少なくし、よりシンプルにMakefileを記述するための機能である自動変数とパターンルールを説明します。

自動変数を使って記述

以下は前回のMakefileです。
prog2: main.o sub.o
        gcc -o prog2 main.o sub.o

main.o: main.c sub.h
        gcc -c -o main.o main.c

sub.o: sub.c sub.h
        gcc -c -o sub.o sub.c
これを自動変数を使うと次のように記述することができます。$@$^のように$から始まる単語が自動変数です。 それぞれの意味を下の表に示します。
prog2: main.o sub.o
        gcc -o $@ $^

main.o: main.c sub.h
        gcc -c -o $@ $<

sub.o: sub.c sub.h
        gcc -c -o $@ $<
自動変数 説明
$@ ターゲット(入力)ファイル(コロンの左側)
$^ すべての依存(出力)ファイル(コロンの右側のファイル全て)
$< 最初の依存ファイル(コロンの右側の最初のファイル)
このMakefileをカレントディレクトリに配置してmakeを実行すると以下のように自動変数が、具体的なファイル名に置換されて実行されていることが分かります。
$ make
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o prog2 main.o sub.o

パターンルール

上記の自動変数を使ったMakefileでmain.oとsub.oを作成するコマンドが同じになっています。ソースコードが増えていっても、同様の記述になるでしょう。 Makefileは、*.cから*.oを作成するような同じコマンドになるルールを共通化する仕組みを持っています。この仕組みをパターンルールと呼びます。 パターンルールを使って、記載したMakefileの例を以下に記載します。
prog2: main.o sub.o
        gcc -o $@ $^

%.o: %.c
        gcc -c -o $@ $<

main.o: main.c sub.h
sub.o: sub.c sub.h

青字で示した%.o: %.cという行があります。これは、*.cから*.oを作るルールという意味です。 そのコマンドとして、gccを使ったコンパイル処理が記述されています。ここでの$@$<は 実際のファイルに置換されるため、自動変数を使うとファイル名だけ異なる類似の処理をひとつのコマンドとして記載できます。

また、main.oとsub.oのルールにはコマンドがありません。具体的なコマンドはパターンルールで定義してあるため、 ここでは入出力(依存)関係のみを記述すればよくなります。

まとめ

本記事では、自動変数とパターンルールについて説明しました。

以下には、make入門の全記事がリストアップされています。

Linuxカーネルの主要なCONFIG

Linuxの主要なCONFIGを以下のテーブルに記載します。とくにmake defconfigで有効化されないものを中心に記載します。 また、この記事の内容はLinux 6.2.7をベースに作成されています。
CONFIG 説明
BRIDGE ブリッジ機能
SQUASHFS squashfsサポート
SQUASHFS_XATTR squashfsでのxattrのサポート
OVERLAY_FS オーバイレイFSのサポート。Dockerなどで使用されている。
FUSE_FS FUSE FSのサポート

CONFIGの追加例

CONFIG_BRIDGEを有効にする例を示します。Linuxのソースディレクトリで次のコマンドを実行すると、最初は以下のように無効化されています。
$ cat .config | grep CONFIG_BRIDGE
# CONFIG_BRIDGE is not set
有効化するには以下を実行します。
scripts/config --enable BRIDGE
再度.configを確認すると有効化されています。
$ cat .config | grep CONFIG_BRIDGE
CONFIG_BRIDGE=y
また、この変更に伴って新たに設定可能になったCONFIGを以下で確認できます。
$ make listnewconfig
CONFIG_BRIDGE_NF_EBTABLES=n
CONFIG_BRIDGE_IGMP_SNOOPING=y
CONFIG_BRIDGE_MRP=n
CONFIG_BRIDGE_CFM=n
これらについて個別に設定しても良いですし、以下のようにするとデフォルトに設定できます。
make olddefconfig

参考記事

make入門 #2 複数のルール

前回の記事(make入門 #1 makeとは)では、makeがどのようなものかを説明しました。 今回は、C言語を使った開発などでよくある複数のソースファイルから1つの実行ファイルを作成するMakefileの書き方を説明します。

処理のフロー

図のように3つのソースファイルを用意します。それぞれのファイルの概要は次のとおりです。
ファイル名 説明
main.c メインのソースコード
sub.c ユーザ定義関数を含むソースコード
sub.h ユーザ定義関数のヘッダファイル
*.o *.cファイルをコンパイルして生成したオブジェクトファイル
prog2 2つのオブジェクトファイルをリンクして作成される実行ファイル
これを行うMakefileは次のようになります。
prog2: main.o sub.o
        gcc -o prog2 main.o sub.o

main.o: main.c sub.h
        gcc -c -o main.o main.c

sub.o: sub.c sub.h
        gcc -c -o sub.o sub.c
各ソースコードについては、末尾に掲載します。

Makefileの説明

処理ルールと入力ファイル

図の青い部分は、処理を表しています。Makefileでもそれに対応して、3つのルールが記載されています。 前回は、:の右側の入力ファイルがひとつだけの例を紹介しました。今回の例のように入力ファイルを複数記述することも可能です。 その場合、入力ファイルのどれか一つでも変更があれば、出力ファイルを作成するための処理が実行されます。

ルール記述の順序

Makefileの中でprog2の作成ルールが最も先頭に記載されています。例えば、main.oの作成ルールを先頭にすることはできません。 なぜなら、Makefileは、複数のルールが記述されていた場合、先頭のルールで記述された出力ファイルを作成しようとするからです。 つまり、main.oの作成ルールが先頭にあると、makeを実行してもmain.oが作成されて動作が完了します。

prog2の入力ファイルとしてmain.oとsub.oが記述されています。 初めてmakeを実行する場合、これらのファイルが存在しません。 その場合、makeは、次のようにmain.oとsub.oの作成ルールにしたがって、それらを作成します。

$ make
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o prog2 main.o sub.o

一部のファイルを更新

ここで、sub.cだけを更新してみます。実際には内容を更新しますが、ここではmakeの動作を確かめるのが目的なので、touchコマンドでタイムスタンプのみ更新します。
touch sub.c
makeを実行するとsub.oが再作成され、それに依存しているprog2も再作成されました。 main.oについては、その入力ファイルであるmain.cが更新されていないため、なにも処理が行われませんでした。 このように依存関係にしたがって、必要な処理のみを実行することがmakeの利点です。
$ make
gcc -c -o sub.o sub.c
gcc -o prog2 main.o sub.o

ヘッダファイルの更新

sub.hは、main.cでもsub.cでも使われており、main.oおよびsub.o両方の入力ファイルとしてMakefileに記載されています。 このヘッダファイルを更新すると、以下のようにどちらの.oファイルが更新されて、それに依存するprog2も再作成されます。
$ touch sub.h 
$ make
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o prog2 main.o sub.o

ソースコード

main.c

#include <stdio.h>
#include "sub.h"

int main() {
    printf("Calculated result: %d\n", calc(1, 3));
    return 0;
}

sub.h

int calc(int a, int b);

sub.c

int calc(int a, int b)
{
    return a * 2 + b - 1;
}

まとめ

本記事では、複数のルールをMakefileに記述する方法について説明しました。 次回は、自動変数と拡張子ルールという機能を説明します。

以下には、make入門の全記事がリストアップされています。

QEMUでLinux(Ubuntu 22.04)をテキストモード(シリアルコンソール)でインストールする

最近のLinuxのインストーラは、GUIベースのものが多いです。 ただし、サーバにインストールする場合、テキストベースで操作が完結できるとが便利な場合があります。 この記事では、Ubuntu 22.04をQEMUの仮想マシンにテキストモード(シリアルコンソール)でインストールする方法を紹介します。

インストーラISOイメージのダウンロード

wget https://releases.ubuntu.com/22.04.2/ubuntu-22.04.2-live-server-amd64.iso

カーネルとinitrdをファイルとしてアクセスするための準備

以下のようのインストーラのISOイメージをマウントし、その中のカーネルとinitrdを直接ファイルとしてアクセスできるようにします。
mkdir mnt
sudo mount -o ro ubuntu-22.04.2-live-server-amd64.iso mnt
また、インストール終了後、以下のようにアマウントします。
sudo umount mnt

仮想ディスクイメージの作成

qemu-img create -f raw drive1.img 10G
参考: QEMU用の仮想ドライブイメージファイルの作成例

QEMUの起動

ここでは、以下のシェルスクリプトを作って実行します。もちろん、コマンドラインに直接入力してもよいですが、コマンドラインが長いのでファイルに記載して実行します。 このコマンドラインでは、カーネルをファイルとして直接指定し、カーネルオプションにconsole=ttyS0を渡します。 ttyS0は、シリアルポートデバイスを意味します。これにより、インストーラがシリアルコンソール用に動作します。 また、-nographicオプションは、QEMUを起動した端末(xtermなど)を仮想マシンのシリアルポートとして使用するためののオプションです。
#!/bin/sh

QEMU=qemu-system-x86_64
CD_IMAGE=ubuntu-22.04.2-live-server-amd64.iso
DISK_IMAGE=drive1.img
MEMORY=2048

${QEMU} \
-cpu host \
-enable-kvm \
-m ${MEMORY} \
-drive format=raw,file=${DISK_IMAGE},if=virtio \
-cdrom ${CD_IMAGE} \
-nographic \
-kernel mnt/casper/vmlinuz \
-append console=ttyS0 \
-initrd mnt/casper/initrd
起動すると、以下のようなインストーラの画面が表示されます。
================================================================================
  Serial                                                              [ Help ]
================================================================================
                                                                              
  As the installer is running on a serial console, it has started in basic    
  mode, using only the ASCII character set and black and white colours.       
                                                                              
  If you are connecting from a terminal emulator such as gnome-terminal that  
  supports unicode and rich colours you can switch to "rich mode" which uses  
  unicode, colours and supports many languages.                               
                                                                              
  You can also connect to the installer over the network via SSH, which will  
  allow use of rich mode.                                                     
                                                                              
                                                                              
                                                                              
                                                                              
                                                                              
                                                                              
                                                                              
                          [ Continue in rich mode  > ]                        
                          [ Continue in basic mode > ]                        
                          [ View SSH instructions    ]                        
                                                      
あとは、キーボードで項目を選択したり、ユーザ名を入力するなどGUIのインストーラと同じように操作します。 以下は、インストール完了時に画面です。
================================================================================
  Install complete!                                                   [ Help ]
================================================================================
  ┌──────────────────────────────────────────────────────────────────────────┐
  │            writing etc/fstab                                            ^│
  │            configuring multipath                                         │
  │            updating packages on target system                            │
  │            configuring pollinate user-agent on target                    │
  │            updating initramfs configuration                              │
  │            configuring target system bootloader                          │
  │            installing grub to target devices                             │
  │final system configuration                                                │
  │  configuring cloud-init                                                  │
  │  calculating extra packages to install                                   │
  │  downloading and installing security updates                             │
  │    curtin command in-target                                              │
  │  restoring apt configuration                                             │
  │    curtin command in-target                                             ││
  │subiquity/Late/run                                                       v│
  └──────────────────────────────────────────────────────────────────────────┘

                               [ View full log ]
                               [ Reboot Now    ]

上記でReboot Nowを選択すると、仮想マシンの再起動後に再度インストーラが起動します。そのためCtrl-a xで、QEMUを一旦終了します。

インストールしたLinuxの起動

インストールに使用したQEMUのコマンドラインからインストーラISOイメージドライブとカーネルの指定を除いた以下で、インストールしたLinuxが起動できます。
#!/bin/sh

QEMU=qemu-system-x86_64
DISK_IMAGE=drive1.img
MEMORY=2048

${QEMU} \
-cpu host \
-enable-kvm \
-m ${MEMORY} \
-drive format=raw,file=${DISK_IMAGE},if=virtio \
-nographic

参考記事

仮想マシンのドライブやネットワークインターフェイスカードなどをカスタマイズするには以下の記事を参照。

sshホスト鍵の作成例

ssh-keygen -N '' -t ed25519 -f /etc/ssh/ssh_host_ed25519_key
  • -N ''でパスフレーズをなしに設定
  • -t ed25519でED25519の鍵を作成。他にもRSAやECDSAのホスト鍵を作ることが一般的だが、私の環境では、すべてのSSHクライアントがED25519に対応しているのでこれだけ作成
  • -fは、作成した鍵を保存するパスを指定するオプション

QEMUでNIC(ネットワークインターフェイスカード)を追加するオプション例

ユーザモードNICの追加

ユーザモードNICは、QEMU内でNATされたネットワークに接続されており、プライベートIPが割り当てられる。NETDEV_IDにはn1やnetdev1など任意の名前を設定可能。
-netdev user,id=NETDEV_ID \
-device virtio-net-pci,netdev=NETDEV_ID
あるいは単純に以下のようにしてもよい。
-nic user,model=virtio-net-pci

ポートフォワーディングの設定

ユーザモードネットワークでは、外部から仮想マシンへの通信ができません。 以下のようにhostfwd=...を追加することで、ホストのポートをゲストに転送することができます。以下の例ではホストの5522ポートが、ゲストの22ポートに転送されます。
-netdev user,id=NETDEV_ID,hostfwd=tcp::5522-:22 \
-device virtio-net-pci,netdev=NETDEV_ID
-nicを用いる記述でもポート転送を指定できます。以下の例ではさらに、複数のポートを転送する例を記載します。
-nic user,model=virtio-net-pci,hostfwd=tcp::5522-:22,hostfwd=tcp::5580-:80

ブリッジモードNICの追加

ブリッジモードでは、tapインターフェイスが作成され、ホストOSのブリッジに組み込まれます。ブリッジに組み込むためのスクリプトとしてDebianやUbuntuでは、 /etc/qemu-ifupが使用されます。そのスクリプトでは、デフォルトGWになっているブリッジに作成されたtapインターフェイスが組み込まれます。 次の記事にホストOSでブリッジを作成する方法、および、ブリッジにtapが組み込まれた状態の図が掲載されています。 ブリッジモードでは、mac=で、作成するNICのMACアドレスを指定する必要があります。 他とNICと重複しないように適宜変更します。また、ブリッジモードを使用するにはroot権限でQEMUを実行する必要があります。
-netdev tap,id=NETDEV_ID \
-device virtio-net-pci,mac=02:12:34:56:78:9a,netdev=NETDEV_ID
ブリッジに関しても-nicオプションでより簡潔に記載することが可能。
-nic tap,model=virtio-net-pci,mac=02:12:34:56:78:9a

NICを使用しない場合

上記で説明したオプションを明示しない場合、デフォルトでユーザモードのNICが仮想マシンに作られます。 このデフォルト動作を無効にしてNICのない仮想マシンを作成するには以下を指定します。
-nic none

参考記事

QEMU用の仮想ドライブイメージファイルの作成例

QCOW2

QCOW2は、ドライブの使用量に応じて増加していくので、ホストOS上のストレージサイズ消費量が抑えられる。 例えば、仮想マシン上で、ドイラブのサイズは10GiBに見えても、ホストOS上では、実際の使用量程度である。
$ qemu-img create -f qcow2 drive.img 10G
Formatting 'drive.img', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=10737418240 lazy_refcounts=off refcount_bits=16
ls-sオプションを指定してファイルサイズを見てみる。一番左端の196KiBは、そのファイルのストレージ上に占めるサイズ。 所有者foo fooの右側の193KiBは、そのファイルのサイズ。 両者は、ほぼ同じ(これが一般的)。上記コマンドで10GiBのドライブイメージファイルを作成したが、実際のファイルサイズは初期状態では非常に小さい。
$ ls -lhs drive.img 
196K -rw-r--r-- 1 foo foor 193K Mar 23 20:30 drive.img

RAW

RAW形式は物理的なドライブと同じ形式である。そのため、物理ドライブからddなどで内容を読み出しものは、RAW形式のドライブイメージファイルとしてそのまま使用できる。また、一般的にQCOW2より性能的に優れるとされる。
$ qemu-img create -f raw drive.img 10G
Formatting 'drive.img', fmt=raw size=10737418240
QCOW2の場合と同様にlsでファイルサイズを調べると、上記コマンドで指定したとおり10GiBである。しかし、一番左端の実サイズは非常に小さい。実はRAW形式も、Linuxの多くのファイルシステム(例えばext4)では、スパースファイルと呼ばれる状態のファイルとして作成される。利用に応じて実サイズが増加する。
$ ls -lhs drive.img 
4.0K -rw-r--r-- 1 foo foo 10G Mar 23 20:31 drive.img

関連記事

QEMUの記事一覧

QEMUでドライブを追加するオプション例

VIRTIOドライブ

  • Linux上では、このドライブは/dev/vda[N](N=1,2,3...)に見える
  • VIRTIOは性能がよく、特段の理由がなければ、これがベター
  • DRIVE_IDは、例えばdrive0のような任意の名前でよい。複数指定する場合は重複しないようにする

QCOW2イメージ

-drive id=DRIVE_ID,format=qcow2,file=FILE_NAME.qcow2,if=virtio

RAWイメージ

-drive id=DRIVE_ID,format=raw,file=FILE_NAME.img,if=virtio

SCSIドライブ

SCSIインターフェイスを1つ作成
-device virtio-scsi-pci
次いでドライブを作成
  • 複数のドライブが必要な場合、以下の2行をidを変えて繰り返す
  • 以下ではドライブイメージがqcowの場合を示しています。rawの場合は上記を参照
  • これらのドライブはLinuxは、/dev/sda[N](N=1,2,3...)に見える
-drive id=DRIVE_ID,format=qcow2,file=FILE_NAME.qcow2,if=none \
-device scsi-hd,drive=DRIVE_ID

USBドライブ

xHCIインターフェイスを1つ作成
-device nec-usb-xhci,id=xhci
次いでドライブを作成。
  • 複数定義する場合、SCSIドライブと同様に以下の2行をidを変えて繰り返す。
-drive id=DRIVE_ID,format=raw,file=FILE_NAME.raw,if=none \
-device usb-storage,bus=xhci.0,drive=DRIVE_ID

参考

QEMUの記事一覧

tarコマンドで指定のディレクトリへアーカイブを展開する

tarは、アーカイブをデフォルトではカレントディレクトリに展開します。 -Cオプションを用いることで、tarを指定のディレクトリで実行できます。

以下のように2つのファイルを含むarchive.tar.gzを指定のディレクトリに展開してみます。
$ tar tf archive.tar.gz
dir1/
dir1/b.dat
dir1/a.txt

展開コマンド

ここでは、flower/sunというディレクトリ以下に展開することにします。 最初に指定するディレクトリを作る必要があります。 というのは、-Cオプションは先にも述べましたが、tarをそのディレクトリで実行するオプションです。 ですので、実行時にそのディレクトリが存在している必要があります。
mkdir -p flower/sun
tar xf archive.tar.gz -C flower/sun

確認

期待通りに展開されています。
$ find flower -type f
flower/sun/dir1/b.dat
flower/sun/dir1/a.txt

make入門 #1 makeとは

概要

Linuxにはmakeというコマンドがあります。もともとはC言語などで作られたプログラムをコンパイルやリンクするために開発されました。そして、今もその用途が中心です。 大規模なプログラムでは、ソースコードファイルが数千や数万以上になります。いくつかのソースコードファイルを変更するたびに、全てをコンパイルしなおすと膨大な時間がかかります。そのため、makeは、変更があったソースコードのみを抽出してコンパイルする手段を提供します。

では、どうやって変更があったソースコードを検出しているのでしょうか?実は、その仕組みはとても単純です。ソースコードのタイムスタンプ(変更日時)とそれをコンパイルして生成されるバイナリファイルのタイムスタンプを比較するだけです。このため、何かのファイルから別のファイルを生成するような用途には、プログラムに限らずに利用できます。例えば、数値データのCSVファイルからグラフファイルを作成するような場合です。

Makefileの書式

makeを使用するには、makeに以下の3つの情報を与える必要があります。
  • 入力ファイル (Input File)
  • 出力ファイル (Output File)
  • 処理方法 (Command)
これを図示すると次のようなイメージです。
この情報を記載したファイルをMakefileといいます。ファイル名はMakefile以外を使用することもできますが、makeコマンドはカレントディレクトリにMakefileがあると、明示的に指定しなくともそれを使用します。 Makefileの書式は次のとおりです。 赤字で[TAB]と記載してしているように、処理方法の前はスペースでなくタブ1つでなければなりません。
出力ファイル: 入力ファイル
[TAB]処理方法
また、出力ファイルと入力ファイルは、より抽象的に、それぞれ、ターゲットと依存ファイルと呼ばれることもあります。

C言語のプログラムをコンパイルする例

以下のソースコードprog.cをコンパイルして、実行ファイルprogを作成してみます。
#include <stdio.h>

int main() {
    printf("Happiness depends upon ourselves.\n");
    return 0;
}
コマンドラインからビルドするには以下のように入力します。
gcc -o prog prog.c
つまり、先に述べた3つの情報は次のようになります。
  • 入力ファイル: prog.c
  • 出力ファイル: prog
  • 処理方法: 上記のgccのコマンドライン
つまりMakfileとしては次のようになります。再度の注意ですが、2行目gccの前はTABが1つです。
prog: prog.c
        gcc -o prog prog.c
Makefileがあるディレクトリでmakeと入力すると以下のようにコンパイルが実行され、progが作成されていることが分かります。 また、makeの副次的な利点として、gccのコマンドラインオプションすべてを入力しなくても良い点があります。
$ make
gcc -o prog prog.c
$ ls -l
total 28
-rw-r--r-- 1 foo foo    33 Mar 21 09:01 Makefile
-rwxr-xr-x 1 foo foo 16608 Mar 21 10:44 prog
-rw-r--r-- 1 foo foo   100 Mar 21 09:01 prog.c

入力ファイルと出力ファイルのタイムスタンプによるmakeの振る舞い

一度コンパイルした状態で、再度makeを実行しても、make: 'prog' is up to date(progは最新です)と表示され、コンパイルは行われません。
$ make
make: 'prog' is up to date.
冒頭で説明したように、出力ファイルのタイムスタンプが、入力ファイルのタイムスタンプより新しいため、makeは何も処理を実行しなかったのです。ここで、以下のようにtouchコマンドを実行して、入力ファイルのタイムスタンプを更新します。
$ touch prog.c 
$ ls -l
total 28
-rw-r--r-- 1 kyamato kyamato    33 Mar 21 09:01 Makefile
-rwxr-xr-x 1 kyamato kyamato 16608 Mar 21 10:44 prog
-rw-r--r-- 1 kyamato kyamato   100 Mar 21 11:01 prog.c
この状態でmakeを実行すると、コンパイルが行われます。
$ make
gcc -o prog prog.c

まとめ

本記事では、make概要とMakefileの基本的な記述方法について説明しました。次回は、Makefileに複数のルールを記載する方法を説明します。

以下には、make入門の全記事がリストアップされています。

Linuxでのプロセスの標準入出力

単独で動作するプロセスの標準入出力

Linuxでは、すべてのものはファイルとして扱われます。例えば、キーボード入力や画面出力もファイルとして扱われます。 具体的に見ていきましょう。まず以下のように入力してみます。この出力にはいろいろな知見が含まれます。順に説明します。

# ls -l /proc/self/fd
total 0
lrwx------ 1 root root 64 Mar 19 19:26 0 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 19 19:26 1 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 19 19:26 2 -> /dev/pts/1
lr-x------ 1 root root 64 Mar 19 19:26 3 -> /proc/147982/fd

/proc/self/fd

Linuxカーネルが提供する仮想ファイルシステム上のディレクトリです。 オープンしているファイルへのシンボリックリンクが格納されています。シンボリックリンクのファイル名はファイルデスクリプタ番号です。 詳しくは、以下の記事を参照ください。

ファイルデスクリプタ0, 1, 2

多くのケースで、プロセスはファイルデスクリプタ番号0, 1, 2の3つのファイルがオープンされた状態で起動されます。 この3つは、次のように用途が決まっています。
  • 0: 標準入力。プロセスが情報を入力するのに使用されます
  • 1: 標準出力。プロセスが通常出力を行うのに使用されます
  • 2: 標準エラー出力。プロセスがエラー出力を行うのに使用されます

/dev/pts/[数値]

上記のlsでは、ファイルデスクリプタ0,1,2に対して、どれも/dev/pts/1がオープンされています。 この/dev/pts/[数値]は、xtermなどの端末に相当するファイルです。 キーボードからの入力はそのファイル/dev/pts/[数値]に追記されているようにプロセスからは見えます。 プロセスが、ファイル/dev/pts/[数値]に書き込んだ内容は、対応する端末に文字列として表示されます。 この様子を図示すると次のようになります。

出力をファイルにリダイレクトする場合

標準出力をファイルに設定

次のようにコマンドの出力をファイルに保存することはよくあります。
ls -l /proc/self/fd > output.txt
作成されたファイルoutput.txtの内容(すなわち、lsがオープンしているファイル)は次のとおりです。
total 0
lrwx------ 1 root root 64 Mar 21 08:20 0 -> /dev/pts/1
l-wx------ 1 root root 64 Mar 21 08:20 1 -> /root/output.txt
lrwx------ 1 root root 64 Mar 21 08:20 2 -> /dev/pts/1
lr-x------ 1 root root 64 Mar 21 08:20 3 -> /proc/160788/fd
つまり、コマンドライン上で>を使ってファイルへ保存するとき、lsコマンドの標準出力はリダイレクトされたファイルに なっていることが分かります。以下のその様子を図示します。

標準エラー出力もファイルに設定

上記のように2>とその右側にファイル名を記載すると、そのファイルが標準エラー出力になります。
ls -l /proc/self/fd > output.txt 2> error.txt
output.txtの内容を確認すると以下のようになっています。
total 0
lrwx------ 1 root root 64 Mar 21 01:00 0 -> /dev/pts/5
l-wx------ 1 root root 64 Mar 21 01:00 1 -> /root/output.txt
l-wx------ 1 root root 64 Mar 21 01:00 2 -> /root/error.txt
lr-x------ 1 root root 64 Mar 21 01:00 3 -> /proc/75664/fd
以下にこの状態を図示します。

標準出力と標準エラー出力を同じファイルに設定

標準出力と標準エラー出力を同じファイルに保存する場合、以下のように2&>1を使います。 これはファイルデスクリプタ1(標準出力)をファイルディスクリプタ2(標準エラー出力)にコピーするという指示です。 そのため、> output.txtの後に記載する必要があります。
ls -l /proc/self/fd > output.txt 2&>1
output.txtの内容は次のとおりです。
total 0
lrwx------ 1 root root 64 Mar 21 02:33 0 -> /dev/pts/1
l-wx------ 1 root root 64 Mar 21 02:33 1 -> /root/output.txt
l-wx------ 1 root root 64 Mar 21 02:33 2 -> /root/output.txt
lr-x------ 1 root root 64 Mar 21 02:33 3 -> /proc/75694/fd
この状態を図示すると以下のようになります。

入力のリダイレクト

lessでファイル読む場合

以下のようにlessで引数にファイルを指定した場合、ファイルデスクリプタはどうなっているでしょうか?
less a.txt
この場合、4番に引数で指定したa.txtが割り当てられています。標準入力は端末のままです。 lessは、標準入出力とは別に引数で与えられたファイルをオープンしたということになります。 なお、このlessのPIDの調べ方は、次節の「パイプでプロセス間の入出力を連結する場合」で説明します。
total 0
lrwx------ 1 root root 64 Mar 21 03:11 0 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 21 03:11 1 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 21 03:11 2 -> /dev/pts/1
lr-x------ 1 root root 64 Mar 21 03:11 3 -> /dev/tty
lr-x------ 1 root root 64 Mar 21 03:11 4 -> /root/a.txt
この状態を図示すると以下のようになります。

標準入力にファイルを設定

一方、次のようなコマンドラインではどうなるでしょうか?
less < a.txt
答えは次のとおり、標準入力は<の右側に指定したファイルになっています。 この違いについては、この記事の最後の「標準入出力は誰が設定するのか?」で詳しく説明します。
total 0
lr-x------ 1 root root 64 Mar 21 04:29 0 -> /root/a.txt
lrwx------ 1 root root 64 Mar 21 04:29 1 -> /dev/pts/6
lrwx------ 1 root root 64 Mar 21 04:29 2 -> /dev/pts/6
lr-x------ 1 root root 64 Mar 21 04:29 3 -> /dev/tty
また、これまで同じく図も示します。

パイプでプロセス間の入出力を連結する場合

Linuxでは以下のようにパイプを使うケースも多いと思います。
cat a.txt | grep abc

この場合の標準入出力を見てみます。 ただし、上記のコマンドは一瞬で終了するため、実行中の/proc/[PID]/fdを調べることができません。 そのため、少しコマンドを工夫しますが、その前にcatやgrepを実行するbashのPID(プロセスID)を調べておきます。

変数$$には、実行中のbashのPIDが格納されています。以下のとおり、操作しているbashの74570であり、そのbashから実行されたコマンドの親PIDは、74570になります。

$ echo $$
74570
次いで以下のコマンドを実行します。catに引数がありません。 この場合、catは標準入力すなわちキーボードから入力を待っている状態になり、ファイルを読み込んだ時のように瞬時には終了しなくなります。
cat | grep abc
ここでcatとgrepのPID(プロセスID)を調べます。同じPCの別の端末で、先ほど調べたbashのPIDを親にもつプロセスを探します。
$ ps --ppid 74570
TIME CMD
  75104 pts/6    00:00:00 cat
  75110 pts/6    00:00:00 grep
catとgrepのPIDはそれぞれ75104と75110であることが分かりました。 それぞれについて、/proc/[PID]/fd以下のファイルを見ます。
$ ls -l /proc/75104/fd
total 0
lrwx------ 1 root root 64 Mar 20 09:01 0 -> /dev/pts/1
l-wx------ 1 root root 64 Mar 20 09:01 1 -> 'pipe:[936222]'
lrwx------ 1 root root 64 Mar 20 09:01 2 -> /dev/pts/1
$ ls -l /proc/75110/fd
total 0
lr-x------ 1 root root 64 Mar 20 09:01 0 -> 'pipe:[936222]'
lrwx------ 1 root root 64 Mar 20 09:01 1 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 20 09:01 2 -> /dev/pts/1
上記の結果から、catの標準出力がpipe:[936222]というのになっており、grepの標準入力も同じくpipe:[936222]になっています。 これは、下図のようにcatがパイプというファイルに出力を書き込み、grepは同じパイプというファイルからデータを読み出していることを意味します。 また、catの標準入力や標準エラー出力、grepの標準出力と標準エラー出力は、端末である/dev/pts/1となっています。つまり、catは端末を通じてキーボードからデータを入力し、grepが標準出力に書き込んだデータは、端末に表示されることが分かります。

標準入出力は誰が設定するのか?

このように、リダイレクトやパイプを使うことで起動されるプロセスの標準入出力が変化します。 標準入出力は、誰がいつ設定しているのでしょうか?答えは、起動されるプロセスの親プロセスです。 端末でコマンドを起動しているなら、bashなどのシェルが親プロセスです。 bashが、コマンドライン上のリダイレクトやパイプの記号を解釈して、子プロセスの標準入出力をユーザの指示通りになるように設定して起動します。

Linuxの/procとは(入門者向け)

/procとは

Linuxには、/procというディレクトリがあります。このディレクトリの中のファイルはLinuxカーネルが内部情報を提供するための仮想的なもです。SSD上のファイルのように、どこかにファイルの実体があるわけではありません。

ディレクトリ内容の例

実際に見てみると以下のようになっています。ls-Fオプションをつけてあるので、 末尾に/があるものはディレクトリ、@があるのは、シンボリックリンクです。
$ ls -F /proc/
1/      73669/     crypto          irq/         modules       sys/
3674/   73681/     devices         kallsyms     mounts@       sysrq-trigger
3675/   73687/     diskstats       kcore        mtrr          sysvipc/
38/     91/        dma             keys         net@          thread-self@
4771/   92/        driver/         key-users    pagetypeinfo  timer_list
4777/   acpi/      dynamic_debug/  kmsg         partitions    tty/
4778/   asound/    execdomains     kpagecgroup  pressure/     uptime
61/     buddyinfo  fb              kpagecount   schedstat     version
72/     bus/       filesystems     kpageflags   self@         vmallocinfo
73/     cgroups    fs/             loadavg      slabinfo      vmstat
73632/  cmdline    interrupts      locks        softirqs      zoneinfo
73638/  consoles   iomem           meminfo      stat
73639/  cpuinfo    ioports         misc         swaps
一見して分かるように、数字だけのディレクトリ(例: 1/, 3674/など)と、それ以外のファイルやディレクトリ(例: acpi/, buddyinfoなど)があります。 数字のディレクトリは、その値のプロセス番号をもつプロセスの情報をそのディレクトリ以下にファイルとして持ちます。 それ以外のファイルやディレクトリ(以下のファイル)には、システム全体の情報が格納されています。

プロセス用のディレクトリ

いま、例としてsleepコマンドを実行させ、プロセスを起動します。 プロセスID 73717で起動しました。
$ sleep 3600 &
[1] 73717
このプロセスに関する/procの情報を見てみましょう。
$ ls -F /proc/73717/
arch_status         fd/                net/           setgroups
attr/               fdinfo/            ns/            smaps
autogroup           gid_map            numa_maps      smaps_rollup
auxv                io                 oom_adj        stack
cgroup              ksm_merging_pages  oom_score      stat
clear_refs          ksm_stat           oom_score_adj  statm
cmdline             limits             pagemap        status
comm                loginuid           patch_state    syscall
coredump_filter     map_files/         personality    task/
cpu_resctrl_groups  maps               projid_map     timens_offsets
cpuset              mem                root@          timers
cwd@                mountinfo          sched          timerslack_ns
environ             mounts             schedstat      uid_map
exe@                mountstats         sessionid      wchan
いろんなファイルやディレクトリがありますが、筆者がよく参照するものを2つここではあげます。

exe

そのプロセスの実行ファイルへのリンクです。プロセスの実行ファイルは/usr/bin/sleep であることが分かります。
 $ ls -l /proc/73717/exe
lrwxrwxrwx 1 foo foo 0 Mar 19 11:06 /proc/73717/exe -> /usr/bin/sleep

fd

そのプロセスがオープンしているファイルへのシンボリックリンクが格納されています。 シンボリックリンクのファイル名はデスクリプタ番号です。 この場合、標準入出力のためのファイル3つがオープンしていることが分かります。
$ ls -l /proc/73717/fd
total 0
lrwx------ 1 foo foo 64 Mar 19 11:15 0 -> /dev/pts/5
lrwx------ 1 foo foo 64 Mar 19 11:15 1 -> /dev/pts/5
lrwx------ 1 foo foo 64 Mar 19 11:15 2 -> /dev/pts/5

システム全体の情報

システム全体のファイルについても、3つ紹介します。

partitions

Linuxカーネルが認識しているパーティションが格納されています。以下ではNVMeのストレージのパーティションが表示されいてます。 USBストレージを挿入するとsda, sda1, sda2など、USBストレージに対応する項目が追加されます。Linuxカーネルが 接続したストレージを認識しているかを確認するときに有用です。
$ cat /proc/partitions 
major minor  #blocks  name

 259        0 1953514584 nvme0n1
 259        1     498688 nvme0n1p1
 259        2   19530752 nvme0n1p2
 259        3   19530752 nvme0n1p3
 259        4   19530752 nvme0n1p4
 259        5   19530752 nvme0n1p5
 259        6 1874891776 nvme0n1p6

uptime

Linuxが起動してからの時間が格納されています。
$ uptime
 11:22:23 up  9:19,  2 users,  load average: 0.00, 0.00, 0.00

self

あるプロセスがこのファイルを参照する時、それは、そのプロセス用ディレクトリへのリンクになっています。つまり、読み出すプロセスによって異なるリンク先を示します。

以下の例では、リンク先になっている73729は、/procを読み出しているlsコマンド自身のプロセスIDを持つディレクトリです。

$ ls -l /proc/self
lrwxrwxrwx 1 root root 0 Mar 19 02:02 /proc/self -> 73729

より詳しい情報

この記事の例で示したように/procにはたくさんのファイルがあります。それらの詳しい説明は以下を参照ください。

Linuxカーネルのビルド方法

この記事では、Linuxカーネルをビルドする方法を紹介します。Linuxコミュニティの最新カーネルでのみサポートされる機能を使う場合や、独自のLinux OSを作成する際に有用です。ここに掲載したコマンドは、Debian 11で実行を確認しましたが、他のLinux OSでも(パッケージのインストール方法を除いて)基本的には同じです。

ビルド準備

以下のパッケージをインストールします。
sudo apt install gcc make flex bison bc libncurses-dev libelf-dev libssl-dev

カーネルソースコードの取得

以下のサイトにカーネルのソースコードが掲載されています。

この記事を書いた日の最新安定版は6.2.7でした。以下は、それをビルド用マシンの作業用ディレクトリにダウンロードする例です。
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.2.7.tar.xz

ビルド

ソースコード展開

tar xf linux-6.2.7.tar.xz

設定

ここではデフォルト設定を行います。
cd linux-6.2.7
make defconfig

さらなる設定を行う場合 (TUI)

make menuconfig
上記のように入力すると、次のTUIベースの設定画面が表示されます。これを使って必要なCONFIGを追加/削除できます。

さらなる設定を行う場合 (コマンドライン)

以下のようにscript/configを使用する
scripts/config -e CONFIG_DEBUG_INFO_DWARF5
その後、設定されていないシンボルをデフォルトにする
make olddefconfig

参考

以下にコマンドラインでCONFIGを設定する方法を説明しています。

コンパイル

make -j32

上記の-j32は、32並列でのコンパイルを行うオプション。実際のCPU数に合わせて適宜変更。

次のメッセージが表示されればビルド成功。下記のbzImageは、多くのLinux OSでvmlinuz-6.1.0-5-amd64のような名前になっているものと同じです。

Kernel: arch/x86/boot/bzImage is ready  (#1)

起動テスト

ここではrootfsにDebian 11のドライブイメージを使い、QEMUで起動してみます。なお、QEMUの使用方法に関しては、以下に関連記事がまとめられています。

rootfsドライブイメージのダウンロード

以下のようにrootfsのイメージをダウンロードします。
wget https://cloud.debian.org/images/cloud/bullseye/20230124-1270/debian-11-nocloud-amd64-20230124-1270.qcow2

起動

次のとおりQEMUを実行します。--nographicオプションを付与しているので、仮想マシンにシリアルデバイスが作成されます。 また、カーネルオプションにconsole=ttyS0を付与して、コンソールをシリアルデバイスに設定しています。 これらによりQEMUを実行している端末が、そのままシリアルデバイスへの入出力端末となります。

なお、defconfigで設定した場合、virtio経由のストレージ機能がカーネルに組み込まれます。 initrdなしで直接rootfsをカーネルがマウントできます。

sudo qemu-system-x86_64 \
-cpu host \
--enable-kvm \
-m 1024 \
-nographic \
-drive id=drive0,format=qcow2,file=debian-11-nocloud-amd64-20230124-1270.qcow2,if=virtio \
-kernel arch/x86_64/boot/bzImage \
-append "root=/dev/vda1 console=ttyS0"
以下のようなログインプロンプトが表示されれば成功です。
(省略)
Debian GNU/Linux 11 debian ttyS0

debian login:
このイメージでは、ユーザ名としてrootと入力すると、パスワードなしで以下のようにログインできます。
Linux debian 6.2.7 #1 SMP PREEMPT_DYNAMIC Sun Mar 19 02:39:21 UTC 2023 x86_64
(省略)
root@debian:~#

起動失敗時のQEMU強制終了方法

QEMU起動中にCtrl-a、続いてcを入力するとQEMUのモニタ画面に移行します。ここでqを入力します。
(Ctrl-a, cを入力)
QEMU 7.2.0 monitor - type 'help' for more information
(qemu) q

Debian 11(サーバ)でのネットワーク設定例(ブリッジあり)

設定例

以下は/etc/network/interfacesの内容。ここでは、ブリッジbr0に物理NICenp6s0を含む。
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug enp6s0
iface enp6s0 inet manual

auto br0
iface br0 inet static
  bridge_ports enp6s0
  address 192.168.10.10
  netmask 255.255.255.0
  gateway 192.168.10.1
  dns-nameservers 192.168.10.1
  bridge_stp off
  bridge_maxwait 0

注記

上記の設定には'bridge-utils'パッケージが必要です。

ブリッジを使う利点

物理NICをブリッジに含めておくと、下図のようにLXCコンテナやQEMUのインスタンスをホストマシンが所属するネットワークに容易に接続できる。macvlanやmacvtapだと、ホストOSと仮想マシンが通信できない課題があるが、この方法ならそれを解決できる。

topコマンドの見方

topを実行すると、(Debian 12の)デフォルトでは以下の画面が表示される。特に重要な図中の赤枠の3箇所について説明する。

(1) CPUの実行時間

ここには、全てのCPUの合計を100%として、実行種別ごとの割合が表示される。
種別 説明
us ユーザモードでのプログラム実行時間。プログラムの通常処理の時間
sy カーネルモードでのプログラム実行時間。プログラムがシステムコールを呼び出すことにより、カーネル内のコードが実行された時間。ファイルの読み書きや、ネットワーク処理などがこれに相当
ni 正のNICE値が設定され、低優先度となっているプロセスのプログラム実行時間
id アイドル時間。何も実行していない時間
wa ストレージなどのデバイスへの入出力処理で、デバイスから応答を待っている時間
hi ハードウェア割り込み処理の実行時間
si ソフトウェア割り込み処理の実行時間
st ハイパーバイザ型仮想マシン上の仮想CPUが、物理CPUの割り当てを待っている時間

(2) メモリ使用量

OS全体のメモリ使用量が表示される。デフォルトの単位は、MiB。
分類 項目 説明
Mem total 全メモリ容量
free 空きメモリ容量
used 使用中のメモリ容量
buff/cache ディレクトリエントリやファイル内容のキャッシュ容量。Linuxは、空きメモリを積極的にこれらのキャッシュに割り当てるため、使用するに従い空きメモリが減少し、これらの値が増加する傾向にある
Swap total 全スワップ容量
free 空きスワップ容量
used 使用中のスワップ容量
avail Mem 利用可能なメモリ容量。大雑把には空きメモリ容量とキャッシュの合計

(3) プロセスごとの状態

ヘッダの各列の意味は次のとおり。デフォルトではプロセスはCPU使用率の降順で表示される。メモリ関連の容量のデフォルト単位はKiB。  
項目 説明
PID プロセスID
USER そのプロセスの実行ユーザー
PR プロセスの実行優先度。20が標準。数値が小さいほど優先度が高い
NI NICE値。この値が正なら、その値に応じて低優先度で実行される。負ならその値に応じて高優先度で実行される
VIRT 仮想メモリ使用量。プロセスが確保したメモリ量。この値の一部にしか物理メモリが割り当てられていない。この値が大きくてもそれほどメモリを使っていない場合もある。
RES 物理メモリ使用量。プロセスに割り当てられた物理メモリ量。そのプロセスの実際のメモリ使用量。
SHR 共有メモリ使用量。RESのうち、他のプロセスと共有されているメモリ量。
S プロセスの状態: R (実行中)、S (スリープ)、D(割り込み不可のスリープ)、Z(ゾンビ状態)
%CPU CPU使用率。ひとつのCPUをフルに使った場合100%となる。複数のCPUを使っている場合、100%を超えることがある
%MEM メモリ使用率
TIME+ プロセスのCPU使用時間。起動してからの時間ではなく、CPUが割り当てられた(つまりそのプロセスのプログラムが実行された)時間の合計。1/100秒単位。
例: 1:23.45の場合、1分23秒450ミリ秒
COMMAND 実行コマンド

参考

topコマンドの記事一覧

noVNCでWebブラウザをVNCクライアントとして利用

以下の記事は、Ubuntu 20.04上で下図の構成での実行記録。

準備と実行

以下の手順をnoVNC Server上で行う

 noVNCのダウンロード

git clone https://github.com/novnc/noVNC.git

websockifyがインストールされていない場合、インストール

sudo apt install python3-websockify

実行

--web=オプションには、ダウンロードしたnoVNCのレポジトリを指定。次いで公開するポート番号。最後にVNCサーバのIPアドレスとポート番号。
websockify --web=noVNC 8081 192.168.1.20:5901

Webブラウからのアクセス

以下のURLへWebブラウザでアクセス

  • http://192.168.1.10:8081/vnc.html
以下の画面が表示されます。

Debianでfirmware-nvidia-gsp 525.85.12がないと表示された場合の対応

firmware-nvidia-gspのこのバージョンからは、non-free-firmwareレポジトリに含まれる。 そのため、例えば、/etc/apt/sources.listに以下のようにnon-free-firmwareを追加する。
deb http://deb.debian.org/debian/ bookworm contrib non-free non-free-firmware

小規模な利用ならLXDよりLXCが使い勝手がよい

LXDは、大雑把に言えば、LXCに管理機能を強化し大規模な運用にも利用しやすくしたもの。 個人で簡易的な仮想マシンとして使う場合、LXDの機能がやや冗長に感じることがある。 そのため私はLXDでなくLXCを主に使っている。以下にその理由を述べる。

ストレージ(コンテナ内のファイルの格納場所)

LXC

ホストOS上の特定のディレクトリ/var/lib/lxc/[コンテナ名]/rootfsの下に、そのコンテナのファイルシステムがそのまま格納されている。 ホスト側から見ると、デフォルトでファイル共有されているようなもの。

LXD

デフォルトでは、ホストOS上にストレージプール用のループバックマウント用のファイルを作成する。作成時点でそのサイズを決める(例えば100GiB)。

このストレージプール用のファイルは、シンプロビジョニングされるので、最初のサイズは0。使った分だけ増えていく。ただし、コンテナを削除したり、コンテナ内のファイルを削除しても、ストレージプールの容量は減少しない。これがパーソナルユースでは、ストレスになる。ストレージプール用のファイルを拡張できるものの、最初に最大サイズをいくらにするかを決めるのは、なかなか悩みどころである。

ストレージプールのループバックファイル上btrfsなどのパーティションを作れば、スナップショットやコンテナのコピーを高速に取得できるなどのメリットもある。しかし、私の使い方では、動作中にスナップショットをとるような用途もほとんどない。コンテナそのものは、それほど肥大化しないので、コピーすることがあっても、少し待てば終わる。そのため、これらのメリットをほぼ享受できない。

また、LXCでも同様のことは可能である。自前でループバックマウント用ファイルを作ってbtrfsでフォーマットし、rootfsをその上に設定すればよい。

設定

LXC

/var/lib/lxc/[コンテナ名]/configに設定が集約されている。構成を変えたいときは、このテキストファイルを編集すればよい。構成の保存もこのファイルをバックアップすればよい。

LXD

lxc configなどのコマンドで設定する。設定をまとめて保存したり、設定するのがやや面倒。

インストール方法

LXC

DebianでもUbuntuでもaptでインストールできる。

LXD

snapで提供される。snap自体それほどメジャーでもないし、パッケージ管理システムが増えるのは、システムが複雑になりあまり嬉しくない。

SSHサーバをrootでのパスワード認証を許可して一時的に起動する方法

最近の多くのLinuxディストリビューションの多くでは、デフォルトでrootでのパスワード認証が禁止されている。 以下のコマンドは、一時的にrootでパスワード認証でログインできるSSHサーバを独立して起動する。
/usr/bin/sshd -d -p 8022 -o "PermitRootLogin yes"
  • -d: フォアグランでデバッグメッセージを表示しながら実行
  • -p: ポート番号
  • -o: rootでのパスワード認証を許可するためのオプション

クライアントからログインする方法
ssh -p 8022 -lroot サーバのIPアドレス

Debian 11 (server)でのDNSの設定

/etc/network/interfacesに以下のようにdns-nameservers行を記載
auto enp6s0
iface enp6s0 inet static
  bridge_ports enp6s0
  address 192.168.1.100
  netmask 255.255.255.0
  gateway 192.168.1.1
  dns-nameservers 192.168.1.254
参考: https://wiki.debian.org/NetworkConfiguration

My favorite simple Linux software

Category Name
Window Manager OpenBOX
Task Bar Tint2
Terminal RXVT unicode
Filer PCManFM
Music Player Pragha