APUE Memo
Chapter. 7 プロセスの環境
プロセス制御の基本の前に単一プロセスの環境について
- プログラムが実行されたときに
main
関数はどのように実行されるのか - コマンド行引数はどのように新たなプログラムに渡されるのか
- 典型的なメモリ配置
- 追加のメモリの割り当て方
- プロセスはどのように環境変数を使えるのか
- プロセスを終了する方法
- 関数
longjmp
やsetjmp
7.2 関数 main
- カーネルがCプログラムを実行すると、
main
よりも前に始動ルーティンを呼び出すexec
関数群 = システムコールのひとつ
- 実行可能プログラムでは、そのルーティンを開始アドレスに設定する
- このセットはCコンパイラが起動した、リンケージエディタ(リンカ?)が設定する
- 始動ルーティンは、カーネルから値(argv と envs)を受け取り、
main
を呼ぶ準備をする
7.3 プロセスの終了
正常終了
main
から戻るexit
を呼ぶ_exit
か_Exit
を呼ぶ- スレッドの開始ルーティンの最後のスレッドから戻る (11.5 節)
- 最後のスレッドから
pthread_exit
を呼ぶ (11.5)
異常終了
abort
を呼ぶ 10.17- シグナルを受け取る 10.2
- キャンセル要請に対する最後のスレッドの応答 11.5, 12.7
とりあえず 11,12章まではスレッドがらみの終了については無視
exit
関数群
exit
と_Exit
は ISO C_exit
は POSIX.1exit
はカーネルへ戻る前に、後始末をする- オープンしているストリームに対して
fclose
を呼ぶ
- オープンしているストリームに対して
- それぞれ脱出状態を表す
int
の引数を取る main
から整数値を返すこととexit
を同じ値を渡して呼ぶのは同値gcc -std=c89
でコンパイルすると、13
が返っていた
atexit
脱出ハンドラ
exit
は登録されているハンドラを呼び出し、その後fclose
する- ハンドラの登録
atexit
- 登録した順番の逆順に呼ばれ、登録した回数分呼ばれる
7.4 コマンドライン引数
exec
を呼ぶプロセスは引数を渡すことができる- ISO C と POSIX.1 で
argv[argc]
はヌルポインタであることが保証される
7.5 環境リスト
- 環境リスト == 文字へのポインタの配列
- 各ポインタは null terminated string のアドレス
- 文字列は
name=value
という形式
- 文字列は
- 大域変数
environ
にその配列へのアドレスが入っている main
の第3引数へも渡せる- 特定の環境変数に対しては
getenv
とsetenv
- 環境全体を扱うには
environ
が便利
env
コマンドの簡単な実装
#include <stdio.h>
extern char** environ;
int main(int argc, char *argv[]) {
char **envp = environ;
while ( *envp )
printf("%s\n", *envp++);
}
7.6 C プログラムのメモリ配置
- テキストセグメント
- 機械語命令
- (初期化)データセグメント
- 関数定義の外の宣言
int a = 12;
など - プログラムがその値に初期化する
- 関数定義の外の宣言
- 非初期化データセグメント bss
- 関数定義外の
long arr[123];
など - プログラムの実行前にカーネルが数値 0 や null ポインタなどに初期化する
- 関数定義外の
- スタック
- 関数が呼ばれるたびに、戻りアドレス、マシンレジスタなどの呼び出し側の環境についての情報を保存する
- スタックフレームが伸びていることで、再帰呼び出しを実現する
- ヒープ
- 動的にメモリを割り付ける
- 以下のようなレイアウト
- Intel x86 32bit Linux だと、
0x08048000
からテキストセグメント、スタックの底は0xC0000000
size
コマンドでテキスト・データ・bssの各セグメントのサイズを表示できる
- Intel x86 32bit Linux だと、
スタック --- 高位アドレス
|
v
^
|
ヒープ
BSS --- exec が 0 に初期化
データ
テキスト --- 低位アドレス
7.7 共有ライブラリ
- 実行バイナリサイズの低減と呼び出しのオーバーヘッド
gcc -static
でコンパイルしてみる
7.8 メモリ割付
malloc
- 指定したバイト数分だけ割付
- 初期値は不定
calloc
- 指定したサイズのオブジェクト用に指定した個数分の領域を割り当て
- 領域は0フィルされる
realloc
- すでに割り付けた領域のサイズを増減させる
- サイズを増やす場合、追加分の場所を核をするために既存の領域の移動をともなう場合がある
- サイズを増やす場合、増えた部分の初期値は不定
3つの関数が返すポインタは任意のオブジェクト対して使用できる適切なアラインメントである. 例. あるシステムで double は 8の倍数のメモリ位置から始まるのであれば、そのようなアライメントになるということ
-
strace は便利
strace -e brk,mmap <your program>
-
free
- ptr が指す領域を解放する
- 解放した領域は、利用可能なメモリプールに置かれ、次のXalloc関数で呼ばれたときに使われる
7.9 環境変数
Todo
7.10 setjmp と longjmp 関数
7.11 getrlimit と setrlimit 関数* 非ローカルのgoto
- setjmp で戻る場所を決める
- 直接呼ばれたら、0を返し、longjmp に呼ばれたら、0以外を返す
- env はスタックを戻すための jmp_buf 型の大域変数
- longjmp には env と戻り元を表す int 型の値を渡す
自動変数はどうなるの?
以下の2つには影響あり。
- auto 自動変数
- registerレジスタ変数
setjmp の頃に戻るか、longjmp の直前に戻るかは処理系に依存する
TODO: jmp_buf には何が入っている?
7.11 getrlimit と setrlimit 関数
各プロセスについて、資源の利用制限
- プロセスは、自身のハードリミットまでならば、自身のソフトリミットを変更できる
- プロセスは、自身のソフトリミットまでなら、自身のハードリミットを減らすことができる
- スーパユーザプロセスのみが、ハードリミットを増やせる
どういう資源?
RLIMIT_AS プロセスの全使用可能メモリの最大サイズ(バイト単位)
RLIMIT_DATA データセグメントの最大サイズ 初期データ・非初期化データ・ヒープの総和
RLIMIT_RSS resident set size プロセスが実際に使用している物理メモリ量
RLIMIT_STACK スタックの最大サイズ
演習
7.1 printf の戻り値が返った。 7.2 exit から呼ばれる fclose での flush 後 対話的なら行バッファリングなので、改行後。ファイルにリダイレクトされているなら、完全バッファリングなので、標準入出力の後始末のとき。 7.3 ない 7.4 null ポインタとして使えるように終端を与える。 7.5 スキップ 7.6 calloc なら0フィルなので、どちらも初期化される →0に初期化するが、ISO Cだとこの0が浮動小数のゼロかnull ptrかは保証しない。 7.7 ヒープやスタックが必要になるのは実行時だから 7.8 アライメントが必要だから →デバッグ用のシンボルテーブルがあるから。strip コマンドでなくせる。 7.9 共有ライブラリのに必要な全てのコードがリンクされるから →大部分が標準入出力ライブラリに閉められるから 7.10 val が 0 以外なら不正。