mruby を Linux カーネル内で動作させる

mruby を Loadable Kernel Module (LKM) として Linux カーネル内動作させて printk する方法をメモしておきます。

1年以上前に書きかけていたものを完成させたので、mrubyのバージョンが古いです。最近のバージョンだとどうなるのかは分かりません。

mruby のビルド

まず GitHub から mruby のソースコードをダウンロードします。今回試したのは f65a39f4d19b1de019b0b805ccfb081757e5b7b5 (Wed Sep 18 20:00:35 2013 -0700) です。

$ git clone https://github.com/mruby/mruby.git
$ cd mruby
$ git checkout f65a39f4d19b1de019b0b805ccfb081757e5b7b5

次にカーネル内で動作するバージョンをクロスビルドするための設定を追加します。これにより、ホスト上で動く通常の mruby とは別に、build/kernel 以下にカーネル内動作用の mruby がビルドされます。

$ cat >> build_config.rb
MRuby::CrossBuild.new('kernel') do |conf|
  toolchain :gcc

  conf.cc.flags << "-Iinclude/kernel -mcmodel=kernel -mno-red-zone -mfpmath=387 -mno-sse -mno-sse2 -mno-mmx -mno-3dnow -msoft-float -fno-asynchronous-unwind-tables -fno-omit-frame-pointer"
  conf.cc.defines << %w(DISABLE_STDIO)
  conf.cc.defines << %w(DISABLE_FLOAT)
  conf.cc.defines << %w(MRB_INT64)
end
(ctrl-d)

次にカーネル内で動作させるためのヘッダファイルを作成します。
stdlib.h は必要最小限のものに置き換えます。これにより浮動小数点関係のエラーが減ります。

$ mkdir include/kernel
$ cat > include/kernel/stdlib.h
typedef unsigned long size_t;
void free(void *ptr);
void *realloc(void *ptr, size_t size);
int abs(int j);
unsigned long int strtol(const char *nptr, char **endptr, int base);
unsigned long int strtoul(const char *nptr, char **endptr, int base);
void exit(int status);
#define EXIT_SUCCESS 0
#define EXIT_FAILURE (-1)
void abort(void);
int atoi(const char *nptr);
# define strtod(p,e) strtol(p,e,10)
(ctrl-d)

stdarg.h は vsnprintf 関係のものだけを gcc の builtin 関数で記述します。

$ cat > include/kernel/stdarg.h
typedef unsigned long size_t;
typedef __builtin_va_list __gnuc_va_list;
typedef __gnuc_va_list va_list;
#define va_start(v,l) __builtin_va_start(v,l)
#define va_end(v)     __builtin_va_end(v)
#define va_arg(v,l)   __builtin_va_arg(v,l)
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
(ctrl-d)

浮動小数点は使わないので、全部ごまかします。

$ cat > include/kernel/math.h
# define fmod(x,y) (x)
# define pow(x,y) (x)
# define log10(x) (x)
# define floor(x) (x)
# define ceil(x) (x)
# define isinf(x) 0
# define isnan(x) 0
(ctrl-d)

内部のfloat型(mrb_float)をlongで置き換えます。

$ cat > value.h.patch
--- include/mruby/value.h.orig  2013-09-19 13:24:11.378647350 +0900
+++ include/mruby/value.h       2013-09-19 16:15:33.647687793 +0900
@@ -7,7 +7,13 @@
 #ifndef MRUBY_VALUE_H
 #define MRUBY_VALUE_H

-#ifdef MRB_USE_FLOAT
+#if defined(DISABLE_FLOAT)
+  typedef long mrb_float;
+# define double long
+int sprintf(char *str, const char *format, ...);
+# define mrb_float_to_str(buf, i) sprintf(buf, "%d", i)
+# define str_to_mrb_float(buf) strtol(buf, NULL, 10)
+#elif defined(MRB_USE_FLOAT)
   typedef float mrb_float;
 # define mrb_float_to_str(buf, i) sprintf(buf, "%.7e", i)
 # define str_to_mrb_float(buf) strtof(buf, NULL)
