10/27(火) · 12:12
第3章 後半: ファイルと入出力、環境、そしてPythonの並行処理
『日本語技術教科書 Vol.0』の第3章 後半。ファイルと入出力、環境、そしてPythonの並行処理を、2 人の掛け合いで解説する。
この回を聴く
0:0012:12
音声を読み込めませんでした。記事は下で読めます。
AIこの記事は AI の Claude が書きました。
- ファイル記述子は小さな整数で、開いている状態と読み書きの位置を共有でき、パイプは区切りのないバイトの通路である。
- 環境と作業ディレクトリは親から子へコピーされ、Python の仮想環境とコンテナは別の目的の分離である。
- GIL は競合を防がず、複数のプロセスは並列を得る代わりに、転送と容量の費用を払う。
教材の 3.4 節から 3.5 節まで。
3.4.1〜3.4.3 ファイル、パイプ、環境
『日本語技術教科書 ボリュームゼロ』の第3章後半は、ファイルを開いたときに手に入る整数、ファイル記述子から始まる。この整数はプロセスごとの一覧表の何番目かを表し、表の先に、読んでいる位置や開き方をまとめた「開いている状態」がある。名前、整数、開いている状態、中身の記録は、4 つの別のものである。教材の実験では 6 バイトの ABCDEF を使う。整数 3 を複製して 4 を作ると、2 つの番号が同じ状態を指し、3 から 2 バイト読んで AB、続けて 4 から読むと位置が共有されているので CD になる。開き直した 5 は別の状態で、AB から読み始める。
標準入力、標準出力、標準エラー出力は、慣例で 0、1、2 の番号が当てられる。そのため print の行き先が画面とは限らず、端末のことも、ファイルやパイプのこともある。パイプは書いた側のバイトを読む側が順に受け取る通路で、容量は有限である。ABC の 3 バイトを入れて 2 バイトずつ読むと、AB のあとに C が出て、書く口がすべて閉じて空になると 0 バイトが返る。これは文字の 0 ではなく「もう来ない」という合図だ。3 バイトを一度に書いても 2 つに割って読めるため、区切りが要るなら長さや改行を決めておく。読み手が遅いとパイプが満杯になり、書き手が待たされる。
出力にはアプリ側の溜めもあり、改行を書いてもすぐ届くとは限らない。溜めを押し出すフラッシュは、fsync が頼む保存とは別である。
子を起動するときに渡す設定として、環境変数、引数、作業ディレクトリが挙がる。環境変数は名前に文字列を結んだ組で、子の環境は親の環境をもとに作られる。子で書き換えても親には戻らず、教材の実験では出力が親、子、親の順になる。引数は数字に見えても文字列で、数として使うなら変換と範囲の確認が要る。作業ディレクトリは相対パスの出発点で、同じスクリプトでも別の場所から動かすと別のファイルを探すことがある。
3.4.4〜3.4.5 ファイルシステム、環境の分離
ファイルシステムでは、名前と実体が別々に管理され、実体の側が種類、持ち主、大きさ、中身の位置を覚えている。名前を消す操作で消えるのは名前との結び付きだけで、すでに開いている参照があれば中身は読める。教材の例では、名前を消したあと、存在の確認は偽で、読み出しは 2 バイトの OK だった。権限は持ち主、グループ、その他の 3 者に、読む、書く、実行か探索を割り当てる。4、2、1 の足し算で、0640 なら持ち主が読み書き、グループが読み取りだけになる。ディレクトリの実行は、通り抜けて中を探す許可を意味する。
Python の仮想環境は、案件ごとに Python 本体とパッケージの置き場を別々に用意する仕組みで、別の CPU やカーネルが生まれるわけではない。実行ファイルを探す一覧と、Python が部品を探す一覧は別である。コンテナは、見える範囲を分ける仕組みと資源の使い方を管理する仕組みを組み合わせたもので、同じホストのカーネルを使う点が仮想マシンと異なる。中から見える CPU 数やメモリ上限は、ホストのものとは違い得る。どちらも資源を増やしてはくれない。
3.5.1〜3.5.2 GIL と複数のプロセス
通常の CPython の GIL は、1 つのインタプリタの中で、Python のコードを動かせるスレッドを一度に 1 つにする調整役である。OS が 2 つのスレッドを別のコアに載せても、Python の計算の区間は交互にしか進まない。入出力で待つ間は手放され、数値計算の拡張が安全な区間で手放せば、その区間は並列に走る。一方で GIL があっても競合は防げず、読む、判断する、変えるという一連の流れは、ロックで囲む責任が残る。教材は、GIL を外せる自由スレッド版のビルドにも触れている。ただし、ビルドが対応していることと、実行中に外れていることは別である。
複数のプロセスに分ければ、それぞれが自分のインタプリタと GIL を持つので、Python の計算も複数のコアで同時に進む。代わりに、辞書やリストは勝手に共有されず、直列化したバイト列として通信で渡し、受け側には別のオブジェクトが復元される。教材の数字では、4 つの仕事が 64 メビバイトずつで実効 8 ギガバイト毎秒なら、転送だけで約 33.55 ミリ秒かかる。計算が 5 ミリ秒ずつなら、20 ミリ秒の直列が 4 並列で 5 ミリ秒に縮んでも、転送を足すと 38.55 ミリ秒以上になる。
開始方式は、spawn(新しく起動)、fork(親から作る)、forkserver(専用サーバー経由)の 3 通りで、既定は版と OS で変わる。教材の例は spawn を明示し、2 つのバイト列の合計として 6 と 15 を得る。渡す関数や引数は復元できる形でなければならず、外から来た信用できないデータは pickle で復元しない。
並列の数はコア数だけでは決まらず、4 つのプロセスが 8 つずつスレッドを動かせば 32 本になり、論理 CPU が 8 つなら過剰並列になる。200 メビバイトの重みを 4 つのプロセスが複製すれば 800 メビバイトで、GPU のメモリを各自が確保すればさらに増える。待ちが主ならスレッド、Python の計算が主なら複数のプロセス、と教材は整理する。
次は第4章の前半で、ビットと整数、小数の表し方を扱う。
出典
- オーナー(AI で作った教材、Web には公開していない) 日本語技術教科書 Vol.0 コンピュータからPython・機械学習・LLMへ 第3章 2026-10-18




