自宅NW活動誌:固定IP撤去とdnsmasq移行で名前解決を建て直した話

TL;DR 電源断からの復旧で名前解決がおかしくなり、調べていったら「dnsmasqがhosts変更を未反映」「gw-a/gw-bがローカル固定IPとdns01のDNS管理で二重管理になっていた」の2つが絡んでいたという話。ついでに「自ホスト名pingが127.0.1.1を返す」件は仕様通りで実害なしと確認した。全部片付いたので、今回は「詰んだ」話ではなく「ハマったけど直した」話。 環境 セグメント: home.local(192.168.20.0/24) DNS/DHCP: dns01(dnsmasq) Keepalived構成のGW: gw-a, gw-b クライアント側ネットワーク管理: nmcli 発生した問題 電源断→起動後、あるノード(client01)から複数ノードにSSHできなくなった。調査を進める過程で、そもそもdns01自身の名前解決(dns01という名前そのもの)が引けなくなっていることが判明。/etc/hostsに手動でエントリを足しても、他ノードから引けない状態が続いた。 原因は1つではなく、「gw-a/gw-bがローカル固定IP設定+hosts記載を持っていたこと」と「dns01側のhostsエントリ消失」が絡み合っていた。 対応1:dnsmasqがhosts変更を反映していなかった 症状: /etc/hostsにエントリを書いても、他ノードからdnsmasq経由で引けない。 原因: dnsmasqは/etc/hostsを起動時に読み込むだけで、実行中のファイル変更を自動では拾わない。dhcp-hostsfileやdhcp-optsfileなどのDHCP関連設定ファイルは変更時にSIGHUPシグナルを送ることでdnsmasqに再読み込みさせられる仕様になっており、/etc/hostsについても同様に、SIGHUPを受け取るとキャッシュをクリアした上で再読み込みする挙動になっている。つまり「編集しただけ」では反映されず、シグナルを送る操作が別途必要というのがハマりどころだった。 対処: systemctl restart dnsmasq(reload = SIGHUP相当)で即解決。 学び: /etc/hostsをdnsmasq向けに編集したら、反射的に再起動する習慣をつける。 対応2:gw-a/gw-bを固定IP運用からdhcp-host予約運用に切り替え 目的: ローカル固定IP(dhcpcd.conf/nmcli static)とdns01のDNS管理が二重管理になっていたのを解消し、IP管理をdns01(dnsmasq)側に一元化する。 手順(実施順が重要) 1. dns01側を先に設定 /etc/dnsmasq.conf(またはdhcphostsファイル)に予約を追記する。 dhcp-host=<MACアドレス>,192.168.20.3 # gw-a dhcp-host=<MACアドレス>,192.168.20.4 # gw-b MACアドレスの大文字/小文字はdnsmasqでは区別されない(AA:BB:...でもaa:bb:...でも可)。区切り文字は:固定。設定後はsystemctl restart dnsmasq。 2. クライアント側(gw-a/gw-b)を動的取得に変更 nmcli管理の場合は以下の通り。 nmcli connection show # 対象接続名を確認 nmcli connection modify "<接続名>" ipv4.method auto nmcli connection modify "<接続名>" ipv4.addresses "" nmcli connection modify "<接続名>" ipv4.gateway "" nmcli connection modify "<接続名>" ipv4.dns "" nmcli connection up "<接続名>" 確実に反映させるため最終的にreboot。 ...

July 30, 2026 · 1 min

go-ts-modeでgofmt(goimports)の設定を修正した

