Magnolia Tech

いつもコードのことばかり考えている人のために。

一家に一冊『詳解UNIXプログラミング 第3版』

先日、sambaのソースコードを読んだ話をブログに書いた。

blog.magnolia.tech

その時に、傍に置いて参照したのが『Advanced Programming in the UNIX Environment』、邦題『詳解UNIXプログラミング 第3版』。

手元には10年前に買った原著しかなく、和訳の紙版を買おうとしたら、いつの間にかどこにも売られていなくなってしまっていた......電子書籍版は今でも入手できるけど、この手の定番書籍は紙で持っておきたいんだよなぁ。

内容は、LinuxmacosFreeBSDSolarisなどのシステムコールや、POSIX仕様と照らし合わせながら各OSの差異などがB5・896ページに渡って解説された凄まじい1冊。

とりあえずプロセス、ファイル入出力、ネットワークなど、POSIX準拠なOSの挙動を理解するために必要な情報がギッシリ入っています。

少々値は張りますが、一家に一冊有ってほしい内容です。

原著が出たのがもう10年前で、そろそろ新しい版が出るといいな。


目次

  • 1 UNIX システムの概要
  •  1.1 はじめに
  •  1.2 UNIXアーキテクチャ
  •  1.3 ログイン
  •  1.4 ファイルとディレクト
  •  1.5 入出力
  •  1.6 プログラムとプロセス
  •  1.7 エラー処理
  •  1.8 ユーザの識別
  •  1.9 シグナル
  •  1.10 時間
  •  1.11 システムコールとライブラリ関数群
  •  1.12 まとめ
  • 2 UNIX の規格と実装
  • 3 ファイル入出力
  • 4 ファイルとディレクト
  • 5 標準入出力ライブラリ
  • 6 システムデータファイルと情報
  • 7 プロセスの環境
  • 8 プロセスの制御
  • 9 プロセスの関係
  • 10 シグナル
  • 11 スレッド
  • 12 スレッドの制御
  • 13 デーモン
  • 14 高度な入出力
  • 15 プロセス間通信
  • 16 ネットワークIPC: ソケット
  • 17 高度なプロセス間通信
  • 18 端末入出力
  • 19 擬似端末
  • 20 データベースライブラリ
  • 21 ネットワークプリンタとの通信


ちなみに、最近出た『Linuxのしくみ』を経て、その先の詳細にいろいろなことを知りたい時にもいいですね(分厚いけど…)

sambaの「--foreground」「--no-process-group」について

SambaをDocker上で起動しようとして、smbdコマンドを実行してみると、上手くいかない......プロセスが終わってしまい、コンテナを抜けてしまう。

なぜだろうと思って調べた記録


参考に、Ubuntuのsambaのserviceファイルを見てみると以下のような記述が有った。

ExecStart=/usr/sbin/smbd --foreground --no-process-group $SMBDOPTIONS

sambaの公式ドキュメントを、公式ドキュメントで見てみる(それにしてもsambaの公式サイトは、古き良き時代のデザインなので色使いが目に痛いし、フォントが読みづらい......)。

-F|--foreground
If specified, this parameter causes the main smbd process to not daemonize, i.e. double-fork and disassociate with the terminal. Child processes are still created as normal to service each connection request, but the main process does not exit. This operation mode is suitable for running smbd under process supervisors such as supervise and svscan from Daniel J. Bernstein's daemontools package, or the AIX process monitor.

--no-process-group
Do not create a new process group for smbd.

foregroundオプションはデーモン化しないためのオプション、--no-process-groupはプロセスグループを作らないためのオプションとなっている。

辿っていくと、この二つのオプションは、sambaのソースコードではbecome_daemon.cという関数の引数に使われていることがわかった。

github.com

void become_daemon(bool do_fork, bool no_session, bool log_stdout)

foregroundオプションが設定されるとdo_forkfalseが設定され、no-process-groupオプションが設定されるとno_sessiontrueが設定される。


どうでもいいけど、Becoming a daemonとメッセージを出しておいて、オプション引数の内容によってデーモン化を制御するって、デバッグメッセージと矛盾しているような......