(ctrl-d)
$ patch -p0 < value.h.patch

浮動小数点演算をしているところを片っ端から潰します。計算結果のことはここでは考えません。

$ cat > numeric.c.patch
--- src/numeric.c.orig  2013-09-19 13:24:11.389647270 +0900
+++ src/numeric.c       2013-09-19 16:34:56.098316936 +0900
@@ -209,7 +209,7 @@
       if (m < 0) {
         m -= 1;
       }
-      n = n / pow(10.0, m);
+      n = n / pow((mrb_float)10.0, m);
       m = 0;
     }
     else {
@@ -222,15 +222,15 @@

     /* puts digits */
     while (max_digit >= 0) {
-      mrb_float weight = pow(10.0, m);
-      digit = (int)floor(n / weight + FLT_EPSILON);
+      mrb_float weight = pow((mrb_float)10.0, m);
+      digit = (int)floor(n / weight + (mrb_float)FLT_EPSILON);
       *(c++) = '0' + digit;
       n -= (digit * weight);
       max_digit--;
       if (m-- == 0) {
         *(c++) = '.';
       }
-      else if (m < -1 && n < FLT_EPSILON) {
+      else if (m < -1 && n < (mrb_float)FLT_EPSILON) {
         break;
       }
     }
@@ -324,7 +324,7 @@
   mrb_float div;
   mrb_float mod;

-  if (y == 0.0) {
+  if (y == (mrb_float)0.0) {
     div = str_to_mrb_float("inf");
     mod = str_to_mrb_float("nan");
   }
@@ -336,7 +336,7 @@
       div = (x - mod) / y;
     if (y*mod < 0) {
       mod += y;
-      div -= 1.0;
+      div -= (mrb_float)1.0;
     }
   }

@@ -457,7 +457,7 @@

   d = (mrb_float)mrb_fixnum(num);
   /* normalize -0.0 to 0.0 */
-  if (d == 0) d = 0.0;
+  if (d == 0) d = (mrb_float)0.0;
   c = (char*)&d;
   for (hash=0, i=0; i<sizeof(mrb_float);i++) {
     hash = (hash * 971) ^ (unsigned char)c[i];
@@ -615,10 +615,10 @@

   mrb_get_args(mrb, "|i", &ndigits);
   number = mrb_float(num);
-  f = 1.0;
+  f = (mrb_float)1.0;
   i = abs(ndigits);
   while  (--i >= 0)
-    f = f*10.0;
+    f = f*(mrb_float)10.0;

   if (isinf(f)) {
     if (ndigits < 0) number = 0;
@@ -630,13 +630,13 @@
     else number *= f;

     /* home-made inline implementation of round(3) */
-    if (number > 0.0) {
+    if (number > (mrb_float)0.0) {
         d = floor(number);
-        number = d + (number - d >= 0.5);
+        number = d + (number - d >= (mrb_float)0.5);
     }
-    else if (number < 0.0) {
+    else if (number < (mrb_float)0.0) {
         d = ceil(number);
-        number = d - (d - number >= 0.5);
+        number = d - (d - number >= (mrb_float)0.5);
     }

     if (ndigits < 0) number *= f;
@@ -662,8 +662,8 @@
 {
   mrb_float f = mrb_float(num);

-  if (f > 0.0) f = floor(f);
-  if (f < 0.0) f = ceil(f);
+  if (f > (mrb_float)0.0) f = floor(f);
+  if (f < (mrb_float)0.0) f = ceil(f);

   if (!FIXABLE(f)) {
     return mrb_float_value(mrb, f);
(ctrl-d)
$ patch -p0 < numeric.c.patch

make して build/kernel/lib/libmruby.aがビルドできればOKです。その他のコンパイルエラーは放置します。

$ make
...
AR    build/kernel/lib/libmruby.a
ar: /home/shina/mruby-kernel/mruby/build/kernel/lib/libmruby.a を作成しています
...

ホスト環境での mruby での Hello World

まずはホスト環境(Linux)で mruby の動作を確認します。host というディレクトリを mruby の隣に作って試します。

$ cd ..
$ mkdir host
$ cd host

下記の main.c は mruby の実行環境を呼び出すプログラムです。今回は "Kernel" というモジュールと "printk" というクラスメソッドを用意します。実体は printf しているだけです。

$ cat > main.c
#include "mruby.h"
#include "mruby/proc.h"
#include "mruby/string.h"

extern uint8_t code[];

static mrb_value
kernel_printk(mrb_state *mrb, mrb_value self)
{
        mrb_value retval;
        mrb_value str;

        mrb_get_args(mrb, "S", &str);
        printf("%s", RSTRING_PTR(str));
        retval.value.i = 0;
        return retval;
}

int
main(int argc, char **argv)
{
        mrb_state *mrb;
        struct RClass *kernel;
        mrb_value ret;

        mrb = mrb_open();
        kernel = mrb_define_module(mrb, "Kernel");
        mrb_define_class_method(mrb, kernel, "printk",
                                kernel_printk, ARGS_REQ(1));

        ret = mrb_load_irep(mrb, code);
        return ret.value.i;
}
(ctrl-d)

次に実行する ruby のプログラム hello.rb を用意します。Kernel クラスの printk メソッドを呼び出すだけです。

$ cat > hello.rb
Kernel.printk "Hello World!\n"
(ctrl-d)

ビルドするための Makefile です。先ほどビルドした mruby のホスト環境を使います。mrbc は ruby のコードを mruby のバイトコード(のC言語の配列表現)に変換するプログラムです。

$ cat > Makefile
CFLAGS = -I../mruby/include -g
LDFLAGS = -lm
OBJS = main.o hello.o
LIBS = ../mruby/build/kernel/lib/libmruby.a

main: $(OBJS) $(LIBS)
	$(CC) $(OBJS) $(LIBS) $(LDFLAGS) -o $@

hello.c: hello.rb
	../mruby/bin/mrbc -Bcode $<
(ctrl-d)

make して実行して動作を確認します。

$ make
cc -I../mruby/include -g   -c -o main.o main.c
../mruby/bin/mrbc -Bcode hello.rb
cc -I../mruby/include -g   -c -o hello.o hello.c
cc main.o hello.o ../mruby/build/kernel/lib/libmruby.a -lm -o main
$ ./main
Hello World!

Linux カーネル内での mruby での Hello World

次に Linux で動作するカーネルモジュールを作成します。kernel というディレクトリを mruby の隣に作って試します。

$ cd ..
$ mkdir kernel
$ cd kernel

まずはカーネルモジュールの初期化・終了をおこなうコードです。

$ cat > lkm.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ctype.h>

extern int mruby_main(void);

static int lkm_init(void)
{
        printk(KERN_INFO "LKM: init\n");
        return mruby_main();
}

static void lkm_exit(void) {
        printk(KERN_INFO "LKM: exit\n");
}

module_init(lkm_init);
module_exit(lkm_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("mruby");
MODULE_AUTHOR("Takahiro Shinagawa");
(ctrl-d)

次に mruby を呼び出すコードです。先ほどのホストで動作するものとほとんど同じです。

$ cat > main.c
#include <linux/kernel.h>
#include "mruby.h"
#include "mruby/irep.h"
#include "mruby/string.h"

extern uint8_t code[];

static mrb_value
kernel_printk(mrb_state *mrb, mrb_value self)
{
        mrb_value retval;
        mrb_value str;

        mrb_get_args(mrb, "S", &str);
        printk(KERN_INFO "mruby: %s\n", RSTRING_PTR(str));
        retval.value.i = 0;
        return retval;
}

int
mruby_main(void)
{
        mrb_state *mrb;
        struct RClass *kernel;
        mrb_value ret;

        mrb = mrb_open();
        kernel = mrb_define_module(mrb, "Kernel");
        mrb_define_class_method(mrb, kernel, "printk",
                                kernel_printk, ARGS_REQ(1));

        ret = mrb_load_irep(mrb, code);
        printk("mruby: ret = %d\n", ret.value.i);
        return 0;
}
(ctrl-d)

次に libc をエミュレーションするライブラリです。必要最低限しかエミュレーションしていないので、ちゃんと定義されていない関数が呼び出されたら正常に動作しないでしょう。

$ cat > libc.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>

typedef unsigned long size_t;
void *realloc(void *ptr, size_t size);
void free(void *ptr);

typedef int jmp_buf[6];
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

int * __errno_location(void);
const unsigned short * * __ctype_b_loc (void);

long int strtol(const char *nptr, char **endptr, int base);
unsigned long int strtoul(const char *nptr, char **endptr, int base);

void abort(void);
void exit(int status);


void *realloc(void *ptr, size_t size)
{
	return krealloc(ptr, size, GFP_KERNEL);
}

void free(void *ptr)
{
	kfree(ptr);
}

int _setjmp(jmp_buf env)
{
	return __builtin_setjmp(env);
}

void longjmp(jmp_buf env, int val)
{
	__builtin_longjmp(env, 1);
}

int * __errno_location(void)
{
	static int errno;
	return &errno;
}

const unsigned short * * __ctype_b_loc (void)
{
	printk("%s\n", __FUNCTION__);
	return NULL;
}

const unsigned short * * __ctype_tolower_loc (void)
{
	printk("%s\n", __FUNCTION__);
	return NULL;
}

const unsigned short * * __ctype_toupper_loc (void)
{
	printk("%s\n", __FUNCTION__);
	return NULL;
}

int isspace(int c)
{
	return (c == 0x20) | (0x09 <= c && c <= 0x0d);
}

int isdigit(int c) 
{
	return ('0' <= c && c <= '9');
}

int isupper(int c)
{
	return ('A' <= c && c <= 'Z');
}

int islower(int c)
{
	return ('a' <= c && c <= 'z');
}

int isalpha(int c)
{
	return isupper(c) || islower(c);
}

long int strtol(const char *nptr, char **endptr, int base)
{
	printk("%s: %s\n", __FUNCTION__, nptr);
	return 0;
}

unsigned long int strtoul(const char *nptr, char **endptr, int base)
{
	printk("%s: %s\n", __FUNCTION__, nptr);
	return 0;
}

void abort()
{
	printk("%s\n", __FUNCTION__);
}

void exit(int status)
{
	printk("%s\n", __FUNCTION__);
}
(ctrl-d)

最小限のヘッダファイルを作成します。

$ cat > stdint.h
typedef unsigned char uint8_t;
(ctrl-d)
$ echo > inttypes.h

最後に Makefile です。上記のコードに加えて、先ほど hello.rb をバイトコードに変換したものと、mruby のライブラリをリンクします。

$ cat > Makefile
ccflags-y += -DDISABLE_STDIO -I$(PWD)/../mruby/include -I$(PWD)
obj-m := mruby.o
mruby-objs := lkm.o main.o libc.o ../host/hello.o ../mruby/build/kernel/lib/libmruby.a

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) CFLAGS_MODULE=$(CFLAGS) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
(ctrl-d)

make すると少し警告が出ますが、mruby.ko が生成されるはずです。

$ make
...
$ ls mruby.ko
mruby.ko

出来たカーネルモジュールをロードすると、カーネルのログに Hello World! が出力されるはずです。

$ sudo insmod mruby.ko
$ dmesg | grep mruby
...
[   75.744877] mruby: Hello World!
[   75.744879] mruby: ret = 0

GitHub にコードを置きました.https://github.com/utshina/mruby-lkm

ツールキットを使わずに UEFI アプリケーションの Hello World! を作る

UEFI アプリケーションを作成するためのツールキットには、EDK IIgnu-efi などがあります。しかし、EDK II は複雑すぎて中身の構造が分かりにくく、とっつきにくい印象があります。また、gnu-efi は wrapper 経由で関数を呼び出さなければならないなど、標準的な UEFI アプリケーションのソースコードと若干異なるものになってしまうのが気になります。

そこで、UEFI の勉強も兼ねて、これらのツールキットを使わずに、本当に最小限の Hello World を表示するだけのアプリケーションを作ってみました。

開発環境は Linux です。必要な物は PE32+ executable を作るためのクロスコンパイラです。私は Fedora を使っているので、下記のコマンドでインストールできました。他のディストリビューションを使っている場合は、適宜探してインストールしてみてください。

yum install mingw64-gcc

実際に作成してみたソースコード(main.c)は以下のとおりです。定義はコンソール出力に必要な物だけに絞っており、かなり端折ってあります。その代わり、見える部分の定義はなるべく UEFI の仕様書のとおりになるようにしてみました。

#define IN
#define EFIAPI
#define EFI_SUCCESS 0

typedef unsigned short CHAR16;
typedef unsigned long long EFI_STATUS;
typedef void *EFI_HANDLE;

struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
typedef
EFI_STATUS
(EFIAPI *EFI_TEXT_STRING) (
    IN struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
    IN CHAR16                                   *String
    );

typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
    void             *a;
    EFI_TEXT_STRING  OutputString;
} EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;

typedef struct {
    char                             a[52];
    EFI_HANDLE                       ConsoleOutHandle;
    EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *ConOut;
} EFI_SYSTEM_TABLE;

EFI_STATUS
EFIAPI
EfiMain (
    IN EFI_HANDLE ImageHandle,
    IN EFI_SYSTEM_TABLE *SystemTable
    )
{
    SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
    while(1);
    return EFI_SUCCESS;
}

EfiMain がエントリーポイントです。この関数に渡される EFI_SYSTEM_TABLE 型の SystemTable が各種システム・サービスへのポインタとなっています。その中に EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 型の ConOut というデータがあり、オブジェクト指向のような関数テーブルとなっています。この中の OutputString という関数を呼び出すと、コンソールに文字列を出力することが出来ます。出力する文字列は Unicode なので、文字列の定義に L を付けます。

上記の main.c をコンパイルするための Makefile は以下のとおりです。

CC = x86_64-w64-mingw32-gcc
CFLAGS = -shared -nostdlib -mno-red-zone -fno-stack-protector -Wall \
         -e EfiMain

all: main.efi

%.efi: %.dll
	objcopy --target=efi-app-x86_64 $< $@

%.dll: %.c
	$(CC) $(CFLAGS) $< -o $@

qemu: main.efi OVMF.fd image/EFI/BOOT/BOOTX64.EFI
	qemu-system-x86_64 -nographic -bios OVMF.fd -hda fat:image

image/EFI/BOOT/BOOTX64.EFI:
	mkdir -p image/EFI/BOOT
	ln -sf ../../../main.efi image/EFI/BOOT/BOOTX64.EFI

OVMF.fd:
	wget http://downloads.sourceforge.net/project/edk2/OVMF/OVMF-X64-r15214.zip
	unzip OVMF-X64-r15214.zip OVMF.fd

clean:
	rm -f main.efi

ロスコンパイラとして x86_64-w64-mingw32-gcc を指定しています。他のディストリビューションでは名称が異なるようですので、適宜修正してください。

このクロスコンパイラを使って、まず普通の PE 形式のファイル main.dll を作ります。余計なものをリンクしないように、-nostdlib を付けています。また、エントリポイントとして EfiMain を指定しています。

生成された main.dll は Windows 等で使われる DLL などと同じ形式になっているので、objcopy を使って subsystem が 10 になるように変換して、UEFI アプリケーションとして認識されるようにします。


生成された main.efiqemu を使って実行してみます。

まず、qemu 用の UEFIファームウェアである OVMF.fd を入手します。http://tianocore.sourceforge.net/wiki/OVMF から最新のファームウェアをダウンロードできます。執筆時点では、r15214 でした。先ほどの Makefile を使うと自動的に入手できます。

make OVMF.fd

次に先ほどの Makefile を使って qemu を起動します。

make qemu

-nographic を指定することで、グラフィックスモードは使わずに、コンソールから起動するようにしています。-biosUEFIファームウェアのファイルを指定しています。-hda fat:image とすることで、image ディレクトリ以下を FAT フォーマットの仮想ディスクとして見えるようにしています。image 以下には EFI/BOOT/BOOTX64.EFI というファイル名で生成された EFI ファイルを置いておくことで、仮想マシンの起動時に自動的に EFI アプリケーションが実行されるようにしています。

うまくいけば、Hello World! と表示されて停止するはずです。Ctrl-a x で qemu を抜けましょう。

Boot Failed. EFI Floppy
Boot Failed. EFI Floppy 1
Boot Failed. EFI DVD/CDROM
Hello World!
QEMU: Terminated

GitHub にコードを置きました.https://github.com/utshina/uefi-simple

OS & システムソフトウェアの一流国際会議

OSやシステムソフトウェアをメイントピックとする著名な国際会議を4つ紹介します。

SOSP (ACM Symposium on Operating Systems Principles)

概要

OSやシステムソフトウェアの分野における世界最高峰の国際会議。名称は "Operating Systems" だが、分散システム、ネットワーク、ストレージ、セキュリティ、組み込み等に関するシステムソフトウェア全般を幅広く扱う。

  • 2年に1回しか開催されない(下記のOSDIと交互に開催)
    • 第1回は1967年、2011年に第23回
  • 最近7回(1999年〜2011年)の平均採択率は 17% (154本/885本)
1999 2001 2003 2005 2007 2009 2011
投稿 90 85 128 155 131 139 157 885
採択 19 17 22 20 25 23 28 154
採択率 21.1% 20.0% 17.2% 12.9% 19.1% 16.5% 17.8% 17.4%
  • 2011年の査読は3ステージ制
    • double blind、論文1本につき3〜8名の査読者、査読総数719、最終的にはコアPCメンバー13名で採択を決定
  • 内容の濃い論文
    • レターサイズ14ページ程度
  • 高い被引用数
論文数 被引用数 平均被引用数 H-index
ACM Digital Library (2013年1月現在) 609 (-82※) 22,315 36.64 -
Microsoft Academic Search のデータ (2013年1月現在) 413 21,287 51.5 73

ACMのデータは関連ワークショップ論文を含む。

論文

※「会議ページ」からは論文やスライド、ビデオ等が無料でダウンロード可能

OSDI (USENIX Symposium on Operating Systems Design and Implementation)

概要

SOSP と並ぶOSやシステムソフトウェアの分野における超一流国際会議。SOSP が徹底した "evaluation" により完全に完成した(≒終了した)仕事が多いのに対し、OSDI は(比較的)設計・実装の面白さを重視した論文が多い。

  • 2年に1回しか開催されない(上記の SOSP と交互に開催)
    • 第1回は1994年、2012年に第10回
    • 歴史は比較的浅いものの、ACM SIGOPSと連携しており、当初よりレベルの高い論文が多い。
  • 最近7回(2000年〜2012年)の平均採択率は 15.5% (188本/1,211本)
2000 2002 2004 2006 2008 2010 2012
投稿 111 150 193 150 193 199 215 1,211
採択 24 27 27 27 26 32 25 188
採択率 21.6% 18.0% 14.0% 18.0% 13.5% 16.0% 11.6% 15.5%
  • 2010年の査読は3ラウンド制
      • 第1ラウンドは査読者2名(約35本がreject)、第2ラウンドは査読者3名(約80本がreject)、第3ラウンドは査読者2〜3名(ボーダーラインの論文が協議の末reject)、残った論文をPC会議の前半で全て一通り議論してaccept・acceptable・questionableに分類、accept・acceptableグループを採択にしたのち、PC会議の後半で残りの"questionable"論文の採択を議論、PC会議後さらに"heavier"なPCメンバーによるメール投票で数本を追加採択。
  • 内容の濃い論文
    • レターサイズ14〜16ページ程度
  • 高い被引用数
論文数 被引用数 平均被引用数 H-index
ACM Digital Library (2013年1月現在) 221 7,817 35.37 -
Microsoft Academic Search のデータ (2013年1月現在) 236 15,631 66.23 70

EuroSys (The European Conference on Computer Systems)

概要

2006年に誕生したばかりで歴史は浅いが、当初より高いレベルを保っている一流国際会議。異常にレベルの高い SOSP や OSDI を補完する受け皿として、普通のトップカンファレンスとしての地位を確立しつつある。

  • 毎年開催される
    • 第1回は2006年、2012年に第7回
  • 最近7回(2006年〜2012年)の平均採択率は 17.9% (185本/1,036本)
2006 2007 2008 2009 2010 2011 2012
投稿 144 131 131 149 141 161 179 1,036
採択 29 29 24 25 27 24 27 185
採択率 20.1% 22.1% 18.3% 16.8% 19.1% 14.9% 15.1% 17.9%
  • 2012年の査読は3ラウンド制
    • 第1ラウンドは査読者3名以上(96本が次のラウンドへ)、第2ラウンドは査読者2名以上、評価の分かれた論文は第3ラウンドで更なる査読、査読総数750
    • 査読コメントに対する著者の反論機会あり
    • 全ての採択論文にはPCメンバーによるシェパードが付く
  • 内容の濃い論文
    • レターサイズ14ページ程度
  • 高い被引用数
論文数 被引用数 平均被引用数 H-index
ACM Digital Libraryのデータ(2013年1月現在) 382(-197※) 3,184 8.34 -
Microsoft Academic Search のデータ(2013年1月現在) 178 3,356 18.85 30

ACMのデータは関連ワークショップ論文を含む。

論文

USENIX ATC (Annual Technical Conference)

概要

近年アカデミックな論文発表の場として定着してきた一流国際会議。EuroSys と並んで SOSP や OSDI に通らなかった論文の受け皿として成長してきた。EuroSys に落ちた論文を投稿できるように日程調整等がされるときもある。

  • 団体としての USENIX は "The Advanced Computing Systems Association" とも称される
    • "Unix User Group" が起源とされるが、近年は「UNIXユーザ会」的な印象は薄れている
    • 特に Technical Conference は、2000年以降くらいから急速にアカデミックな論文発表の場としての格が上昇し、EuroSys に次ぐ1流(1.5流?)国際会議としての地位を確立している
  • 近年は年に1回開催される
    • Winter と Summer の年2回開催されていた時期もある
    • "Annual" と付くようになったのは 1996 年頃から
    • Technical Conference 自体は1980年代始めから開催されていたようである
    • 開催回数が不明確なため、正式名称は "2012 USENIX Annual Technical Conference" のように年を記載する
  • 最近7回(2006年〜2012年)の平均採択率は 15.1% (182本/1,202本)
2006 2007 2008 2009 2010 2011 2012
投稿 153 170 176 191 147 180 185 1,202
採択 21 24 28 28 22 27 32 182
採択率 13.7% 14.1% 15.9% 14.7% 15.0% 15.0% 17.3% 15.1%
  • 2012年の査読は3ラウンド制
    • 第1ラウンドは査読者2人、第2ラウンドでは3人目の査読者が付く場合があり、第3ラウンドでは4,5人目の査読者が付く場合がある
    • 全ての採択論文にはPCメンバーによるシェパードが付く
  • 内容の濃い論文
    • レターサイズ12〜14ページ程度
  • 高い被引用数
論文数 被引用数 平均被引用数 H-index
Microsoft Academic Search のデータ(2013年1月現在) 1,471 41,035 27.90 93
論文