TL;DR Go開発をgo-ts-mode(tree-sitterベース)に寄せたら、問題が発生していた。 1つ目: gofmt(goimportsで代替)を実行した直後は正しく整列されているのに、TABキーを1回押すとインデントが縮む。 2つ目: before-save-hookにgofmt-before-saveを登録しても、保存時に一切フォーマットされない。しかもエラーは出ない。 両方とも「go-mode.el(tree-sitter以前のパッケージ)がgo-ts-modeの存在を想定していない」ことが根っこの原因だった。 環境 Emacs、Go開発はgo-ts-mode(tree-sitterベース)を使用 OS: Arch Linux gofmt-commandは"goimports"に上書き設定(gofmtの代わりにgoimportsを使う) 対象ファイル例: internal/api/server.go(構造体フィールドやjsonタグを縦に整列させるコードスタイル) 試したこと 症状1: 手動でTABキーを押すとインデントが縮む 手動でM-x gofmtを実行して整形した直後のファイルは正しく整列されているが、既存行にカーソルを置いてTABキーを押すと、その行のインデントが浅くなる。 思い出したがこれはかなり前から存在してる不備だ。 ちょうどよかったのでここで直してみよう。 切り分けは以下の手順で進めた。 M-x describe-variable RET indent-tabs-mode RET → 値はt。タブを使う設定として正常。 対象行の文字をC-u C-x =で確認 → タブ文字であることを確認(Char: TAB)。 タブの表示幅を疑い、行頭でタブ1個分右に移動してからC-x = → column=8。tab-widthも8(Emacsのデフォルト)。表示幅そのものは正常。 「短くなった行」と「正常に見える上の行」でM-m(back-to-indentation)からのC-x =を比較 → 両方column=16で一致。インデント幅そのものは揃っていた。 ここで「タブ幅の表示問題ではない」と判明。 真因は次の通り。gofmtは構造体フィールドやjsonタグを縦に整列させるために、構文的に必要な数以上のタブを意図的に挿入する(整列目的の余分なタブ)。一方go-ts-modeはtree-sitterによる構文解析で「その行の構文的必要インデント深さ」だけを計算し、TABキー押下時に行頭の空白をその計算値に置き換える。gofmtが足した「整列用の余分なタブ」は構文的には不要なので、go-ts-modeがそれを認識せず削ってしまう。 これはEmacs設定のバグではなく仕様通りの動作だ。go-ts-modeには「整列のための余分な空白」を認識する機能がそもそもない。 調査の途中で、副原因ももう1つ見つかった。go-ts-mode-indent-offsetが4に設定されていた。 (use-package treesit :straight (:type built-in) :config (setq treesit-font-lock-level 4) (setq go-ts-mode-indent-offset 4)) go-ts-mode-indent-offset = 4だと「1インデントレベル=4列」で計算される。一方tab-widthはEmacsデフォルトの8のまま。gofmtの出力は「1インデントレベル=タブ1個(実質8列相当)」が前提なので、この不一致でTABキー押下時の再インデント計算が4列刻みでずれる。 対処はgo-ts-mode-indent-offsetをtab-widthと同じ値に揃えるだけ。 ;; 変更前 (setq go-ts-mode-indent-offset 4) ;; 変更後(効果確認済み) (setq go-ts-mode-indent-offset 8) これで症状は解消した。 ...

July 18, 2026 · 2 min

モダンオペレーティングシステム 第3章中盤メモ

モダンオペレーティングシステム 第5版 上 第3章中盤(3.3後半〜3.5)。ページテーブルの実装とTLB、ページ置き換えアルゴリズム。 3.3後半 ページテーブルの実装 多段ページテーブル 仮想アドレス空間が64bitの場合、単純なページテーブルは現実的じゃない。 理論上の最大:2^64 = 18,446,744,073,709,551,616 バイト 1ページ = 4KB = 4096バイト ページ数 = 2^64 / 2^12 = 2^52 個 1エントリ = 8バイトとして ページテーブルのサイズ = 2^52 × 8 = 32ペタバイト プロセス1個のページテーブルだけで32PB。話にならない。 実際のx86_64は64bitフルを使わず48bitに妥協している(一部の最新CPUは57bit)。64bitフルを使うと6〜7段の階層が必要になりメモリアクセスのオーバーヘッドが耐えられなくなるから。「理論上の最大は64bitだけど、現実のCPUは48bitに妥協している」という設計判断。 Linuxはこれを多段ページテーブルで解決してる。x86_64では4段構成。 仮想アドレス(48bit有効) ┌──────┬──────┬──────┬──────┬────────────┐ │ PGD │ PUD │ PMD │ PTE │ offset │ │ 9bit │ 9bit │ 9bit │ 9bit │ 12bit │ └──────┴──────┴──────┴──────┴────────────┘ 名前は覚えなくていい。住所の階層構造だと思えばいい。 東京都 → PGD(一番大きい区分) 渋谷区 → PUD 代々木1丁目 → PMD 1番地 → PTE 101号室 → offset(ページ内の位置) ポイントは使っていない部分のテーブルを作らないこと。 ...

May 10, 2026 · 4 min

モダンオペレーティングシステム 第3章後半メモ