if (cmdline_daemon_cfg->daemon) {
    DBG_NOTICE("Becoming a daemon.\n");
    become_daemon(cmdline_daemon_cfg->fork,
        cmdline_daemon_cfg->no_process_group,
        false);
} else if (!cmdline_daemon_cfg->interactive) {
    daemon_status("samba", "Starting process...");
}

あと、interactiveモードというのが有るので、最初はこちらを使えば良いのかと思ったら......

-i|--interactive
If this parameter is specified it causes the server to run "interactively", not as a daemon, even if the server is executed on the command line of a shell. Setting this parameter negates the implicit daemon mode when run from the command line. smbd will only accept one connection and terminate. It will also log to standard output, as if the -S parameter had been given.

smbd will only accept one connection and terminate.と書かれている通りに、接続を試みたら、terminateされてしまって意味が無かった。最初なぜterminateのメッセージが出て終了してしまうのかと思ったら、そうドキュメントに書いてあった。


さて、become_daemon関数の中を見てみる。短いので、全部引用してみると、こんな関数になっている。

void become_daemon(bool do_fork, bool no_session, bool log_stdout)
{
    pid_t newpid;
    if (do_fork) {
        newpid = fork();
        if (newpid == -1) {
            exit_daemon("Fork failed", errno);
        }
        if (newpid) {
            _exit(0);
        }
#if defined(HAVE_LIBSYSTEMD_DAEMON) || defined(HAVE_LIBSYSTEMD)
    } else if (sd_notifications) {
        sd_notify(0, "STATUS=Starting process...");
#endif
    }

    /* detach from the terminal */
#ifdef HAVE_SETSID
    if (!no_session) {
        int ret = setsid();
        if (ret == -1) {
            exit_daemon("Failed to create session", errno);
        }
    }
#elif defined(TIOCNOTTY)
    if (!no_session) {
        int i = open("/dev/tty", O_RDWR, 0);
        if (i != -1) {
            ioctl(i, (int) TIOCNOTTY, (char *)0);
            close(i);
        }
    }
#endif /* HAVE_SETSID */

    /* Close fd's 0,1,2 as appropriate. Needed if started by rsh. */
    /* stdin must be open if we do not fork, for monitoring for
    * close.  stdout must be open if we are logging there, and we
    * never close stderr (but debug might dup it onto a log file) */
    if (do_fork) {
        int ret = close_low_fd(0);
        if (ret != 0) {
            exit_daemon("close_low_fd(0) failed: %s\n", errno);
        }
    }
    if (!log_stdout) {
        int ret = close_low_fd(1);
        if (ret != 0) {
            exit_daemon("close_low_fd(1) failed: %s\n", errno);
        }
    }
}

デフォルトではdo_forktrueが渡されるので、さらにforkする。 (あと、systemd経由で起動する場合は、forkしないけど、通知は出す......なるほど)

no_sessionfalseなのでsetsid関数が呼ばれ、新しいセッションが生成され、生成された子プロセスがプロセスグループのリーダとなる。

最後にioctlの引数にTIOCNOTTYが渡されて元のターミナルから切り離され、デーモン化が完了する。


foregroundオプションだけを指定してsambaを起動すると、setsidが失敗し(manに、呼び出したプロセスが既にプロセスリーダーの場合、失敗すると書かれている)、起動に失敗する(sambaのログに、 exit_daemon: daemon failed to start: Failed to create session, error code 1と出力される)。

これは、sambaが自身がプロセスリーダーになっているためだ。forkされた子プロセスだったら、プロセスリーダーにはなっていない。

なお、このあたりの仕組みは、『試して理解 Linuxのしくみ」に分かりやすくかかれていた。

blog.magnolia.tech


以上のことから、foregroundと、no-process-groupはセットで指定する必要があることがわかった。


つまり、Docker上で起動してうまくいかなかったのは、自分がオプション無しで起動した結果、起動した元のプロセスはforkして抜けてしまって、プロセスが終了になりコンテナが終了してしまったためだった。

以下のコマンドでうまく起動することができた。

smbd --foreground --no-process-group

現代、そんなに自前でデーモン化する機能を持たなくてもいいのに……デーモン化せずにログも全部標準出力に出すだけでいいのに......と思ってしまった。


ちなみに、デーモン化に関して、そういえば2回forkするって話が有ったよなと疑問に思ったら、下記のブログエントリを見つけた。

