トップ «前の日記(2011-01-11) 最新 次の日記(2011-01-13)» 編集

ヨタの日々

2001|08|09|10|11|12|
2002|01|02|03|04|05|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|12|
2024|01|02|03|04|

2011-01-12 :-(

_ 午前

0500 起床

0830 出勤

0900 仕様読みよみ

_ 午後

1400 改良

1600 打ち合わせ

1730 退勤

_

1830 飯支度

2000 飯。塩鮭

_ NetBSD ローダブルカーネルモジュール入門

http://www.netbsd.org/docs/ からリンクされてるが外部リソースなのでオレオレ翻訳してみた。

_ [翻訳][NetBSD][ローダブルカーネルモジュール][LKM]Introduction to NetBSD loadable kernel modules

NetBSD ローダブルカーネルモジュール入門

Introduction (はじめに)

Loadable kernel modules (LKMs) are quite popular on most modern operating systems such as GNU/Linux, FreeBSD and of course Microsoft Windows, just to name a few. They offer you the possibility to extend the kernel's functionality at runtime without recompiling or even rebooting the system. For example nearly every Linux device driver is available - or can be made available - as a loadable kernel module, that can be loaded at runtime to get support for a particular device (or even a pseudo-device).

ローダブルカーネルモジュール (LKM) は、たとえば GNU/Linux、FreeBSD、そしてもちろん Microsoft Windows のようにモダンなオペレーティングシステムで採用されているものである。これにより、システムを再コンパイルしたり再起動することなしに、カーネル実行中にカーネルの機能を拡張できる。たとえば、最近の Linux デバイスドライバーはすべてこの方式であり、実行中に個々のデバイス (疑似デバイスも含む) を有効にできる。

With NetBSD, LKMs are not that popular yet. At the time of this writing only a few drivers are available as loadable modules (mostly filesystem and compat drivers, and a few others such as the linuxrtc emulation). This might change in near future.

NetBSD の LKM はまだそこまでできていない。ローダブルモジュールとして書かれたいくつかのドライバ (ファイルシステムと互換性ドライバの大部分、そして他のいくつかの linuxrtc エミュレーション) については可能である。これは近々変更される予定だ。

The loadable kernel module interface was originally designed to be similar in functionality to the loadable kernel modules facility provided by SunOS. The lkm(4) facility is controlled by performing ioctl(2) calls on the /dev/lkm device, but since all operations are handled by the modload(8), modunload(8) and modstat(8) programs, you should never have to interact with /dev/lkm directly. Note, that you need to run a kernel compiled with the LKM option in order to make use of LKMs.

もともとローダブルカーネルモジュールのインターフェースは、SunOS のローダブルモジュールと同等の機能を実現するために設計された。lkm(4) は /dev/lkm デバイスを ioctl(2) で呼ぶことで制御できるのだが、modload(8)、modunload(8)、そして modstat(8) プログラムから呼ぶことにより操作する。けっして直接 /dev/lkm を操作してはいけない。(注意: LKM を利用するにはカーネルを LKM オプション付きでコンパイルする必要がある)

Writing the module (モジュールを書こう)

I'd like to show you how to write a simple character device driver that does nothing but the simple job of calculating the FIBONACCI numbers (I'll therefore name the module fibo.o and let all the function's names begin with fibo_). The driver will provide 8 minor devices /dev/fibo0 to /dev/fibo7. Each minor device offers the following functions:

フィボナッチ数を計算するだけの簡単なキャラクタデバイスドライバの書き方を示す( モジュール名は fibo.o とし、関数名は先頭に fibo_ を付けた )。ドライバは /dev/fibo0 から /dev/fibo7 の 8 個をマイナーデバイスを提供する。各デバイスは以下の関数を持つ:

static int fibo_open(dev_t, int, int, struct proc *);
static int fibo_close(dev_t, int, int, struct proc *);
static int fibo_read(dev_t dev, struct uio *, int);

You can open and close a device provided by this driver and you'll be able to read from it (we'll have a closer look at the parameters later, when we discuss the actual functions). Now we need to tell the kernel that we provide a character device with the 3 functions listed above. Therefore we need to fill in a struct cdevsw (cdevsw means character device switch and the struct cdevsw is defined in sys/conf.h).