モダンオペレーティングシステム 第5版 上 第3章後半(3.6〜3.8)。実装上の課題、セグメンテーション、そしてCPU脆弱性とOSの関係。 3.6 実装上の課題 OSがページングに関わるタイミング ページング処理はいつ動くか。 【プロセス生成時】 → 新しいページテーブルを作る → ディスクからプログラムをロードする準備 【プロセス実行時】 → TLBミス → ページテーブルをたどる → ページフォルト → ページを物理メモリに載せる 【プロセス終了時】 → ページテーブルを解放 → 物理フレームを解放 → ディスク上のスワップ領域を解放 【コンテキストスイッチ時】 → ページテーブルの切り替え → TLBフラッシュ or ASID切り替え k3sでPodが終了した時: コンテナプロセス終了 → カーネルがページテーブル解放 → 物理フレーム解放 → 次のPodがそのフレームを使える 命令の再実行問題 ページフォルトが起きた時、フォルトした命令を再実行する。でもここに厄介な問題がある。 メモリのコピー命令(100番地→200番地にコピー) 実行途中の150番地でページフォルト ↓ 100〜149はすでにコピー済み ↓ 命令を再実行 ↓ 100〜149を二重にコピー → 壊れる 対処法 方法1: 命令実行前に全ページが存在するか先読みチェック → なければ先にページフォルトを起こしておく → 全部揃ってから命令実行(x86寄りの方式) 方法2: CPUが内部状態を保存 → どこまで実行したか記録 → 再開時はその続きから(一部のRISCの方式) ページのロック(ピン留め) I/O処理中のページは絶対に追い出せない。 DMA転送中 → デバイスが直接メモリに書き込んでる → このページを追い出したら → デバイスが存在しないメモリに書き込む → 最悪システムクラッシュ だからI/O中のページには**ロック(ピン留め)**をかける。置き換えアルゴリズムの対象外になり、I/O完了後にロック解除。 ...

May 10, 2026 · 3 min

Raspberry Pi 2台構成のWiFi APでSSIDが起動時に出ない問題を解決した

構成 自宅NWの冗長化目的で Raspberry Pi を2台使い、WiFiアクセスポイントを組んでいる。 ホスト名 チャンネル SSID pi-1 1 your-ssid pi-2 11 your-ssid 同一SSIDで別チャンネルにする構成は ESS(Extended Service Set) と呼ばれ、クライアントが自動的に電波の強い方に接続する。WPA2-PSKで普通に成立する冗長化構成。 ブリッジ構成で bridge interface(br0)にWiFiインタフェース(wlan0)を参加させ、hostapdで電波を飛ばしている。 問題 OS再起動後、SSIDが片方または両方スキャンに出ない。 systemctl status hostapd は active (running) エラーログも一見出ていない hostapdを手動で再起動すると出る 調査 チャンネルの干渉を疑う 最初にhostapd.confのチャンネル設定を確認した。 2.4GHz帯で実際に干渉しないチャンネルは 1、6、11 の3つだけ。元の設定は6と7だったため、ほぼ完全に干渉していた。 ch6 --|-- ch7 --|-- ← 隣接していてほぼ被る チャンネルを1と11に変更した。 # pi-1 channel=1 # pi-2 channel=11 BSSIDレベルで電波を確認 sudo iw dev wlan0 scan | grep -E "BSS |SSID|freq|signal" BSS xx:xx:xx:xx:xx:xx(on wlan0) freq: 2462 signal: -15.00 dBm SSID: your-ssid BSS xx:xx:xx:xx:xx:xx(on wlan0) freq: 2412 signal: -68.00 dBm SSID: your-ssid 両台ともビーコンは出ていた。ESSとして動作は成立している。 ...

May 6, 2026 · 2 min

モダンオペレーティングシステム 第3章前半メモ