sleepy-yoshi.hatenablog.com

抜き出すと、こうだ。

SVR4では,制御端末が割り振られていないセッションリーダが端末をオープンしようとすると,自動的に制御端末が付与されてしまう

これはマズイ!

O_NOCTTYフラグを指定すれば防げるけど,忘れたらマズイよね

SVR4の特性を発動させないためには,孫プロセス (非セッションリーダ) つくればよい.終.

なるほど。

sambaでは2回forkをやっていないのは、特にsambaの中ではターミナルデバイスをopenすることが無いから?なのか。

バランスを崩さずに、モデルや、コードを直していくことって難しいよね

おそらく教科書的には、抽象度がキープされるように修正しましょう、元の設計者の意図を踏まえて修正しましょう、依存関係のレイヤーが崩れないように修正しましょうって話になると思うんだけど、一方で修正しないといけない”難しくて複雑な”要件が目の前に有り、それを実現することを考えるだけでも設計の関心量をガシガシ持っていかれる中で、果たして教科書的な振る舞いにかける気持ちがどこまで残っているものなのか……

終わってみてダメだったらリファクタリングするのでは?というのも一つの教科書的な回答ではあるのだけど。

汚いコードより、綺麗なコードの方がよっぽど怖いよね、自分が手を入れることで負債のレイヤーをちょっとでも加えてしまうかもしれないっていう怖さと戦わないといけないんだからさ。

というツイートを連投していたので、ここに記録しておきます。

『エンジニアリング組織論への招待 ~不確実性に向き合う思考と組織のリファクタリング 』を読んで不確実性と向き合う

所謂ビジネス書、マネジメント論、組織論みたいな本が苦手であまり読むことが無いのだけど、以前読んだ『エンジニアリング組織論への招待 ~不確実性に向き合う思考と組織のリファクタリング 』は、「あーこれなら読めるし、納得感があるな」と、当時周りの人に勧めまくった。

2018年の出版で、出てすぐに読んだ割には、このブログでは紹介していなかったなーと思って、2023年の再読の感想。


サブタイトルに「リファクタリング」というキーワードが出てくるくらいなので、エンジニアリングに対して一定の理解のある人には刺さるキーワードが数多く出てきます。冒頭から「不確実性」の話が始まり、それは「エントロピー」へつながり、コンピュータサイエンスを学んだ人にはお馴染み(?)のシャノンのエントロピーの定義が登場します……組織論の本なのに。

エンジニア視点のキーワードから人の思考の話、コミュニケーションの話、組織の話、マネジメントの話と続いていくのだけど、そのキーワードの差し込み方が絶妙なところが良い。


取り組む作業の5W1Hが揃っていない状態、つまり遂行にあたって何らかの課題がある場合、対象を構造的に、抽象的に捉え、さまざまな情報を収集、組み合わせ欠けている要素を補完するスキルが必要になってくる……というか、世の中そんなことばかりなわけで。

ただ、そんな時に、先輩・上司のオレオレ経験談は過去に向かっては有効だけど、不確実性が高くなっている状況で、現在、または未来に全然役に立たなかったり、逆に間違っていたりすることもある。そんなときはこの手の、「問題の構造」を明らかにしてくれる書籍を一通り読んでおくのは有効で、決してすべてを鵜呑みにはしてはいけないし、杓子定規に適用してもいけないけど、一つのリファレンスとして引っ張り出してくる価値はある。

それはマネジメントする側もそうだし、マネジメントされる側も知っておいた方がいい。自分がどう見られているか、どう管理・コントロールされようとしているか、その手の内くらいは理解していた方がいいよね。


目次
Chapter 1 思考のリファクタリング
1-1 すべてのバグは,思考の中にある
1-2 不確実性とエンジニアリング
1-3 情報を生み出す考え方
1-4 論理的思考の盲点
1-5 経験主義と仮説思考
1-6 全体論とシステム思考
1-7 人間の不完全さを受け入れる

Chapter 2 メンタリングの技術
2-1 メンタリングで相手の思考をリファクタリング
2-2 傾聴・可視化・リフレーミング
2-3 心理的安全性の作り方
2-4 内心でなく行動に注目する