ドライバを使うことにより、デバイスの開閉と読み込みができるようになる( ここに書いた関数は後述するような引数をもつ )。前述した、このキャラクタデバイスがもつ 3 つの関数を使えばカーネルと通信できるようになる。そのためには cdevsw 構造体を埋める必要がある( cdevsw はキャラクタデバイススイッチという意味であり、sys/conf.h で定義されている )。

static struct cdevsw fibo_dev = {
  fibo_open,
  fibo_close,
  fibo_read,
  (dev_type_write((*))) enodev,
  (dev_type_ioctl((*))) enodev,
  (dev_type_stop((*))) enodev,
  0,
  (dev_type_poll((*))) enodev,
  (dev_type_mmap((*))) enodev,
  0
};

enodev is a generic function that simply returns the errno(2) ENODEV (Operation not supported by device) which says that we does not support any operations besides open, close and read. So, for example, whenever you try to write to the device, the write(2) will fail with ENODEV.

enodev は errno(2) の ENODEV (Operation not supported by device) を返す関数 である。{generic function はプログラミングのテクニックとしての「ジェネリック」のことか? } fibo.o はデバイスの開閉と読み込みしかサポートしないためだ。たとえば write(2) でデバイスに書きこもうとすると ENODEV が返る。

Furtheron we need to tell the kernel how the module is named and where to find information about operations provided by the module. This is a quite simple task with the lkm interface: we use the preprocessor macro MOD_DEV, which is defined in sys/lkm.h to hand the information over. The MOD_DEV macro was changed in NetBSD-current, therefore we use the following construct to get things working with both NetBSD 1.6 and earlier and NetBSD 1.6H and later (thanks to Anil Gopinath for the hint).

では、モジュールを使ってカーネルと通信するためにモジュールが提供する操作について見てみよう{ is named って???? }。 ここでは sys/lkm.h で定義されている MOD_DEV マクロを使い、lkm インターフェースでの簡単な操作をする。MOD_DEV マクロは NetBSD-current で変更されたので、NetBSD 1.6 以前と NetBSD 1.6H 以降の両方で動作するように、以下のように定義しておく( ヒントをくれた Anil Gopinath ありがとう )。

#if (__NetBSD_Version__ >= 106080000)
MOD_DEV("fibo", "fibo", NULL, -1, &fibo_dev, -1);
#else
MOD_DEV("fibo", LM_DT_CHAR, -1, &fibo_dev);
#endif

