[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
本章では、Lispライブラリをあらかじめロードした実用的なEmacsの実行形式の ダンプ方法、メモリ領域の割り当て方、 Cプログラマに興味があるようなGNU Emacsの内部について述べます。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
本節では、Emacsの実行形式を構築する手順を説明します。 メイクファイルが自動的にこれらすべてを行うので、 Emacsを構築してインストールするために本節のことがらを 読者が知っている必要はありません。 本節の内容は、Emacsの保守者向けです。
ディレクトリ`src'のCソースファイル群をコンパイルすると、 `temacs'と呼ばれる実行形式ファイルが作られます。 これは裸のインピュアEmacs(bare impure Emacs)とも呼びます。 これには、Emacs Lispインタープリタと入出力ルーティンが含まれますが、 編集コマンドは入っていません。
コマンド`temacs -l loadup'で、 実用的なEmacsの実行形式を作るために`temacs'を使います。 これらの引数は、`temacs'に対して ファイル`loadup.el'で指定したLispファイル群を評価するように指示します。 これらのファイルはEmacsの通常の編集環境を作り上げ、 その結果、Emacsは裸ではありませんがまだインピュアです。
標準のLispファイル群をロードするにはかなり時間が必要です。 しかし、読者がEmacsを実行するたびにこれを行う必要はありません。 `temacs'は、必要なファイルをあらかじめロードした`emacs'という 実行形式プログラムとしてダンプできます。 `emacs'はファイル群をロードする必要がないので素早く起動します。 これが通常インストールされるEmacsの実行形式です。
`emacs'を作るにはコマンド`temacs -batch -l loadup dump'を使います。 ここでの`-batch'の目的は、 `temacs'が端末に関するデータを初期化しないようにするためです。 これにより、ダンプしたEmacsでは端末情報の表が空であることを保証できます。 引数`dump'は、`emacs'という名前の新たな実行形式を ダンプするように`loadup.el'に指示します。
ダンプできないオペレーティングシステムもあります。 そのようなシステムでは、Emacsを使うたびに コマンド`temacs -l loadup'でEmacsを起動する必要があります。 これにはかなり時間がかかりますが、多くても1日に1回、あるいは、 ログアウトしなのであれば週に1回Emacsを起動する必要があるだけでしょうから、 余分な時間は重大問題にはならないでしょう。
あらかじめロードしておく追加のファイルは、 それらをロードする`site-load.el'という名前のライブラリを 書くことで指定できます。 追加データのための領域を確保するために `src/puresize.h'のPURESIZE
の値を増やす必要があるかもしれません。 (十分な大きさになるまで20000ずつ増やして試すこと。) しかし、マシンが速くなればなるほど、あらかじめロードしておくファイルを 追加することの利点は減少します。 最近のマシンでは、このようにする必要はないでしょう。
`loadup.el'が`site-load.el'を読み終えると、 Snarf-documentation
(see 節 23.2 説明文字列の参照)を呼び出して、 基本関数やあらかじめロードした関数(および変数)の説明文字列を それらの説明文字列を格納したファイル`etc/DOC'から探します。
ダンプする直前に実行すべきList式を指定するには、 `site-init.el'という名前のライブラリにそれらのLisp式を入れておきます。 このファイルは、説明文字列を探し終えてから実行されます。
関数定義や変数定義をあらかじめロードしたいときには、 それを行ってあとでEmacsを実行したときにそれらの説明文字列を 参照できるようにする方法が3つあります。
byte-compile-dynamic-docstrings
の値にnil
以外を指定し、 `site-load.el'か`site-init.el'でそれらのファイルをロードする。 (これには、それらの説明文字列がつねにEmacsの領域を占めてしまう 欠点がある。)無変更の普通のEmacsにユーザーが期待する機能を変更するようなものを `site-load.el'や`site-init.el'に入れることは勧められません。 読者のサイトでは普通の機能に優先させるべきであると思うときには、 `default.el'でそれを行います。 そうすれば、ユーザーは好みに応じて読者が行った変更を無効にできます。 See 節 39.1.1 概要:始動時の動作手順。
すでにダンプしたEmacsでこの関数を使うときには、 `-batch'を指定してEmacsを実行すること。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
Emacs Lispでは、ユーザーが作成したLispオブジェクト向けに 2種類のメモリ、普通メモリ(normal storage)と ピュアメモリ(pure storage)を使います。 普通メモリは、Emacsセッション中に新たに作成されるすべてのデータを置く場所です。 普通メモリに関する情報は以下の節を参照してください。 ピュアメモリは、あらかじめロードした標準Lispファイル群の特定のデータ、 つまり、Emacsの実行中にけっして変化しないデータを収めるために使います。
ピュアメモリは、`temacs'があらかじめロードする標準Lispライブラリを ロードしている最中にのみ割り当てられます。 ファイル`emacs'では読み出し専用 (これができるオペレーティングシステムでは)と印が付けられ、 当該マシンで同時に実行されているすべてのEmacsのジョブで メモリ領域を共有できるようにします。 ピュアメモリは拡張できません。 Emacsをコンパイルしたときに固定サイズが割り当てられ、 あらかじめロードするライブラリに対して十分な大きさがないと `temacs'はクラッシュします。 その場合には、ファイル`src/puresize.h'のコンパイルパラメータ PURESIZE
を増やす必要があります。 あらかじめロードするライブラリを追加したり標準機能に機能を追加しなければ、 そのようなことは普通は起こらないはずです。
この関数は、Emacsを構築してダンプするとき以外ではなにもしない。 普通はファイル`emacs/lisp/loaddefs.el'でのみ呼び出されるが、 あらかじめロードするとこれを呼び出すようなパッケージも少数だがある。
defun
が関数定義をピュアメモリに コピーすべきかどうかを決定する。 nil
以外であると、関数定義をピュアメモリにコピーする。
Emacsを構築中の初期段階ですべての基本的な関数をロード中には (これらの関数を共有してガベッジコレクションの対象にしないように)、 このフラグはt
である。 実行形式としてEmacsをダンプするときには、 ダンプ前後の実際の値には関係なくこの変数にはnil
を書く。
実行中のEmacsでこのフラグを変更するべきではない。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
プログラムがリストを作成したり、(ライブラリをロードするなどして) ユーザーが新たに関数を定義すると、 それらのデータは普通メモリへ置かれます。 普通メモリが足りなくなると、Emacsはオペレーティングシステムに 1kバイトの倍数のブロックでメモリ割り当てを要求します。 各ブロックは1つの種類のLispオブジェクトに使いますから、 シンボル、コンスセル、マーカなどはメモリの異なるブロックに分離されます。 (ベクトル、長い文字列、バッファ、特定の編集向けデータ型などの 比較的大きなものは各オブジェクトごとに独立のブロックを割り当てるが、 短い文字列は8kバイトのブロックに詰め込む。)
あるメモリ部分をしばらく使ってから、 (たとえば)バッファを削除したり オブジェクトに対する最後の参照を削除するなどして 当該メモリを解放することはよくあることです。 Emacsには、このような放置されたメモリを回収する ガベッジコレクタ(garbage collector)があります。 (この名前は伝統的だが、 『ガベッジリサイクル』のほうがこの機能を直観的に表すかもしれない。)
ガベッジコレクタは、Lispプログラムから現時点で参照可能な すべてのLispオブジェクトを探して印を付けることで動作します。 まず、すべてのシンボル、それらの値、それらに関連付けられた関数定義、 および、スタック上の任意のデータは参照可能であると仮定します。 参照可能なオブジェクトから間接的に辿れる任意のオブジェクトも 参照可能です。
印付けが終ったときには、無印であるすべてのオブジェクトは ゴミ(ガベッジ)です。 Lispプログラムやユーザーがなにをしようと、 無印のオブジェクトに辿り着く方法はないのでそれらを参照することは不可能です。 無印のオブジェクトを使っているものはいないので、 それらのメモリ領域は再利用できます。 ガベッジコレクタの2段目の動作(『掃く』(sweep))は、 無印のオブジェクトのメモリ領域を再利用できるようにすることです。
掃き作業では、未使用のコンスセルを自由リスト(free list)に入れて、 将来の割り当てに備えます。 シンボルやマーカについても同様です。 参照可能な文字列は8kバイトのブロックより小さな領域を占めるように詰め込み、 不要になった8kバイトのブロックは解放します。 ベクトル、バッファ、ウィンドウ、他の大きなオブジェクトは、 malloc
やfree
を使って個別に割り当てたり解放します。
Common Lispに関した注意:他のLispと異なり、GNU Emacs Lispでは、 自由リストが空になってもガベッジコレクタを呼び出さない。 そのかわりに、オペレーティングシステムにメモリ割り当てを単純に要求し、
gc-cons-threshold
バイトを使い尽くすまでは処理を継続する。つまり、ガベッジコレクタを明示的に呼び出した直後のLispプログラムの部分では、 (プログラムのその部分で2度目にガベッジコレクタを呼び出すほど 多くのメモリを使わないと仮定すれば) その部分を実行中にはガベッジコレクタが呼ばれないことを保証できるのである。
gc-cons-threshold
バイト以上のLispデータを使うと 自発的なガベッジコレクタの起動を引き起こす。)
garbage-collect
が返すリストにはつぎの情報が含まれる。
((used-conses . free-conses) (used-syms . free-syms) (used-miscs . free-miscs) used-string-chars used-vector-slots (used-floats . free-floats) (used-intervals . free-intervals)) |
例を示す。
(garbage-collect) => ((106886 . 13184) (9769 . 0) (7731 . 4651) 347543 121628 (31 . 94) (1273 . 168)) |
各要素の意味はつぎのとおりである。
nil
以外であると、 Emacsはガベッジコレクションの始まりと終りにメッセージを表示する。 デフォルト値はnil
であり、そのようなメッセージを表示しない。最初の閾値は400,000である。 より大きな値を指定すると、ガベッジコレクションの起動回数が少なくなる。 ガベッジコレクションに費す時間を減少できるが、 全体のメモリ使用量を増加させる。 大量のLispデータを作成するようなプログラムを実行するときに設定する。
10,000までの小さな値を指定すると、 ガベッジコレクションの回数を増やせる。 10,000未満の値が意味を持つのはつぎにガベッジコレクションが起きるまでである。 garbage-collect
は閾値を10,000に戻す。
garbage-collect
が返す値は、データ型ごとのLispデータのメモリ使用量です。 対照的に、関数memory-limit
は、 Emacsが現在使用中のメモリ総量に関する情報を与えます。
読者の操作がメモリ使用量にどのように影響するかを調べるのに使える。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
これらの変数は、Emacsが割り当てたデータ型ごとのメモリ総量に関する 情報を与えます。 これらと(garbage-collect)
が返す値との違いに注意してください。 (garbage-collect)
の値は現存するオブジェクトを数えますが、 これらの変数は、すでに解放したオブジェクトを含めて 割り当てたオブジェクトの個数やサイズを数えます。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
Lisp基本関数は、Cで実装したLisp関数です。 Lispから呼び出すためのCの関数とのインターフェイスの詳細は、 数個のCのマクロで処理しています。 新たにCのコードを書く方法をほんとうに理解する唯一の方法は、 ソースを読むことですが、ここではその一部を説明します。
スペシャルフォームの例は、`eval.c'から引用したor
の定義です。 (普通の関数も同じように見える。)
DEFUN ("or", For, Sor, 0, UNEVALLED, 0, "Eval args until one of them yields non-nil; return that value.\n\ The remaining args are not evalled at all.\n\ If all args return nil, return nil.") (args) Lisp_Object args; { register Lisp_Object val; Lisp_Object args_left; struct gcpro gcpro1; if (NULL (args)) return Qnil; args_left = args; GCPRO1 (args_left); do { val = Feval (Fcar (args_left)); if (!NULL (val)) break; args_left = Fcdr (args_left); } while (!NULL (args_left)); UNGCPRO; return val; } |
マクロDEFUN
の引数の詳しい説明から始めます。 その雛型はつぎのとおりです。
DEFUN (lname, fname, sname, min, max, interactive, doc) |
or
である。
For
を呼び出す。 引数はLisp_Object
型である必要があることに注意してほしい。 ファイル`lisp.h'では、 Lisp_Object
型の値を作成するためのさまざまなマクロや関数を宣言してある。
or
は最小0個の引数を許す。
UNEVALLED
、 評価済みの引数を何個でも受け取ることを表すMANY
(&rest
に等価)でもよい。 UNEVALLED
もMANY
もマクロである。 maxが数であるときには、 それはminより小さくなく、かつ、7より大きくないこと。
interactive
の引数に使う文字列である。 or
の場合には0(空ポインタ)であり、 or
は対話的に呼び出せないことを表す。 値""
は、対話的に呼び出されると この関数は引数を受け取らないことを表す。
マクロDEFUN
の呼び出しのあとには、 Cの関数に必須な引数名の並びを書き、引数に対する普通のCの宣言を続けます。 引数の最大個数が固定されている関数では、 各Lisp引数向けにCの引数宣言を書き、 それらをすべてLisp_Object
型にします。 Lisp関数に引数の個数に上限がないとき、 それを実装するCの関数は実際には2つの引数を受け取ります。 第1引数はLisp引数の個数であり、 第2引数はそれらの値を収めたブロックのアドレスです。 引数の型はint
とLisp_Object *
です。
関数For
自身の内側では、 マクロGCPRO1
とUNGCPRO
を使っていることに注意してください。 GCPRO1
は、ガベッジコレクションから変数を『保護』するために使います。 つまり、ガベッジコレクタに対してこの変数を調べてその内容を 参照可能なオブジェクトとみなすように指示します。 Feval
やFeval
を直接/間接的に呼び出すものを呼ぶときには、 このようにする必要があります。 そのような場面では、再度参照する意図がある任意のLispオブジェクトは 保護する必要があります。 UNGCPRO
は、この関数での変数の保護を取り消します。 これは明示的に行う必要があります。
ほとんどのデータ型では、少なくともそのオブジェクトへの1つのポインタを 保護すれば十分であり、そのオブジェクトに循環がない限り、 そのオブジェクトへのすべてのポインタは正しく保たれます。 文字列にはこれはあてはまりません。 ガベッジコレクタがそれらを移動するからです。 ガベッジコレクタが文字列を移動すると、 それに対する既知のポインタをすべて再配置し、 それ以外のポインタは不正になります。 したがって、ガベッジコレクタが動く可能性のある任意の部分では、 文字列へのすべてのポインタを保護する必要があります。
マクロGCPRO1
は1つのローカル変数のみを保護します。 2つ保護したい場合にはかわりにGCPRO2
を使います。 GCPRO1
を繰り返しても働きません。 GCPRO3
やGCPRO4
のマクロもあります。
これらのマクロはgcpro1
などのローカル変数を暗黙のうちに使いますが、 読者はこれらを型struct gcpro
で明示的に宣言する必要があります。 したがって、GCPRO2
を使う場合には、 gcpro1
とgcpro2
を宣言する必要があります。 残念ですが、ここではすべての詳細は説明しきれません。
Emacsをいったんダンプしたあとでも静的やグローバルな変数に書き込むのであれば、 それらの変数にはCの初期化構文を使ってはいけません。 初期化構文を伴うそれらの変数は、Emacsをダンプすると (オペレーティングシステムによっては)その結果として 読み出し専用のメモリ領域に割り当てられます。 See 節 C.2 ピュアメモリ。
関数の内側では静的変数を使わずに、 すべての静的変数はファイルのトップレベルに置きます。 オペレーティングシステムによっては Emacsはキーワードstatic
を空のマクロと定義することもあるので、 これは必要なことなのです。 (このような定義を使うのは、そのようなシステムは、 初期化構文があろうとなかろうと静的と宣言した変数を ダンプ後には読み出し専用にしてしまうからである。)
Cの関数を定義しただけではLisp基本関数としては使えません。 基本関数に対するLispシンボルを作成し、 その関数セルに適切なsubrオブジェクトを保存する必要があります。 そのコードはつぎのようになります。
defsubr (&subr-structure-name); |
ここで、subr-structure-nameはDEFUN
の第3引数に使った名前です。
すでにLisp基本関数が定義されているファイルに新たな基本関数を追加するときには、 (ファイルの末尾近くで)syms_of_something
という名前の関数を探し、 それにdefsubr
の呼び出しを追加します。 ファイルにこの関数がなかったり、新たなファイルを作成した場合には、 syms_of_filename
(たとえばsyms_of_myfile
)を追加します。 そして、ファイル`emacs.c'でこれらの関数を呼び出している箇所を探して、 そこにsyms_of_filename
の呼び出しを追加します。
関数syms_of_filename
は、 Lisp変数として見える任意のCの変数を定義する場所でもあります。 DEFVAR_LISP
は、Lispから見えるLisp_Object
型のCの変数を作ります。 DEFVAR_INT
は、Lispからはつねに整数を値として見える int
型のCの変数を作ります。 DEFVAR_BOOL
は、Lispからはt
かnil
を値として見える int
型のCの変数を作ります。
ファイルだけに有効なLisp_Object
型のCの変数を定義した場合には、 つぎのようにして、syms_of_filename
の中でstaticpro
を 呼び出してその変数をガベッジコレクションから保護する必要があります。
staticpro (&variable); |
つぎは、少々複雑な引数を取る別の関数の例です。 これは`window.c'から取ったもので、 マクロとLispオブジェクトを操作する関数の使い方を例示します。
DEFUN ("coordinates-in-window-p", Fcoordinates_in_window_p, Scoordinates_in_window_p, 2, 2, "xSpecify coordinate pair: \nXExpression which evals to window: ", "Return non-nil if COORDINATES is in WINDOW.\n\ COORDINATES is a cons of the form (X . Y), X and Y being distances\n\ ... If they are on the border between WINDOW and its right sibling,\n\ `vertical-line' is returned.") (coordinates, window) register Lisp_Object coordinates, window; { int x, y; CHECK_LIVE_WINDOW (window, 0); CHECK_CONS (coordinates, 1); x = XINT (Fcar (coordinates)); y = XINT (Fcdr (coordinates)); switch (coordinates_in_window (XWINDOW (window), &x, &y)) { case 0: /* NOT in window at all. */ return Qnil; case 1: /* In text part of window. */ return Fcons (make_number (x), make_number (y)); case 2: /* In mode line of window. */ return Qmode_line; case 3: /* On right border of window. */ return Qvertical_line; default: abort (); } } |
Cのコードでは、関数がCで定義されていない限り、 関数をその名前で呼び出せないことに注意してください。 Lispで書かれた関数を呼び出す方法は、Lispの関数funcall
を 内蔵するFfuncall
を使うことです。 Lisp関数funcall
は任意個数の引数を受け付けるので、 Cでは2つの引数、Lispレベルの引数の個数と それらの値を収めた一次元の配列を受け取ります。 Lispレベルの最初の引数は呼び出すべきLisp関数であり、 残りはそれに渡す引数です。 Ffuncall
はエバリュエータを呼び出すので、 Ffuncall
を呼び出す周りでは、 ガベッジコレクションからポインタを保護する必要があります。
Cの関数、call0
、call1
、call2
などは、 固定個数の引数を受け取るLisp関数を簡便に呼び出す手軽な方法です。 これらはFfuncall
を呼び出して動作します。
`eval.c'は例を調べるにはとてもよいファイルです。 `lisp.h'には重要なマクロや関数の定義が入っています。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
GNU Emacs Lispは、さまざまな型のデータを扱います。 実際のデータはヒープに保存されていて、 プログラムはポインタを介してそれらを参照します。 ほとんどの実装では、ポインタは32ビット長です。 Emacsをコンパイルしたオペレーティングシステムやマシンの種類に依存しますが、 オブジェクトのアドレスには28ビットを使い、 残りのビットはガベッジコレクションの印や オブジェクトの型を表す識別子であるタグに使います。
Lispオブジェクトはタグ付ポインタとして表現しますから、 任意のオブジェクトのLispデータ型を判定することが可能です。 CのデータLisp_Object
は、任意のデータ型のLispオブジェクトを保持できます。 普通の変数はLisp_Object
型ですから、 Lispの任意の値の任意の型を保持できます。 実際のデータ型は、実行中にのみ判定できます。 関数引数についても同じことがいえます。 特定の型の引数のみを受け付る関数が必要な場合には、 適切な述語(see 節 2.5 型述語) を使って型を明示的に検査する必要があります。
C.6.1 バッファの内部 Components of a buffer structure. C.6.2 ウィンドウの内部 Components of a window structure. C.6.3 プロセスの内部 Components of a process structure.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
バッファには、Lispプログラマが直接には参照できないフィールドがあります。 それらをCのコードで使っている名前で以下に述べます。 多くはLisp基本関数を介してLispプログラムから間接的に参照できます。
name
save_modified
modtime
auto_save_modified
last_window_start
window-start
(表示開始)位置を保持する。
undo_list
syntax_table_v
downcase_table
upcase_table
case_canon_table
case_eqv_table
display_table
nil
である。 see 節 37.14 表示テーブル。
markers
backed_up
mark
markers
にも含まれている。 see 節 30.7 マーク。
mark_active
nil
以外である。
local_var_alist
base_buffer
nil
を保持する。
keymap
overlay_center
overlays_before
overlays_after
enable_multibyte_characters
enable-multibyte-characters
の バッファローカルな値を保持しており、 t
かnil
である。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
ウィンドウには以下のような参照可能なフィールドがあります。
frame
mini_p
nil
以外。
buffer
dedicated
nil
以外である。
pointm
start
force_start
nil
以外であると、 Lispプログラムが明示的にウィンドウをスクロールしたことを表す。 ポイントがスクリーンからはみ出しているとつぎの再表示の動作に影響する。 ポイントの周りのテキストをウィンドウに表示するようにスクロールするかわりに、 スクリーン上に位置するようにポイントを移動する。
last_modified
modified
である。
last_point
left
top
height
width
next
nil
である。
prev
nil
である。
parent
親ウィンドウはバッファを表示せず、 その子ウィンドウの形以外には、表示に関してはなんの役割も持たない。 Emacs Lispプログラムでは親ウィンドウを参照せず、 バッファを実際に表示する木の葉にあるウィンドウを操作する。
hscroll
use_time
get-lru-window
がこのフィールドを使う。
display_table
nil
である。
update_mode_line
nil
以外であると、ウィンドウのモード行を更新する必要があることを表す。
base_line_number
nil
。 これは、モード行にポイント位置の行番号を表示するために使われる。
base_line_pos
nil
である。
region_showing
nil
である。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
プロセスにはつぎのようなフィールドがあります。
name
command
filter
nil
。
sentinel
nil
。
buffer
pid
childp
nil
以外である。 ネットワーク接続であるとnil
以外。
mark
kill_without_query
nil
以外であると、このプロセスが動作中にEmacsを終了しようとしても プロセスをキルすることに関して確認を求めない。
raw_status_low
raw_status_high
wait
で返されるプロセス状態の各16ビットを記録する。
status
process-status
が返すべきプロセス状態。
tick
update_tick
pty_flag
nil
以外であり、 パイプを使用している場合にはnil
である。
infd
outfd
subtty
nil
である。)
tty_name
nil
である。[ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |