LoginSignup
7
9

More than 5 years have passed since last update.

NetBSD-7.0_RC3でLuaカーネルモジュールを試してみる

Posted at

少し前の話になりますが、NetBSD-7.0のLuaカーネルモジュール機能で遊ぶ会という勉強会を開催し、NetBSD-7系で利用できるLuaカーネルモジュール機能を試してみました。

これに前後する形でNetBSD-7.0_RC3が8/15にリリースされていました。手元の環境ではNetBSD-7.0_RC1での手順を調べていたので、改めてNetBSD-7.0_RC3における、Luaカーネルモジュール機能の利用手順をまとめてみました。

NetBSD-7.0_RC3のインストール

まずはNetBSD-7.0_RC3のインストールです。ISOイメージは以下のURLからダウンロードできます。

NetBSDのインストールは以下の記事を参考にしながら行えます。が、NetBSD-7系では一部インストール手順が変更になっているので注意してください。

Luaカーネルモジュールを試す

用意されているLuaカーネルモジュール

インストールしたNetBSD-7.0_RC3に用意されているLuaカーネルモジュールを確認してみます。Lua関連のカーネルモジュールは3つあるようです。

# ls /stand/amd64/7.0/modules/lua*/*.kmod 
/stand/amd64/7.0/modules/lua/lua.kmod
/stand/amd64/7.0/modules/luapmf/luapmf.kmod
/stand/amd64/7.0/modules/luasystm/luasystm.kmod

Luaに関するmanページ

それぞれのカーネルモジュールがどんな機能を提供しているのがmanページで確認してみます。

# find /usr/share/man/man* -type f | grep lua 
...中略...
/usr/share/man/man8/luactl.8
/usr/share/man/man9lua/intro.9lua
/usr/share/man/man9lua/pmf.9lua
/usr/share/man/man9lua/systm.9lua

各manページをざっと見て行きましょう。

intro.9lua

intro.9luaはLuaカーネルモジュールの概要を記載したmanページのようです。ただ、現状ではホントに概要のみのようです...。

# man 9lua intro 
man: Formatting manual page...
INTRO(9lua)                          9lua                          INTRO(9lua)

NAME
     intro -- introduction to the Lua kernel bindings

DESCRIPTION
     This section provides an overview of the Lua kernel bindings, their
     functions, error returns and other common definitions and concepts.

SEE ALSO
     lua(1), luac(1), intro(3lua), lua(4), pmf(9lua), systm(9lua)

HISTORY
     An intro manual for Lua kernel bindings appeared in NetBSD 7.0.

NetBSD 7.0_RC3                 October 26, 2013                 NetBSD 7.0_RC3

pmf.9lua

pmf.9luaはパワーマネジメント関連のカーネルモジュールのようです。

# man 9lua pmf
man: Formatting manual page...
PMF(9lua)                            9lua                            PMF(9lua)

NAME
     pmf -- Lua binding to the power management framework

SYNOPSIS
     local pmf = require 'pmf'

     pmf.system_shutdown(howto)
     pmf.set_platform(key, value)
     value = pmf.get_platform(key)

DESCRIPTION
     The pmf Lua binding provides access to the power management framework.
...

systm.9lua

systm.9luaはLuaカーネルモジュールの基本的な機能について記述しているようです。

# man 9lua systm
man: Formatting manual page...
SYSTM(9lua)                          9lua                          SYSTM(9lua)

NAME
     systm -- access to general kernel functionality from Lua

SYNOPSIS
     local systm = require 'systm'

     systm.print(msg)
     systm.print_nolog(msg)
     systm.uprint(msg)
     systm.aprint_normal(msg)
     systm.aprint_naive(msg)
     systm.aprint_verbose(msg)
     systm.aprint_debug(msg)
     systm.aprint_error(msg)
     count = systm.aprint_get_error_count()
     systm.panic(msg)
...

Hello,World.

Luaカーネルモジュールで提供されている機能をざっと把握したところで、さっそくLuaカーネルモジュールの機能を使ってみましょう。先ほどのsystm.9luaで解説されていたsystm.print()を使うと"Hello,World."が実行できそうです。

まずはLuaスクリプトを作成します。

-- hello.lua
local systm = require 'systm' 
systm.print('Hello,World.')

Luaカーネルモジュール上でLuaスクリプトを動かす

これをLuaカーネルモジュールから実行してみます。一連のコマンドは以下になります。

# modload lua
# luactl create hello
# luactl load hello ./hello.lua
# luactl destroy hello
# modunload luasystem
# modunload lua

実行結果は以下です。無事に"Hello,World."というメッセージがカーネルから出力されていいます(NetBSDではカーネル内でprintf()した内容は緑色で出力されます)。

img101.png

Luaカーネルモジュールの一連の流れ

Luaカーネルモジュールとluactlコマンドを相互に使う形でLuaスクリプトを実行しています。一連の手順がちょっとややこしいので、簡単なシーケンス図を描いてみました。

img105.png

