リモートマシンのファイル群の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自体それほどメジャーでもないし、パッケージ管理システムが増えるのは、システムが複雑になりあまり嬉しくない。