This means that our module is named fibo, we'll provide a character device (minor devices are handled by the module itself, so they doesn't matter for now), we want to retrieve a dynamic major device number from the kernel (if you want to use a specific major device number you'll need to specify that instead of the -1, but beware of getting in conflict with other device drivers) and we provide the information about the supported operations in fibo_dev.

ここでキャラクタデバイスのモジュール名を fibo とした( マイナーデバイスはモジュール自身によって扱われるが、それは重要ではない )。カーネルからダイナミックメジャーデバイス番号を取得したいので、fibo_dev でサポートしている操作の情報を提供する( メジャーデバイス番号について詳細を取得したい場合、-1 を指定すればよい。ただし他のデバイスドライバと競合するので注意 )。

In order to ensure proper unloading of the module we need to keep a global reference counter of opened minor devices.

モジュールをアンロードするためにオープン済みのマイナーデバイスのグローバル参照カウンタを保持することにする。

static int fibo_refcnt = 0;

And furtheron we need to keep a bunch of information about each minor device.

次に、マイナーデバイスごとの情報を保持しておく。

struct fibo_softc {
  int       sc_refcnt;
  u_int32_t sc_current;
  u_int32_t sc_previous;
};
#define	MAXFIBODEVS	8
static struct fibo_softc fibo_scs[MAXFIBODEVS];

As mentioned above our driver will provide 8 minor devices. Each minor device stores information about how often it was opened (in our example each minor device can only be opened once because of simplicity), the current number and the previous number for calculating the FIBONACCI numbers. If you don't know how to calculate the FIBONACCI numbers, you should have a look on a book about algorithms, as explaining this is beyond the scope of this article.

前述したとおり、ここで作成するデバイスドライバーは 8 個のマイナーデバイスを扱う。各マイナーデバイスはどのようにオープンされたかという情報( 簡単にするためにここでは 1 度だけオープンされるものとする )と、フィボナッチ数の現在の計算結果と前回の計算結果を保持する。フィボナッチ数の計算についてはこの文書の範囲外なので、アルゴリズムの本を参照するとよい。

Each kernel module needs to have an entry point which is passed to ld(1) by modload when the module is linked. The default module entry point is named xxxinit. If xxxinit cannot be found, an attempt to use modulename_lkmentry will be made, where modulename is the filename of the module being loaded without the .o. In general the entry function will consist entirely of a single DISPATCH line, with DISPATCH being a preprocessor macro defined in sys/lkm.h to handle loading, unloading and stating for us. So our fibo_lkmentry function will look like this:

リンク済みのあらゆるカーネルモジュールは modload の ld(1) から呼ばれるためのエントリーポイントを必要とする。エントリーポイントの名前は既定では xxxinit となっている。xxxinit が見つからなければ modulename_lkmentry を使って作成してみるとよい。モジュールがロードされると、モジュールのファイル名から .o を省いた名前でロードされる。一般的にエントリーポイントは DISPATCH マクロ 1 行だけから成る関数である。DISPATCH は sys/lkm.h で定義されたマクロで、アンロードやステータス取得に使用される。我々の fibo_lkmentry 関数は以下のようになるだろう:

int
fibo_lkmentry(struct lkm_table *lkmtp, int cmd, nt ver)
{
  DISPATCH(lkmtp, cmd, ver, fibo_handle, fibo_handle, fibo_handle);
}

Now we need a handler function for our module to do module specific tasks when loading, unloading or stating the module. The name of this handler function is passed to DISPATCH (see above) to tell the kernel which function it has to call when doing such things. A pointer to the module entry in the LKM table and an integer representing the desired command (LKM_E_LOAD, LKM_E_UNLOAD or LKM_E_STAT) are passed to the handler function. The handler is called after the module is linked and loaded into the kernel with the LKM_E_LOAD command. Then we need to check whether the module was already loaded into the kernel and initialize our data structures. When unloading the module, the handler is called with the LKM_E_UNLOAD command and we need to check if the module is not required any more (e.g. check if all devices are closed for char/block driver modules) before confirming the unload command.

モジュールをロード、アンロード、モジュールの状態を得るときにはどうすればよいか。これらの処理をおこなうときはカーネルと通信するのだが、そのためには前述した DISPATCH を書いた関数を利用する。LKM テーブル内にあるエントリーへのポインタと、そのためのコマンド (LKM_E_LOAD, LKM_E_UNLOAD or LKM_E_STAT) へのハンドラ (整数) が利用でき、これらを利用することでハンドラ関数を利用することができる。このハンドラはリンク済みモジュールを LKM_E_LOAD コマンドでカーネルへロードする。次にモジュールがロード済みかどうか、データ構造が初期化済みかどうかチェックしなくてはならない。とくに後処理が必要ないならば( キャラクタ、ブロックドライバモジュールがすべてクローズされているかどうかをチェックしたり ) LKM_E_UNLOAD コマンドでアンロードする。

static int
fibo_handle(struct lkm_table *lkmtp, int cmd)
{
  switch (cmd) {
  case LKM_E_LOAD:
    /* check if module was already loaded */
    if (lkmexists(lkmtp))
      return (EEXIST);

    /* initialize minor device structures */
    bzero(fibo_scs, sizeof(fibo_scs));
    printf("fibo: FIBONACCI driver loaded successfully\n");
    break;

  case LKM_E_UNLOAD:
    /* check if a minor device is opened */
    if (fibo_refcnt > 0)
      return (EBUSY);
    break;

  case LKM_E_STAT:
    break;

  default:
    return (EIO);
  }

  return (0);
}

The open function is quite simple as most of the hard stuff is already handled by the NetBSD kernel (e.g. the kernel will automatically allocate a vnode(9) for you). The parameters for the open function are the major and minor device numbers (use the major and minor macros), the flag and mode arguments as described in open(2) and a pointer to the struct proc of the process that did the open system call.

オープン処理はとても簡単で、NetBSD カーネルがやってくれている( たとえばカーネルは自動的に vnode(9) を確保してくれる )。オープン処理の引数にはデバイスのメジャー番号とマイナー番号を渡す ( major と minor マクロを利用すればよい )。フラグとモードは open(2) と同じ。プロセスの proc 構造体へのポインタも open システムコールと同じである。

So the first thing to do is to check if the minor device number we got when the device was opened is not out of range, and if the minor device is not already opened. You should always keep in mind that the minor device handling is completely up to you and that this is a never ending source of mistakes! Then we need to initialize the minor device data (the FIBONACCI starting numbers: = 1, 0 + 1 = 1, 1 + 1 = 2, 1 + 2 = 3, ...) and increase the minor device and the global module reference counter.

最初にやることは、デバイスがオープン済みかどうかをチェックすることだ。これはマイナーデバイス番号が out of range ではないことをチェックすればよい。

static int
fibo_open(dev_t dev, int flag, int mode, struct proc *p)
{
  struct fibo_softc *fibosc = (fibo_scs + minor(dev));

  if (minor(dev) >= MAXFIBODEVS)
    return (ENODEV);

  /* check if device already open */
  if (fibosc->sc_refcnt > 0)
    return (EBUSY);

  fibosc->sc_current = 1;
  fibosc->sc_previous = 0;
  /* increase device reference counter */
  fibosc->sc_refcnt++;

  /* increase module reference counter */
  fibo_refcnt++;

  return (0);
}

The close function has the same parameters with the same meanings as the open function described above. It is used to free the internal data structures of a minor device opened before. You do not need to worry whether the device was opened before or to do things like releasing the vnode associated with the device, all you need to do is to cleanup the module specific stuff. In our example this means decreasing the minor device and the global module reference counters and so that our close function is quite simple.

close 関数は open 関数と同じような引数を受け取り、オープンされているマイナーデバイスの構造体( これは内部データである )を解放する。ユーザーはデバイスがオープン済みかどうか、vnode が関連付けられているかどうかを気にする必要はない。モジュールがよきに計らってくれる。ここで示す close 関数の例では、マイナーデバイスとグローバルモジュールリファレンスカウンタを減算するだけの簡単なものになっている。

static int
fibo_close(dev_t dev, int flag, int mode, struct proc *p)
{
  struct fibo_softc *fibosc = (fibo_scs + minor(dev));

  /* decrease device reference counter */
  fibosc->sc_refcnt--;

  /* decrease module reference counter */
  fibo_refcnt--;

  return (0);
}

Last but not least the read function. This function has 3 parameters: the device major and minor numbers like in the open and close functions, a flag field indicating for example whether the read should be done in a non-blocking fashion or such things and a pointer to a struct uio defined by sys/uio.h. A struct uio typically describes data in motion, in case of a read(2) system call data moved from kernel-space to user-space. This may look a bit strange if you have already done device driver progamming on GNU/Linux, but the uio concept used by the NetBSD kernel simplifies a lot of things and provides a generic and consistent interface for kernel-space to user-space and kernel-space to kernel-space data moving. See uiomove(9) for more information.

最後に read 関数に触れておく。この関数は 3 つの引数を受け取る。open 関数や close 関数と同じようにメジャーデバイス番号、マイナーデバイス番号、非ブロック実行などを示すフラグ、そして uio 構造体へのポインタ( sys/uio.h で定義されている ) だ。uio 構造体は、read(2) システムコールのようにカーネル空間からユーザー空間へデータを移動させる場合、たいてい以下のように定義される { ????? } 。あなたが GNU/Linux でのデバイスドライバプログラミングの経験があるならば、少し違うということに気付くかもしれない。NetBSD カーネルで使用されている uio 実装は、多様な場面で利用でき、汎用的であり、カーネル空間からユーザー空間へのデータ移動と、カーネル空間からカーネル空間へのデータ移動のインターフフェースに一貫性を持たせることが簡単に出来る。詳細は uiomove(9) を参照。

Back on stage, we should first have a look at the read function and discuss the details afterwards.

まず read 関数を見て、そのあとに詳細を議論しよう。

static int
fibo_read(dev_t dev, struct uio *uio, int flag)
{
  struct fibo_softc *fibosc = (fibo_scs + minor(dev));

  if (uio->uio_resid < sizeof(u_int32_t))
    return (EINVAL);

  while (uio->uio_resid >= sizeof(u_int32_t)) {
    int error;

    /* copy to user space */
    if ((error = uiomove(&(fibosc->sc_current),
    		    sizeof(fibosc->sc_current), uio))) {
      return (error);
    }

    /* prevent overflow */
    if (fibosc->sc_current > (MAXFIBONUM - 1)) {
      fibosc->sc_current = 1;
      fibosc->sc_previous = 0;
      continue;
    }

    /* calculate */ {
      u_int32_t tmp;

      tmp = fibosc->sc_current;
      fibosc->sc_current += fibosc->sc_previous;
      fibosc->sc_previous = tmp;
    }
  }

  return (0);
}

So the first thing we do, is to check whether the process requests less than sizeof(u_int32_t) bytes (actually 4 bytes). Our read function always reads a bunch of 4-byte blocks and to keep it simple and easy to understand we disallow reading less than 4 bytes at a time (uio->uio_resid holds the number of remaining bytes to move to user-space, automatically decreased by uiomove).

最初にやることは、プロセスリクエストが sizeof(u_int32_t) (たいてい 4 バイト)以下であることをチェックすることだ。read 関数は 4 バイトブロックとして読み込む。4 バイト以上とすることで簡単で理解しやすい実装になる( uio->uio_resid はユーザー空間へ移動させたバイト数を保持していて、uiomove により自動的に減算されていく )。

The function copies the current FIBONACCI number into the user-space buffer, checks for a possible overflow (only the first 42 FIBONACCI numbers fit into u_int32_t) and calculates the next FIBONACCI number. If there is enough space left in the user-space buffer, the function loops and restarts the process of moving, checking and calculating until the buffer is filled up to the possible maximum or uiomove(9) returns an error. Note, that a read(2) system call on this device will never return 0, and so it will never reach an end-of-file (EOF), so the device will generate FIBONACCI numbers forever.

フィボナッチ数計算処理におけるユーザー空間でのバッファーコピーではオーバーフローをチェックでき( ただし u_int32_t 以内に収まる 42 回目の計算まで ) それから次のフィボナッチ数を計算する。ユーザー空間にじゅうぶんな空き領域があるならば、最大数 { u_int32_t のこと ???? } に達したり uiomove(9) がエラーを返さない限りは処理はループし、ユーザー空間への移動処理を再開し、計算結果をチェックしつつフィボナッチ数を計算し続ける。注意: このデバイスでの read(2) システムコールは 0 を返さない。EOF も検出しない。計算したフィボナッチ数を返し続ける。

If you're familar with GNU/Linux device driver programming you might have noticed that we do not return -ERRNO on failure, and in case of the read system call the number of bytes read, but instead we return 0 on success and the positive errno value on failure. Everything else is handled by the NetBSD kernel itself, so we do not need to care about.

GNU/Linux でのデバイスドライバプログラミングに慣れているならば、失敗時には -ERRNO を返し、read システムコールは読み込んだバイト数を返すことを期待するだろうが、ここでは、成功時に 0 を返し、失敗時は errno を使うようにしている。これは NetBSD カーネルの慣習にならったものである。

Loading the module (モジュールをロードしよう)

Now that our device driver module is completed, we need a shell script that will be executed when the module is successfully loaded to create the required device nodes in /dev. This shell script (or program) is always passed three arguments: the module id (in decimal), the module type (in hexadecimal) and the character major device number (this differs for other types of LKMs such as system call modules). So our script is pretty simple:

デバイスドライバーは完成した。/dev にデバイス作成の要求があったときにモジュールがちゃんとロードがされるようにシェルスクリプトを書く。シェルスクリプト( またはプログラム ) は 3 つの引数をとる。モジュール ID (10進数)、モジュールタイプ (16進数)、そしてキャラクタデバイスのメジャー番号( システムコールモジュールを使う他の LKM とは異なる )だ。スクリプトはじつに簡単になる。

if [ $# -ne 3 ]; then
  echo "$0 should only be called from modload(8) with 3 args"
  exit 1
fi

First check whether all three command line arguments are present and exit with error code if not.

最初にコマンドライン引数が 3 つあるかチェックしている。無ければエラーとともに終了する。

for i in 0 1 2 3 4 5 6 7; do
  rm -f /dev/fibo$i
  mknod /dev/fibo$i c $3 $i
  chmod 666 /dev/fibo$i
done
exit 0

And finally (re)create the required special device nodes. Now we are ready to give our module a first test run. Compile the module and load the module with the following command (this needs to be run as superuser):

最後に要求があったスペシャルデバイスノードを作成する。これで第一歩の準備が整った。モジュールをコンパイルし、ロードするには以下のコマンドを実行する( スーパーユーザーで実行すること )。

modload -e fibo_lkmentry -p fibo_post.sh fibo.o

If everything went well, the modstat(8) program should present you output similar to this:

成功した場合、modstat(8) を実行すると以下のような出力になる。

Type    Id  Off Loadaddr Size Info     Rev Module Name
DEV       0  29 dca4f000 0004 dca4f260   1 fibo

Testing the module (モジュールをテストしよう)

In order to test your new kernel module, we need a small test program that does nothing more than reading a 32bit unsigned integer value from /dev/fibo0 and outputs the value to standard output. See the sample program below:

新しいカーネルモジュールをテストするために、/dev/fibo0 から 32 ビット符号なし整数を読み込み、標準出力へ出力するだけの小さいテストプログラムを書く。たとえばこう

#define	DEVICE	"/dev/fibo0"
int
main(int argc, char **argv)
{
  u_int32_t val;
  int fd, ret;

  if ((fd = open(DEVICE, O_RDONLY)) < 0)
    err(1, "unable to open " DEVICE);

  while ((ret = read(fd, &val, sizeof(val))) == sizeof(val))
    printf("%u\n", val);

  if (ret < 0)
    err(2, "read(" DEVICE ")");

  close(fd);
  return 0;
}

When you run this sample test program, it will output FIBONACCI numbers below 2971215074 until you interrupt or kill the program. To unload the kernel module, you need to run the following command (as superuser):

このテストプログラムを実行すると、途中で Ctrl+C させるか、または kill しない限り 2971215074 までのフィボナッチ数を出力し続ける。カーネルモジュールをアンロードするにはスーパーユーザーで以下のコマンドを実行する。

modunload -n fibo

A tar archive which contains the complete sources from the example above with a Makefile can be found here. I hope you liked this small introduction to the NetBSD lkm system. If you have any questions or if you would like to give me some feedback feel free mailing to benny@xfce.org.

ここで示したサンプルプログラムの tar 書庫は こちら からダウンロードできる。これには Makefile なども含まれている。これがあなたにとって NetBSD lkm システムを知る足がかりとなることを願う。質問など何かフィードバックがある方はフリーのメーリングリスト benny@xfce.org に投げてほしい。

本日のツッコミ(全2件) [ツッコミを入れる]
_ 桐原正治 (2011-09-23 19:27)

NetBSD での LKM の文書の一つである「Introduction to NetBSD loadable kernel modules」の日本語訳,ありがとうございます.<br>当方それに刺激されてもう一つの「Writing a LKM Driver for NetBSD」の日本語訳の page を作りました.( http://www.geocities.jp/mki_open/NetBSD/LKM-writing-ja.html )<br>そこにはあなたの page への link も作っています.<br>ご覧になって頂ければ幸いです.<br>(当方 VirtualBox の extension がなんとか NetBSD (as a guest OS) で使えるようにできないものか,と,LKM を勉強し始めたところです.でも無謀な計画なような…….)<br>e-mail: mki_open <at> yahoo.co.jp

_ みわ (2011-09-24 00:22)

連絡ありがとうございます。私のほうも超訳です ^^;<br><br><br><br><br>LKM写経しようとしてて放置してたわ....