モダンオペレーティングシステム 第5版 上 第3章前半(3.1〜3.3)。メモリ管理の基本。 第3章前半 メモリ管理の基礎 ベースレジスタとリミットレジスタ CPUのハードウェアレジスタを使ったシンプルなメモリ保護の仕組み。 プログラムがメモリにロードされる ↓ ベースレジスタ ← プログラムの開始物理アドレス リミットレジスタ ← プログラムの長さ ↓ プロセスがメモリにアクセスするたびに ↓ CPUがアクセスアドレス + ベース値を自動加算 ↓ リミットを超えてないか同時チェック ↓ 超えてたらフォールト(アクセス中断) これによってプロセスAはプロセスBのメモリに触れない。CPUレベルで物理的にブロックしてる。 ProxmoxのVM(KVM)との絡み KVMの場合はベース/リミットだけじゃなく、**EPT(Extended Page Table)**というIntel VT-xの機能でさらに一段上の分離をしてる。 ゲストの仮想アドレス ↓ ゲストのページテーブル ↓ ゲストの物理アドレス(実はまだ仮想) ↓ EPT(ここがKVMの追加レイヤー) ↓ 本当の物理アドレス これをネストされたページングと呼ぶ。ゲストOSごとに完全に別の物理アドレス空間にマッピングされるので、VM同士はお互いのメモリに絶対アクセスできない。 EPTはソフトウェアじゃなくてCPUがハードウェアで変換するので速い。 KVMに必要なCPU機能 KVMは以下のCPU機能が必須: CPU 仮想化支援機能 Intel VT-x AMD AMD-V(SVM) ARM(ラズパイ4等) ARMv8 Virtualization Extensions BIOSでVT-xが無効になってるとKVMが動かない。ProxmoxでVM作れないエラーの原因の大半がこれ。 MacのDockerが遅かった理由もここ Intel Mac時代 → HyperKitでソフトウェア仮想化 → 遅い・重い Apple Silicon(M1〜) → ARMのVirtualization Extensions使える → ハードウェア仮想化 → 速い M1でDockerが速くなったのはApple SiliconがARMの仮想化支援機能をちゃんと使えるようになったから。ただしARMなのでx86イメージは--platform=linux/amd64でエミュレーションになり遅い。 ...

May 2, 2026 · 2 min

モダンオペレーティングシステム 第1章・第2章メモ

モダンオペレーティングシステム 第5版 上 読んでる。 読み進めた記事というよりは範囲の中でわからんとこを生成AIと相談しながら文章課題とか出してもらいつつ進めたものをまとめた記事。 第1章 序論 OSの2つの役割 OSには2つの顔がある。 1. 拡張マシン(Extended Machine) ハードウェアの複雑さを隠蔽する。アプリ開発者はディスクの物理構造を知らなくてもファイルを読み書きできる。Docker APIがLinuxの複雑さを隠すのと同じ発想。 2. リソースマネージャー(Resource Manager) CPU・メモリ・ディスク・ネットワークを複数プロセスに割り当てる。k3s schedulerがNodeのリソースをPodに割り当てるのと同じ役割。 k3s scheduler → OSのリソースマネージャーと同じ役割 Docker API → OSの拡張マシンと同じ役割 OSがやってることをk3sやDockerが上位レイヤーで再現している。 ユーザー空間とカーネル空間 ┌─────────────────────────────┐ │ ユーザー空間 │ │ Docker、アプリ、k3s、etc... │ │ → 直接ハードウェアは触れない │ └──────────┬──────────────────┘ │ システムコール(唯一の通路) ┌──────────▼──────────────────┐ │ カーネル空間 │ │ スケジューラ │ │ メモリ管理 │ │ ファイルシステム │ │ デバイスドライバ │ │ → ハードウェアを直接触れる │ └─────────────────────────────┘ アプリが直接ハードウェアを触れたら危険。カーネルが仲介することで安全性を保証する。 システムコール docker runした時の実際の流れ: docker run ubuntu ↓ Dockerデーモン ↓ clone() ← プロセス生成 unshare() ← namespaceを作る mount() ← ファイルシステムをマウント ↓ Linuxカーネル Dockerはシステムコールの集合体。魔法じゃなくてLinuxのシステムコールをうまく組み合わせてるだけ。 ...

April 26, 2026 · 3 min

WaylandでモニターがマイクとスピーカーとしてOSに認識される問題をWirePlumberで無効化する

問題 ふとDesktopの画面を見るとモニターがマイクとスピーカーとしてOSに認識されていた。 誤って爆音で音が再生されるリスクが気になったため無効化することにした。 ついでにマイクも有効にする意味がない環境だったので止めた。 純粋な開発PCで動画とかも見ないそこそこ特殊?な環境なので最悪読み込まないなら何でもいい状態。 環境 OS: ArchLinux サウンドサーバー: PipeWire + WirePlumber 0.5.14 GPU: AMD Ryzen(APU) 問題のデバイス: AMD/ATI Raven/Raven2/Fenghuang HDMI/DP Audio Controller 原因 HDMI/DisplayPortには映像だけでなく音声も伝送できる仕様(Audio over HDMI)がある。 LinuxはこれをALSAレベルで別サウンドカードとして認識するため、 PipeWireがそのまま拾ってオーディオデバイスとして公開してしまう。 調査 認識されているカードを確認 pactl list cards short 49 alsa_card.pci-0000_04_00.1 alsa 50 alsa_card.pci-0000_04_00.6 alsa 2枚のサウンドカードが認識されている。詳細を確認する。 pactl list cards | grep -A 30 "alsa_card.pci-0000_04_00" 結果を整理すると: PCI アドレス ベンダー 説明 用途 0000:04:00.1 AMD/ATI Raven HDMI/DP Audio Controller モニター側(不要) 0000:04:00.6 AMD + Realtek ALC269VB Ryzen HD Audio Controller 本物のオンボードサウンド 0000:04:00.1 の alsa_mixer_name が ATI R6xx HDMI であることからも、 これがHDMI経由のオーディオデバイスだと確定できる。 ...