Chapter 3 アジャイルなチームの原理
3-1 アジャイルはチームをメンタリングする技術
3-2 アジャイルの歴史
3-3 アジャイルをめぐる誤解
3-4 アジャイルの格率

Chapter 4 学習するチームと不確実性マネジメント
4-1 いかにして不確実性を管理するか
4-2 スケジュール予測と不確実性
4-3 要求の作り方とマーケット不安
4-4 スクラムと不安に向き合う振り返り

Chapter 5 技術組織の力学とアーキテクチャ
5-1 何が技術組織の“生産性”を下げるのか
5-2 権限委譲とアカウンタビリティ
5-3 技術的負債の正体
5-4 取引コストと技術組織
5-5 目標管理と透明性
5-6 組織設計とアーキテクチャ

『オブザーバビリティ・エンジニアリング』で学ぶ”既知の未知”と、”未知の未知”との付き合い方

2023/2/4 書名のコピペをミスって間違っていました…直しました

すいません>各位


オブザーバビリティィィィィィイ!!!!!

なんか必殺技の名前っぽいですよね、オブザーバビリティ。

リング状のエネルギーが放出されて、回転しながら相手を切り刻むイメージです。


そんなことはサテオキ

この現代、バラバラに設計された、断片的な情報しか教えてくれないアプリケーションログと、よく分からない閾値に基づいた監視メトリクスと、設計意図の分からないダッシュボードと、運用メンバの経験と勘で運用するのは限界があるよなーというのは、全システム運用者の共通の課題認識ではないでしょうか。

そんな課題へのヒントがあればなーと思って、『オブザーバビリティ・エンジニアリング』を読みました。


第一部のハイライトは、5〜6ページにかけて並べれた質問のいくつかの質問。

例えば、以下のような質問が挙げられている。

  • あなたのソフトウェアを使う任意の特定のユーザーが、任意の時間に体験していることを理解できるか?

確かにすぐに答えられないかもしれない。どの質問も改めて聞かれると、「頑張って調べれば分かるかもしれないけど、そんな観点で調べることを考えたこともなかった」ということに気づくと思います。


本書は、システム構成や、アプリケーションコードがどんどん複雑化する中、従来のモニタリングによる「既知の未知」だけでなく、オブザーバビリティという概念を導入することによって「未知の未知」への対応力を高めていきましょう、ということが解説された、包括的なオブザーバビリティに関する解説書です。

一般的に「従来の監視」では、モニタリングツールが計測するメトリクスに対する閾値チェックか、アプリケーションやミドルウェアのログを「ERROR」などのキーワードチェックでシステムに問題が発生していないかを計測してきました。

そこから、例えばアクセスログから応答時間の変動や、一歩進んで各種データ量の増減傾向(「このタイミングでこのデータはゼロ件のはずだ」「この日程が終わる頃には、○○件以上のデータが蓄積されているはずだ」等)の計測結果を使った監視が追加されていくこともあるかもしれません。

みんな可視化のために頑張ってきた、いろんな工夫を加えてきた……ただ、この手の個別に作り込まれた個々のアプリケーション固有の振る舞いに関する計測方法は、最初から設計に組み込まれるというより、発生したインシデント等のフィードバック(いわゆる再発防止!!なぜなぜ!!)に基づいて追加されるものだったり、決して包括的なものでもないし、網羅的なものでもないことが多かったりしませんか、どうですか。

じゃあこの複雑化した、色々なサービスや、コンポーネントが絡みあうプロジェクトに対してはどうしますか、何かできることは有りますか?という疑問が湧いた時に、「オブザーバビリティ」というキーワードが出てきた……という話に本書ではなっていくのだけど、正直第I部は概念的な話とかモニタリングの問題点といった話が続き、「うーん、で、どうやってそれが実現されるの?」という、ハテナが続く状態で読み進めていたのだけど、第II部の具体的な方法論へ突入し、構造化イベントの概念、構造化イベントを繋ぎ合わせるトレースの考え方、そしてOpenTelemetryを使った標準化……と、ここに来てようやく腹落ちしたというか、ようやく”オブザーバビリティ”というものが何か見えてきた……気がする。

なお、OpenTelemetryについては、下記のサイトが非常に参考になりました。

opentelemetry.io

syu-m-5151.hatenablog.com