Luaカーネルモジュールへの機能追加

最初から用意されているsystm.print()だけでなく、独自にLuaカーネルモジュールに関数を追加してみたいと思うのが人情(?)です。というわけで、Luaカーネルモジュールに独自の関数を追加してみます。

カーネルソースコードの用意

カーネルモジュールへの機能追加なので、以下のURLからNetBSD-7.0_RC3のソースコードを取得します。

# curl -O http://ftp.jaist.ac.jp/pub/NetBSD/NetBSD-7.0_RC3/source/sets/gnusrc.tgz
# curl -O http://ftp.jaist.ac.jp/pub/NetBSD/NetBSD-7.0_RC3/source/sets/sharesrc.tgz
# curl -O http://ftp.jaist.ac.jp/pub/NetBSD/NetBSD-7.0_RC3/source/sets/src.tgz
# curl -O http://ftp.jaist.ac.jp/pub/NetBSD/NetBSD-7.0_RC3/source/sets/syssrc.tgz

ダウンロードしたソースコードは以下のようにして展開します。展開先は/usr/srcになります。

# for i in `echo *.tgz` ; do tar zxvf $i -C / ; done

build.shによるツールチェイン構築

カーネルソースコードを展開した後、build.shを使用してツールチェインを構築します。

# cd /usr/src
# ./build.sh -m `uname -m` -a `uname -p` -T /usr/cross/NetBSD-`uname -r`/`uname -m` -r -o -U tools

これで開発準備が整いました。以下の手順でLuaカーネルモジュールをビルドできます(nbmakeではなくnbmake-amd64を使うのがポイントです)。

# cd /usr/src/sys/modules/luasystm/
# /usr/cross/NetBSD-7.0_RC3/amd64/bin/nbmake-amd64

ビルドしたLuaカーネルモジュールに差し替えます。

# cp -p \
    /usr/src/sys/modules/luasystm/luasystm.kmod \
    /stand/amd64/7.0/modules/luasystm/luasystm.kmod

以降の例ではこの手順でluasystm.kmodをビルドしています。

Luaカーネルモジュールに関数を追加する

九九の表を出力する関数を追加する

まずは簡単に、九九の表を出力する関数を追加してみます。luasystm.cを以下のように修正します。

--- luasystm.c  2014-07-20 03:38:35.000000000 +0900
+++ luasystm.c  2015-09-15 21:14:34.000000000 +0900
@@ -141,6 +141,26 @@
    return 1;
 }

+static int
+kuku(lua_State *L)
+{
+   int x, y, val;
+
+   for (x = 1; x <= 9; x++) {
+       for (y = 1; y <=9; y++) {
+           val = x * y;
+           if (val < 10) {
+               printf("  %d", val);
+           } else {
+               printf(" %d", val);
+           }
+       }
+       printf("\n");
+   }
+
+   return 0;
+}
+
 /* panicing */

 static int
@@ -171,6 +191,7 @@
        { "aprint_debug",       systm_aprint_debug },
        { "aprint_error",       systm_aprint_error },
        { "aprint_get_error_count", systm_aprint_get_error_count },
+       { "kuku",           kuku },

        /* panicing */
        { "panic",          systm_panic },

Luaスクリプトからは単に"systm.kuku()"を呼び出すだけです。

-- kuku.lua
local systm = require 'systm'
systm.kuku()

実行結果は以下になります。

img106.png

プロセス一覧を出力する関数を追加する

次にカーネル内の情報を参照する例として、プロセスの一覧を出力する関数を追加してみます。luasystm.cの修正内容は以下になります。

--- luasystm.c  2014-07-20 03:38:35.000000000 +0900
+++ luasystm.c  2015-09-15 21:45:46.000000000 +0900
@@ -39,6 +39,11 @@
 #endif
 #include <sys/systm.h>

+#include <sys/proc.h>
+#include <sys/filedesc.h>
+#include <sys/syscallargs.h>
+#include <sys/file.h>
+
 #include <lua.h>
 #include <lauxlib.h>

@@ -141,6 +146,22 @@
    return 1;
 }

+static int
+show_all_process(lua_State *L)
+{
+   struct proc *p;
+
+   LIST_FOREACH(p, &allproc, p_list) {
+       if (curproc->p_pid == p->p_pid) {
+           printf("* %5d %s\n", p->p_pid, p->p_comm);
+       } else {
+           printf("  %5d %s\n", p->p_pid, p->p_comm);
+       }
+   }
+
+   return 0;
+}
+
 /* panicing */

 static int
@@ -171,6 +192,7 @@
        { "aprint_debug",       systm_aprint_debug },
        { "aprint_error",       systm_aprint_error },
        { "aprint_get_error_count", systm_aprint_get_error_count },
+       { "show_all_process",       show_all_process },

        /* panicing */
        { "panic",          systm_panic },

先ほどのLuaスクリプトと同じく、こちらも"systm.show_all_process()"を呼び出すだけです。

-- show_all_process.lua
local systm = require 'systm'
systm.show_all_process()

