Skip to content
2009/02/01 / highmt

詳解UNIXプログラミングひとり勉強会メモ

詳解UNIXプログラミング(ISBN4-89471-319-5)をようやく読み終わったのでメモ。
多分今後も何度かリファレンス的に読むことになるだろう。

全部読むのが目標だったの全部読んだのだれども時間がない人は、3章、4章、5章、8章、9章、10章、11章あたりを読めばいいのではなかろうか。
さらにサーバをつくる人やサーバが何をやっているか知りたい人は、11章、12章、13章、14章、15章、19章だろうか。
17章、18章のソースを読んで理解できる人は多分読むのは時間の無駄。

ひとつのまとまった体系を一から学ぶのは最初は知らないことばかりでとても時間がかかる。
学ぶ内容に相互に依存関係があるので、あることを学ぶのに、どうしても知らないことがある状態で学んでいかないといけない。
でもいずれ後から理解できるようになるので気にせず先に進む。そのうちクロスリファレンスできるようになる。
つまみ食いばっかりだと、この、知らないところがなかなか埋まっていかないので注意しないといけない。自分の場合、インターネットから情報を得るのが簡単になってからついついそんな感じになってしまいがちなので、またまじめにやろうと思った次第。

 
= 1章 概要

UNIXプログラミングで出てくる基本的な概念について

- ログイン
-- /etc/passwd
--- ログイン名、パスワード、ユーザID番号、グループID番号、注釈フィールド、ホームディレクトリ、シェルプログラム

- シェル

- ファイルシステム
- ディレクトリ
-- opendir, readdir, closedir
- ファイル名
-- 使えない文字:"/", null文字
-- 特殊なファイル名:".", ".."
- パス名
- 作業ディレクトリ
-- chdir
- ホームディレクトリ
-- passwdファイルで決まる

- ファイル記述子
- 標準入力、標準出力、標準エラー出力
- アンバッファド入出力
-- open, read, write, lseek, close
- 標準入出力

- プログラム
-- ディスクファイルに収められた実行可能ファイル
-- exec
- プロセス
-- プログラムの実行中のインスタンス
-- プロセスID
--- getpid
- プロセス制御
-- fork, exec, waitpid
-- execは子側で呼び出す
-- fork + exec = スポーン
-- 親は子の終了を待つ必要がある

- ANSI C
-- 関数プロトタイプ
-- 汎用ポインタ
--- ANSI C以前はchar *, ANSI Cではvoid *
-- 基本的なシステムデータ型
-- sys/types.h

- エラー処理
-- errno
--- errnoはエラーがないときにクリアされることはないのでエラー時にのみ参照する
-- strerror
--- エラー番号 -> エラーメッセージ
-- perror
--- errno -> エラーメッセージ
--- argv[0]を渡すことが多い。どのプログラムが失敗したのかがわかる。

- ユーザID
-- ユーザIDが0:rootあるいはsuperuser
-- getuid
- グループID
-- getgid
-- 1つのユーザを16までのグループ(補足グループID)に属させることができる
-- /etc/group
--- グループ名とグループIDの関連付け

- シグナル

- 時計
-- カレンダー時間
--- time_t
--- UTC 1970/01/01 00:00:00 からの経過秒数
-- プロセス時間
--- clock_t
--- CLK_TCK
--- sysconf

- システムコール
-- man 2
-- カーネルサービス
-- システムコールは置換不可能
- ライブラリルーチン
-- man 3


= 2章 UNIXの標準化と実装

UNIXの標準化の話
UNIXの実装と標準化の関係の話

- ANSI C
-- さまざまなオペレーティングシステムへのCプログラムの移植性を確保すること
-- 標準ライブラリも規定

- POSIX
-- IEEE
-- Portable Operating System Interface for Computer Environment
-- POSIX.1 = IEEE 1003.1-1990

- XPG3
-- X/Open

- FIPS
-- Federal Information Processing Standard
-- POSIXの厳密バージョン