やっぱり何でも動く実装を見ることが大事ですね。

標準化されていることで、例えばJavaの実装の例では、以下のサイトに紹介されたようなライブラリやフレームワークはあらかじめ自動的に構造化イベントを取得する方法が用意されているとのこと。

github.com


第Ⅲ部は、組織としてオブザーバビリティ・エンジニアリングを実践していくための考え方、事例などが紹介されています。まぁ、ここは必要ですけど、その前にとりあえず動かせるものは動かして、手に馴染ませるのが大事かな、と思った。


従来のログが「点」を表現するものであり、その「点」と「点」をどうやって結びつけていくのかが解析の腕の見せどころだったのに比べて、構造化イベントは最初から「線」を表現することを目指していて、今度は「線と線から面へ」という、より先に進んだ解析ができるようになる訳で、そりゃ確かになーという気持ちと、「で、果たして既存のアプリケーションが既にたくさん積み上がっている中、どこから手をつけていくのか?」ということを考えさせれる1冊でした。

テーマがより深いアプリケーションの挙動の可視化と、振る舞いへの理解、なので、当然一定以上の複雑さを持つシステムじゃないとあまり恩恵も無いだろうし、実感も湧かないと思うので、いきなりこの本だけ読んで本質が理解できる訳ではなく、きっかけに過ぎないけど、それでも改めて自分たちの課題へ向き合うヒントとして、凄く学びの多い内容だった。

まずは、OpenTelemetryの言語別の実装を触ってみるところから始めてみます。


そういえば、”テレメトリー”という単語、その前に読んだ『はやぶさ2のプロジェクトマネジャーはなぜ「無駄」を大切にしたのか?』にも繰り返し出てきた。

blog.magnolia.tech

二日連続で”テレメトリー”という単語が出てくる本を読んでた。どんだけ監視と運用が好きなんだ。

—-

その後、考えたことを追記

『はやぶさ2のプロジェクトマネジャーはなぜ「無駄」を大切にしたのか?』は、マネジメントと、運用と、監視が語られた1冊だった

人に「こんな本を読んでいる」とか、「この本が凄く面白かった」と聞くとなるべくすぐに買って読むようにしている。

はやぶさ2」プロジェクトのプロジェクトマネージャーである津田雄一さんが、プロジェクトの立ち上がりから、地球へサンプルを持ち帰るまでの一連の出来事を、プロジェクトマネジメントの視点で書かれた1冊。

タイトルにある「無駄」というキーワードはあまり出てこなかったけど、繰り返し出てきた「想定外を想定する」は運用をする上は本当に大事なことだと自分も常日頃思っていたことなので、凄く共感できた。

また、地球からコマンドを発行しても、届いて結果が返ってくるまで何十分もかかる環境の中で、得られるフィードバックを元に次の一手を考えていく様は「メトリクスを監視すること」の大切さがよく分かる。

あとはやはりメンバに対する機会を与える姿勢と、繰り返される訓練は、先を見据えて組織を作っていくプロジェクトマネージャーにとって一番大事な視点なんじゃないかと。


本屋でこの本を見かけても買うことはなかっただろうなーと考えると、やはり人に紹介してもらった本はまずは読んでみるって姿勢はアリだなーと、改めて実感した。

「不確実性」に向き合う人たちの気持ちを考える

現代のソフトウェア開発のプラクティスは、「複雑なこと」と「不確実なこと」にどうやって対応していくか?というテーマを元に進化を続けている。

そのうち「不確実なこと」に関しては、そのリスクを可視化し、分解していって、それぞれの要素に対する打ち手の数を増やすことと、そのフィードバックに対するレスポンスの速さを支えるプロセスや、ツールの議論が盛んに行われている。

一方で、それを扱う「人」は果たしてそれに対応できているのか?という疑問がある。

上記のツイートのリプライや、引用RTにはさまざまな意見が集まっているのだけど……組織文化によってはリスクを挙げることそのものがタブーみたいなところもあったりと、リスクへの許容度というものは組織文化によるところが大きいし、個々の人の性格的な側面も大きい。また、経験や、教育、立場や裁量によっても変わってくる。

特に答えが有る話ではないけど、結局こんなことをぼんやりと考えていた、という記録のエントリでした。