実行結果は以下です。プロセスの一覧がだーっと出力されています。

img102.png

カーネルスレッドの作成・破棄を行う関数を追加する

先の例では単に何か実行してそれで終わりだったのですが、今度はカーネルスレッドを立ち上げて定期的に処理が走る例を作成してみます。luasystm.cの修正内容は以下です。

--- luasystm.c  2014-07-20 03:38:35.000000000 +0900
+++ luasystm.c  2015-09-15 22:36:40.000000000 +0900
@@ -39,12 +39,23 @@
 #endif
 #include <sys/systm.h>

+#include <sys/types.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+
 #include <lua.h>
 #include <lauxlib.h>

 #ifdef _MODULE
 MODULE(MODULE_CLASS_MISC, luasystm, "lua");

+static kcondvar_t  yry2_wait;
+static kmutex_t        yry2_waitlock;
+static lwp_t       *yry2_worker_lwp;
+
+static void yry2_thread(void *arg);
+
 /* Various printing functions */
 static int
 print(lua_State *L)
@@ -141,6 +152,70 @@
    return 1;
 }

+static int
+yry2_kthread_create(lua_State *L)
+{
+   mutex_init(&yry2_waitlock, MUTEX_DEFAULT, IPL_NONE);
+   cv_init(&yry2_wait, "balloon");
+
+   yry2_worker_lwp = (lwp_t *)0xdeadbabe;
+
+   if (kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL,
+           yry2_thread, NULL, &yry2_worker_lwp, "yry2_thread")) {
+       printf("cannot create kthread(luasystm).\n");
+
+       cv_destroy(&yry2_wait);
+       mutex_destroy(&yry2_waitlock);
+
+       return 1;
+   }
+
+   return 0;
+}
+
+static int
+yry2_kthread_destroy(lua_State *L)
+{
+   lwp_t *l = yry2_worker_lwp;
+
+   /* Notify the worker and wait for the exit. */
+   mutex_enter(&yry2_waitlock);
+   yry2_worker_lwp = NULL;
+   cv_broadcast(&yry2_wait);
+   mutex_exit(&yry2_waitlock);
+   kthread_join(l);
+
+   cv_destroy(&yry2_wait);
+   mutex_destroy(&yry2_waitlock);
+
+   printf("thread destroyed.\n");
+
+   return 0;
+}
+
+
+static void
+yry2_thread(void *arg)
+{
+   int sleeptime = 10000;
+   int count = 0;
+
+   for (;;) {
+       const bool finish = (yry2_worker_lwp == NULL);
+       mutex_enter(&yry2_waitlock);
+       if (finish) {
+           mutex_exit(&yry2_waitlock);
+           break;
+       }
+       printf("hello,world. (%d)\n", count++);
+       cv_timedwait(&yry2_wait, &yry2_waitlock,
+           mstohz(sleeptime));
+       mutex_exit(&yry2_waitlock);
+   }
+
+   kthread_exit(0);
+}
+
 /* panicing */

 static int
@@ -171,6 +246,8 @@
        { "aprint_debug",       systm_aprint_debug },
        { "aprint_error",       systm_aprint_error },
        { "aprint_get_error_count", systm_aprint_get_error_count },
+       { "yry2_kthread_create",    yry2_kthread_create },
+       { "yry2_kthread_destroy",   yry2_kthread_destroy },

        /* panicing */
        { "panic",          systm_panic },

カーネルスレッドを生成・破棄するため、Luaスクリプトは2つ作成します。

-- kthread_create.lua
local systm = require 'systm'
systm.yry2_kthread_create("")
-- kthread_destroy.lua
local systm = require 'systm'
systm.yry2_kthread_destroy()

Luaスクリプトからカーネルスレッドを起動してみます。一定間隔でメッセージが表示されます。

img103.png

カーネルスレッドを停止してみます。

img104.png

ちなみに、カーネルスレッドの実行中にluactl destroyを実行するとカーネルパニックが発生します...。実行中の状態を破棄したらそれはダメだよなと思いつつ、チェック機構とかは無いのかという気持ちも。

ともかく、こんな感じで引数を伴わない簡単な関数についてLuaカーネルモジュールに追加することができました。

まとめ

NetBSD-7.0_RC3でLuaカーネルモジュール機能を試してみました。Luaスクリプトから呼び出せるのはsystm.print()的なものやpmf関連のもので、提供されている機能がまだ少ない感じです。各システムコールがLuaスクリプトから触れられるようになると、応用範囲が広がりそうではありますが、何かユースケースを思いついておかないと「XXという機能が欲しいんだけど、C言語でカーネルモジュールとして実装すればイイじゃん」という話になってしまいそうな気もします。

この応用例が思いつかない感じ、これは既視感があるぞと記憶を辿ったら、Linuxのライブパッチを試してみた時と同じく、便利そうではあるけれどイマイチ応用例が思いつかない、というのと同じ感覚なのでした。

参考URL

7
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
9