ioctl

読み:アイオウ・コントロール
外語:ioctl: I/O control
品詞:名詞

UNIXシステムコールの一つで、デバイスを制御する際に用いるもの。

#include <sys/ioctl.h>

int ioctl(int d, int request, ...);

ioctl()関数呼び出しはVersion 7 AT&T UNIXから登場し、以降引き継がれた。

但し、比較的新しいOS(例えばBSD系、Linux)では、二番目の引数は単なるintではなくunsigned longである。

引数

ioctl()関数は、open()されたスペシャルファイルを構成するデバイスの制御を行なうものである。特に、キャラクタデバイスでよく用いられている。

一番目の引数dは、open()で得られたファイルディスクリプタとする。

二番目の引数requestは、デバイスごとに固有のリクエストコードで、これによって機能を選択する。

三番目以降の引数は、必要に応じて与えられる。型はvoid *であるが、古いUNIXではchar *であった。

リクエストコード

構造

二番目の引数requestは、次の情報を符号化した数値である。

  1. 各ioctlを区別するためのシーケンス番号
  2. パラメータが入力か、出力か、両方であるか
  3. パラメータの大きさ(sizeof)

基本的にはrequestは32ビットの整数であるが、内部でどのように符号化されるかはOSに依存する。

マクロ

環境に依存せずにrequestの値を作るため、_IO、_IOW、_IOR、_IOWR、という四つのマクロが用意されている。

  • _IO(group,num) ‐ パラメータなし
  • _IOR(group,num,len) ‐ copyoutパラメータあり (ユーザから見て入力、カーネルから見て出力)
  • _IOW(group,num,len) ‐ copyinパラメータあり (ユーザから見て出力、カーネルから見て入力)
  • _IORW(group,num,len) ‐ copyout/copyin双方のパラメータあり

一番目の引数groupは、ドライバ等を識別するための識別子である。本来はドライバと一対一であるべきだったが、今やドライバは大量にあるため、番号は他のドライバと共用となっている。

二番目の引数numは、各ioctlを区別するためのシーケンス番号である。

group、numは、識別という意味ではさほど重要ではない。実際、group、numは競合が頻発しているが、競合しても実際には動作に問題はない。なぜなら、デバイス自体はopen()の時点で一意に特定されているため、そのデバイス内で機能ごとにrequestが一意でさえあれば、何の問題も無いからである。

基本的には、誤って他のデバイスに対してioctlをしていないか、をドライバ側でチェックする目的で用いられている。

フラグ

一般的な実装では、requestの値の上位3ビットがin/out/voidのフラグ、続く13ビットが長さ(sizeof)で、続く8ビットがgroup、最下位8ビットはnumとなる。

OSにより差はあるがフラグは一般的に次のような定義になっている。

#define IOC_VOID (unsigned long)0x20000000
#define IOC_OUT  (unsigned long)0x40000000
#define IOC_IN   (unsigned long)0x80000000
#define IOC_INOUT(IOC_IN|IOC_OUT)

_IORはIOC_OUT、_IOWはIOC_IN、_IORWはIOC_INOUTとなり、引数の無い_IOはIOC_VOIDである。INもOUTも無ければVOIDとすればフラグは2ビットで事足りるはずだが、VOIDに1ビット使うように実装されたため、今もこのような仕様となっている。

引数の大きさ

長さが13ビットなので、理論上、引数と出来る大きさの上限は213−1バイト、つまり8,191バイトである。

これ以上はポインタ渡しとし、カーネル側のプログラムは、随時ユーザ空間とin/outする処理を書くことになるだろう(BSDならcopyin()/copyout()、Linuxならcopy_from_user()/copy_to_user()、など)。