ablog

不器用で落着きのない技術者のメモ

LINUXシステムプログラミング一人読書会 1章

Linuxシステムプログラミング

Linuxシステムプログラミング

これのてけとーな要約です。

1章 概要および主要概念

1.1 システムプログラミング
1.2 API と ABI
  • API
    • APIはソフトウェア間のソースレベルでのインターフェースを定義したもの。
  • ABI
    • ABIは特定のアーキテクチャにおけるソフトウェア間の低レベルなバイナリインターフェースを定義したもの。
1.3 標準仕様
  • LinuxPOSIXとSUSに準拠することを目標にしている。
  • POSIXは1980年代にRichard Stallmanが提案し、IEEEが標準化を策定した。
  • SUSは1990年代初頭のUnix Warsの最中にThe Open Groupが公開した。SUSはPOSIXを包含している。
  • K&R C -> ANSI C -> ISO C95 -> ISO C99
1.4 Linux プログラミングの概念
  • ファイルとファイルシステム
  • ハードリンク
    • 異なる複数のファイル名から同じinodeにマッピングされること。
    • 同じinodeに対して、ファイル名が異なる複数の組が存在する状態。
    • ハードリンクは同一ディレクトリでも、異なる複数のディレクトリ間でも作成可能。
    • ファイルを削除するとき、ディレクトリからリンクを削除する(unlink)。
    • すなわち、ディレクトリ内から名前とinode番号の組を削除する。
    • unlinkでinodeや対応するデータを削除しないのは、ハードリンクが存在するかもしれないから。
    • ハードリンクは異なるファイルシステムをまたぐことができない。
  • シンボリックリンク
  • スペシャルファイル
  • キャラクタデバイス
    • キャラクタデバイスはバイトがリニアに並んだものとしてアクセスし、デバイスドライバがキューにデータを1バイトずつ置き、ユーザ空間からキューにおかれたデータを読み取る。
  • ブロックデバイス
    • ブロックデバイスはデータをバイト配列としてアクセスするもの。
  • 名前つきパイプ
  • ソケット
    • IPCが進化したもので、プロセス間の通信に使用する。
  • ファイルシステム
    • ブロックデバイスへのアクセスはセクタという単位で行われる。
    • セクタはデバイス上の物理的なサイズで、2の累乗になる。512バイトがよく使われる。
    • ブロックデバイスへはセクタより小さい単位ではアクセスできない。
    • すべてのI/Oは1つまたは複数のセクタ単位で行われる。
    • ファイルシステムへのアクセスはブロックという単位で行われる。
    • ブロックはファイルシステムの概念で、ファイルシステムが存在する物理メディアの概念ではない。
    • セクタサイズ<=ブロックサイズ<=ページサイズ
    • 一般的なブロックサイズは512B、1KB、4KB。
  • 名前空間
  • プロセス
    • Unixシステムでファイルの次に重要な概念はプロセス。
    • プロセスとは実行状態にあるオブジェクトコード、実行中のプログラム。
    • プロセスは実行可能なオブジェクトコードから生成される。
    • Linuxで最も広く利用されているのはELF。
    • 実行形式はメタデータとセクションからなる。
    • セクションはコードセクション、データセクションからなる。
    • もっともよく使われるセクションはテキストセクション、データセクション、BBSセクション。
    • テキストセクションは実行コードと定数などの読み取り専用のデータを保存する。
    • データセクションはプログラムで初期化した変数など初期化済みデータを持ち、読み取り書き込み両方可能のマークがつけられる。
    • bssセクションには未初期化のグローバルデータが置かれている。
    • C言語仕様では変数のデフォルト値はすべて0のため、ディスク上のオブジェクトコードに0を改めて埋める必要はなく、未初期化の変数の一覧をbssセクションのオブジェクトコードに保存しておき、セクションをメモリにロードする際にカーネルがゼロページをマッピングする。bssセクションはこのために開発されたもの。
    • bss という名前は block started by symbol、block storage segment の頭文字。1950年代にIBMが使用した名前。
    • ELF形式の他のセクションでよく使用されるのは、ノンリロケータブルシンボルを格納したアブソリュートセクションと、その他を格納したundefinedセクション。
    • プロセスはシステムコールを使ってリソースを要求、操作する。
    • リソースには、タイマ、配送前のシグナル、オープンしたファイル、ネットワーク接続、ハードウェア、IPCで使用するものなどがある。
    • プロセスに関連するデータや統計情報はカーネル内のプロセスディスクリプタに保存される。
    • プロセスは仮想的な抽象概念。
    • 各プロセスには仮想的なプロセッサおよび仮想的なメモリ空間が与えられる。
    • プロセスからは自分がシステム全体を支配しているように見える。
    • カーネルは途切れることがないように、また透過的にプリエンプトし、プロセスを再スケジューリングし、システムのプロセッサを実行中のすべてのプロセスで共有する。
    • プロセスにはそれぞれリニアアドレス空間が与えられ、システムのメモリ全てを自分が独占しているように見える。
    • 仮想メモリとページング機能を使用し、カーネルが複数のプロセスをシステム上に共存させており、それぞれのプロセスは異なるアドレス空間で動作する。
    • カーネルの仮想化技術には現代のプロセッサが備えるハードウェア機能が必要。
  • スレッド
    • プロセスには1つまたは複数のスレッドがある。
    • スレッドとはプロセス内の実行単位で、処理実行を担当する抽象概念。
    • 大半のプロセスはスレッドを1つしか持たない。これをシングルスレッドという。
    • 複数のスレッドを持つプロセスはマルチスレッド。
    • Unixの簡潔さの歴史に由来するが、従来のUnixアプリケーションはシングルスレッド。
    • 高速なプロセス作成や信頼性の高いIPCの影響もあり、マルチスレッドが必要不可欠となることは少ない。
    • スレッドは専用のスタック、プロセッサ状態、オブジェクトコードの現在位置を持つ。
    • プロセスの他の部分のほとんどは全てのスレッドで共有される。
    • Linuxカーネル内部ではスレッドは独立したものとして実装されている。
    • 通常のプロセスと同じだが、リソースの一部を共有している。
    • ユーザ空間では、Linuxのスレッド実装はPOSIX 1003.1c (pthread)に準拠している。
    • 現在のLinuxのスレッド実装の名前はglibcの一部でもあるNPTL(Native POSIX Threading Library)。
  • プロセスの階層
    • プロセスにはプロセスIDという一意の正の整数が割り振られる。
    • 最初のpidは1で、以降のプロセスには連続した一意のpidが与えられる。
    • Linuxではプロセスは階層構造になっており、これをプロセスツリーと言う。
    • プロセスツリーの最上位には最初のプロセスinitがある。
    • 新規にプロセスを作成するのはfork()システムコール
    • fork()はコールした元のプロセスのコピーを作成する。
    • 元のプロセスを親プロセス、新規に作成したプロセスを子プロセスと呼ぶ。
    • 最初のプロセス以外は全て親プロセスが存在する。
    • 子プロセスより先に親プロセスが終了した場合、カーネルが子プロセスをinitプロセスにつなぎ直す(reparent)。
    • プロセスは終了してもすぐに削除されない。
    • カーネルはプロセスの一部をメモリ内に残しておき、親プロセスが子プロセスの状態を確認できるようにする。
    • これをプロセスの終了待ち(waiting on)と言う。
    • 親プロセスの子プロセスが終了待ちが完了すると、子プロセスは完全に削除される。
    • 終了していても驟雨料待ち合わせが行われていないプロセスをゾンビプロセスと呼ぶ。
    • initプロセスは定期的に全ての子プロセスの終了を待ち合わせる。
    • つなぎ直されたプロセスが永久にゾンビになってしまわないようにするため。
  • ユーザとグループ
    • Linuxでの認証はユーザとグループという単位で管理される。
    • ユーザにはユーザID(uid)という一意な正の整数が割当てられる。
    • プロセスには実行したユーザを識別できるようにuidが対応づけられ、プロセスの実uid(real uid)と言う。
    • ユーザ名とuidの対応は /etc/passwd に保存され、ライブラリがマッピングする。
    • ログイン処理では、ユーザがユーザ名とパスワードをlogin(1)コマンドへ入力する。
    • 認証に成功すると、login(1)コマンドは/etc/passwd で指定されたユーザのログインシェルを起動し、シェルプロセスのuidをユーザのuidに変更する。
    • 子プロセスは親プロセスからuidを受け継ぐ。
    • uid 0 は root という特殊なユーザを表す。
    • root ユーザは特権を持ち、システム上でほぼ全てのことを行える。
    • 例えばプロセスのuidを変更できるのはrootユーザだけ。
    • このため、login(1)コマンドはrootユーザの権限で実行される。
    • プロセスは実uid以外に、実効uid、saved uid、filesystem uid を持つ。
    • 実uidが常にプロセスを起動したユーザを表すのに対し、実効uidは他のユーザ権限でプロセスを実行可能にするルールの範囲内で変化する場合がある。saved uid は元の実効uid。
    • filesystem uid は通常実効uidと同じ。ファイルシステムへアクセスする際の権限確認に使用される。
    • ユーザは一つまたは複数のグループに属する。/etc/passwd に記述したグループをプライマリグループまたはログイングループと呼ぶ。
    • その他にも複数のグループに参加可能で、/etc/group に記述する。
    • uid同様に、実gid、実効gid、saved gid、filesystem gid がある。
    • プロセスは通常ログイングループと関連づけられる。

14時間前 お気に入り 返信 削除

1.5 システムプログラミングへ出発