検索エンジンから来た人に注意このマニュアルは、Emacs ver. 19.x 向けのマニュアルを Mule 2.x の開発にあたり邦訳したものを、 電脳外道学会がミラーリングしているものであり、旧版製品パラノイアであるところの椅子人の趣味によるものです。しかしながら、現在の Emacs の主流は ver. 20 以降であり、ver 19.x と ver 20.x とでは、仕様の違いが少なからずあります。 したがって、一般的な Emacs ユーザーにとっては、
このマニュアルと実機の動作とが符合しない場合があります。
|
この章では、 稼動中のEmacs実行可能形式を どのようにEmacs Lispライブラリをプリロードしたままダンプするのか、 どのように記憶領域を割りつけるのか、 そして、 Cのプログラマが興味をもつようなGNU Emacsのいくつかの内部構造について 述べます。
この節では、 Emacsの実行可能形式を作るときに起きている諸段階について説明します。 この種のことはメークファイルが自動的に行なってくれるので、 Emacsを作ってインストールするときに知っておく必要はありません。 この情報はEmacsの保守に関係あります。
`src'ディレクトリでCのソース・ファイルをコンパイルすると、 `temacs'という実行可能ファイルができますが、 これをbare impure Emacsといいます。 これにはEmacs LispインタプリタとI/Oルーチンがありますが、 編集コマンドはついていません。
コマンド`temacs -l loadup'では、 本当に実行ができるEmacs実行可能ファイルをつくるのに、 `temacs'を使います。 この引数は`temacs'に、 ファイル`loadup.el'中で指定したLispファイル群を評価させる、 ということを指示しています。 このファイル群で通常のEmacs編集環境を準備すると、 Emacsはbareではなくなりますが、まだimpureではあります。
標準Lispファイル群の読込みには相当時間がかかります。 幸運にも、 Emacsを起動するたびにこれを繰り返すことはありません。 `temacs'は、 このファイル群をプリロードした`emacs'という実行可能プログラムを ダンプすることができます。 `emacs'はもうこのファイルのロードがいらないので、 とても速く立ち上がります。 普通にインストールされるのはこちらのEmacsです。
`emacs'を作るには、 コマンド`temacs -batch -l loadup dump'を使います。 この`-batch'で`temacs'が 端末のデータを初期化しないようにしています。 これでダンプ後のEmacsの端末情報が空のままになります。 引数`dump'で`loadup.el'にしたがって`emacs'という 新しい実行可能ファイルをダンプしています。
オペレーティング・システムによっては、 ダンプのできないことがあります。 こういうシステムでは、 Emacsを使うたびに、 `temacs -l loadup'というコマンドで始める必要があります。 これにはけっこう時間がかかりますが、 Emacsを立ち上げるのは高々日に1回か、 ログ・アウトしないなら週に1回でしょうから、 この時間もそれほど深刻な問題ではありません。
`site-load.el'ライブラリを書いて
プリロードするファイルを追加することができます。
追加ファイル用の場所をあけるために、
`src/puresize.h'中のPURESIZE
の値を
増やす必要があることもあります
(十分な大きさになるまで20000ずつ増やしてみてください)。
しかしながら、
追加ファイルのプリロードの利点はマシンが速くなるにつれて減ってきています。
最近のマシンではだいたい、
そんなに得策ではありません。
ダンプ前に実行するほかのLisp式を`site-init.el'という ライブラリに書いて指定します。 しかしながら、 ユーザが通常の無修正のEmacsに期待する振舞いを変更する場合は、 ユーザが望むときに優先できるように`default.el'に置く方が いいでしょう。See section 立ち上がりの際の一連の動作一覧。
`loadup.el'が新しい実行可能ファイルをダンプする前に、
Snarf-documentation
を呼び出して(see section 説明文字列へのアクセス)、
プリミティブ関数とプリロードした関数(と変数)を定義したファイルから
説明文字列を探し出します。
`emacs'実行可能ファイルを小さくするために、
説明文字列は実行可能ファイルから取り除かれます。See section 説明文字列の基礎。
すでにダンプしてしまったEmacsでこの関数を使う場合、
まずcommand-line-processed
をnil
にしなければ
うまくいきません。See section コマンド行引数。
(emacs-version) => "GNU Emacs 19.29.1 (i386-debian-linux) \ of Tue Jun 6 1995 on balloon"
対話的に呼び出されると、この関数は、エコー領域に同じ情報を印字します。
emacs-build-time => "Tue Jun 6 14:55:57 1995"
"19.29.1"
といった文字列になります。
以下の二つの変数はEmacsのバージョン19.23まではなかったため、 今のところそれほど役には立ちませんが、 私たちは将来これが役に立つであろうことを期待しています。
Emacs Lispには、 ユーザの作るLispオブジェクト用に 通常記憶領域(normal storage)と純粋記憶領域(pure storage) の2種類の記憶領域があります。 通常記憶領域は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の走行中にこのフラグを変更してはいけません。
プログラムがリストを作ったり、 (ライブラリをロードして)ユーザが新しい関数を定義したり場合、 それらは通常記憶領域に置かれます。 通常記憶領域が足りなくなった場合、 Emacsはオペレーティング・システムに それ以上のメモリを1kバイトのブロックで 割りつけるよう要求します。 割りつけられたブロックは、それぞれある1種類の型用に用いられます。 つまり、シンボル、コンス・セル、 マーカなどはメモリ中の異なるブロックにわけられることになります (ベクタ、長い文字列、 バッファおよびそれ以外の一部の非常に大きな編集用の型は、 各オブジェクトごとに割りつけられ、 短い文字列は8kバイトのブロックに詰められます)。
記憶領域をある期間だけ用いて、その後(たとえば)バッファをkillしたり、 オブジェクトに対する最後のポインタを消去したりすることにより、 開放することは、 非常に一般的なことです。 Emacsでは、この放棄された記憶領域を再生する ガベージ・コレクタ(garbage collector)が提供されています (この名前は伝統的なもので、 この機能の直感的なたとえとしては「ゴミ再利用」"garbage recycler"の方が いいかもしれません)。
ガベージ・コレクタは、 Lispプログラムから用いることのできるすべてのLispオブジェクトをスキャンし、 印をつけることで処理を行ないます。 はじめにすべてのシンボル、 その値と対応する関数定義、 現在スタック上に存在するデータは用いられているものであると仮定します。 これらを通して間接的に到達可能なオブジェクトも すべて用いられているという刻印を押します。
印づけが終わったとき、 刻印のないものはすべてガベージです。 Lispプログラムやユーザが何をしようとも、 もはやここへ到達する方法がないため、 ここを使うことは不可能です。 この空間は再利用してもほかと影響しません。 ガベージ・コレクタの第2の「掃き出し」("sweep")段階では、 この場所の再利用を行ないます。
この「掃き出し」段階において、
用いられてないコンス・セル、シンボル、マーカは、
将来の割りつけ用にフリーリスト(free list)につなげられます。
使用中の文字列はなるべく少ない8kブロックに収まるようまず圧縮され、
その後、空いた8kブロックは解放します。
ベクタ、バッファ、ウィンドウ、そのほか大きなオブジェクトは、
個々にmalloc
とfree
を用いて割りつけと解放を行ないます。
Common Lisp注意書き:ほかのLispと異なり、GNU Emacs Lispでは フリーリストが空になった場合にガベージ・コレクタは呼ばれません。 そのかわり、GNU Emacs Lispは、 ただ単にオペレーティング・システムへもっと多くの記憶領域を割りつけするよう要求し、
gc-cons-threshold
バイト使われるまで処理を続けます。これは、陽にガベージ・コレクタを呼び出すことによって、 Lispプログラムのある部分を実行中にガベージ・コレクタが動かないことを (プログラムの一部が次のガベージ・コレクションを引き起こすほど 大きな空間を消費しないことを前提にして)保証できます。
gc-cons-threshold
バイト以上Lispデータが用られた場合、
自動的に行なわれます)。
garbage-collect
は以下の情報を含むリストを返します。
((used-conses . free-conses) (used-syms . free-syms) (used-markers . free-markers) used-string-chars used-vector-slots (used-floats . free-floats)) (garbage-collect) => ((3435 . 2332) (1688 . 0) (57 . 417) 24510 3839 (4 . 1))
次の表は各要素の説明です。
この閾値の初期値は300,000です。 もし大きな値を設定すると、ガベージ・コレクションは起こりにくくなります。 ガベージ・コレクションに使われる時間は減りますが、 メモリの総使用量は増加します。 多量のLispデータを作るようなプログラムを使う場合には、 このようにしたくなるかもしれません。
ガベージ・コレクションの回数を増やすため、
10,000以上の小さな値に設定することもできます。
これを10,000以下の値に設定しても、次のガベージ・コレクションまでしか効果はありません。
そのときにgarbage-collect
はこれを10,000に戻します。
メモリ使用量により影響を受ける動作の決定にこれを使うことができます。
Lispプリミティブとは、Cで実装したLisp関数のことです。 LispでCの関数が呼び出せるようにするインタフェースの詳細を、 Cのマクロで扱っています。 Cのコードの書きかたを本当に理解する唯一の方法は、 ソースを読むことですが、 ここではある程度の情報を示しておきます。
特殊形式の例としてor
の定義を`eval.c'から取りました
(通常の関数も同様に一般的な形をしています)。
DEFUN ("or", For, Sor, 0, UNEVALLED, 0, "Eval args until one of them yields non-nil, then 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
です。
-
)をすべてアンダースコアに変えたものです。
したがって、
Cコードからこの関数を呼ぶ場合、
それはFor
を呼ぶことになります。
引数はLisp_Object
でなくてはいけないことを覚えておいてください。
型Lisp_Object
を作るさまざまなマクロと関数は、
ファイル`lisp.h'で宣言されています。
or
の場合、引数のない場合を許しています。
UNEVALLED
か、
(&rest
と等価な)
任意個の評価される引数をもつことを示すMANY
をもつことになります。
UNEVALLED
とMANY
は両方ともマクロです。
maxが数の場合、minより小さかったり、
7より大きかったりしてはいけません。
interactive
への引数に用いる文字列と
同じ interactive 指示です。
or
の場合、これは0(ヌル・ポインタ)で、
or
を対話的に呼ぶことはできないことを示します。
値""
は、
対話的に呼んだ場合に引数をとらない関数を示します。
DEFUN
マクロ呼びだしの後で、
すべてのCの関数にあるような引数名リストと、
その後に普通のCの引数宣言を書かなくてはなりません。
引数の最大個数が固定の関数は、
各Lispの引数に対してCの全引数をLisp_Object
型として
宣言しなくてはいけません。
Lisp関数に引数の数の上限がないとき、
Cの実装ではちょうど二つの引数を取ります。
1番目がLisp引数の個数で、
2番目が引数の値を格納したブロックのアドレスです。
これらの型はそれぞれint
とLisp_Object *
です。
関数For
中で
マクロGCPRO1
とUNGCPRO
の使い方に
注目してください。
GCPRO1
は変数をガベージ・コレクタから保護するのに使います。
これは、
ガベージ・コレクタがこの変数の中を見て、
この内容がアクセス可能なオブジェクトであると
みなすようにします。
これはFeval
を呼び出す場合や、
Feval
を直接間接に呼び出すもののうちのいずれかを呼び出す場合には
常に必要となります。
このような場合、
(そのような関数の呼出しの後で)再び
参照しようとしているLispオブジェクトも全部保護しなければなりません。
UNGCPRO
は現在の関数で保護している変数の保護を取消します。
これは陽にそうする必要があります。
ほとんどのデータ型については、 オブジェクトへのポインタを少なくとも一つ保護すれば十分です。 オブジェクトが再利用されるまでは、 そこへのポインタはずっと有効なままです。 ガベージ・コレクタが移動させてしまうので、文字列について、 これはあてはまりません。 ガベージ・コレクタが文字列を移動するとき、 わかる範囲のすべてのポインタのさしかえを行ないますが、 それ以外のポインタは無効になります。 したがって、 ガベージ・コレクションが起こりうる場所以降のすべての 文字列へのポインタを保護しなければなりません。
マクロGCPRO1
は局所変数を1個保護します。
2個保護したいときは、
GCPRO2
を使います。
GCPRO1
を繰り返してもうまくいきません。
なおマクロGCPRO3
とGCPRO4
もあります。
このマクロ群は暗にgcpro1
といった局所変数を使います。
これはstruct gcpro
型で陽に宣言しなければなりません。
したがって、
GCPRO2
を使うときはgcpro1
とgcpro2
を宣言します。
残念ながら、ここでトリッキーな詳細について説明することはできません。
いったんダンプしてしまったEmacsでは、 (いったんEmacsをダンプしたからにはもう書き変えないという場合以外) 静的変数や外部変数でCの初期値を使ってはいけません。 ダンプ後のEmacsで、初期値のある変数は、 (あるオペレーティング・システムで) 読出し専用のメモリ領域に割りつけられるためです。
関数の中では、静的な変数を使ってはいけません。
静的変数はファイルのトップ・レベルに置いてください。
これは、あるオペレーティング・システムにおいて
キーワードのstatic
を空マクロとして定義するために
そうする必要があります
(この定義は、初期化の有無にかかわらず、そのようなシステムに
おいて静的に宣言した全変数がダンプ後に読出し専用になってしまうために、
使われています)。
Cの関数を定義するだけではLispプリミティブは有効になりません。 プリミティブのLispシンボルを生成し、 関数セルに適切なsubrオブジェクトを格納する必要があります。 これは以下のようなコードになります。
defsubr (&subr-structure-name);
ここでsubr-structure-nameはDEFUN
の第3引数にした
(訳注: subrオブジェクトの)変数名です。
すでに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
のどちらかを値に取るように見えるCのint
型変数を作ります。
次に、より複雑な引数をもつ別の関数を示します。 これはXウィンドウ・システム用のコードからのもので、 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 POSITIONS is in WINDOW.\n\ \(POSITIONS is a list, (SCREEN-X SCREEN-Y)\)\n\ Returned value is list of positions expressed\n\ relative to window upper left corner.") (coordinate, window) register Lisp_Object coordinate, window; { register Lisp_Object xcoord, ycoord; if (!CONSP (coordinate)) wrong_type_argument (Qlistp, coordinate); CHECK_WINDOW (window, 2); xcoord = Fcar (coordinate); ycoord = Fcar (Fcdr (coordinate)); CHECK_NUMBER (xcoord, 0); CHECK_NUMBER (ycoord, 1); if ((XINT (xcoord) < XINT (XWINDOW (window)->left)) || (XINT (xcoord) >= (XINT (XWINDOW (window)->left) + XINT (XWINDOW (window)->width)))) return Qnil; XFASTINT (xcoord) -= XFASTINT (XWINDOW (window)->left); if (XINT (ycoord) == (screen_height - 1)) return Qnil; if ((XINT (ycoord) < XINT (XWINDOW (window)->top)) || (XINT (ycoord) >= (XINT (XWINDOW (window)->top) + XINT (XWINDOW (window)->height)) - 1)) return Qnil; XFASTINT (ycoord) -= XFASTINT (XWINDOW (window)->top); return (Fcons (xcoord, Fcons (ycoord, Qnil))); }
Cで定義してある関数以外は、
Cのコードから関数名での呼出しができないことに注意してください。
Lispで書いた関数を呼び出すには、
Lisp関数funcall
の本体であるFfuncall
を使います。
Lisp関数のfuncall
は引数を無制限に受けつけるので、
CではLispレベルでの引数の個数と、
その値を格納した1次元配列の二つの引数を受けつけます。
Lispレベルでの最初の引数は呼び出そうとするLispの関数で、
残りはそれへの引数です。
Ffuncall
は評価子を呼び出すことがあるので、
Ffuncall
のまわりではポインタを保護する必要があります。
Cの関数call0
、call1
、call2
などによって、
一定個数の引数でLisp関数を手軽に呼び出すことができます。
これはFfuncall
の呼出しによるものです。
例になるものを探すには、`eval.c'は大変適したファイルです。 `lisp.h'には、 いくつか重要なマクロの定義や関数(訳注: の宣言)があります。
GNU Emacs Lispは、多くの異なる型を扱います。 実際のデータはヒープに格納され、 プログラムは唯一ポインタを通し、 そのデータに対しアクセスします。 ポインタはほとんどの実装において32ビットの幅をもちます。 オペレーティング・システムと、 Emacsをコンパイルするマシンの型によって、 3ビットにオブジェクトの型を示すタグを用い、 残りの28ビットくらいでオブジェクトを指します (原著では 「オブジェクトを指すのに24ビットから26ビット分ぐらいを用い、 残りの6ビットから8ビットをオブジェクトの型を示すタグとして用います。」 としている)。
Lispオブジェクトに対するアクセスは、
すべてタグのつけられたポインタで表わすため、
どんなオブジェクトであってもオブジェクトのLispデータ型を決めることは
常に可能です。
Cのデータ型Lisp_Object
は、
どんな型のLispオブジェクトでも保持することができます。
普通の変数は、Lisp_Object
型なので、
どんなLisp値でもとることができます。
実際のデータ型は実行時に決定できます。
関数引数も同様です。
ある型の引数のみを受け取る関数が必要な場合、
適切な述語を用い、明示的に型の検査を行なう必要があります
(see section 型述語)。
おのおののバッファは、 Lispプログラマが直接アクセスすることのできないフィールドがあります。 ここではCのコードで使う名前でこれらを解説します。 LispプログラムからはLispプリミティブでアクセス可能なフィールドが、 数多くあります。
name
save_modified
modtime
auto_save_modified
last_window_start
window-start
位置、すなわち、
最後にバッファがウィンドウに表示された際、
その表示を開始したバッファ中の位置を格納します。
undo_list
syntax_table
syntax_table_v
としています)
このフィールドは、
バッファに対する構文テーブルを格納します。See section 構文テーブル。
downcase_table
upcase_table
case_canon_table
case_eqv_table
display_table
nil
を格納します。See section 表示テーブル。
markers
backed_up
mark
markers
にも含まれます。See section マーク。
mark_active
nil
です。
local_var_alist
base_buffer
nil
を保持します。
keymap
overlay_center
overlays_before
overlays_after
ウィンドウは、アクセス可能な以下のフィールドがあります。
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
hscroll
use_time
get-lru-window
で用いられます。
display_table
nil
。
update_mode_line
nil
はウィンドウのモード行の更新が必要なことを意味します。
base_line_number
nil
。
これはモード行にポイントの行番号を表示するときに使います。
base_line_pos
nil
。
region_showing
nil
。
プロセスのフィールドは、以下のとおり。
name
command
filter
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
。
Go to the first, previous, next, last section, table of contents.