【翻訳】driver_entry #Erlang

[公式ドキュメント[driver_entry]](http://www.erlang.org/doc/man/driver_entry.html)の翻訳です。

---

# driver_entry

## Cライブラリ
driver_entry

## ライブラリの概要
driver-entry構造はErlangドライバによって使われる。

## 説明

---

<font color="red">Warning</font>

---

**この機能は細心の注意を払って使って下さい！**
ドライバのコールバックはVMのネイティブコードの直接的拡張として実行される。実行は安全な環境で行われていない。VMは、プリエンプティブスケジューリングやメモリ保護のようなErlangのコードを実行する際に提供される同じサービスが提供 *されない* 。ドライバのコールバック関数が良くない挙動をすると、VM全体が予期せぬ動作をする。

* クラッシュしたドライバのコールバックはVM全体をクラッシュする
* 誤って実装されたドライバのコールバックは、ドライバのコールバックへの呼び出しの後の任意の時点でVM、またはVMのその他の誤作動のクラッシュを引き起こす可能性があり　、VM内部状態の矛盾を引き起こす可能性がある。
* 戻ってくる前に[長い処理]()を行うドライバコールバックはVMの応答性を低下させ、その他の奇妙な行動を起こすかもしれない。このような奇妙な行動には、極端なメモリ使用量、および悪いロードバランスがスケジューラの間行われる（起こる挙動はこれだけには限らない）。長時間の処理により発生しうるおかしな挙動はOTPのリリースバージョン間で異なる可能性もある。

---

ertsのバージョン5.9（OTPバージョンR15B）でドライバのインターフェイスはコールバック[output]()、[control]()、[call]()がより大きな型に変更された。詳しくは[erl_driver]()の[driver version management]()参照。

---

注意

---

古いドライバ（5.9より前のバージョンのertsのerl_drivers.hでコンパイルされているもの）は更新して拡張インターフェースを使わなければならない。（[version management]()参照）

---

driver_entry構造は全Erlangドライバで定義されたCの構造体である。ErlangのコードがドライバにアクセスするときErlangエミュレータによって呼び出されるErlangドライバのエントリポイントを持つ。
erl_driverドライバAPI関数はドライバインスタンス（とエミュレータ内のポート）を識別するポートハンドルが必要。これは `start` 関数だけが通る（その他の関数は通らない）。 `start` 関数はその他の関数を通るドライバで定義されたハンドルを返す。共通の課題は `start` 関数がアプリケーションで定義されたいくつかの構造をアロケートし、その中にポートハンドルをスタッシュし、ドライバAPI関数と一緒に後で使われることである。
ドライバのコールバック関数はErlangエミュレータから同期的に呼ばれる。処理が完了するまでに時間がかかりすぎる場合は、エミュレータでタイムアウトする可能性がある。エミュレータが反応しなければならないため、必要に応じてキューまたは非同期呼び出しを使わなければならない。
ドライバ構造はドライバの名前と15の関数ポインタを持つ。これらのポインタはエミュレータによって異なるタイミングに呼ばれる。
driverから唯一エクスポートされている関数は `driver_init` である。この関数はドライバ内の他の関数のポインタを持つ `driver_entry` 構造を返す。`driver_init` 関数はマクロ `DRIVER_INIT(drivername)` で宣言される。（異なるのOSがそれに異なる名前を持つため。）
C++でドライバを書くとき、ドライバエントリはCのリンケージであるべき。これを行う1つの方法は、ドライバのエントリの前のどこかにこの行を置くことである。 `extern "C" DRIVER_INIT(drivername);`
ドライバはエミュレータを越えてdriver_entryを通過したときに、ドライバがdriver_entryを変更することはできない。
`--enable-static-drivers` を使用して静的インクルードのためのドライバをコンパイルする場合は、 `DRIVER_INT` 宣言の前に `STATIC_ERLANG_DRIVER` を定義する必要がある。

---

注意

---

`driver_entry` をconstで宣言しては *いけない* 。これはエミュレータが `handle` と `handle2` フィールドを変更しなければいけないからである。静的アロケートとconst宣言された `driver_entry` はエミュレータのクラッシュを起こすROMに配置するかもしれない。

# DATA TYPES
## ErlDrvEntry
```c

typedef struct erl_drv_entry {
    int (*init)(void);          /* called at system start up for statically
                                   linked drivers, and after loading for
                                   dynamically loaded drivers */ 

#ifndef ERL_SYS_DRV
    ErlDrvData (*start)(ErlDrvPort port, char *command);
                                /* called when open_port/2 is invoked.
                                   return value -1 means failure. */
#else
    ErlDrvData (*start)(ErlDrvPort port, char *command, SysDriverOpts* opts);
                                /* special options, only for system driver */
#endif
    void (*stop)(ErlDrvData drv_data);
                                /* called when port is closed, and when the
                                   emulator is halted. */
    void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len);
                                /* called when we have output from erlang to 
                                   the port */
    void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event); 
                                /* called when we have input from one of 
                                   the driver's handles */
    void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event);  
                                /* called when output is possible to one of 
                                   the driver's handles */
    char *driver_name;          /* name supplied as command 
                                   in open_port XXX ? */
    void (*finish)(void);       /* called before unloading the driver -
                                   DYNAMIC DRIVERS ONLY */
    void *handle;               /* Reserved -- Used by emulator internally */
    ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command,
                            char *buf, ErlDrvSizeT len,
			    char **rbuf, ErlDrvSizeT rlen);
                                /* "ioctl" for drivers - invoked by 
                                   port_control/3 */
    void (*timeout)(ErlDrvData drv_data);        /* Handling of timeout in driver */
    void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev);
                                /* called when we have output from erlang
                                   to the port */
    void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data);
    void (*flush)(ErlDrvData drv_data);
                                /* called when the port is about to be 
                                   closed, and there is data in the 
                                   driver queue that needs to be flushed
                                   before 'stop' can be called */
    ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command,
                         char *buf, ErlDrvSizeT len,
			 char **rbuf, ErlDrvSizeT rlen, unsigned int *flags);
                                /* Works mostly like 'control', a synchronous
                                   call into the driver. */
    void (*event)(ErlDrvData drv_data, ErlDrvEvent event,
                  ErlDrvEventData event_data);
                                /* Called when an event selected by 
                                   driver_event() has occurred */
    int extended_marker;        /* ERL_DRV_EXTENDED_MARKER */
    int major_version;          /* ERL_DRV_EXTENDED_MAJOR_VERSION */
    int minor_version;          /* ERL_DRV_EXTENDED_MINOR_VERSION */
    int driver_flags;           /* ERL_DRV_FLAGs */
    void *handle2;              /* Reserved -- Used by emulator internally */
    void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor);
                                /* Called when a process monitor fires */
    void (*stop_select)(ErlDrvEvent event, void* reserved);
                                /* Called to close an event object */
 } ErlDrvEntry;
```
    
### `int (*init)(void)`
`erl_ddll:load_driver/2` によってドライバがロードされた後にすぐに呼ばれる。（実際はドライバがドライバリストに追加された時）ドライバは0、またはドライバは初期化に失敗したら `-1` を返すべき。

### `ErlDrvData (*start)(ErlDrvPort port, char* command)`
`open_port/2` を読んで、ドライバが初期化された時に呼ばれる。ドライバは0以上の数かポインタを返すべき。またはドライバが起動できなかった時は3つのエラーコードを返す。

1. `ERL_DRV_ERROR_GENERAL`
    * 汎用的なエラー、エラーコードはない
2. `ERL_DRV_ERROR_ERRNO`
    * `erl_errno` にエラーコードが入っている時のエラー
3. `ERL_DRV_ERROR_BADARG`
    * エラー、 `badarg`

もしエラーコードが返された場合、ポートは起動していない。

### `void (*stop)(ErlDrvData drv_data)`
`port_close/1` または `Port ! {self(), close}` でportが閉じた時に呼び出される。ポートの呼び出し元のプロセスが閉じた時にポートが閉じることも気を付けなければならない。 `drv_data` が `start` で確保したメモリ領域へのポインタの場合は、 `stop` で開放する。

### `void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)`
Erlangプロセスがポートへデータを送った時に呼ばれる。データは `buf` によってポイントされ、 `len` バイトである。データは `Port ! {self(), {command, Data}}` または `port_command/2` でポートに送られる。ポートが開かれた方法に応じて、それは整数0〜255またはバイナリのリストのどちらかでなければならない。詳しくは、 `open_port/3` 、 `port_command/2` を参照。

### `void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event)`
### `void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)`
（イベントパラメータで与えられた）ドライバイベントが通知されたときに呼び出される。何かが起こるとき、これは、非同期ドライバを「wake up」するために使われる。unix上でイベントはパイプまたはソケットハンドル（ `select` システムコールが把握する何か）である。
Windows上でイベントはEventまたはSemaphore（または `WaitForMultipleObjects` API関数が把握している何か）である。（エミュレータで何らかの策略を、使用する64のEventの組み込みの制限を超えることができる。）
Enea OSEのイベントは [`erl_drv_ose_get_signal`]() 使用して取得することができる1つ以上の信号。
スレッドと非同期ルーチンでこれを使用するには、UNIXのパイプや、WindowsのイベントやEnea OSEに固有の信号数を作成する。ルーチンが完了すると、パイプに書き込み（Windows上でSetEventを使用、またはEnea OSEのエミュレータプロセスにメッセージを送る）、エミュレータで `ready_input` または `ready_output` を呼ぶ。
あやしいイベントが発生する可能性もある。つまり、本当のイベント通知されていない時でさえ `ready_input` または `ready_output` を呼ぶ。現実にはそれは稀（およびOSに依存）するが、堅牢なドライバは、それにもかかわらず、このようなケースを処理できる必要がある。

### `char *driver_name`
ドライバの名前で、 `open_port` で使われるアトムとドライバライブラリファイルの名前（拡張子を除く）に対応していなければならない。

### `void (*finish)(void)`
この関数は `erl_ddll` ドライバによってドライバがunloadされた時に呼ばれる。（動的ドライバ呼び出しに限る）
ドライバは `unload_driver/1` を呼び出した時、または、エミュレータが停止した時だけunloadされる。

### `void *handle`
このフィールドはエミュレータの内部使用のために確保されている。エミュレータはこのフィールドを修正する。それゆえに、 `driver_entry` がconstで宣言されていないことは重要である。

### `ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)`
Erlangの関数 `port_control/3` で呼び出される特殊なルーチン。Erlangドライバのための"ioctl"に少し似ている働きをする。 `port_control/3` に与えられたデータは `buf` と `len` に代入される。ドライバはデータを `rbuf` と `rlen` を使って送り戻せる。
この関数はドライバを呼び出し、戻り値を受け取る最も速い方法である。Erlangのエミュレータにコンテキストスイッチを作らず、メッセージパッシングを要求しない。Erlangがとても遅い時、より速い実行を得るためにC関数を呼び出すことに適切である。
ドライバはデータを返したい時、 `rbuf` にそれを返すべきである。 `control` が呼び出された時、 `*rbuf` はデータを返すために使われる　`rlen` バイトの既定のバッファを指す。データはポート制御フラグ（[`set_port_control_flags`]()によってセットされた値）により異なって返す。
フラグに `PORT_CONTROL_FLAG_BINARY` がセットされていたら、バイナリが返る。小さいバイナリは既定のバッファの中にローデータを書くことによって返せる。バイナリは、[`driver_alloc_binary`]()で割り当てられたバイナリを指すように `*rbuf` を設定することにより戻すことができる。制御が戻った後に、このバイナリは自動的に解放される。 ドライバは[`driver_free_binary`]()で後で解放することで[`driver_binary_inc_refc`]()と読み取り専用アクセス用のバイナリを得ることができる。これは、制御が戻った後、バイナリを変更することが許可されることはない。 `*rbuf` が `NULL` に設定されている場合は、空のリストが返される。
フラグに0がセットされている場合、データは整数のリストを返す。異なるバッファを使うか `*rbuf` により大きなバッファを指す場合は[`driver_alloc`]()と一緒に領域を確保する。バッファは制御が戻った後、自動的に開放される。
バイナリを使用すると、より多くの数バイトが返された場合も高速である。
戻り値は `*rbuf` で返されたバイトの数である。

### `void (*timeout)(ErlDrvData drv_data)`
この関数はdriverのタイマが0になった後の常に呼び出される。タイマは　`driver_set_timer` で起動される。ドライバ間には優先順位や順序付けはされていないので、いくつかのドライバのタイムアウトが同時に起これば、そのいずれかが最初に呼び出される。

### `void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev)`
ポートが書き込まれるたびに、この関数が呼び出される。それが `NULL` の場合、 `output` 関数が代わりに呼び出される。この関数はデータのコピーを行わず `ErlIOVec` を直接用いるため、 `output` よりも速い。ポートはバイナリモードであるべきである（詳細は `open_port/2` ）。
`ErlIOVec` は `writev` に適した `SysIOVec` と1つ以上のバイナリの両方を保持する。これらのバイナリを保持する必要がある場合は、ドライバが `outputv` から戻ったときに、それらは（代わりに[`driver_enq_bin`]()を使用して）キューに入れることができ、また、それらは静的変数またはグローバル変数に保管されている場合、参照カウンタをインクリメントすることができる。

### `void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data)`
この関数は非同期呼び出しが完了した後に呼び出される。非同期呼び出しは[`driver_async`]()で始まる。この関数はいくつかのスレッド（マルチスレッドが有効になっている場合）で呼び出される非同期関数とは対照的に、Erlangエミュレータスレッドから呼び出される。

### `ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, unsigned int *flags)`
この関数は `erlang:port_call/3` から呼び出される。 `control` コールバックとよく似た働きをするが、入出力に外部タームフォーマットを使用する。
`command` はErlangのcallから得る整数である（ `erlang:porot_call/3` の第2引数）。
`buf` と `len` はcallに引数で提供される（ `erlang:port_call/3` の第3引数）。 `ei` 関数を使ってデコードされる。
`rbuf` は戻り値のバッファを指し、 `rlen` バイトの長さである。戻すデータは外部（バイナリ）フォーマットの有効なErlang項であるべきである。これはErlang項に変換され、 `erlang:port_call/3` によって呼び出し元に返される。もし、 `rlen` バイトより多くの領域が戻すデータのために必要であれば、 `*rbuf` は `driver_alloc` でメモリ領域を確保することができる。このメモリはcallが戻った後、自動的に開放される。
戻り値は `*rbuf` で返されるバイトの長さである。もし、 `ERL_DRV_ERROR_GENERAL` が返される（または実際には、0より小さい何か）と、 `erlang:port_call/3` は `BAD_ARG` を投げる。

### `void (*event)(ErlDrvData drv_data, ErlDrvEvent event, ErlDrvEventData event_data)`
意図的に非公開。

### `int extended_marker`
このフィールドは `ERL_DRV_EXTENDED_MARKER` または 0 にすべき。古いドライバ（拡張ドライバインターフェースを認識していない）はこのフィールドに0をセットすべきである。このフィールドが0の場合、このフィールド以降の全てのフィールドが0、またはポインタフィールドの場合 `NULL` でなければならない。

### `int major_version`
このフィールドはもし、 `extended_marker` フィールドが `ERL_DRV_EXTENDED_MARKER` である場合、 `ERL_DRV_EXTENDED_MAJOR_VERSION` にすべきである。

### `int minor_version`
このフィールドはもし、 `extended_marker` フィールドが `ERL_DRV_EXTENDED_MARKER` である場合、 `ERL_DRV_EXTENDED_MINOR_VERSION` にすべきである。

### `int driver_flags`
このフィールドは、ランタイムシステムにドライバ機能やその他の情報を渡すために使用される。 `extended_marker` フィールドが `ERL_DRV_EXTENDED_MARKER` に等しい場合は、0またはドライバフラグ（ `ERL_DRV_FLAG_*` ）ビット毎の論理和（OR）を含める必要がある。現在、次のドライバフラグが存在する。

#### `ERL_DRV_FLAG_USE_PORT_LOCKING`
ドライバはSMPをサポートしたランタイムシステムで実行された場合、ランタイムシステムは、ドライバレベルのロックの代わりにこのドライバを実行するすべてのポートでポートレベルのロックを使用する。詳細については[erl_driver]()のマニュアルを参照する。

#### `ERL_DRV_FLAG_SOFT_BUSY`
ドライバインスタンスは、ドライバインスタンスがビジーとして自身をマークしているにもかかわらず、[`output`]()および/または[`outputv`]()コールバックで呼び出されて処理できることをマークする（[`set_busy_port()`]()を参照）。ertsバージョン5.7.4以来、このフラグはErlang分散で使用されるドライバのために必要とされる（動作は、常に分散で使用されるドライバが必要とされる）。

#### `ERL_DRV_FLAG_NO_BUSY_MSGQ`
ビジーポートメッセージキュー機能を無効にします。詳細については、[`erl_drv_busy_msgq_limits()`]()関数のドキュメントを参照する。

### `void *handle2`
このフィールドは、エミュレータの内部使用のために用いられる。エミュレータはこのフィールドを変更する。したがって、 `driver_entry` は定数で宣言されていないことが重要である。

### `void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)`
監視対象のプロセスが終了したときにこのコールバックが呼び出される。 `drv_data` は、プロセスが（[`driver_monitor_process`]()を使用して）監視するためのポートに関連付けられたデータであり、 `monitor` は、 `monitor` を作成するときに満たされる `ErlDrvMonitor` 構造に対応している。ドライバインターフェイス関数[`driver_get_monitored_process`]()は `ErlDrvTermData` として終了するプロセスのプロセスIDを取得するために使用することができる。

### `void (*stop_select)(ErlDrvEvent event, void* reserved)`
この関数は、イベントオブジェクトをクローズしても安全であるとき[`driver_select`]()の代わりに呼び出されます。

Unix上の典型的な実装は `close((int)event)` の実行である。

引数 `reserved` は、将来の使用を意図していて、無視されるべきである。

他のコールバック関数のほとんどとは対照的に、 `stop_select` は任意のポートとは無関係で呼び出される。 `ErlDrvData` 引数は関数に渡されない。ドライバのロックやポートのロックが行われることは保証されない。 `driver_select` を呼び出したポートでさえ、 `stop_select` を読んだ時に閉じられるかもしれない。しかし、それはまた `stop_select` が `driver_select` により直接呼び出される場合であってもよい。

`stop_select` から[ドライバAPI]()の任意の関数を呼び出すことはできない。この厳格な制限は、 `stop_select` が呼び出されるかもしれない呼び出しコンテキストのためである。