少し前の話になりますが、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系では一部インストール手順が変更になっているので注意してください。
- NetBSDをVirtualBoxにインストールしてみる
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()した内容は緑色で出力されます)。
Luaカーネルモジュールの一連の流れ
Luaカーネルモジュールとluactlコマンドを相互に使う形でLuaスクリプトを実行しています。一連の手順がちょっとややこしいので、簡単なシーケンス図を描いてみました。
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()
実行結果は以下になります。
プロセス一覧を出力する関数を追加する
次にカーネル内の情報を参照する例として、プロセスの一覧を出力する関数を追加してみます。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()
実行結果は以下です。プロセスの一覧がだーっと出力されています。
カーネルスレッドの作成・破棄を行う関数を追加する
先の例では単に何か実行してそれで終わりだったのですが、今度はカーネルスレッドを立ち上げて定期的に処理が走る例を作成してみます。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スクリプトからカーネルスレッドを起動してみます。一定間隔でメッセージが表示されます。
カーネルスレッドを停止してみます。
ちなみに、カーネルスレッドの実行中にluactl destroyを実行するとカーネルパニックが発生します...。実行中の状態を破棄したらそれはダメだよなと思いつつ、チェック機構とかは無いのかという気持ちも。
ともかく、こんな感じで引数を伴わない簡単な関数についてLuaカーネルモジュールに追加することができました。
まとめ
NetBSD-7.0_RC3でLuaカーネルモジュール機能を試してみました。Luaスクリプトから呼び出せるのはsystm.print()的なものやpmf関連のもので、提供されている機能がまだ少ない感じです。各システムコールがLuaスクリプトから触れられるようになると、応用範囲が広がりそうではありますが、何かユースケースを思いついておかないと「XXという機能が欲しいんだけど、C言語でカーネルモジュールとして実装すればイイじゃん」という話になってしまいそうな気もします。
この応用例が思いつかない感じ、これは既視感があるぞと記憶を辿ったら、Linuxのライブパッチを試してみた時と同じく、便利そうではあるけれどイマイチ応用例が思いつかない、というのと同じ感覚なのでした。
参考URL
- NetBSD 7.0_RC3
- Lua
-
Modern net bsd kernel module
- 最近のNetBSDカーネルモジュールの解説。いつの間にやらLKM(Loadable Kernel Module)ではなくなっていた...。
-
NetBSDをVirtualBoxにインストールしてみる
- 手前味噌ながら自分の投稿内容...