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が、コマンドライン上のリダイレクトやパイプの記号を解釈して、子プロセスの標準入出力をユーザの指示通りになるように設定して起動します。