- 実装
-- 最初:PDP-11 UNIXタイムシェアリングシステム バージョン6、バージョン7
-- 分岐1:システムIII、システムV (AT&T)
-- 分岐2:4.x BSD (カリフォルニア大学バークレー校
-- 分岐3:バージョンバージョン8、バージョン9、バージョン10

- システムVリリース4
-- SVR4
-- SVR3.3 + SunOS + 4.3BSD + Xenix
-- POSIX.1、XPG3
-- システムVインタフェース定義 (SVID)

- 4.3+BSD

- 制限事項の取得
-- コンパイル時オプション
-- コンパイル時制限事項
-- 実行時制限事項


- ANSI Cの制限事項
-- コンパイル時制限事項
-- limits.h 最大値、最小値
-- float.h 浮動小数点
-- stdio.h FOPEN_MAX, TMP_MAP

- POSIXの制限事項
-- sysconf, pathconf, fpathconf
-- 実行時に判定不可能な制限事項
--- パス名
--- オープンファイルの最大個数

- XPG3の制限事項
- FIPSの制限事項

- 機能の有無をテストするマクロ
-- _POSIX_SOURCE
-- _XOPEN_SOURCE
-- __STDC__

- 規格間の競合

= 3章 ファイル入出力

ファイルのオープン、読み取り、書き込み

- ファイル記述子
-- open, craetが返す
-- 標準入力、標準出力、標準エラー STDIN_FILENO, STDOUT_FILENO, STDERR_FILE_NO unistd.h

- open

- creat
-- open(pathname, O_WRONLY | O_CREAT | O_TRUNK, mode)と等価
--- 初期バージョンではopenにO_CREATが渡せなかったため。
--- O_WRONLYと等価なので書き込み専用でしか作成できない

- close
-- ファイルレコードロックも解放される
-- プロセス終了時に暗黙にcloseされる

- lseek
-- lseek(fd, 0, SEEK_CUR) カレントオフセットの取得
-- ファイル先頭=0
-- ファイルのオフセットはその時点のファイルサイズを超えてもよい
--- 書き込み時にファイルは拡張される
--- できた間隙は0埋めされる

- read
-- カレントオフセットから読み取り、読み取りバイト数だけオフセットを増加する
-- 要求バイト数よりも少ないバイト数を読み取る場合がある
--- ファイルの末尾に達した場合
--- 端末装置から読み取る場合、1行分まで。(別途変更可能)
--- ネットワーク。バッファリングの影響。
--- 磁気テープ。1レコードしか読めないものがある。

- write
-- 要求バイト数より書き込みバイト数が小さい場合はエラー

- ファイルの共有
-- プロセスごとにプロセステーブルの項目が存在する
-- プロセステーブルの項目にファイル記述子のテーブルが存在する
-- ファイル記述子 -> ファイル記述子のフラグ、ファイルテーブル項目へのポインタ
-- オープンされたファイルにはファイルテーブルが存在する
-- ファイルテーブル項目には、ファイル状態フラグ、カレントファイルオフセット、vノードテーブルの項目へのポインタが含まれる
-- vノードにはファイルに関する情報やファイルに作用する関数のポインタ、iノードが含まれる
-- iノードにはファイルの所有者、ファイルのサイズ、ファイルが存在するデバイス、ファイルのデータブロックのディスク上の実位置を示すポインタが含まれる

-- 複数のファイル記述子が1つのファイルテーブル項目を参照できる
--- dup
--- fork

-- ファイル記述子のフラグとファイル状態フラグの違いに注意
--- 影響する範囲が異なる
--- fcntl

-- 複数のファイルテーブル項目が1つのvノードを参照できる
--- 同一ファイルに対して別プロセスが独立したカレントオフセットを持てる

- アトミック操作
-- O_APPENDはファイル状態フラグにフラグを立てる
--- write時にこのフラグを見てlseekと同様のことを行うが、ユーザがlseekを呼ぶ場合と異なりアトミックに行われる
-- O_CREATとO_EXCLの例も同様

- dup, dup2
-- dup2は、新たなファイル記述子を指定できるが、既に使用されている場合はcloseする
--- close + fcntl(... F_DUPFD ...) と異なりアトミック操作。

- fcntl
-- ファイル記述子の複製
-- ファイル記述子フラグの取得、設定
--- close-on-execフラグ:ファイル記述子フラグ。execの後にファイルを閉じるかどうか。
-- ファイル状態フラグの取得、設定
-- SIGIO, SIGURGのプロセスIDやプロセスグループIDの取得、設定

- ioctl
-- 他の関数で実現できないその他の操作を引き受ける

- /dev/fd
-- パス名によるファイル記述子へのアクセス(複製)
-- 主にシェルで使う

= 4章 ファイルとディレクトリ

ファイルのオープン、読み込み、書き込み以外のファイルシステムの機能、ファイルの属性に関すること

- stat
-- パス名に基いてファイルに関する情報を返す
- fstat
-- ファイル記述子に基いてファイルに関する情報を返す
- lstat
-- シンボリックリンクに対してシンボリックリンク自身の情報を返すstat

- ファイルの種類
-- 普通のファイル
-- ディレクトリファイル
-- 文字型特殊ファイル
-- ブロック型特殊ファイル
-- FIFO(名前つきパイプ)
-- ソケット
-- シンボリックリンク
--- ファイルの種類はstat構造体のst_modeにコード化されている
--- S_ISREG()などのS_ISXXX()マクロで判別できる

- 実ユーザID、実グループID
-- 実際に誰であるかを示す
- 実効ユーザID、実効グループID、補足グループID
-- ファイルアクセスパーミッションの検査に使用する
- 保存セットユーザID、保存セットグループID
-- execしたときの実効ユーザID、実効グループIDの複製
- セットユーザIDビット、セットグループIDビット
-- 通常、プログラムファイルの実行したときそのプロセスの実効ユーザIDは実ユーザIDになる
-- st_modeにセットユーザIDビットが立っているとプロセスの実効ユーザIDはプログラムファイルの所有者のユーザIDになる

- ファイルアクセスパーミッション
-- ユーザ(u)、グループ(g)、他ユーザ(o)ごとに読み取り(r)、書き込み(w)、実行(x)のパーミッションを持つ
-- ファイルをオープンするときには、すべてのディレクトリの実行許可が必要
-- ディレクトリの読み取り許可は、ディレクトリ内のファイルの名称を得ることを許可する
-- ディレクトリの実行許可は、ディレクトリがパス名の要素であるときにディレクトリを辿ることを許可する
-- PATH環境変数に実行許可のないディレクトリを指定すると、そのディレクトリは検索に使われない
-- ファイルの読み取り許可は、openで読み取り用にオープンすることを許可する
-- ファイルの書き込み許可は、openで書き込み用にオープンすることを許可する
-- openにO_TRUNCを指定するには、ファイルの書き込み許可が必要
-- ディレクトリにファイルを追加するには、ディレクトリの書き込み許可と実行許可が必要
-- ディレックトリからファイルを削除するには、ディレクトリの書き込み許可と実行許可が必要
--- ファイル自体の読み取り許可や書き込み許可は不要
-- execするにはファイルの実行許可が必要、またファイルは普通のファイルであることが必要
-- ファイルのオープン、作成、削除には次のチェックが行われる
--- プロセスの実効ユーザIDが0(スーパーユーザ)のときアクセスは許可される
--- プロセスの実効ユーザIDがファイルの所有者IDと等しい場合
    適切なユーザアクセスパーミッションビットが設定されていれば許可される(グループアクセスパーミッションは見ない)
    なければ許可されない
--- プロセスの実効グループIDまたはプロセスの補足グループIDの1つがファイルのグループIDに等しいときは
    適切なグループアクセスパーミッションビットが設定されていれば許可される(ユーザアクセスパーミッションは見ない)
    なければ許可されない

- open、creat、mkdir時の新規ファイル、ディレクトリの所有権
-- 新規ファイルのユーザIDにはプロセスの実効ユーザIDが割り当てられる
-- 新規ファイルのグループIDには、POSIX.1では2つのオプションがある
--- 新規ファイルのグループIDは、プロセスの実効グループIDとする
--- 新規ファイルのグループIDは、そのファイルを収めるディレクトリのグループIDとする
-- SVR4では、セットグループIDビットが設定されているときは後者の動作になる
   設定されていないときは前者の動作になる
-- 4.3+BSDでは、常に後者の動作になる

- access
-- 実ユーザIDや実グループIDに基づいてアクセス検査を(カーネルの検査とは別に)行いたいときaccessを使用できる
-- accessは実ユーザIDおよび実グループIDに基づいてアクセス検査を行う

- umask
-- プロセスに対するファイルモード作成マスクを設定しそれ以前の値を返す
-- ファイルモード作成マスクは、プロセスがファイルやディレクトリを新規作成する度に使用される
-- openでO_CREATを使用する場合、またはcreatを使用する場合、新規ファイルのアクセスパーミッションビットを指定するmode引数をとる
-- mode引数中のファイルモード作成マスクでオンになっているビットはオフにされる

- chmod、fchmod
-- ファイルアクセスパーミッションを変更する
-- ファイルアクセスパーミッションを変更するにはプロセスの実効ユーザIDはファイルの所有者に等しいかまたはスーパーユーザでなければならない
-- セットグループIDビットがオンでグループ実行許可ビットがオフのとき、必須レコードロックが有効であることを示す
-- chmodは、最終状態変更時刻のみを更新する
   最終修正時刻は変更しない
-- スーパーユーザ特権を持たずに普通のファイルのスティッキービットを設定しようとすると
   スティッキービットはオフにされる
   普通のファイルに対してはスーパーユーザだけがスティッキービットをオンにできる
-- 新規作成されるファイルのグループIDがプロセスの実効グループIDにも補足グループIDにも等しくなく
   プロセスにスーパーユーザ特権がなければセットグループIDビットはオフにされる
--- ユーザが属さないグループが所有するようなセットグループIDファイルの作成を防止する

- スティッキービット
-- 実行可能なファイルでスティッキービットがオンの場合
   プロセスが終了したあとそのテキスト領域をスワップ領域に保存する
--- 次回のメモリへのロードがはやくなる
--- 今ではあまり使われない
-- ディレクトリでスティッキービットがオンの場合
   そのディレクトリ内のファイルを削除したり名称を変更したりできるのは
   ファイルの所有者、または
   ディレクトリの所有者、または
   スーパーユーザ
   である場合に限られる
--- /tmpなど誰でも書き込めるディレクトリで他人のファイルを削除できないようにする

- chown、fchown、lchown
-- lchownとchownの関係は、lstatとstatの関係と同じ
-- バークレー派生のシステムでは、chownはスーパーユーザのみがファイルの所有者を変更できる
--- 自分のファイルを他のユーザに押し付けてディスク使用量制限をごまかすために利用されるのを防ぐ
-- SVR4では、_POSIX_CHOWN_RESTRICTEDが有効な場合
   スーパーユーザ特権があるあれば変更可能
   または、
   プロセスがそのファイルを所有し(実効ユーザIDとファイルのユーザIDが等しい)、かつ、
   指定するユーザIDがファイルのユーザIDと等しく、
   指定するグループIDがプロセスの実効グループIDか補足グループIDのいずれかと等しい場合変更可能
--- _POSIX_CHOWN_RESTRICTEDはファイルシステム単位に有効無効を切り替えられるため
    実際の動作は各ファイルに依存する

- ファイルサイズ
-- 普通のファイル、ディレクトリ、シンボリックリンクの場合のみ意味を持つ
-- 普通のファイルでは0バイトのファイルサイズが許される
-- ディレクトリでは、ファイルサイズは16の倍数か512の倍数
-- シンボリックリンクでは、ファイルサイズはファイル名のバイト数
-- 間隙のあるファイルでは、ファイルサイズは使用するディスクスペースよりも大きくなる
--- readは間隙の部分に対して0を返す
--- cpなどで間隙のあるファイルをコピーするとコピー先のファイルは間隙がなくなり0のバイト列になる

- truncate、ftruncate
-- POSIX.1外
--- ポータブルに実施するには必要なバイト数だけを別ファイルにコピーする
-- ファイルを指定サイズに切り詰める
-- 現在のファイルサイズが指定サイズより小さいとき結果はシステムに依存する
-- SVR4には、ファイルに任意の部分を解放する拡張機能(fcntl(F_FREESP))がある

- ファイルシステム
-- ディスク
-- パーティション
-- ファイルシステム
-- iノード
--- 固定長
--- 各iノードにはそのiノードを指すディレクトリ項目の個数を収めるリンクカウントがある
--- リンクカウントが0になるとファイルを削除できる
--- リンクカウントはstat構造体のst_nlinkフィールドにある
--- iノードを指すリンクをハードリンクと呼ぶ
--- その他の種類のリンクをシンボリックリンクと呼ぶ
--- シンボリックリンクのデータブロックにはその指すファイル名称が格納される
--- iノードにはファイルのほとんどの情報が収められている
--- statは主にiノードから情報を取得する
--- ディレクトリ項目にはiノード番号とファイル名のみが収められている
--- ディレクトリ項目内のiノード番号は同じファイルシステム内のiノードを指すため
    別のファイルシステムのiノードを指すようなディレクトリ項目は使えない
--- ファイルシステムを変えずにファイル名を変更する場合
    ファイルの実際の内容を移動する必要はなく、
    既存のiノードを指す新しいディレクトリ項目を作成し
    古いディレクトリ項目を削除すればよい
--- 任意の末端ディレクトリのリンクカウントは2になる
---- そのディレクトリの親ディレクトリ、およびディレクトリ自身の「.」から参照される
--- 任意の末端でないディレクトリのリンクカウントは3以上になる
---- そのディレクトリの子の「..」から参照される

- link、unlink、remove、rename
-- linkは既存ファイルへのリンク(ディレクトリ項目)を作成する
-- 新規ディレクトリ項目の作成とリンクカウントの増加はアトミック操作である必要がある
-- スーパーユーザ特権のプロセスのみがディレクトリへの新たなリンクを作成できる
--- ループを作成してしまう可能性があり(シンボリックリンクと違ってほとんどの関数がループに対処できず)危険であるため
-- unlinkは既存のディレクトリ項目を削除する
-- ファイルをアンリンクするにはディレクトリ項目を削除するためのディレクトリの書き込み許可と実行許可が必要
   スティッキービットがある場合は、さらにその制約を満たす必要がある(スーパーユーザかファイルの所有者かディレクトリの所有者)
-- リンクカウントが0になるとファイルの内容を削除できる
-- プロセスがファイルを開いているとファイルの内容は削除されない
--- ファイルをクローズすると、カーネルは開いているプロセス数を確認する
    プロセス数が0のときリンクカウントを確認する
    リンクカウントが0のときファイルの内容を削除する
---- プログラムがクラッシュしても作成した一時ファイルが残らないようにするにはこの性質を利用する
-- unlinkはシンボリックリンクを指定された場合シンボリックリンク自体を削除する
-- スーパーユーザはunlinkはディレクトリを指定することもできる
-- ディレクトリの削除にはrmdirを使うべき
-- removeは通常のファイルに対してはunlink、ディレクトリに対してはrmdirと同じ
-- renameはファイルやディレクトリの名称変更を行う
--- 旧名称がファイルの場合、ファイル名を変更する
    新名称が既存の場合、新名称はディレクトリであってはならない
    また既存のファイルは削除される
    両方のディレクトリを変更するため両方のディレクトリの書き込み許可が必要
--- 旧名称がディレクトリの場合、ディレクトリ名を変更する
    新名称が既存の場合、新名称は空ディレクトリでなければならない
    また既存のディレクトリは削除される
    新名称は旧名称を含んではならない
--- 旧名称と新名称が同じ場合、何もせずに正常終了する

- シンボリックリンク
-- ハードリンクの制約を回避するために導入された
--- 異なるファイルシステム上のディレクトリ項目を指していてよい
--- 誰でもディレクトリに対するリンクを作成できる
-- 名前によってファイルを参照する関数を使うときは
   その関数がシンボリックリンクを辿るかどうかを知っている必要がある
-- シンボリックリンクを使用してファイルシステムにループを導入することがありえる
--- シンボリックリンクを辿るほとんどの関数はループを検出するとerrnoにELOOPを返す
-- シンボリックリンクのループはunlinkによって簡単に削除できる
--- ハードリンクの場合削除は困難
-- シンボリックリンクが指すファイルが存在しない場合、
   openはファイルをオープンできない旨のエラーを返す

- symlink、readlink
-- symlinkでシンボリックリンクを作成する
-- readlinkは、open、read、closeの組み合わせと同じことをするが、openと違ってシンボリックリンクを辿らない

- ファイルの時刻
-- 修正時刻(st_mtime)は、ファイルの内容を最終的に変更した時刻
-- 状態変更時刻(st_ctime)は、iノードを最終的に変更した時刻
-- 最終参照時刻(st_atime)は、ファイルの内容を最終的に参照した時刻
   iノードに対しては該当するものはない
-- ls(1)で-t、-c、-uでそれぞれ修正時刻、状態変更時刻、最終参照時刻でソートできる

- utime
-- ファイルの修正時刻と参照時刻はutimeで変更する
-- nullポインタを指定すると現在時刻に設定する
   これには、プロセスの実効ユーザIDがファイルのユーザIDと等しいか
   プロセスがファイルに対して書き込み許可を持つかのいずれかである必要がある
-- nullポインタ以外を指定するときは
   プロセスの実効ユーザIDがファイルのユーザIDと等しいか
   スーパーユーザ特権が必要

- mkdir、rmdir
-- mkdirは新たな空のディレクトリを作成する
   指定したモードはプロセスのファイルモード作成マスクでマスクされる
   実行許可を忘れるとディレクトリのファイル名を辿ることができなくなる
-- SVR4では親ディレクトリのセットグループIDビットを引き継ぐ
-- rmdirは空のディレクトリを削除する
-- rmdirの結果リンクカウントが0になるとディレクトリが占めていた領域は解放できる
--- ディレクトリをオープンしているプロセスがなければすぐに解放される
--- ディレクトリをオープンしているプロセスがあると、
    最後のリンクと.と..は削除され、ディレクトリにはファイルを作成できなくなる
    プロセスが全て終了するとディレクトリ領域は解放される

- ディレクトリの読み取り
-- ディレクトリへの読み取りは許可があればどのプロセスでも読める
-- ディレクトリへの書き込みはカーネルだけが行える
-- ディレクトリの実際の形式は実装によるためディレクトリを読み取るプログラムはシステムに依存する
   これを単純化するためPOSIX.1にディレクトリ操作用の一連のルーティンが含まれる
-- opendir、readdir、rewinddir、closedir
-- dirent構造体は実装に依存する
-- DIR構造体は、FILE構造体と同じように、ディレクトリを読み進めるための情報を管理する

- chdir、fchdir、getcwd
-- ログイン時にホームディレクトリがカレント作業ディレクトリに設定される
-- ホームディレクトリはログイン名の属性、カレント作業ディレクトリはプロセスの属性
-- プロセスのカレント作業ディレクトリはchdir、fchdirで変更する
--- fchdirはPOSIX.1にはない
-- chdirはプロセスの属性であるためchdirを呼び出すプロセスを起動したプロセスには影響しない
--- シェルのカレントディレクトリを変更するためにはシェル自身がchdirしなければならない
-- カーネルはプロセスのカレント作業ディレクトリのiノード番号と装置の識別子を管理する
-- カーネルの管理する情報からカレント作業ディレクトリの絶対パスに変換して取得するには
   getcwdを使用する

- 特殊装置ファイル
-- 各ファイルシステムはメジャー装置番号とマイナー装置番号で区別される
-- メジャー装置番号とマイナー装置番号はdev_t型にmajorマクロとminorマクロを適用することで参照できる
-- st_devは、ファイル名と対応するiノードを収めたファイルシステムの装置番号
-- st_rdevは、文字型特殊ファイルとブロック型特殊ファイルの実際の装置に対する装置番号

- sync、fsync
-- カーネルはディスク入出力を遅延出力するためのバッファキャッシュを持つ
-- バッファキャッシュとディスク上の実際のファイルシステムの一貫性を保証するためにsync、fsyncがある
-- syncは、出力用に修正されたブロックバッファを待ち行列に入れて戻る
   実際の入出力操作の完了を待たない
-- syncは、30秒ごとにシステムデーモンから呼び出されカーネルのブロックバッファを書き出す
-- fsyncは、入出力の完了を待って戻る
-- fsyncは、データベースなどで修正したブロックが実際にディスクに書き出されたことを保証するために使用する

= 5章 標準入出力ライブラリ

標準入出力ライブラリはバッファの割り付けや指摘な大きさでの入出力操作などの詳細を隠蔽する

- ストリームとFILEオブジェクト
-- すべての入出力ルーティンはファイル記述子に作用する
-- 標準入出力ライブラリではストリームを扱う
-- 標準入出力ライブラリでファイルをオープンすることをファイルにストリームを結び付けるという
-- ストリームをオープンすると標準入出力関数fopenはFILEオブジェクトへのポインタを返す
-- FILEオブジェクトはストリームを管理するための情報が収められている
--- ファイル記述子、ストリーム用のバッファへのポインタ、バッファのサイズ、バッファに現在入っている文字数、エラーフラグなど
--- アプリケーションコードがFILEオブジェクトの内部を参照する必要はない

- 標準入力、標準出力、標準エラー出力
-- STDIN_FILEINFO、STDOUT_FILEINFO、STDERR_FILE_NO
-- stdin, stdout, stderr

- バッファリング
-- read、writeの呼び出し回数を減らすのが目的
-- 3種類のバッファリング
--- 完全なバッファリング
---- 標準入出力のバッファが一杯になったときに実際の入出力が行われる
---- 標準入出力ライブラリはディスク上のファイルに対して完全なバッファリングを行う
---- 標準入出力バッファの書き出しをフラッシュという
---- fflushはフラッシュを行う
---- 端末ドライバでいうフラッシュとは別物
--- 行バッファリング
---- 改行文字に出会うと入出力を行う
---- 行バッファリングは典型的には端末を指すストリームに対して使用される
---- バッファサイズは固定であるため改行を書く前にバッファが一杯になり入出力が行われる場合がある
---- 標準入出力ライブラリを介してアンバッファドストリームまたは行バッファリングストリームに入力を要求すると
     それがカーネルにデータを要求した場合すべての行バッファリングの出力ストリームをフラッシュする
     行バッファリングでは必要なデータがバッファにあればカーネルにデータを要求することはない
--- アンバッファド
---- バッファリングしない
---- 標準エラーストリームは通常アンバッファド
-- ANSI Cでは以下の性質を要求する
--- 標準入力と標準出力はそれが対話的な装置を指さない場合完全なバッファリングを行う
--- 標準エラー出力は完全なバッファリングを行うことはない
-- SVR4と4.3+BSDのデフォルトは以下のとおり
--- 標準エラー出力は常にアンバッファド
--- 他の全てのストリームは、端末装置を指す場合は行バッファリング、そうでなければ完全なバッファリング
-- setbuf、setvbufでバッファリングのデフォルトを変更できる
-- setbuf、setvbufは、オープン後、入出力操作前に呼び出す必要がある
-- setbufでは、BUFSIZのバッファを指すポインタを渡すことで完全なバッファリングがされるようになる
   ただし端末装置を指すストリームの場合には行バッファリングになることもある
-- setvbufでは、任意のバッファリング方式を指定することができる
-- 関数内の自動変数を標準入出力バッファに割り付けたときは、関数から戻る前にストリームをクローズする必要がある
-- SVR4ではバッファの一部を管理用に使用するため指定サイズよりも小さいサイズが実際のバッファリングに使用される
-- 一般にはバッファサイズの選択をシステムにまかせるべき
   このときストリームをクローズすると標準入出力ライブラリがバッファを解放する
-- fflushによりストリームはいつでもフラッシュできる
-- fflushにnullポインタを渡すとすべての出力ストリームがフラッシュされる

- fopen、freopen、fdopen
-- ストリームをオープンする
-- freopenは、指定したストリームに指定したファイルをオープンする
--- 指定したストリームが既にオープンされているときはまずそのストリームをクローズする
--- 標準入力、標準出力、標準エラー出力の定義済みストリームに指定したファイルをオープンする場合に使用される
-- fdopenは既存のファイル記述子を受け取り、それを標準入出力ストリームへ結び付ける
--- パイプやネットワーク通信チャネルなどはfopenではオープンできないため
    装置固有の関数を呼び出してファイル記述子を得てfdopenして標準入出力ストリームに結び付ける
-- ファイルを入出力用にオープンした場合
   入力に続けてfflush、fseek、fsetpos、rewindを行わない限り直接出力はできない
   出力に続けてfseek、fsetpos、rewindを行わない限り直接入力はできない
-- ファイルを新規作成するときPOSIX.1では S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
   でファイルが作成されることを要請する
-- デフォルトでは、オープンしたストリームは完全なバッファリングを行う
   端末装置を指す場合は行バッファリングを行う
-- ストリームをオープンした後ストリームに操作を行う前であればsetbuf、setvbufでバッファリングの方式を変更できる

- fclose
-- オープンしたストリームはfcloseでクローズする
-- ファイルをクローズする前にバッファに残っているデータはフラッシュされ
   バッファに残っている入力データは捨てられる
   バッファを標準入出力ライブラリが割り当てている場合は解放される
-- exitまたはmain関数からのreturnによりプロセスが正常終了すると
   バッファにデータが残っているすべての標準入出力ストリームはフラッシュされ
   オープンされた標準入出力ストリームをすべてクローズする

- 書式付けしない入出力
-- 1文字ずつの入出力
-- 1行ずつの入出力
-- 直接入出力

- getc、fgetc、getchar
-- getcharは、getc(stdin)と同じ
-- getcはマクロであるので
   副作用のある式を渡せない
   アドレスを取れない
   fgetcより効率がよい
-- unsigned charをintに変換して返す
-- unsignedは上位ビットがセットされても負の値を返さないようにするため
-- intなのはエラーの発生やファイルの終わりに達したことを示せるようにするため
-- エラー発生の場合もファイルの終わりの場合も同じ値を返す
--- ferrorかfeofのどちらかを呼んで区別する

- ferror、feof、clearerr
-- エラーフラグはFILEオブジェクトのストリームごとに管理される
-- clearerrによりエラーフラグをクリアできる

- ungetc
-- ストリームから読み取った文字をストリームに戻す
-- ANSI Cで要請しているのは少なくとも1文字戻せることなので
   2文字以上戻すべきではない
-- ストリームに戻す文字は読み取った文字と同じ文字である必要はない
-- EOF文字を戻すことはできない
-- ファイルの終わりに達したときに文字を戻すことはできる
--- ungetcはファイルの読み取り終わりフラグをクリアする

- putc、fputc、putchar
-- putchar(c)はputc(c, stdout)と同じ
-- putcはマクロ

- fgets、gets
-- getsは標準入力から読み取る
-- fgetsではバッファのサイズも指定する必要がある
--- 行末の改行を含めて読み取る
--- バッファはnullで終端する
--- 行が長い場合はつぎのfgetsで残りを読み出せる
-- getsはバッファのサイズを指定できない
--- 使用するべきではない

- fpus、puts
-- fputsは、改行文字は必須ではないので、1行を一度に出力しなくてもよい
-- putsは、標準出力へ書き込んだ後、改行文字を出力する
-- fgetsとfputsだけを使用するようにすれば改行文字についての混乱を避けられる
   改行文字を常に扱う必要がある、ということになる

- fread、fwrite
-- 構造体・バイナリの配列を読み書きする
-- 読み書きした要素の数を返す
--- 要求数より小さい数が返ってきた場合、エラーを調べる必要がある
-- 互換性の問題
-- コンパイラやシステムが異なるとアラインメントの違いにより
   構造体内のメンバーのオフセットが変わることがある
-- マシンアーキテクチャが異なると
   要素のバイナリ形式が異なるととがある

- ftell、fseek、fgetpos、fsetpos
-- fgetposとfsetposはあとからANSI Cに追加された
   ポータブルなアプリケーションではこれを使用すべき
-- ftellは現在のファイル内の位置を返す
-- fseekはファイル内の位置を設定する
-- バイナリファイルではファイル内の位置は単なるバイトオフセットだが
   テキストファイルではそうでない場合がある
-- テキストファイルで位置決めする場合
   SEEK_SETを使い、ファイルの先頭かftellの値以外は指定できない
-- rewindでストリームの先頭に位置決めできる
-- fgetposは現在のファイル内の位置を指定したfpos_tに保存する
-- 保存したfpos_tはfsetposに渡して位置決めすることができる

- 書式付き入出力
-- printf、fprintf、sprintf
--- printfは標準出力に書き込む
--- fprintfは指定したストリームに書き込む
--- sprintfは書式付けした結果を配列に書き込む
-- vprintf、vfprintf、vsprintf
--- vなしバージョンの可変引数のかわりにva_listを使用する
-- scanf、fscanf、sscanf

- fileno
-- ストリームに結び付けられたファイル記述子を返す
-- dupやfcntlを呼ぶ場合に必要になる

- 一時ファイル
-- tmpnam、tmpfile
--- tmpnamは既存のファイル名とは異なる正当なパス名となる文字列を生成する
---- 最大TMP_MAX回呼ばれるまでは1回ごとに異なるパス名を生成する
--- tmpfileはプログラム終了時に自動的に削除される一時的なバイナリファイルを作成する
-- tempnam
--- tempnamは、生成するパス名のディレクトリや接頭辞を呼び出し側が指定できる

- 標準入出力ライブラリの代替
-- fio
-- sfio
-- mmap

= 6章 システムデータファイルと情報

データファイルに対するポータブルなインタフェース

- パスワードファイル
-- /etc/passwd
-- getpwuid、getpwnam
--- getpwuidは、ユーザIDからpasswdエントリを得る
--- getpwnamは、ログイン名からpasswdエントリを得る
-- getpwent、setpwend
--- getpwentは、パスワードファイルの次の登録項目を取得する
---- 最初に呼び出したときにファイルをオープンする
---- 登録項目の順番には意味がない
--- setpwentは、パスワードファイルの先頭に戻る
--- endpwentは、パスワードファイルをクローズする

- シャドウパスワード
-- 暗号化されたパスワードからパスワードを推定されるのを防ぐため
   暗号化されたパスワードをパスワードファイルとは別のファイルに格納する
--- SVR4では/etc/shadow、4.3+BSDでは/etc/master.passwd
-- login(1)、passwd(1)などの数個のプログラムからアクセスできればよい
   これらはルートにセットユーザIDされる

- グループファイル
-- getgrgid、getgrnam
-- getgrent、setgrent、endgrent

- 補足グループID
-- もともとユーザは一度にひとつのグループにのみ属し、newgrpでユーザのグループを変更することができた
-- 補足グループIDを使用すると明示的にグループを変更する必要がなくなる
-- getgroups、setgroups、initgroups
--- getgroupsは、補足グループを取得し配列に埋めて返す
--- setgroupsは呼び出し側プロセスの補足グループIDを設定する
    スーパーユーザ特権が必要
--- initgroupsはユーザの補足グループをグループファイルから取得しsetgroupsする
    スーパーユーザ特権が必要

- その他のデータファイル
-- getXXX、setXXX、endXXX
-- キー検索関数
-- 構造体
-- /etc/hosts
-- /etc/networks
-- /etc/protocols
-- /etc/services

- ログイン記録
-- utmp
--- 現在ログインしているユーザを記録する
-- wtmp
--- すべてのログインとログアウトを記録する

- システムの識別
-- uname
-- gethostname

- 時刻と日付
-- time
--- カレンダー時間
-- gmtime、localtime
--- カレンダー時間->tm構造体
-- mktime
--- tm構造体->カレンダー時間
-- asctime
--- tm構造体->文字列
-- strftime
--- tm構造体->書式付き文字列
-- ctime
--- カレンダー時間->文字列

= 7章 UNIXプロセス環境

単一プロセスの環境

- main
-- リンケージエディタによって起動ルーティンとして設定されたルーティンが呼ばれる
-- 起動ルーティンはカーネルからコマンド行引数と環境変数を受け取りmainを呼び出す

- プロセスの終了
-- 正常終了
--- mainから戻る
--- exitを呼ぶ
--- _exitを呼ぶ
-- 異常終了
--- abortを呼ぶ
--- シグナルで終了させられる

- exit、_exit
-- _exitは直接カーネルに戻る
-- exitはさまざまな後始末を行ってからカーネルに戻る
-- exitは標準入出力ライブラリの後始末を行う
--- オープンしているストリームに対してfcloseを呼ぶ
-- exitも_exitも終了状態を引数にとる
-- mainで値を与えずにreturnした場合、exit、_exitを値を与えずに呼んだ場合
   プロセスの終了状態は未定義

- atexit
-- exitが自動的に呼び出す最大32個の関数(終了ハンドラ)を登録できる
-- exitは登録順と逆順に関数を呼び出す
-- 各関数は登録された回数だけ呼び出される
-- exitは終了ハンドラを呼んだ後にストリームをクローズする

- コマンド行引数

- 環境リスト
-- extern char **environ
--- 環境文字列ポインタ配列へのポインタ
-- mainの第3引数
-- 特定の環境変数を参照する場合は、getenv、setenvを使用する

- Cプログラムのメモリ配置
-- テキストセグメント
-- 初期化されたデータセグメント
-- 初期化されていないデータセグメント(bssセグメント)
--- プログラム開始前にカーネルによって0に初期化される
--- ディスク上には存在しない
-- スタック
-- ヒープ

- 共有ライブラリ

- メモリ割り付け
-- malloc、calloc、realloc
--- callocは指定サイズの指定個数分の領域を割り当て、0に初期化する
--- reallocは既に割り付けた領域のサイズを変更する
    サイズを増加させた場合領域を移動する場合もある
    その場合元の内容を新たに確保した領域にコピーし元の領域は解放される
    サイズを増加させた場合の増えた領域部分の内容は不定
--- malloc、calloc、reallocが返すポインタは適切に整列されていることが保証される
-- free
--- 領域を解放する
-- alloca
--- スタックフレームからメモリを割り付ける
--- 関数を呼び出した後にスタックフレームのサイズを増やせないシステムでは使用できない

- 環境変数
-- ANSI Cでは環境変数を取り出す関数は定義されているが環境の内容は実装で定義される
-- getenv
--- "name=value"のvalueへのポインタを返す
-- 環境変数の解釈はアプリケーションに任されている
-- POSIX.1、XPG3で定義される環境変数もある
-- 環境変数の操作ができる対象は現プロセスとその子に限られる
-- putenv、setenv、unsetenv、clearenv
--- putenvはname=value形式の文字列をとり環境リストへ追加する
    nameが既存であればまずその定義を取り除く
--- setenvはnameにvalueを設定する
    nameが既存であれば、rewrite引数が0でないなら定義を取り除き、0なら取り除かない
--- unsetenvは定義を取り除く
    定義がなくてもエラーにはならない
-- 環境変数の領域は最初はスタック先頭にあるため
   拡大するときにヒープへ移動される

- setjmp、longjmp
-- gotoでは別の関数にあるレベルへ移動できない
-- 深くネストされた関数呼び出しで発生したエラー状態を扱うのに有効

- getrlimit、setrlimit
-- ソフトリミット
--- 現在の制限
-- ハードリミット
--- 制限の最大値
-- ハードリミット以下であればどのプロセスでもソフトリミットを変更できる
-- ソフトリミット以上であればどのプロセスでもハードリミットを減少できる
-- ハードリミットを増加できるのはスーパーユーザのみ
-- リソースリミットは呼び出したプロセスに影響し、子に継承される

= 8章 プロセス制御

新規プロセスの作成、プログラムの実行、プロセスの終了
実/実効/保存セットユーザID、グループID

- プロセス識別子

- スワッパ
-- プロセスID=0
-- スケジューリングを行う

- initプロセス
-- プロセスID=1
-- オーファンドプロセスの親になる

- ページデーモン
-- プロセスID=2

- fork
-- 1回呼ばれて2回返る
--- 親には子プロセスIDが返る
--- 子には0が返る
-- 子は親の複製になる(共有ではない)
-- メモリがコピーされるのでフラッシュ前のバッファもコピーされる
-- ファイル記述子がdupされるのでファイルテーブル項目が共有される
--- 分離するためにはfork後親と子で互いに不要な記述子をクローズする
-- その他親から子に継承される属性
--- 実ユーザID、実グループID、実効ユーザID、実効グループID
--- 補足グループID
--- プロセスグループID
--- セッションID
--- 制御端末
--- セットユーザIDフラグ、セットグループIDフラグ
--- カレント作業ディレクトリ
--- ルートディレクトリ
--- ファイルモード作成マスク
--- シグナルマスク、シグナル処理
--- オープンされたファイル記述子のclose-on-execフラグ
--- 環境
--- 共有メモリセグメント
--- リソースリミット
-- 親と子の違い
--- forkからの戻り値
--- プロセスID
--- 親プロセスID
--- tms構造体(プロセス時間) 子は0
--- 親が設定したファイルロックは子に継承されない
--- 子に対する保留アラームはクリアされる
--- 子に対する保留シグナルの集合は空になる
-- 失敗原因
--- システムのプロセス数
--- 実ユーザIDの持つプロセス数
-- 使い方
--- コードの異なる部分を親と子で同時に実行できるようにする (サーバ)
--- プロセスが別のプロセスを実行したい場合 (シェル)
---- fork後execする(スポーン)

- vfork
-- execするために使用する
--- exec(かexit)するまで、子は親のアドレス空間を共有する
--- exec(かexit)するまで、親は待つ
-- 子がexitすると標準入出力ストリームがクローズされる
--- exec前にexit(3)すると親の標準入出力ストリームがクローズされてしまう
--- _exit(2)で回避

- exit
-- プロセスの正常終了
--- return
--- exit
--- _exit
-- プロセスの異常終了
--- abort
--- 特定のシグナルを受け取ったとき
-- プロセス終了時の処理
--- ファイル記述子のクローズ
--- メモリ解放
-- プロセス終了の親への通知
--- wait、waitpid
-- 最終状態と終了状態
--- 最終状態(exit status):正常終了の方法によって渡す値。カーネルが終了状態に変換する。
--- 終了状態(termination status)
-- 子が終了する前に親が終了した場合
--- initプロセスが親プロセスになる
-- 親が子の終了状態を取得する前に子が終了した場合
--- wait, waitpidに備えてカーネルが終了したプロセスの状態を保存する
--- 親が終了状態を取得するまで残る(リソースは解放される):ゾンビプロセス
--- initはその生成したプロセスを必ずwaitする
-- 子が終了すると親にSIGCHLDが送られる
--- デフォルトでは無視

- wait
-- 子のひとつの終了状態を取得して戻る
--- プロセスIDによりどの子が終了したのかわかる
--- 子が終了していなければブロックする
--- 子が終了しているか存在しないならすぐに戻る
- waitpid
-- どの子を対象とするか指定できる
-- 子のステータスを取得するときにブロックしないオプションを持つ
-- 終了以外のステータスを取得するオプションを持つ(ジョブ制御を扱える)

- 終了状態判定マクロ
-- WIFEXITED()
--- 正常終了の場合に真
--- WEXITSTAUS()で最終状態を取得できる
-- WIFSIGNALED()
--- 異常終了の場合に真
--- WTERMSIG()で終了原因となったシグナル番号を取得できる
-- WIFSTOPPED()
--- 子が休止している場合に真
--- WSTOPSIG()で子を休止させたシグナル番号を取得できる

- fork/forkイディオム
- レースコンディション
-- 親が先か子が先かは保証されない

- exec
-- プロセスがexecを呼ぶとプロセスは新しいプログラムに置き換えられる
-- 新しいプログラムのmainから実行が開始される
-- execl, execv, execle, execve, execlp, execvp
--- lは可変引数で、vは配列で引数を渡す
---- lの場合、引数の最後はnullにする
--- 無印とeはパス名で指定する
--- pはファイル名でも指定できる
---- ファイル名指定時はPATH環境変数を使用して探索する
---- PATH環境変数にはディレクトリを:で区切ったものを指定する
---- 空文字列(最初や最後に:があったり:に囲まれていたり)はカレントディレクトリを表す
--- eは引数で環境を渡す
--- 無印とpはenviron変数で環境を渡す
-- execの後も引き継がれる属性
--- プロセスID、親プロセスID
--- 実ユーザID、実グループID
--- 補足グループID
--- プロセスグループID
--- セッションID
--- 制御端末
--- アラームクロックの残り時間
--- カレント作業ディレクトリ
--- ルートディレクトリ
--- ファイルモード作成マスク
--- ファイルロック
--- プロセスシグナルマスク
--- 保留シグナル
--- リソースリミット
--- tms構造体
-- ファイル記述子にclose-on-execフラグが立っている場合、ファイル記述子はクローズされる
-- 実効ユーザID、実効グループIDは、プログラムファイルのセットユーザIDビットに依存して決まる
--- セットユーザIDビットが立っている場合、実効ユーザID、実効グループIDはプログラムの所有者になる
    そうでないときはexec前の実効ユーザID、実効グループIDを引き継ぐ


- setuid, setgid
-- setuidは、実ユーザID、実効ユーザIDを変更する
-- setgidは、実グループID、実効グループIDを変更する

- ユーザID変更規則(POSIX_SAVED_IDSが有効な場合)
-- プロセスがスーパーユーザ特権を持っていればsetuidは実ユーザID、実効ユーザID、保存セットユーザIDをuidに変更する
-- プロセスがスーパーユーザ特権を持っていなれば
   uidが実ユーザIDか保存セットユーザIDに等しいとき、setuidは実効ユーザIDをuidに変更する
-- 上記のいずれでもない場合、setuidはエラーを返し、errnoにEPERMをセットする

- 各ユーザIDの変更規則
-- スーパーユーザのみが実ユーザIDを変更できる
--- 通常login(1)が実ユーザIDを設定し、以降変更されない
--- login(1)はスーパーユーザプロセスなので実/実効/保存セットの3つとも変更される
-- プログラムファイルのセットユーザIDビットが立っていれば
   execは実効ユーザIDをプログラムファイルの所有者に変更する
   セットユーザIDビットが立っていなければ実効ユーザIDはexec後も変化しない
--- 変化した実効ユーザIDは、setuidにより、実ユーザIDか保存セットユーザIDに戻せる
-- 保存セットユーザIDは、execが実効ユーザIDからコピーする
   セットユーザIDフラグが立っているときは、ファイルの所有者ユーザIDを実効ユーザIDに設定した後でコピーする

- setreuid, setregid
-- 実ユーザIDと実効ユーザIDを切り替える(4.3+BSD)
--- 4.3BSDには保存セットユーザIDがなかったため

- seteuid, setegid
-- 特権を持つユーザは実効ユーザIDを変更できる
-- 特権を持たないユーザは実効ユーザIDを実ユーザIDまたは保存セットユーザIDに変更できる
-- POSIX.1外。SVR4、4.3+BSDで使用可能

- 解釈実行ファイル(interpreter file)
-- #! ではじまる
-- #! に指定するファイルの探索にはPATHは使用されない(絶対パスで書く)
-- execシステムコールを処理する過程で解釈される
-- #! のコマンドラインは解析されてargvに入る
--- 解釈実行ファイルに渡した引数はその分後ろにずれて渡される

- system
-- ANSI C
-- シェルに対するインタフェースなのでPOSIX.1には含まれていない
-- systemが使えるかどうかは、systemにnullポインタを渡すと判別できる
-- 内部的にfork、exec、waitpidを呼ぶ
--- forkの失敗時、-1を返し、errnoにエラーがセットされる
--- waitpidでEINTR以外のエラー時、-1を返し、errnoにエラーがセットされる
--- execの失敗時、シェルがexit(127)を実行したときのような結果が返る
--- 成功時、waitpidでのシェルの終了状態が返る
--- 現在のほとんどの実装では、waitpidがEINTRで割り込まれるとエラーを返す
-- fork、execを使った場合に比べて、systemを使うと必要なエラー処理やシグナル処理をしてもらえる
-- セットユーザIDしたプログラムからsystemを呼ぶとセキュリティホールになる
--- 実効ユーザIDが引き継がれてしまうため
--- 明示的にforkし、exec前に実効ユーザIDを戻す

- acct
-- プロセスの実行記録
-- accton(8)コマンドで実行記録をとれる
-- プロセスの実行記録はカーネルがプロセステーブルに保存する
-- プロセスの終了時にプロセステーブルから実行記録ファイルに書き出される
-- fork時にレコードはつくられる
-- execしてもコマンド名が書き換えられるだけでレコードは増えない
-- coreファイルの出力は実行記録に含まれない

- ログインユーザ
-- 実ユーザIDをgetuidで取得しgetpwuidでpasswdファイルのエントリを取得できる
-- passwdファイルには、同一ユーザIDで(異なるユーザ名の)複数のエントリを持てる
--- ログインシェルを変えたりする
-- getloginでログイン時のログイン名を取得できる
-- getpwnamでログイン名からpasswdエントリを取得できる
-- getloginはユーザがログインした端末にプロセスが結び付いていないと失敗する
--- デーモンなど

- times
-- tms構造体にプロセス自身と終了した子に関する時間の情報を取得する
--- 実時間、ユーザCPU時間、システムCPU時間

= 9章 プロセスの関係

- 端末からのログイン
-- 4.3+BSDでの端末からのログイン
--- システム管理者は/etc/ttysにログインを許可する端末装置名とボーレートなどのパラメータを指定する
--- システムが起動するとinitプロセスがシステムをマルチユーザモードにする
--- initプロセスは/etc/ttysを読みログインを許す各端末装置ごとにforkしてgettyプログラムをexecする
--- gettyプロセスは実ユーザID、実効ユーザIDとも0
--- gettyプロセスは空の環境でexecされる
--- gettyプロセスはopenを呼び端末をオープンする
--- ファイル記述子0、1、2を端末に結び付ける
--- プロンプトを出してユーザ名の入力を待つ
--- 入力されたユーザ名を引数としてloginプログラムをexecする
--- gettyはlogin用の環境を作ってloginをexecする
--- loginはgetpwnamを呼んでパスワードファイルの登録項目を取得する
--- getpassを呼んでパスワードの入力を待つ
--- cryptを呼んでパスワードを暗号化しパスワードファイルに登録項目と比較する
--- ログインに失敗すると1を引数としてexitを呼び、initはforkをやりなおす
--- ログインに成功するとホームディレクトリへchdirし
    端末装置の所有者とグループをログインしたユーザにchownする
    端末装置のアクセスパーミッションはユーザは読み書き可能、グループは読み取り可能に変更される
--- setgid、initgroupsしてグループIDを設定する
--- ホームディレクトリ、シェル、ユーザ名、パスの環境を設定する
--- setuidしてからログインシェルをexecする
--- ログインシェルは始動ファイルを読みプロントを出して入力を待つ
--- ログインシェルが終了するとinitにSIGCHLDシグナルが通知されinitはforkをやりなおす
-- SVR4での端末からのログイン
--- gettyを使う場合は4.3+BSDと同じ
--- gettyとは別にttymonを使う方法もある
--- コンソールではgettyを使い、その他の端末からのログインにはttymonを使う
--- ttymonはSAF(Service Access Facility)の一部
--- initはsac(サービスアクセスコントローラ)の親となる
--- sacがforkしてttymonをexecする
--- ttymonはすべての端末を監視し、ログイン名が入力されるとforkする
--- forkされた子がloginをexecする
--- ログインシェルの親はttymonになる

- ネットワークからのログイン
-- 4.3+BSDでのネットワークからのログイン
--- 端末と異なりログインの数を固定にすることができない
--- ログインを待つプロセスを起動しておくかわりにネットワークからの接続要求を待つ
--- initは/etc/rcシェルスクリプトを介してinetdを起動する
--- inetdの親はinitになる
--- inetdは接続要求を待ち接続要求が到着するとforkして適切なプログラムをexecする
--- telnetdがexecされるとtelnetdは疑似端末をオープンしforkして2つに分裂する
--- 親はネットワーク通信を処理する
--- 子はファイル記述子0、1、2を疑似端末に設定しloginをexecする
--- 以下loginは端末の場合と同じ処理を行う
-- SVR4でのネットワークからのログイン
--- 4.3+BSDとほとんど同じ
--- inetdはinitではなくsacから起動される

- プロセスグループ
-- プロセスはプロセスグループに属する
-- プロセスグループは1つ以上のプロセスのあつまり
-- 各プロセスグループにはプロセスグループIDがつけられる
-- getpgrpは呼び出したプロセスのプロセスグループIDを返す
-- 各プロセスグループにはプロセスグループリーダがいる
-- プロセスグループリーダのプロセスIDはプロセスグループIDと等しい
-- プロセスグループリーダはプロセスグループを作成し、グループ内にプロセスを作成したり終了させたりする
-- プロセスグループリーダが終了してもプロセスグループは存続する
-- グループの最後のプロセスは終了するかまたは別のプロセスグループへ移動する
-- setpgidはプロセスのプロセスグループIDを設定する
--- プロセスは自分または子のプロセスに対してのみsetpgidできる
--- プロセスグループIDにプロセスIDと同じ値を指定するとプロセスグループリーダになる
-- ジョブ制御を扱わないシステムでは_POSIX_JOB_CONTROLは未定義で
   setpgidはerrnoにENOSYSを設定しエラーを返す

- セッション
-- セッションは1つ以上のプロセスグループのあつまり
-- 通常、プロセスはシェルのパイプラインでプロセスグループにまとめられる
-- setsidは呼び出したプロセスがプロセスグループリーダでない場合
   新たなセッションを開始する
--- 呼び出したプロセスは新しいセッションのセッションリーダになる
--- 呼び出したプロセスは新しいプロセスグループのプロセスグループリーダになる
--- 呼び出したプロセスは制御端末をもっていたらその結び付きを解かれる
--- 呼び出したプロセスが既にプロセスグループである場合はエラーを返す
---- forkして親は終了し子で実行を継続すると
     子のプロセスIDは親から継承したプロセスグループIDと一致しないため
     プロセスグループリーダでないことを保証できる

- 制御端末
-- セッションは1つの制御端末を持つことができる
--- 端末からのログインの場合端末装置、ネットワークからのログインの場合疑似端末
-- 制御端末との接続を確立したセッションリーダを制御プロセスと呼ぶ
-- セッション内のプロセスグループは
   1つのフォアグラウンドプロセスグループと
   複数のバックグラウンドプロセスグループにわけられる
-- 制御端末をもつセッションは、フォアグラウンドプロセスグループが1つあり
   端末入力を受け取る
-- 端末の割り込みキー(DELETE、Control-C)やクイットキー(Control-\)により
   割り込みシグナルやクイットシグナルがフォアグラウンドプロセスグループの
   すべてのプロセスに送られる
-- 端末インタフェースがモデムの切断を検出すると
   制御プロセスに対してハングアップシグナルが送られる
-- 制御端末は/dev/ttyをopenすると明示的に(標準入力や標準出力ではなく)使用できる

- tcgetpgrp、tcsetpgrp
-- どのプロセスグループがフォアグラウンドであるかを設定、取得する
-- 通常、ジョブ制御を行うシェルが使用する
-- _POSIX_JOB_CONTROLが定義されていなければエラーを返す

- ジョブ制御
-- ジョブ制御を行うには
   ジョブ制御を行えるシェル
   ジョブ制御を行える端末ドライバ
   ジョブ制御シグナル
   が必要
-- ジョブ ≒ プロセスグループ
-- ジョブにはジョブ番号が振られる
-- フォアグラウンドプロセスグループに対するシグナル
--- SIGINT
--- SIGQUIT
--- SIGTSTP
-- フォアグラウンドプロセスグループに対する端末入力
--- バックグラウンドプロセスグループが端末入力を要求すると
    端末ドライバからSIGTTINを受け取る
--- SIGTTINを受け取るとプロセスは休止する
--- 親であるシェルはwait/waitpidでプロセスの休止を検知する
--- シェルのfgコマンドでジョブをフォアグラウンドにできる
    シェルはtcsetpgrpでフォアグラウンドプロセスグループを設定する
    SIGCONTが再開されるプロセスグループに送られる
-- バックグラウンドジョブが制御端末に出力しようとしたときの処理は
   stty(1)によりかわる
--- 出力するジョブが停止させられる設定の場合
    SIGTTOUがプロセスグループに送られる

- シェルによるプログラムの実行
-- ジョブ制御がない場合
--- パイプラインの最後のプログラムがforkしてその前のプログラムをexecする
--- プロセスグループはつくられない
--- 制御端末はつながったままになる
-- ジョブ制御がある場合
--- パイプラインはそのままプロセスグループになる

- オーファンドプロセスグループ
-- プロセスグループ内のプロセスが停止したとき(親や自身によって)そのプロセスを再開できない状態
-- グループ内の各メンバーの親は
   グループのメンバーであるか
   グループのメンバーでなければセッションのメンバーではない
--- 同一セッションの別プロセスグループに親がいれば
    そのプロセスグループがフォアグラウンドになることにより
    対象のプロセスグループ内のプロセスを再開できる
-- POSIX.1では、オーファンドプロセスグループの各プロセスにSIGHUPと続けてSIGCONTを送る
-- オーファンドプロセスグループがバックグラウンドで
   端末からの入力を試みると
   SIGTTOUは送られず、入力がEIOエラーになる
--- SIGTTOUが送られてもプロセスを再開できない




= 10章 シグナル

ソフトウェアによる割り込み

- シグナルの概念
-- シグナルはSIGではじまる名前を持つ
-- 名前に対して正の定数が定義されている
--- 0は特別扱いされる
-- いろいろな状況でシグナルが生成される
--- 特別な端末キー
--- ハードウェア例外
--- kill(2)関数
--- kill(1)コマンド
--- プロセスへの通知イベント
---- SIGURG、SIGPIPE、SIGALRM
-- 非同期
-- シグナルの処置(disposition)
--- シグナルの無視
---- SIGKILLとSIGSTOPは無視できない
--- シグナルの捕捉
--- デフォルト動作の適用

-- coreの生成
--- セットユーザID、セットグループIDされたプログラムは
    実ユーザIDと異なる場合セキュリティ上coreをはかない

-- SIGABRT
-- SIGALRM
-- SIGBUS
-- SIGCHLD
-- SIGCONT
-- SIGEMT
-- SIGFPE
-- SIGHUP
--- 端末の接続断(CLOCALでないとき)
--- セッションリーダ終了時
--- デーモン制御用
-- SIGILL
-- SIGINFO
-- SIGINT
-- SIGIO
--- 非同期入出力
-- SIGIOT
-- SIGKILL
-- SIGPIPE
-- SIGPOLL
-- SIGPROF
-- SIGPWR
-- SIGQUIT
-- SIGSEGV
-- SIGSTOP
-- SIGSYS
-- SIGTERM
-- SIGTRAP
-- SIGTSTP
-- SIGTTIN
-- SIGTTOU
-- SIGURG
-- SIGUSR1
--- ユーザ定義
-- SIGUSR2
--- ユーザ定義
-- SIGVTALRM
-- SIGWINCH
-- SIGXCPU
-- SIGXFSZ

- signal
-- 信頼性のないシグナル
-- シグナルを捕捉するシグナルハンドラを設定する
-- 以前のシグナルハンドラを返す
--- エラー時SIG_ERRを返す
--- シグナル動作を設定しないとシグナルに対する現在の動作を取得できない
-- 特別なシグナル動作
--- SIG_DFL
---- デフォルト動作の適用
--- SIG_IGN
---- 無視
-- fork時、シグナル動作を継承する
-- exec時、シグナル動作は、
   無視する設定であれば無視、
   それ以外であればデフォルト動作の適用になる
--- シグナルハンドラは、アドレスがかわるため継承できない

- kill
-- シグナルを送る

- 信頼性のないシグナル
-- 初期バージョンの問題
--- シグナルを紛失する場合がある
--- シグナルをブロックすることができない
---- 無視するのではなく生起したことを覚えておき準備が整った後に通知してほしい場合
-- 4.2BSDで信頼性のあるシグナルに変更された
-- シグナルを紛失する問題
--- シグナルが発生するたびにシグナル動作がデフォルトに戻ってしまう
--- これに対処するためにシグナルハンドラ内で再度シグナルハンドラを設定する
--- 再設定するまでの間にシグナルが生起する可能性がある
-- シグナルをブロックできない問題
--- シグナル発生時に割り込まれるのではなく
    シグナルを処理できるときになってから処理したいことがあるが
    シグナルをブロックできないのでそうしたことができない
--- シグナルハンドラでフラグを立て
    メイン処理ではフラグが立つまでpauseすることにより
    回避できる
--- フラグのチェックとpauseの間にシグナルが発生すると
    チェックに失敗する

- システムコールの割り込み
-- 低速なシステムコールが割り込まれると
   システムコールはエラーを返し
   errnoにEINTRを設定する
-- 低速とは
   永久にブロックできるシステムコール
--- ファイル(端末入力)を読む操作など
---- ディスクファイルの読み取りは除く
-- 割り込まれるシステムコールでは
   EINTRに適切に対処しなければならない
-- EINTRの場合、よくあるケースでは、処理をやりなおす(再起動する)
-- アプリケーションで明示的に再起動処理を書かなくてすむように
    システムコールを自動的に再起動する
--- SVR4 / signal:再起動しない
--- 4.2BSD /sginal:再起動する
--- SVR4, 4.3BSD / sigaction:オプションで再起動を指定
--- その他システムとシグナルの関数によっていろいろ

- 再入可能(リエントラント)
-- シグナルハンドラで関数を呼ぶと
   その呼び出しは再入である可能性がある
--- 関数の途中で割り込まれてそのハンドラでその関数を呼んだ場合
-- 再入が問題を引き起こす場合
--- malloc
---- リンクリストの処理中に変更してしまう
--- getpwnam
---- 静的領域を書き換えてしまう
-- POSIX.1で再入可能な関数を規定している
-- 再入可能な関数の場合でも
   errnoは大域変数なので書き換えられてしまう可能性がある
--- 呼び出し側がerrnoを保存、復元すべき
-- sigsetjump、siglongjumpを使うときは
   シグナルに割り込まれないようにブロックする必要がある

- SIGCLD
-- SIGCHLDとは異なる
-- SVxの古い扱い方
--- シグナル動作としてSIG_IGNを設定した場合
    このプロセスの子はゾンビプロセスにならない
---- SIG_DFLの場合はゾンビプロセスになる
---- 子の終了状態は破棄される
---- waitを呼ぶと全ての子が終了するまでブロックする
     すべての子が終了すると-1を返し
     errnoにECHILDが設定される
--- シグナル動作として捕捉を設定した場合
    設定時にwaitの対象となる子がいるか調べられ
    いた場合SIGCLDのハンドラが呼び出される
---- これによりSIGCLDでwaitより先にSIGCLDのシグナルの捕捉を再設定すると
     無限再帰する

- 信頼性のあるシグナル
-- シグナルの生成
--- 原因となる事象が発生しカーネルがプロセステーブルに各種のフラグを設定する
-- シグナルの配送
--- シグナルに対する動作を行う
-- シグナルの保留
--- シグナルの生成から配送までの間
-- シグナルのブロック
--- シグナルがブロックされているとき
    シグナルがプロセスに対して生成され
    そのシグナルに対する動作がデフォルト動作かまたは捕捉のとき
    シグナルをアンブロックするかシグナル動作を無視に変更するまで
    シグナルの配送は保留される
--- ブロックされているシグナルに対して
    システムは生成時ではなく配送時に何をすべきかを決定する
---- プロセスはシグナルが配送される前にシグナル動作を変更できる
--- sigpendingにより
    ブロックされているシグナルと保留中のシグナルの情報が得られる
--- シグナルをアンブロックする前にシグナルが複数回生成されたとき
    POSIX.1では、シグナルを1回だけ配送しても複数回配送してもよい
---- 複数回配送するシステムではシグナルはキューされる
---- ほとんどのUNIXシステムではキューしない
--- 複数回配送するときPOSIX1.では順序は規定されていない
    ただしプロセスの現状態に関連したシグナルを優先すべき
--- 各プロセスには現在配送をブロックしているシグナルの集合を定義する
    シグナルマスクがある
--- sigprocmaskによってシグナルマスクを操作できる

- kill、raise
-- killはシグナルをプロセスやプロセスグループに送る
-- raiseはプロセスが自分に対してシグナルを送る
-- 他のプロセスにシグナルを送るためには
   送り手に権限が必要
--- スーパーユーザは任意のプロセスにシグナルを送れる
--- それ以外の場合、送り手の実ユーザIDや実効ユーザに等しい
    実ユーザIDや実効ユーザID(システムによっては保存セットユーザID)を持つ受け手に送れる
--- SIGCONTは同一セッションの任意の他のプロセスに送れる
-- nullシグナルをプロセスの検索に使用できる
--- nullシグナルをkillで送ったとき
    プロセスが実在しなければkillは-1を返しerrnoにESRCHを設定する
-- killでシグナルを生成しそれがブロックされていない場合
   killから戻る前に、指定したシグナルか保留中のブロックされていないシグナルが配送される

- alarm、pause
-- alarmは指定した時間後にタイマーが切れSIGALRMが生成されるようにする
--- SIGALRMのデフォルト動作はプロセスの終了
--- 各プロセスに対してアラームは1つしかない
    タイマーが切れる前にalarmを呼ぶと前のタイマーの残数を返し
    新たにタイマーを設定しなおす
    新たなタイマーの設定値が0のときはタイマーを取り消す
-- pauseはシグナルを捕捉するまで呼び出し側のプロセスを休止する
--- シグナルハンドラが起動されこのハンドラから戻ったときにpauseから戻る
    このとき-1を返しerrnoにEINTRを設定する

- sleepの実装例
-- alarmの干渉
-- SIGALRMシグナルハンドラの干渉
-- alarmからpauseまでのレースコンディション
--- setjmpによる回避
---- シグナルハンドラ間の干渉

- 時間制限つきreadの実装例
-- alarmからreadまでのレースコンディション
-- 自動再起動の問題
--- setjmpによる回避
---- シグナルハンドラ間の干渉

- sigset_t
-- シグナルの集合を表す
-- 操作
--- sigemptyset
--- sigfillset
--- sigaddset
--- sigdelset
--- setgismember

- sigprocmask

- sigpending

- sigaction
-- シグナル動作を変更することなく現在のシグナル動作を取得できる
-- シグナルハンドラのかわりにsigaction構造体を指定する
--- シグナルハンドラが呼ばれるときにマスクされるシグナルを設定できる
---- そのシグナルハンドラに配送されるシグナルは自動的にマスクされる
--- シグナル処理のオプションを指定できる
-- シグナル動作は明示的に変更するまで保存される

- sigsetjmp、siglongjmp
-- longjmpでシグナルハンドラから抜けるとき
   sigaction時に自動的に設定されたマスクの扱いがPOSIX.1では規定されていない
-- sigsetjmpでは、マスクを保存、復元するかどうかを指定できる
-- sig_atomic_t
--- 変数への書き込みが割り込まれない

- sigsuspend
-- sigprocmaskからpauseまでのレースコンディションを回避するために
   シグナルマスクの再設定とプロセスのスリープ状態の設定をアトミックに行う
-- sigsuspendから戻るのはシグナルが配送されたときなので
   -1、errnoはEINTRになる
-- sigsuspendから戻るときシグナルマスクは呼び出す前の値に設定される

- abortの実装
-- SIGABRTシグナルを自プロセスに送る
-- killで実装
-- 標準入出力ストリームの扱い

- systemの実装
-- systemを呼び出したプロセスとsystemによって起動されたプロセスは同じプロセスグループ
-- なのでフォアグラウンドの場合、割り込み文字を入力すると
   systemを呼び出したプロセスにも割り込みシグナルが送られる
--- SIGINT、SIGQUITは無視する必要がある
    POSIX.2
-- SIGCHLDは、systemから戻ってきたときに受け取る必要がある
--- SIGCHLDは、systemから戻るまでブロックする
-- systemはシェルを起動するので戻り値はシェルの戻り値になる
--- シェルが異常終了しないとsystemの戻り値は異常終了にならない

- sleepの実装
-- 指定した時間が経過するか、シグナルを捕捉してシグナルハンドラから戻ったときに戻る
-- SIGALRMを使うと他の処理と相互作用がある
-- signalとpauseを使うとレースコンディションがある

- ジョブ制御関連シグナル
-- SIGCHLD、SIGCONT、SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU
-- 休止シグナルが送られると保留中のSIGCONTは破棄される
   SIGCONTが送られると保留中の休止シグナルは破棄される
-- SIGTSTPに対する動作はinitによりSIG_IGNに設定する
-- ジョブ制御を行うシェルはこれをSIG_DFLに変更する
-- ジョブ制御を行うシェルから起動されたプログラムはこれを継承する
-- SIGTSTPが送られたときの処理を変更するには
--- まずSIGTSTPをシグナルハンドラで受ける
--- シグナルハンドラ内でSIGTSTPに対する動作をSIG_DFLに変更する
--- シグナルハンドラが呼び出されたときに自動で設定されたSIGTSTPのブロックを解除し
    自分にkill(SIGTSTP)する
--- デフォルト動作により休止する
--- SIGCONTにより復帰しkillから戻る
--- SIGTSTPのシグナルハンドラを設定しなおす

- syg_siglist配列、psignal
-- シグナル名称

- siginfo構造体
-- SVR4
-- SA_SIGINFO

- sigcontext構造体
-- 4.3+BSD

= 11章 端末入出力

やっかいな端末入出力についての話

- 端末入出力のモード
-- カノニカルモード
--- 端末からの入力は行として処理される
--- 端末ドライバは読み取り要求ごとに1行を返す
--- デフォルト
-- 非カノニカルモード
--- 端末からの入力を行にまとめることはしない

- BSD型のモード
-- cooked
--- 行にまとめ、特別な文字の処理を行う
-- raw
--- 行にまとめ、特別な文字の処理を行わない
-- cbreak
--- 行にまとめ、特別な文字の処理を行う

- 端末装置の制御
-- 端末装置はカーネル内の端末ドライバが制御する
-- 端末装置には入力キューと出力キューがある

            プロセスが書き込む                    プロセスが読み取る
            次の文字                              次の文字
            |                                     ^
            |                                     |
            V   エコー(許可している場合)          |
   出力キュー <------------------------- 入力キュー
   |                                     ^
   |                                     |
   V                                     |
   装置に送る                            装置より読み取る
   次の文字                              次の文字

-- エコーを許している場合入力キューと出力キューの間に暗黙の関係がある
-- 入力キューの大きさはMAX_INPUT
--- いっぱいになったときの処置は実装依存
-- カノニカル入力行の最大バイト数はMAX_CANON
-- 出力キューの大きさは有限、ただしプログラムからは参照できない
--- 出力キューがいっぱいになるとカーネルが出力プロセスを休止させる
-- tcflushで入力キューや出力キューをフラッシュできる
-- tcsetattrで端末装置の属性変更を指示できる
--- 適用タイミングを指示でき、これによってキューのフラッシュや破棄が行われる

- 端末ラインディシプリン
-- ユーザプロセス<->カーネル[入出力関数<->端末ラインディシプリン<->端末装置ドライバ]<->物理装置

- termios構造体
-- 端末装置に対して取得、変更できるすべての属性を持つ
--- 入力フラグ
---- 端末装置ドライバの入力(8ビット目を落とす、パリティ検査を行うなど)の制御
--- 出力フラグ
---- 端末装置ドライバの出力(改行をCRLFに変更するなど)の制御
--- 制御フラグ
---- シリアルライン(モデムの状態信号を無視する、文字ごとのストップビット数など)
--- ローカルフラグ
---- ドライバとユーザのインタフェース(エコーのオンオフ、削除した文字の表示方法、
     端末生成のシグナルの有無、バックグラウンドからの出力を止めるジョブ制御シグナルなど)
--- 制御文字

- 端末入出力関数
-- tcgetattr、tcsetattr
-- cfgetispeed、cfgetospeed、cfsetispeed、cfsetospeed
-- tcdrain、tcflow、tcflush、tcsendbreak
-- tcgetpgrp、tcsetpgrp
-- ioctlは使わない

- 特別な入力文字
-- POSIX1.は11個、加えて、SVR4は6個、4.3+BSDは7個
-- 2個(CR、NL)は固定
-- それ以外は任意の値に変更できる
--- DISCARD、DSUSP、EOF、EOL、EOL2、ERASE、INTR、KILL、LNEXT、
    QUIT、REPRINT、START、STATUS、STOP、SUSP、WERASE
--- POSIX.1では無効にすることもできる
-- BREAK

- tcgetattr、tcsetattr
-- TCSANNOW、TCSADRAIN、TCSAFLUSH

- stty

- cfgetispeed、cfgetospeed、cfsetispeed、cfsetospeed
-- termios構造体を変更するだけなのでtcgetattr、tcsetattrが必要

- tcdrain、tcflow、tcflush、tcsendbreak

- ctermid、
-- 制御端末の名称(/dev/tty)

- isatty、ttyname
-- ファイル記述子に関連づけられた端末装置

- カノニカルモードの入力

- 非カノニカルモードの入力
-- termios構造体のc_cc[VMIN]、c_cc[VTIME]により
   返すタイミングを制御する

- 端末ウィンドウサイズ
-- winsize構造体
-- ioctl + TIOCGWINSZ、TIOCSWINSZ
-- SIGWINCH

- termcap、terminfo、curses
-- termcapは端末の機能についての情報をコンパイルしたプログラムから分離し
   編集可能としたもの
-- terminfoは、termcapを高速化したもの
-- cursesは、termcap、terminfoを扱うライブラリ

= 12章 高度な入出力

- ブロックしない入出力
-- 低速なシステムコール
--- データが整っていないファイルからの読み取りは無期限にブロックする
--- データを直ちに受理できないようなファイルへの書き込みは無期限にブロックする
--- ある条件が整うまでファイルのオープンはブロックする
--- 必須レコードが有効となっているファイルの読み書き
--- ある種のioctl操作。
--- ある種のプロセス間通信。
--- ディスク入出力に関したシステムコールは低速とは分類しない
-- ブロックしない入出力では
   入出力操作を起動しても無期限にブロックされることはなく
   操作を完了できなければエラーが返る
--- open時にO_NONBLOCKを指定
--- fcntlでO_NONBLOCKを指定

- レコードロック
-- ファイルの領域をロックする
--- fcntl、lockf、flock
-- flock構造体
--- F_RDLOCK
---- 読み取り共有ロック
--- F_WRLOCK
---- 書き込み排他ロック
--- F_UNLCK
---- ロックの解除
--- l_lenに0を指定すると追加されるデータを含めファイルの末尾までロック
-- fcntl
--- F_GETLK
---- ロックが設定できるかどうかを調べる
--- F_SETLK
---- ロックを設定する、ブロックしない
--- F_SETLKW
---- F_SETLK、ブロックする
-- デッドロック発生時エラーが返る
-- プロセスが終了するとロックは解放される
-- 記述子をクローズするとプロセスのその記述子が指すファイルのロックは解除される
--- flock構造体はプロセスIDを持ち、iノードにリンクリストでつながれているイメージ
-- ロックはfork後継承されない
-- ロックはexec後継承される

- 忠告ロック
-- ロックの情報を設定するだけ
-- ロックの情報を無視すればファイルに書き込みできる

- 必須ロック
-- グループ実行ビットをオフ、セットグループIDビットをオンにする
-- open、read、writeのたびにカーネルがロックを確認する
-- openは、O_TRUNCかO_CREATを指定するとO_NONBLOCKの指定にかかわらずEAGAINを返す
-- read、writeはブロックする記述子ではロックが競合するとブロックする
   ブロックしない記述子ではロックが競合するとEAGAINを返す
-- unlinkには効果がない

- ストリーム
-- カーネルの通信ドライバに対する汎用インタフェース
-- プロセスと装置ドライバの間に全二重通信路を提供
-- プロセス側のインタフェースがストリームヘッド
-- ストリームヘッドの下に処理モジュールをプッシュできる
--- 処理モジュールをつくるのは装置ドライバと同じくらいのレベル
-- プロセスとストリームヘッドは3種類のメッセージで通信する
--- M_DATA
---- 入出力するユーザデータ
--- M_PROTO
---- プロトコル制御情報
--- M_PCPROTO
---- 優先順位の高いプロトコル制御構造
-- putmsg、putpmsg
--- ストリームにメッセージを書く
-- ioctl
-- write
-- SNDZERO、SNDPIPE
-- getmsg、getpmsg
-- read

- 入出力の多重化
-- 2つの記述子から読みたい場合
   片方でブロックするともう片方が読めない
-- forkを使う
--- 終了時に親子の同期が必要
-- ブロックしない入出力を用いてポーリングする
--- CPUを浪費する
-- 非同期入出力
--- 移植性が悪い
--- シグナルではどの記述子が準備ができたのかわからない
-- 入出力の多重化
--- 記述子のリストのうちの1つで準備ができるまで戻らない関数を使う
--- select
---- ファイルの終わりに達したときは読み込み準備ができたとみなされる
     readすると0が返る
--- poll

- 非同期入出力
-- SVR4
--- SIGPOLLシグナルハンドラを設定
--- ストリーム装置に対してiocnlでI_SETSIG
-- 4.3+BSD
--- SIGIO、SIGURGシグナルハンドラを設定
--- fcntlでF_SETOWN
--- fcntlでO_ASYNCをF_SETFL(SIGURGの場合は不要)

- スキャターリード、ギャザーライト
-- 1つの関数呼び出しで複数の不連続なバッファに入出力
-- readv、writev

- readn、writen
-- 要求より少ない入出力の扱い

- メモリマップ入出力
-- mmap
-- マップした領域はfork後継承される
   exec後継承されない
-- munmap

= 13章 デーモンプロセス

- デーモンの性質
-- スーパユーザ特権で動作する
-- 制御端末を持たない
-- 通常セッションリーダ

- デーモンの作成
-- forkし親はexitする
--- シェルからデーモンを起動したときに
    フォアグラウンドで起動しても起動コマンドは終了する
--- プロセスグループリーダでないことを保証できる
    setsidを呼ぶため
-- setsidを呼ぶ
   新たなセッションリーダになる
   新たなプロセスグループリーダになる
   制御端末を持たない
-- ルートディレクトリをカレント作業ディレクトリにする
   またはデーモン作業用の特定の場所をカレント作業ディレクトリにする
--- 親から継承したカレント作業ディレクトリはマウントしたファイルシステム上にある可能性があるため
-- ファイルモード作成マスクを0にする
-- 不必要なファイル記述子をクローズする

- エラーの記録
-- 制御端末を持たないので標準エラー出力へは書けない
-- 個別のデーモンが別々のファイルに書いてほしくない
-- SVR4 ストリームlogドライバ

   /var/adm/streams/error.mm-dd
      ^
      |
      |          標準出力      ファイル、コンソール、メール
      |                ^             ^
      |                |             |
   strerr        strace        syslogd

   エラー記録    事象追跡記録  コンソールログ  ユーザプロセス ユーザプロセス
       ^               ^             ^               |              |
       |               |             |               |              |
 getmsg|         getmsg|       getmsg|         putmsg|        write |
       |               |             |               v              v
   /dev/log      /dev/log      /dev/log        /dev/log       /dev/conslog
       ^               ^             ^               |              |
       |               |             |               |              |
       ----------------| -------------               |---------------
                       |                             |
                       |                             |
                       --------- logストリーム <------
                                 装置ドライバ  <------ ストリームモジュール

--- logメッセージの生成
---- カーネル内部はstrlogを使用してログメッセージを生成する
---- ユーザプロセスは/dev/logに対してputmsgを使用
----- エラー記録、事象追跡記録、コンソールログ全てに送られる
---- ユーザプロセスは/dev/conslogに対してwriteを使用
     コンソールログに送られる
---- syslogでコンソールログの受け手にメッセージを送れる
--- logメッセージの読み出し
---- 通常のエラー記録の受け手はstrerr
     バックグラウンドのデーモンとして動きログメッセージを
     /var/adm/streams/error.mm-dd ファイルに追記する
---- 通常の追跡記録の受け手はstrace
     指定されあた追跡メッセージのみを標準出力に書く
---- 標準のコンソールログの受け手はsyslogd
---- 標準の受け手をユーザプロセスに置き換えることができる
---- 受け手が存在しないとlogドライバはログメッセージを破棄する

-- 4.3+BSD syslog機能

        ユーザプロセス    ファイル、コンソール、メール
               |                 ^
               |                 |
               |                 |
               |    ---------> syslogd <--------
         syslog|    |            ^             |
               |    |            |             |
               v    |            |             |
             /dev/log      UPDポート514   /dev/klog
                                 ^             ^
                                 |             |log
                                 |             |
                                 |        カーネル内部ルーチン
                                 |
                           TCP/IPネットワーク

--- ログメッセージの生成
---- カーネル内部ルーチンはlogを呼ぶ
     /dev/klogをreadする任意のユーザプロセスによって読み取られる
---- ユーザプロセスはsyslogを呼ぶ
     UNIXドメインのデータグラムソケット/dev/logに送られる
     openlog、closelogは呼んでも呼ばなくてもよい
---- ホスト上のユーザプロセス、ホストにTCP/IPでネットワーク接続された
     ホスト上のユーザプロセスはUPDポート514にログメッセージを送れる
     syslogはUDPデータグラムは生成しない
     明示的にネットワークを使うようにプログラムする必要がある
--- ログメッセージの読み取り
---- syslogdは/etc/syslogd.confに基いて
     メッセージをどこに送るか決定する

= 14章 プロセス間通信

- IPCの方法
-- UNIXのすべての実装においてポータブルなものは存在しない
-- パイプ(半二重)
-- FIFO(名前付きパイプ)
-- ストリームパイプ(全二重)
-- 名前付きストリームパイプ
-- メッセージキュー
-- セマフォ
-- 共有メモリ
-- ソケット
-- ストリーム

- パイプ
-- すべてのUNIXシステムで使用できる
-- 半二重であるためデータは一方向にしか流れない
-- 共通の祖先を持つプロセス間でのみ使用できる
   パイプを作ったプロセスがforkし親と子の間でパイプを使用する
-- pipe
--- 読み取り用と書き込み用の2つの記述子を返す
--- 2つの記述子は1つのパイプの両端に繋っている
--- forkすると子は親の記述子を継承しパイプは親子で共有される
--- 親が読み取り側をクローズし子が書き込み側をクローズすると
    親から子へのパイプができる
-- 書き込み側をクローズしたパイプからreadする場合
   すべてのデータを読み取った後のreadは0(ファイルの終わり)を返す
-- 読み取り側をクローズしたパイプへwriteする場合
   SIGPIPEが生成される
   シグナルの捕捉にかかわらずwriteはエラーを返しerrnoにはEPIPEが設定される
-- popen、pclose
--- 子をforkしパイプの未使用の端をクローズしexecしwaitする操作を行う
--- コマンドはシェルで実行される
--- 標準入出力ファイルポインタで読み書きする

- コプロセス
-- 標準入力から読み標準出力へ書くプログラムをフィルターと呼ぶ
-- 同じプログラム自身が生成した入力を読むフィルターはコプロセスになる
-- 半二重パイプを2個使って実装する
--- popenではできないので自分でforkする
-- 標準入出力ライブラリを使用すると
   パイプが完全バッファリングされ永久にブロックしてしまう
-- これを回避するにはsetvbufでバッファリングモードを変更する
-- execするプログラムを修正できない場合は
   パイプを端末と錯覚させる手法を使う(疑似端末)

- FIFO
-- 名前付きパイプ
-- 関連しないプロセス間でもデータを交換できる
-- mkfifo
-- mkfifoしたあとは通常のファイル入出力関数が使用できる

-- FIFOを利用した出力ストリームの複製
--- FILOはファイルとして使用できる
--- mkfifo(1)でFILOをつくってプログラムの標準入力をそれにリダイレクトし
    teeの出力ファイルをFIFOにする

-- FIFOを利用したクライアントサーバ
--- クライアント->サーバは、
    サーバが用意した既知のFIFOにクライアントが書き込む
--- サーバ->クライアントは、
    クライアントのプロセスIDに基く一意な名前のFIFOをサーバが用意する
---- クライアントのクラッシュ時に残ったFIFOはSIGPIPEで検出する

- SVR4 IPC
-- メッセージキュー、セマフォ、共有メモリの3種類のIPCがある
-- IPC構造体(メッセージキュー、セマフォ、共有メモリ)
--- 識別子
---- IPC構造体を参照するためのもの
--- キー
---- IPC構造体を作成するためのもの
---- カーネルがキーを識別子に変換する
---- IPC_PRIVATEキー
---- 事前に定義されたキー
---- ftokでパス名とプロジェクトIDから一意のキーを生成する
--- 作成
---- IPC_PRIVATEキーを使う
---- 既知のキーを使い、IPC_CREATフラグを指定する
--- 既存の参照
---- 既知のキーを使う
---- IPC_PRIVATEキーでつくられたものはキーでは参照できない
     識別子をそのまま使う
-- ipc_perm構造体
--- IPC構造体のパーミッションと所有権を定義する
--- 所有者実効ユーザID、所有者実効グループID、作成者実効ユーザID、作成者実効グループID
--- アクセスモード
---- 読み書きのみ
     実行許可に対応するものはない
--- スロット番号、キー

-- 特徴
--- システム全体で共有される
--- 参照カウントを持たない
    明示的に削除されるまで残る
--- ファイルシステムの名前を持たない
--- ファイル記述子を使用しない

- メッセージキュー
-- msgget
--- 既存のキューを開いたり新しいキューを作成したりする
-- msgctl
--- msgid_ds構造体の取り出し
--- msg_perm.uid、msg_perm.gid、msg_perm.mode、msg_qbytesの変更
---- プロセスの実効ユーザIDがmsg_perm.uidまたはmsg_perm.gidに等しいか
     スーパユーザ特権を持つ場合のみ変更可能
---- スーパユーザのみがmsg_qbytesを増加可能
--- メッセージキューの削除
-- msgsnd
--- メッセージを置く
---- メッセージは、メッセージ型を示すlong+メッセージ長のバイト列
--- IPC_NOWAIT時
    メッセージキューがいっぱいのときEAGAINで直ちに戻る
--- 非IPC_NOWAIT時
    メッセージキューに余裕ができるまで戻らない
    または、キューが削除されるとEIDRMで戻る
    または、シグナルが捕捉されシグナルハンドラから戻るとEINTRで戻る
-- msgrcv
--- キューからメッセージを取り出す
--- 単純に先頭からメッセージを取り出すだけでなく
    メッセージの型に基づいてメッセージを取り出すことができる
--- IPC_NOWAIT
-- 新しいアプリケーションでは
   メッセージキューは使用すべきではない

- セマフォ
-- 共有データオブジェクトへの複数プロセスの参照を許すためのカウンタ
-- 単純なセマフォではなく、複数のセマフォの集合を持つ
-- semget
-- semctl
--- セマフォ構造体の操作
--- 集合内のメンバに対する操作
-- semop
--- 集合内のメンバに対する集合操作
--- SEM_UNDO
---- プロセスがexitしたときに確保した資源を解放する

- 共有メモリ
-- データをコピーしないため高速
-- 同期が必要
-- shmget
-- shmctl
-- shmat
--- 共有メモリ領域への接続
-- shmdt
--- 共有メモリ領域からの切断
-- mmapで関連するプロセス間で似たようなことができる
--- /dev/zeroをマップするか、MAP_ANONを使う

- クライアントサーバ
-- forkとexecによるサーバ
--- サーバは子になる
--- 戻せる記述子に制限がある
-- FIFOによるサーバ
-- メッセージキューによるサーバ
--- クライアントを識別できないことに本質的な問題がある
--- クライアントに何か(FIFOなど)を作成してもらい
    その所有者をチェックする

= 15章 高度なプロセス間通信

- ストリームパイプ
-- 全二重
-- SVR4ではpipeで作成できる
-- 4.3+BSDではsocketpairを使う

- プロセス間でのファイル記述子の受け渡し
-- 記述子はプロセステーブルにあるので実体はプロセスごとに別
-- 記述子の指すファイルテーブル項目は共有される
-- SVR4ではioctlを使って受け渡す
--- I_SENDFDでファイル記述子を渡す
--- I_RECVFDでstrrecvfd構造体を受け取り新しい記述子を取得する
-- 4.3+BSDではsendmsgとrecvmsgで受け渡しする

- オープンサーバの実装例
-- 別プログラムによるサーバの利点
--- ライブラリ関数と同じようなユーザビリティ
--- サーバの修正がクライアントに及ばない
--- サーバの権限をクライアントから切り離せる

- クライアントサーバ接続関数の実装
-- SVR4
--- connldストリーム処理モジュールを使用する
--- connldモジュールをストリームパイプの片方の端にプッシュする
--- この端にfattachでファイル名を与える
--- この端をopenすると、新しいストリームパイプが作成される
---- openの戻り値で一方の端が返る
---- もとのストリームパイプのもう一方の端に
     新しいストリームパイプのもう片方の端がioctl I_SENDFDされる
     これはI_RECVFDで取り出せる
     I_RECVFDをO_NONBLOCKなしで行うとメッセージが来るまでブロックするので
     accept動作になる
-- 4.3+BSD
--- サーバのlisten
---- socketでUNIXドメインソケットを作成する
---- sockaddr_un構造体をつくる
---- sockaddr_un構造体をUNIXドメインソケットにbindする
---- listenする
--- クライアントの接続
---- socketでUNIXドメインソケットを作成する
---- sockaddr_un構造体をつくる
---- sockaddr_un構造体をUNIXドメインソケットにbindする
---- サーバに接続するためのsockaddr_un構造体を作成する
---- connectする
--- サーバのaccept
---- acceptする
---- 得られたsockaddr_unからクライアントのつくったパス名を取得し
     statすることでクライアントの実効ユーザIDを得る
     この判定は完全ではない

-- デーモンによるサーバ
--- デーモンの開始処理を行う
--- listen処理を行う
--- 接続用の記述子と、現在の接続の記述子をselectして待つ
--- 接続用の記述子がアクティブになったら
    accept処理をして記述子を現在の接続に追加する
--- 現在の接続のいずれかがアクティブになったら
    readする
    0なら接続が閉じられたので現在の接続から記述子を削除する
--- selectのかわりにpollも使用できる

= 16章 データベースライブラリ

- インタフェース
- ファイル構造
- 集中化、非集中化
- 並列性
-- 粗いロック
-- 細かいロック

= 17章 PostScriptプリンタとの通信

プログラム例

-
    ユーザプロセス
        ^
        |
        v
    入出力関数
      |   ^
      |   |
      v   |
    端末ラインディシプリン
      |   ^
      |   |
      v   |
    端末装置ドライバ
      |   ^
      |   |
      v   |
    PostScriptインタープリタ
        |
        |
        v
    プリントエンジン

- /etc/printcap
-- lprがprintcapにしたがいフィルタを呼ぶ
-- フィルタは特定の引数とともに起動される
-- 印刷すべきファイルが標準入力に
   プリンタ装置が標準出力に接続される

- フィルタの例
-- ログ出力
-- コマンドライン解釈
-- シグナルハンドラ
-- デバッグ用端末オープン
-- プリンタ状態読み取り
--- selectによるシリアルポート待ち合わせ
--- 読み取った状態の出力
-- プリンタ出力
--- selectによるシリアルポート待ち合わせ


= 18章 モデムの発信

- cu(1)、tip(1)

- 開発方針
-- ソースコードを変更せずに新しいモデムに対応できるようにする
--- Dialersファイルを使用する
--- デーモンサーバが処理する
-- 装置のロックが異常終了時に自動的に解除されるようにする
--- デーモンサーバがロックを管理する
    このデーモンサーバを経由する限りロック制御は適切に行われる
-- モデム発信の機能すべてを提供し
   モデムを扱う新たなプログラムでモデムについての処理を
   再発明する必要がないようにする
--- デーモンサーバがモデム発信処理を行う
-- クライアントプログラムが特別な権限を必要としないようにする
--- デーモンサーバに必要な権限を持たせる

- データファイル
-- Systems
--- 名前->種類、電話番号
-- Devices
--- 種類->回線、ダイアラー
-- Dialers
--- ダイアラー->ハンドシェイク

- サーバ
-- 開始するとselectループを開始し、
   新しいクライアントからの接続要求か、
   接続済のクライアントの要求か、
   子の終了を待つ
-- selectループで新しいクライアントからの接続要求があると
   接続を追加する
-- selectループで接続済クライアントからの要求があると
   要求を解析しデータファイルを調べて
   発信方法の1つを取得する
   装置がロックされていたら別の発信方法を取得する
-- forkする
   発信処理に時間がかかるため
   これによりサーバは新しい要求を受け付けられる状態になる
-- forkの親は装置をロックする
   forkの親が装置をロックし終えたら子が処理を開始する
-- 子は発信処理を行う
-- 発信処理に成功したら取得した記述子をクライアントに返しexit(0)する
   発信処理に失敗したらexit(1)する
-- 子がexitするとSIGCHLDを親がハンドルする
   親はwaitpidしクライアントの状態を更新し戻る
   子の終了処理はselectループで行う
-- selectループで子が終了したときは
   SIGCHLDハンドラで終了状態に変更された子を調べ
   子の戻り値を取得する
   子が0で終了していれば何もしない
   子が1で終了していれば次の発信方法を試すために
   再度次の発信方法を取得してforkする
-- selectループでクライアントからEOFを読んだときは
   クライアントの終了処理を行う
   装置のロックを解放する

- クライアント
-- クライアントは、サーバに接続し、コマンド行を送り、
   記述子かエラーが返るのを待つ
-- 記述子が返ると
   ローカルシステムの端末と記述子との間で
   データのコピーを行う
-- ローカルシステムの端末からの入力のうち
   特別なものをフックして
   特別な処理を行う
--- take
--- put

= 19章 疑似端末

- 疑似端末
-- アプリケーションプログラムには端末のように見えるが
   実際の端末ではないもの
-- プロセスが疑似端末のマスター側をオープンしてからforkする
-- 子は新たなセッションを開始し対応する疑似端末のスレーブ側をオープンし
   標準入力、標準出力、標準エラー出力に複製してexecする
--- 疑似端末のスレーブ側は子の制御端末になる
-- スレーブ側のユーザプロセスには標準入力、標準出力、標準エラー出力は
   端末装置に見える
-- マスター側に書き込んだものはスレーブ側の入力になる
   スレーブ側に書き込んだものはマスター側の入力になる
--- ストリームパイプに似ているが
    スレーブ側の上に端末ラインディシプリンがある

                     fork、exec
      ユーザプロセス-------------->ユーザプロセス
          ^                            ^
          |                            | 標準入力、標準出力、標準エラー出力
          v                            v
      read、write                  read、write
        |   ^                        |   ^
        |   |                        |   |
        |   |                        v   |
        |   |                      端末ラインディシプリン
        |   |                        |   ^
        |   |                        |   |
        v   |                        v   |
      疑似端末マスター             疑似端末スレーブ
        |   ^                        |   ^
        |   |                        |   |
        |   --------------------------   |
        ----------------------------------

- 疑似端末の使い道
-- ネットワークログインサーバ
--- telnetd、rlogind
--- マスター側はptyマスターとは別の入出力も同時に行う

                         fork、exec
           rlogindサーバ  ------->  ログインシェル
           ^           ^                ^
           |           |                | 標準入力、標準出力、
           |           |                | 標準エラー出力
           v           |                v
      TCP/IP           |             端末ライン
      プロトコル       |             ディシプリン
           ^           |                ^
           |           |                |
           v           v                v
      ネットワーク    ptyマスター   ptyスレーブ
      装置ドライバ     ^                ^
           ^           |                |
           |           ------------------
           v
      ネットワーク

-- scriptプログラム
--- 端末ラインディシプリンからの出力をファイルにコピーする

                         スクリプトファイル
                               ^
              fork、exec       |       fork、exec
      ログイン ------->  scriptプロセス -------> シェル
      シェル              ^         ^              ^
                          |         |              |
                          |         |              |
                          |         |              v
                          |         |            端末ライン
                          |         |            ディシプリン
                          |         |              ^
                          |         |              |
                          v         v              v
                     端末装置     ptyマスター    ptyスレーブ
                     ドライバ       ^              ^
                          ^         |              |
                          |         ----------------
                          |
                          v
                    端末のユーザ

-- expectプログラム
--- 端末を必要とする対話的プログラムを
    スクリプトから駆動する

-- コプロセスの実行
--- 標準入出力ライブラリを使用するコプロセスでは
    完全バッファリングによるデッドロックの問題がある
--- 完全バッファリングではなく行バッファリングさせるために
    コプロセスの標準入力と標準出力を端末に見せかける

-- 少しづつ出力するプログラムの監視
--- 標準入出力ライブラリを用いて少しづつ出力するプログラムでは
    出力をファイルにリダイレクトすると
    完全バッファリングによりある程度たまらないとフラッシュされない
--- 疑似端末によって標準出力を端末に見せかける

- 疑似端末装置のオープン
-- SVR4
--- /dev/ptmx
---- ptyのマスター側の装置
     ストリームクローン装置であり、
     openすると未使用のマスター側装置を割り当てる
--- grantpt
---- スレーブ側装置のパーミッションを変更する
----- スレーブ側の所有者を実効ユーザIDに変更する
----- 所有者グループをttyに変更する
----- パーミッションをu+rw、g+wのみにする
      グループ書き込みを許すのはwall(1)とwrite(1)のため
--- unlockpt
---- スレーブ側装置の内部的なロックを外す
--- ptsname
---- スレーブ側装置の名前を取得する
     /dev/pts/NNN
--- スレーブ側装置のオープン
---- open
---- 制御端末を持たないセッションリーダである場合は
     これによりスレーブ側装置が制御端末になる
--- ストリームモジュールのプッシュ
---- ptem 疑似端末エミュレーションモジュール
---- ldterm ラインディシプリンモジュール
---- ttcompat 互換性用
-- 4.3+BSD
--- ptyのマスター側装置の検索
---- /dev/ptyXY
--- ptyのスレーブ側装置
---- マスター側に対応する/dev/ttyXY
--- chown、chmod
---- スーパユーザ権限がないときは
     セットユーザIDした別プログラムが必要
--- openしても制御端末は自動的に設定されない
    ioctl TIOCSCTTYする

- ptyプログラムの実装例
-- pty_forkしてexecする
-- 標準入力、標準出力とptyマスター間のコピーは
   select、pollを利用するかまたはforkして
   別プロセスで実施する

- ptyプログラムの利用
-- utmpファイル
-- ジョブ制御
--- ptyの子は新たなセッショングループをつくるので
    オーファンドプロセスグループになる
--- 休止を受け付けない
-- 長時間実行するプログラムの監視
--- バックグラウンドで実行すると
    子が標準入力から読むときに
    たとえファイルの終わりを読むためであっても
    SIGTTINによりptyプロセスは休止する
--- 標準入力をリダイレクトすると
    ptyが標準入力からファイルの終わりを読み
    ptyプロセスは終了する
--- -i オプションを使用する
    標準入力の終わりを無視する
-- script
--- pty "${SHELL:-/bin/sh}" | tee typescript
--- ptyプロセスがptyマスターから標準出力へ書き出すと
    teeがそれをファイルに保存する
-- コプロセスの実行
--- 対話的に実行しないので
    エコーを止める必要がある
--- -e オプション
-- 対話的プログラムの非対話的駆動
--- 対話的プログラムからの出力の待ち合わせ
--- ptyプロセスから駆動プログラムをforkしてストリープパイプで
    ptyプロセスの標準入力、標準出力とつなぎ
    ptyを介して対話的プログラムとのやりとりを制御できるようにする
-- 高度な機能
--- ptyスレーブ側を制御するための機能
---- パケットモード、リモートモード、TIOCSWINSZ、TIOCSIGNAL

広告
%d人のブロガーが「いいね」をつけました。