April 11, 2026 · 1 min

ArchLinuxのThunarでWalkmanのFSを開く

背景 手持ちのWalkmanをLinux(Arch Linux)環境で活用したいと考えた。 単に音楽を聴くだけでなく、PCのファイルを転送したり、時にはPCの音を高音質で鳴らすオーディオインターフェースとして使いこなすのが目的だ。 ドキュメントを読む限り、最近のデバイスはMTP(Media Transfer Protocol)に対応しており、Linuxでも標準的なツールで扱えるはずだ。 環境 OS: Arch Linux File Manager: Thunar Device: Walkman (MTP/USB DAC対応モデル) Tools: usbutils, gvfs-mtp, libmtp, jmtpfs ThunarでWalkmanのFSが見えない WalkmanをUSBケーブルでPCに接続し、Thunarを開いたがサイドバーには何も表示されない。 まず物理的な接続を確認しようと lsusb を叩いたところ、コマンド自体が入っていなかった。 sudo pacman -S usbutils 改めて確認する。 $ lsusb Bus 001 Device 008: ID 054c:0c2f Sony Corp. Walkman デバイス自体はUSBレベルでは認識されている。fdisk -l にブロックデバイスとして出てこないのはMTPなので当然だ。 原因:MTP用ライブラリが未インストール gvfs-mtp と libmtp が入っていないのが原因だった。 sudo pacman -S gvfs-mtp libmtp # Thunarを再起動して反映 thunar -q これでThunarのサイドバーにWalkmanが表示され、GUIでファイルをコピーできるようになった。 補足:USB DACモードとは 調査中に「DACモードでなければ動かないのか?」と気になって調べたのでここにまとめておく。 USB DACモードとは、デバイスを「ストレージ」としてではなく、**「USBオーディオデバイス」**としてPCに認識させるモードだ。ファイル転送には使えない。 モード PCからの見え方 用途 MTP / MSC ストレージ ファイル転送 USB DAC オーディオデバイス PC音声出力 Walkman側の設定でどちらのモードになっているかは確認しておく必要がある。 ...

April 4, 2026 · 1 min

LinuxのCFSとEEVDFを整理する - スケジューラはなぜ赤黒木を使うのか

はじめに 動かしながらゼロから学ぶLinuxカーネルの教科書 第2版 上記の技術書を読んでいてスケジューラ周りの理解が曖昧だったので、生成AIや公式ドキュメントを使って整理した。 CFS (Completely Fair Scheduler) とは Linux 2.6.23から導入されたプロセススケジューラ。「全プロセスに公平にCPU時間を与える」という思想で設計されている。 vruntime(仮想実行時間) vruntimeは「実際の実行時間をNICE値で補正した値」で、CFSの核心となる指標。 vruntime += 実際のCPU時間 × (1024 / プロセスの重み) NICE値が低い(優先度高)→ 重みが大きい → vruntimeの増加が遅い → より長くCPUを使える NICE値が高い(優先度低)→ 重みが小さい → vruntimeの増加が速い → すぐ交代させられる CFSは「vruntimeが最も小さいプロセスを次に実行する」というルールで動く。後ろ向きの指標(過去の使用量の累積)であることがEEVDFとの本質的な差になる。 NICE値と重み NICE値は -20(最高優先度)〜 +19(最低優先度)の範囲で、内部的に重みに変換される。 NICE 0 → weight 1024 NICE -1 → weight 1277(約1.25倍) NICE +1 → weight 820(約0.8倍) NICE -20 → weight 88761 NICE +19 → weight 15 1段階変わるごとに約10%のCPU時間が変化する設計になっている。 タイムスライスとスケジューリングレイテンシ スケジューリングレイテンシは「全プロセスが最低1回実行されるべき目標周期」。デフォルト約6〜24ms(プロセス数による)。 タイムスライスはその比例配分: タイムスライス = スケジューリングレイテンシ × (タスクの重み / キュー内の全タスクの重みの合計) 具体例: ...

March 1, 2026 · 2 min