遅ればせながら NetBSD Advent Calendar 2018 24日目の記事です。
今日はLuaカーネルモジュールからLuaスクリプトの関数を呼び出してみる手順を紹介してみようと思います。
Luaカーネルモジュール
18日目の記事でNetBSD-8.0におけるLuaカーネルの変更箇所を紹介しました(あまり変更箇所はありませんでしたが...)。
今回の記事では、Luaカーネルモジュールをもうちょっと掘り下げて見てみます。
サンプルのluareadを読み込んでみる
3日目の記事でNetBSDカーネルモジュールの紹介をした際に、サンプルとして readhappy
カーネルモジュールを試していました。
readhappy
のLuaカーネルモジュール版として、 luareadhappy
カーネルモジュールがサンプルとして提供されているので、今回はこちらを試してみます。
他のカーネルモジュールと同じく、 make
して modload
するだけです。
# cd /usr/src/sys/modules/examples/luareadhappy/
# ls -F
CVS/ Makefile happy.lua luareadhappy.c
# make
# ls -F
CVS/ happy.lua luareadhappy.kmod machine@
Makefile i386@ luareadhappy.kmod.map x86@
amd64@ luareadhappy.c luareadhappy.o
# modload ./luareadhappy.kmod
# modstat | grep happy
happy misc filesys - 0 646 lua
luareadhappy
カーネルモジュールは、 modload
した時点で happy
という名前のLua stateが内部的に作られています。
# luactl
1 active state:
Name Creator Description
happy kernel Example Happy Number calculator
happy
stateに対し、サンプルディレクトリにある happy.lua
をロードします。
# luactl load happy ./happy.lua
./happy.lua loaded into happy
happy.lua
はソースコメント部分を除くと以下のようなコードになっています。
local HAPPY_NUMBER = 1
local SAD_NUMBER = 4
function dsum(n)
local sum = 0
while n > 0 do
local x = n % 10
sum = sum + (x * x)
n = n / 10
end
return sum
end
function is_happy(n)
while true do
local total = dsum(n)
if total == HAPPY_NUMBER then
return 1
end
if total == SAD_NUMBER then
return 0
end
n = total
end
end
luareadhappy
カーネルモジュールの中では、read(2)された時に happy_read()
が呼ばれ、さらにそこから呼ばれる check_happy()
の中で先述したLuaスクリプト内の関数 is_happy()
を呼び出しているようです。
/usr/src/sys/modules/examples/luareadhappy/luareadhappy.c:
68 static struct cdevsw happy_cdevsw = {
69 .d_open = happy_open,
70 .d_close = happy_close,
71 .d_read = happy_read,
...
92 /* Function that calls a Lua routine and returns whether a number is happy */
93 static int
94 check_happy(unsigned n)
95 {
96 int rv;
97
98 klua_lock(sc.kL);
99 lua_getglobal(sc.kL->L, "is_happy");
100
101 if (!lua_isfunction(sc.kL->L, -1)) {
102 lua_pop(sc.kL->L, 1);
103 klua_unlock(sc.kL);
104 return -1;
105 }
...
154 int
155 happy_read(dev_t self __unused, struct uio *uio, int flags __unused)
156 {
157 int rv;
158 char line[80];
159
160 /* Get next happy number */
161 while ((rv = check_happy(++sc.last)) == 0)
162 continue;
...
168 /* Print it into line[] with trailing \n */
169 int len = snprintf(line, sizeof(line), "%u\n", sc.last);
...
177 /* Send it to User-Space */
178 int e;
179 if ((e = uiomove(line, len, uio)))
180 return e;
181
182 return 0;
183 }
ユーザランド側で、以下のような簡単なサンプルを走らせ、Luaカーネルモジュールの動作を見てみましょう。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char buf[BUFSIZ];
char line[BUFSIZ];
int fd;
fd = open("/dev/happy", O_RDONLY);
if (fd == -1) {
fprintf(stderr, "open() failed.\n");
exit(-1);
}
printf("Ctrl-D to quit > ");
while (fgets(line, BUFSIZ, stdin) != NULL) {
read(fd, buf, BUFSIZ);
printf("%s", buf);
printf("Ctrl-D to quit > ");
}
printf("\n");
close(fd);
return 0;
}
サンプルプログラムを動かすと、以下のような動作になります。カーネルから返された値がユーザランド側で取得されていることが確認できます。
まとめ
NetBSDのLuaカーネルモジュールからLuaスクリプトの関数を呼び出してみる手順を紹介しました。
happy.luaを動かす手順では、単に printf()
的な動作をさせるだけでしたが、Luaカーネルモジュール内でLua stateを作成し、その中からLuaスクリプトの関数を呼び出す形にすると、カーネル内の処理をLuaで記述できるという形になります。むしろこちらの使い方の方がLuaカーネルモジュールで想定されている使い方かもしれません。