Linuxカーネルもくもく会#21に参加してきました

Linuxカーネルもくもく会 #21に参加してきました。iij/ipgenというパケットジェネレータで使用されている、高速パケットI/Oフレームワークの"netmap"に興味が湧きつつあり、少しずつソースコードを読み進めています。 今回と前回のもくもく会でnetmapのLinux実装部分のコードを読んでみたので、把握できた内容を簡単にまとめてみました。

netmapのコードはGitHubから取得できます。現時点ではLinux,FreeBSD,Windows環境向けにnetmapが実装されています。ちなみにnetmapは当初FreeBSDで開発されていたこともあり、netmapのコードはFreeBSDカーネルにマージされています。

Linuxにおけるopen("/dev/netmap",...)処理

さっそくLinuxにおけるnetmapの実装を見てみます。netmapのソースコードには内部構造の詳しい解説コメントが記載されており、実装の理解の助けになります。例えばnetmap.cには以下のコメントがあり、Linuxではlinux_netmap_open()から見て行くと良さそうです。

sys/dev/netmap/netmap.c
/* --- internals ----
 *
 * Roadmap to the code that implements the above.
 *
 * > 1. a process/thread issues one or more open() on /dev/netmap, to create
 * >    select()able file descriptor on which events are reported.
 *
 *      Internally, we allocate a netmap_priv_d structure, that will be
 *      initialized on ioctl(NIOCREGIF). There is one netmap_priv_d
 *      structure for each open().
 *
 *      os-specific:
 *          FreeBSD: see netmap_open() (netmap_freebsd.c)
 *          linux:   see linux_netmap_open() (netmap_linux.c) 

処理の流れを把握するため、linux_netmap_open()の関数呼び出し階層を示します。

LINUX/netmap_linux.c:
1057 static struct file_operations netmap_fops = {
1058     .owner = THIS_MODULE,
1059     .open = linux_netmap_open,
1060     .mmap = linux_netmap_mmap,
1061     LIN_IOCTL_NAME = linux_netmap_ioctl,
1062     .poll = linux_netmap_poll,
1063     .release = linux_netmap_release,
1064 };

関数の呼び出し階層は以下のようになっています。

-> LINUX/netmap_linux.c:linux_netmap_open()
  -> sys/dev/netmap/netmap.c:netmap_priv_new()
    -> LINUX/netmap_linux.c:nm_os_get_module()

linux_netmap_open()は帰り値として常に0を返すようです。変数errorは握りつぶされている気がする...。

1036 static int
1037 linux_netmap_open(struct inode *inode, struct file *file)
1038 {
1039         struct netmap_priv_d *priv;
1040         int error;
1041         (void)inode;    /* UNUSED */
1042
1043         NMG_LOCK();
1044         priv = netmap_priv_new();
1045         if (priv == NULL) {
1046                 error = -ENOMEM;
1047                 goto out;
1048         }
1049         file->private_data = priv;
1050 out:
1051         NMG_UNLOCK();
1052
1053         return (0);
1054 }

netmap_priv_new()はnetmap用の構造体(struct netmap_priv_d)用のカーネルメモリを確保し、linux_netmap_open()に返します。 先ほどの変数errorの値はlinux_netmap_open()から参照できなさそうですが、file->private_data == NULLの判定をエラーが発生しているかどうかは判定できそうです。

 959 struct netmap_priv_d*
 960 netmap_priv_new(void)
 961 {
 962         struct netmap_priv_d *priv;
 963
 964         priv = malloc(sizeof(struct netmap_priv_d), M_DEVBUF,
 965                               M_NOWAIT | M_ZERO);
 966         if (priv == NULL)
 967                 return NULL;
 968         priv->np_refs = 1;
 969         nm_os_get_module();
 970         return priv;
 971 }

nm_os_get_module()は自身のモジュールを取得するだけの処理です。

LINUX/netmap_linux.c:
  63 void
  64 nm_os_get_module(void)
  65 {
  66         __module_get(THIS_MODULE);
  67 }

処理がlinux_netmap_open()に戻ってきたのち、確保したstruct netmap_priv_dへの参照をfile->private_dataに設定します。

1036 static int
1037 linux_netmap_open(struct inode *inode, struct file *file)
1038 {
1039         struct netmap_priv_d *priv;
...
1049         file->private_data = priv;
1050 out:
1051         NMG_UNLOCK();
1052
1053         return (0);
1054 }

linux_netmap_open()の変数errorについて

linux_netmap_open()内の変数errorは握りつぶされている(呼び出し元に変数errorの値が渡らない)ように見える件、例えばLinuxのwrite()に対応する関数ではエラー値を負値にしてreturnしています。これに倣うとlinux_netmap_open()でもreturn (-ENOMEM);とするのが良いのかもしれません。

linux-4.5/fs/read_write.c:
 617 SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
 618                 size_t, count)
 619 {
 620         struct fd f = fdget_pos(fd);
 621         ssize_t ret = -EBADF;
 622
 623         if (f.file) {
 624                 loff_t pos = file_pos_read(f.file);
 625                 ret = vfs_write(f.file, buf, count, &pos);
 626                 if (ret >= 0)
 627                         file_pos_write(f.file, pos);
 628                 fdput_pos(f);
 629         }
 630
 631         return ret;
 632 }

Linuxカーネルmalloc()に対するグルーコード

netmap_priv_new()でmalloc()を呼んでいます。このmalloc()はLinuxでもBSD系と同じ引数で呼び出せるよう、ラッパーコードを挟んでいます。netmapはFreeBSDで実装されたこともあり、グルーコードの形で既存の処理を変更することなく別プラットフォームへの移植を行っている、ということですね。

sys/dev/netmap/netmap.c:
 959 struct netmap_priv_d*
 960 netmap_priv_new(void)
 961 {
 ...
 964         priv = malloc(sizeof(struct netmap_priv_d), M_DEVBUF,
 965                               M_NOWAIT | M_ZERO);
 966         if (priv == NULL)
 967                 return NULL;
 ...
 971 }

*_glue.hで各プラットフォーム向けのmalloc()ラッパーコードが実装されています。

$ global malloc
LINUX/bsd_glue.h
WINDOWS/win_glue.h

Linux/bsd_glue.hではkmalloc()へのラッパーになっています。

LINUX/bsd_glue.h:
304 /*
305  * in the malloc/free code we ignore the type
306  */
307 /* use volatile to fix a probable compiler error on 2.6.25 */
308 #define malloc(_size, type, flags)                      \
309         ({ volatile int _v = _size; kmalloc(_v, GFP_ATOMIC | __GFP_ZERO); })

WINDOWS/win_glue.hではwin_kernel_malloc()を挟んでExAllocatePoolWithTag APIを読んでいます。

WINDOWS/win_glue.h:
315 /*-------------------------------------------
316  *      KERNEL MEMORY ALLOCATION and management
317  */
...
321 #define malloc(size, _ty, flags)                win_kernel_malloc(size, _ty, flags)
...
325 /*
326  * we default to always allocating and zeroing
327  */
328 static void *
329 win_kernel_malloc(size_t size, int32_t ty, int flags)
330 {
331         void* mem = ExAllocatePoolWithTag(NonPagedPool, size, ty);
332
333         (void)flags;
334
335         if (mem != NULL) { // && flags & M_ZERO XXX todo
336                 RtlZeroMemory(mem, size);
337         }
338         return mem;
339 }

まとめ

Linuxにおけるnetmapのopen()に関する処理を追いかけてみました。linux_netmap_open()は比較的シンプルな実装になっています。netmapのソースコードには処理の流れやデータ構造の詳しい説明がコメントの形で記載されており理解の助けになります。

第35回「ネットワーク パケットを読む会(仮)」参加メモ

第35回「ネットワーク パケットを読む会(仮)」に参加してきました。

今回は「パケットジェネレータipgenから見るnetmap」という内容で、ipgennetmapに関するネタで発表してきました。

スライドで解説しているテスト用環境の構築手順はQiitaの記事にまとめてあります。ipgenが使用しているnetmapは面白そうな機能なので、いろいろと調べてみたいと思います。

勉強会メモ

以下は簡単ではありますが、勉強会のメモです。

この勉強会では、『ネットワークパケットを読む』と銘打ってあるように、毎回パケットキャプチャツールの更新情報についての解説があります。今回は以下のツールについてアップデート情報の解説がありました。

  • Wireshark 2.0.3が4/22にリリースされている
  • NetworkMiner
    • NetworkMiner 2.0がリリースされている
    • 2.0での変更点として、MIME Typeがバイナリなファイル(x-msdos-program等)の扱いが挙げられる。
      • これまでは.exeや.jar等のファイルは、拡張子を書き換えることで直接実行させないようになっていた
      • しかし、2.0からはこの機能がなくなっているため、安易にファイルリストのファイルを開くと悪意のあるファイルを実行してしまう恐れがあるので注意が必要
        • 機能が無効化された理由は、"Defang Executables"という有償版の機能に格上げ(?)されたため
  • Microsoft Message Analyzer
    • 公式のblogがあり、ツールの使い方や機能が紹介されているので定期的にチェックするとよさげ
  • Ostinatoというパケットジェネレータが存在するとのこと

他にも、とある通信機材を試してみたという話や、特定分野で現在も稼働し続けているWindowsXPマシンに関するセキュリティ上の懸念に関する話がありました。この勉強会はセキュリティ関連の発表も多く、自分のセキュリティに関する知識・認識のアップデートに役立ちます。

次回は6月の開催予定とのことで、興味を持たれた方はぜひ参加してみてください。

「第4回 エンジニアのためのプレゼン技術研究会」の参加メモ

第4回 エンジニアのためのプレゼン技術研究会「みんなの自由研究発表」と「前佛さんのスライドの作り方」に参加してきました。

プレゼン用の図を自動生成する話

今回のテーマは「みんなの自由研究発表」で、各人の自由研究の成果を持ち時間5分で発表するというものでした。自分も「プレゼン用の図を自動生成する話」というタイトルで発表を行いました。

Webブラウザ上で動作するmermaidAPI.jsというJavaScriptライブラリがあり、これを利用して簡単なテキストから動的に図を生成する、というネタでの発表内容でした。

サンプルプログラムは以下のURLから試してみることができます。

mermaidAPI.jsのサンプルプログラム使用手順

発表時間が5分ということもあり、当日の発表ではデモを行えなかったので、上記のサンプルプログラムを使用した簡単な作業フローを紹介します。

まずはサンプルプログラムでmermaidの記法に沿ったテキストを入力し、図を作成します。図は自動で生成(レンダリング)されます。

例として、アニメ「プリパラ」の人物相関図を作成してみます。以下のような感じですね。

f:id:furandon_pig:20151012174646p:plain

mermaidAPI.jsはSVGを生成するので、Inkscape等のSVGが編集可能なツールで図の修正や微調整が行えます。ただし、mermaidAPI.jsが生成するSVGは、テキスト表示用に独自のSVGタグを使っていたりするので、そのままではInkscapeで表示できないものがあります。上記サンプルプログラムではInkscapeで表示できるよう、生成されたSVGを一部修正しています。

f:id:furandon_pig:20151012174715p:plain

Inkscapeで表示すると以下のような感じになります。図中の矢印は直線で構成されていますが、パスの中間点を削除すると滑らかな曲線になるので、ここは手動で中間点を削除します。他にもパスの端点を矢印の形に変更したりしています。

f:id:furandon_pig:20151012234535p:plain

あとはEPS形式で保存するだけなのですが、その前に画像サイズを調整します。[ファイル]-[ドキュメントのプロパティ(D)...]を選択し、「ページ」タブの中にある「ページサイズを描画全体または選択オブジェクトに合わせる(R)」ボタンをクリックします。

f:id:furandon_pig:20151012235420p:plain

ESPで保存した画像は、KeynotePowerPointなどに挿入して利用します。Keynoteの場合は、メニューから[挿入]-[選択...]でEPSファイルを挿入できます。

f:id:furandon_pig:20151012174725p:plain

まとめ

第4回 エンジニアのためのプレゼン技術研究会で発表した内容の補足説明をまとめてみました。プレゼン技術に関するノウハウはスライドの作り方・構成方法、発表時のハマり所等、以外と範囲が広いです。今後もプレゼン技術研究会でノウハウを集めてゆければと思います。

「第8回 コンテナ型仮想化の情報交換会@東京」の参加メモ

先日開催された第8回 コンテナ型仮想化の情報交換会@東京に参加してきました。 FreeBSD VPSに関するLT発表をさせていただいたのですが、他の方とLTの内容がかぶっているという事態が発生してしまいました...。とはいえ、同じくFreeBSD VPSについてLT発表された方からFreeBSD-10.1向けのVPSパッチを作成したよ!という有益な情報をいただけたので内心満足しています。

勉強会で聞いた内容を忘れないうちにメモしておこうと思います。公開されている発表スライドを参照しつつメモをまとめていますが、私の理解が間違っている可能性もありますのでご注意ください。

Linux Namespaces

発表者は@masami256さん。

LinuxのNamespacesに関する話で、調査対象のバージョンはkernel 4.1、glibc 2.21とのこと。

Namespaces概要

Linuxのコンテナで使われる技術はNamespaces,cgroup等の「プロセス・リソース管理」とbtrfs,overlayfs,aufs等の「ストレージバックエンド」の二つに大別される。Linux名前空間については@TenForwardさんの資料を読むのが確実。

Linux4.1でサポートしている名前空間として、System Vのプロセス間通信とPOSIXメッセージキューを扱う「IPC」、ネットワークデバイスやIPv4,6プロトコルスタック、ルーティングテーブルを扱う「Net」、マウントポイントを扱う「Mount」、プロセスIDを扱う「PID」、UID,GIDを扱う「User」、ホスト名を扱う「UTS」がある。これらの名前空間は昔からあまり変わっていない。

名前空間の機能として「リソースの管理」が挙げられる。ここでのリソースはメモリやCPU等のリソースとは異なる類のもので、IPCやネットワーク、ホスト名等を管理する仕組みのこと。この仕組みを入れ替えることで名前空間の分離が実現できる。名前空間を分離する時の挙動は、元の名前空間の複製(例:Mount名前空間)と、完全に新規の名前空間の作成(例:Net名前空間)という二種類がある。 ユーザランド上での名前空間は/proc//ns/[namespace name]のような形でファイルとして見える。

Napespacesの登場人物

Namespacesの登場人物として、UTS,Net等の「名前空間」、カーネル内で名前空間を管理する構造体である「NSProxy」、個々の名前空間の「参照カウンタ」(実装上はNSProxy構造体のメンバ変数)がある。

基本的に子プロセスは親プロセスと同じ名前空間に所属するが、プロセスの親子関係と名前空間の関係は別。これは単に名前空間のデフォルト値をどうするかという話。また、親プロセスの名前空間からの独立は(例外はあるものの)いつでも可能。

システムコールによる名前空間の操作

システムコールを通じて名前空間に対する「親プロセスの名前空間の共有」、「親プロセスの名前空間から分離」、「別プロセスへの名前空間移動」の操作が行える。

システムコールはclone(2),setns(2),unshare(2)がある。setns(2)は純粋に名前空間を操作するだけ。他の二つは名前空間「も」操作できる。

clone(2)はfork(2)の仲間で、子プロセスを作成する。fork(2)との違いは、フラグを色々設定して細かい制御が可能な点。スレッドを作る場合にも利用される(カーネル内の実装では、do_fork()がfork/cloneの共通処理になっている)。clone(2),unshare(2)で使用するフラグは、CLONE_NEWXXXというマクロ定数。

プロセス起動時から新しい名前空間で動かしたい場合はclone(2)を使う。CLONE_NEWXXXフラグで指定しなかった名前空間は親プロセスと共有される。

unshare(2)を使う場合は、PID名前空間は分離できないという制限がある。名前空間を完全に新しくしたい場合はclone(2)を使う必要がある。

setns(2)は所属したい名前空間のfdを使って、その名前空間に移動する。別のPID名前空間に所属させることはできるが、そのプロセス自身のPID名前空間は変わらないという制限がある。名前空間をまっさらにしたい場合は、unshare(2)の場合と同じくclone(2)する必要がある。

基本的に各名前空間はデータ的に独立しているが、User Namespaceは例外。User名前空間以外の構造体は、User Namespaceへのポインタを持っている。これは名前空間を使用する際にケーパビリティの有無をチェックするために使用される。というわけで、User名前空間だけは他の名前空間と独立ではない。

Hadoopのコンテナのはなし

発表者は@oza_x86さん。

データの処理基盤におけるコンテナのスケジューリングに関する話。

有象無象の生データをDBに入れる際の前処理をETL(Extract/Transform/Load)と呼ぶ。大量の生データを処理する際のプログラミングモデルとしてMapreduceがある(実装としてHadoopがある)。これはMap関数、Reduce関数を記述することで、対故障性を持った処理が行えるというもので、これらの関数はMapスロット、Reduceスロットという形でスケジューラが割り当てを決める。

Hadoopの計算プラットフォームとしてYARN(Yet Another Resource Negotiator)がある。読み方は「ヤーン」。

YARNのスケジューラには、入ってきたジョブから順にスケジューリングする「FIFO Scheduler」(一番簡単なスケジューラ)、ユーザ数で割った分でスケジューリングする「Fair Scheduler」、各人に割り当てられたキャパシティの中でスケジューリングする「Capacity Scheduler」がある。

コンテナのスケジューリングを考える際、異なるサイズのメモリや異なるプロセッサ数のCPUリソースが混在する場合に何をもって"Fairness"とするべきか。

cgroupsの場合はCPU上限を決定してしまうため、計算機資源を使い切れない。その代わり他のタスクを邪魔しない利点がある。これはSLA(Service Level Agreement)が必要な場合に有用かもしれない。 Unixプロセスベースの場合はCPU上限がないので計算機資源は使い切りやすいが、CPUを使いまくる場合は他のジョブの進行を阻害する危険がある。

"Fairness"を実現するため、Apache Mesosに出てくる概念である、Dominant Resource Fairness(DRF)を利用する。

DataCenter OSレベルのisolationになってくると、コンテナ技術単体だけでなくクラスタ単位のスケジューリング技術が必要であり、これにはFacebookによるBistroというスケジューラが提案されている。

SmartOS入門

発表者は@nslopeさん。

コンテナ用OSであるSmartOSに関する話。

SmartOSはJoyentが開発しているillumosベースのクラウド用OS。コンテナ、仮想マシンに特化している。SmartOSで使用できる仮想環境は、Naticve Zone(通常のコンテナ)、LX Baranded Zone(Linuxコンテナ)、KVMの3種類。SmartOSではデフォルトでdtrace機能が利用できる、ただし、カーネルの中の情報を取得するのには制限がある。

【個人的な感想】メモをとり忘れていたので発表の動画が公開されたら改めてメモにまとめてみます...。

Docker Swarm入門

発表者は@zembutsuさん。

Dockerはクライアント・サーバ型で、dockerデーモンとコマンドラインツールから構成される。Docker EngineはDockerの中核となるプログラムでコンテナの制御を行い、他のツールと連携してオーケストレーション機能を実現している。

Dockerとコンテナのオーケストレーションはなぜ必要か?これはクラウドやType1仮想化で複数のホストを管理するのと同じことをコンテナでも実現したいため。Type1仮想化における、VM+ゲストOS相当の箇所がDocker Clusterの管理レイヤにあたる。加えて、クラウド・Type1仮想化の両方をシームレスに管理するのはもう少し先の話かもしれない。

【個人的な感想】クラウド・Type1仮想化の両方をシームレスに管理するユースケースがうまく思いつかない。素人考えで思いつくのはType1仮想化の上でゲストOSを動かしたけど、IOまわりのオーバーヘッドが予想よりも大きかったのでコンテナで動かしたい、というような場合なのかなと思います。

(Dockerだけに限る話ではないが)「インフラの抽象化」は開発者の視点では開発・テスト・リリースの各プロセスにおいて一貫したインフラ環境を利用できる、という開発効率とアプリケーションのポータビリティが良いという利点がある。 ただし、Docker自体が何かするのではなく、あくまでも利用者の利便性向上のツール(またはプラットホーム)という点がポイント。

Docker動作ホスト環境を自動作成するツールにDocker Machineがある。自動でTLSを有効にしたDocker動作環境を構築し、仮想サーバの起動とDockerデーモンのプロビジョニングを行う。ツールの位置付けとしては、boot2dockerの置き換え。また、ソースコードでDockerの環境を管理できるDocker Composeがある。複数のコンテナを定義可能でイメージとしてはDockerfileを複数のコンテナ向けに拡張したもの。構成情報はYAML形式のファイルで指定する。

Docker Swarm(ドッカースウォーム)はDockerクラスタの管理ツール複数OS上のDocker環境を一つのリソースプールとして扱える。コマンドラインからdocker-machineでDockerホスト環境を作成後、docker run swarm createでコンテナクラスタを作成する。Docker Machineとの合わせ技でDockerクラスタを管理する。

Docker Swarmの概念として、Dockerデーモンの代わりにコマンドを受け付ける「マネージャ」(というプログラム)、ノード等のリソース情報をマネージャへの登録を行う「ディスカバリ」、コンテナの自動配置方針を決定する「ストラテジ」、コンテナ配置条件を指定する「フィルタ」がある。

スケジューリングのストラテジには、コンテナ稼働数でランク付けした値を用いる「Spred」(デフォルトのストラテジ)、コンテナをノードに集約する「Binpack」、ランダムに配置する「Random」がある。

Docker SwarmはDockerのAPIと互換性があり、docker ps,run等のコマンドをDocker Swarmで作成したクラスタ全体に対して適用可能。クラスタ群へのAPI実行はマネージャが行う。

フィルタはどのDockerホスト上でコンテナを起動するかを指定する機能。指定したフィルタ条件はストラテジよりも優先される。フィルタの種類として、Constraint,Affinity,Port,Dependency,Healthがある。

MINCS - Container in the shell script

発表者は@mhiramatさん。

コンテナの実装にはDocker以外にもLXC,Runc,OpenVZ等の様々な実装がある。Dockerは多くの機能を提供しているが、すこし規模が大きく、個々の機能を試しにくい。そこでUnix哲学の"Keep It Simple, Stupid"という思想に倣い、MINCS(Minimum Container Shell-scripts)というコマンド群をシェルスクリプトで実装されたとのこと。MINCSはPOSIX shell script(bashスクリプトではない)で作成されているためポータビリティがあり、busybox shell,dash等でも動作可能。

最小限の実行環境分離を実現するため、名前空間の利用、デバイスファイルのバインド、Chroot/pivot_rootによるrootfsの変更、CapabilitiesとCPUSETを利用する。ファイルシステムのレイヤリングにおいては、Linux-3.18以降でOverlayfsが利用可能であり、これを利用してコンテナイメージの管理も試してみたとのこと。

MINCSはフロントエンドとバックエンドの構成でコマンドが用意されている。フロントエンドコマンドは基本的にパラメタのパースのみを行い、パラメタを環境変数に変換してバックエンドを呼び出す役割になっている。

フロントエンドのコマンドにはminc,marten,plecatがある。mincは指定したコマンドをコンテナ内部で動かすもので、chrootやdocker runコマンドのようなもの。デフォルトでは名前空間分離とoverleyfsによる作業空間分離を行い、ネットワークはそのまま見える。martenはマルチレイヤのコンテナイメージの管理する。polecatは自己実行形式のシングルバイナリコンテナアプリを生成する。バックエンドのコマンドにはminc-exec,minc-coat,minc-farm,minc-trapperがある。

mincコマンドはパラメタのパース後、バックエンドコマンドのminc-execを実行する。minc-execの中では以下の処理が行われる。

  • netnsとcpumaskの設定
    • MINC_NETNSで指定された名前のnetnsをip netnsで作る。これは終了時にtrapコマンドで削除される。併せてtasksetコマンドで実行するCPUを指定する
  • 新しい名前空間への移行
  • PIDの保存とutsの設定
    • 後でコンテナ外からPIDを知るために保存しておく
  • コンテナ用rootfsのセットアップ
  • デバイスファイルのバインド
  • 不要なマウントポイントの削除
    • 不要なマウントポイントを残しておくとchroot後にも見えてしまう。umountできないものがあるのでpivot_rootdで処理を行う
  • 新しいrootfsへの移行とcapabilitiesの設定
    • capshコマンドでLinuxケーパビリティを変更する。単にchrootだけを実行した場合、ケーバビリティは変更されない

FreeBSD Jail/VIMAGEの始め方

発表者は@BsdHackerさん。

jailとはchroot(8)の発展系でroot directoryを変更する機能。プロセスの実行環境を分離できる。 Linux emulator機能を利用することでjailでlinux環境を作成できる(ただし現状32bit環境のみ)。

VIMAGEとはFreeBSD-9.0-RELEASEから利用可能なjail毎に異なるネットワークスタックを作成可能にする拡張。ただし、カーネルの再構築が必要。まだ対応できていないネットワークドライバ/スタックもあるが、Jail内にネットワークインタフェースを自由に配置できる。

LXD入門

発表者は@ten_forwardさん。

lxdはGo言語で書かれた、REST APIを提供するコンテナ管理デーモン。lxdと通信し、コンテナを操作するコマンドラインクライアントがlxc。また、nova-compute-lxdというOpenStack Novaプラグインもある。

lxdの特徴として、セキュアであること(デフォルトでは非特権コンテナ)、イメージベースであることとシンプルなAPIコマンドラインの提供、ライブマイグレーションがある。

Ubuntu 15.04ならlxdのインストールは簡単(パッケージが用意されている)。しかし、lxdは頻繁に更新されるため、ubuntu-lxc/lxd-stableリポジトリをadd-apt-repositoryしておいてupdateするとよい。もちろんソースからビルドするのもあり。

コンテナイメージのインポートは、lxc image importコマンドで行う。イメージサーバを登録するとリモートイメージからコンテナを直接起動できる。リモートサーバのコンテナはデフォルトだとUnix domain socket経由での接続のみであるため、必要に応じてリモート接続設定を行う必要がある。

ライブマイグレーションはlxc moveコマンドをリモート間で実行する(ただし残念なことに現時点のバージョン0.18(2015年9月現在)ではライブマイグレーションは行えない)。ローカルで実行すると単にコンテナのリネームになる。ライブマイグレーション機能は内部的にCRIUを利用している。lxdはデフォルトで非特権コンテナであり、CRIUは非特権コンテナに対応していないため、ライブマイグレーションを行う際には特権コンテナにする必要がある。

LT

メインセッションのメモをまとめた段階で力尽きてしまいました。今はこれが精一杯...。

まとめ

第8回 コンテナ型仮想化の情報交換会@東京の参加メモをまとめてみました。後から自分で見返しやすいようにメモを小さくまとめたつもりなのですが、思っていたよりも長いメモになってしまいました...。

それでもコンテナ型仮想化に関する現状とノウハウについて把握できてきたので、次回のコンテナ型仮想化の情報交換会を楽しみにしつつ、メモを見返すようにしようと思います。

【C88】トークイベント「TPPの著作権条項を考える ~非親告罪化、保護期間延長、そして法定賠償金~」の参加メモ

コミックマーケット88の1日目に行われたトークイベント、TPPの著作権条項を考える ~非親告罪化、保護期間延長、そして法定賠償金~に参加してきました。

TPPの著作権条項により、コミケ等の創作活動に影響が出るのでは、という話は以前から聞いていたのですが、具体的に何がどう問題なのか、加えて、現状どういう議論になっているのかが分からない状態のままになっていました。ちょうど良い機会なので、このトークイベントで把握できた内容をメモにまとめておこうと思います。

自分で分かるようにメモをまとめただけなので、議論の順番が前後していたり、(自分の理解が足りず)内容が間違っていたりする可能性もありますのでご注意ください。

また、トークイベントの内容はニコニコ生放送タイムシフト再生で観ることができるようです。

このトークイベントについて

トークイベントのタイトルにもある通り、TPPの著作権条項として議論されている以下の3項目について、パネラーの方々を交えて意見をやりとりするトークセッションでした。

これらの項目について、TPPの現状と、どう問題意識を持っているか、どう考えるべきか、そして今後どう変わって行くかについてパネルディスカッションが繰り広げられました。

モデレーターは香月啓佑さんで、パネラーは以下の方々。

トークイベントはモデレーター、パネラーの方々の自己紹介を兼ねた一言コメントから始まり、TPPの現状、今後どう変わってゆくか、どう考えているかの話から始まりました。

  • パロディや権利者のお目こぼしによるファン活動が今までのように続けられなくなるのではないか、同人活動を萎縮させずに続けるにはどうするか、という点について問題意識を持っている。
  • TPPが妥結したら、次は国内法の整備という段階になる。その段階になると、TPP条文の拡大解釈を防止して表現の自由を守れるよう、国内の偉い人を説得する必要が出てくる。

TPPの著作権条項の各項目に関するディスカッション

次に、TPPの著作権条項で挙げられている各項目についてのディスカッションに入って行きました。

著作権の保護期間の延長について

  • 現状の日本国内における著作権の保護期間は、著者者の生前50年+死後50年の計100年間
  • 日本では2006年に著作権の保護期間の延長に関する議論があったが、2009年に保護期間の延長は一旦見送る形になっている
    • 著作権の保護期間延長について、日本はもともと延長反対の立場を採っている

著作権非親告罪化について

  • TPPのメニューに著作権侵害については「非親告罪化せよ」という項目がある
    • TPPは条約なので、法律よりも優先される
  • 非親告罪の対象になるのは、「故意に商業的規模で経済的利益を得るような」場合
    • そういう観点では、同人誌の頒布は対象になりそう
    • ただし、著作権非親告罪化については「著作権者の利用能力に影響を与える場合」に限って非親告罪を適用するという「セーフガード」を日本は要求している
    • 国内法でどういうセーフガードにするか、という議論は別途必要

法定賠償金制度について

  • TPP議論内容のリーク文書によると、法定賠償金制度については反対もなく取り込まれている模様
  • 賠償金の算出については、被害額を充分に保証できるくらいの額で計算されそう
  • 賠償金が高額になる傾向があるため、米国では「コピーライト・トロール」が問題になっている

同人活動からTPPの著作権条項に関連するあれこれを考える

トークイベントはコミックマーケット内のイベントとして開催されたこともあり、同人活動から見たTPPの著作権条項に関連するあれこれのディスカッションが行われました。

コスプレは著作権侵害に該当する?

コスプレで逮捕された事例は今までには無い。衣装の販売で逮捕された人はいるけれど、これは別の話。

コスプレが私的複製の範囲内かと問われると、大勢に見てもらうような場合のコスプレは私的複製の範囲から外れるかも。コスプレの元になった作品には著作権があり、これを衣服にした場合、著作権侵害なのかどうかの判断は「オリジナリティがあるかどうか」がポイントになる。

【個人的な感想】オリジナリティの有無が要点になるのは理解できますが、「何を持ってオリジナリティがあるか」と判断できる、あるいは主張できるかが個人的に理解しきれていないです。布団にくるまって「ぷよぷよのコスプレ」とした場合は、オリジナリティがあるのだろうか(キラリと光るセンスはありそうだけど...)。

中川さんがコスプレを見て回った際の感想としては、著作権侵害にならないものが多い、という印象を受けたとのこと。

二次創作について

現状と著作権非親告罪化について

現状の同人活動を考えると、グレーゾーンな部分がある。ただし、著作権者が二次創作についてOKを出しているような場合は、検察が起訴するようなことは無いはずで、その観点では非親告罪化についてはあまり心配する必要はなさそう(著作権者がOK出してるワケですから)。

ただ、怖いのは第三者による通報のような「嫌がらせ」を目的とした行為が行われる可能性があるという点。行政はクレームに弱い傾向があるらしく、数百件くらいのクレームで折れてしまう(イベントを中止したり等)ことがあり、こういった嫌がらせ目的の通報等に対抗できるような明文化は必要かもしれない。

明文化といっても、著作権的にセーフかアウトか、という話に留めるべきで、同人誌そのものがセーフ、アウトか、という議論は混ぜるべきではない。

【個人的な感想】言われてみれば当たり前なのですが、著作権的な話と同人誌の是非に関する話は分けて考えるべし、というのは盲点でした。議論の際はこの分けて考える認識がないと、話がかみ合わないことがあるかも。

著作権者が訴えた例としては、任天堂コナミのケースがある(ポケモンときメモの話?)。とはいえ、赤松さんの弁によると、著作権者が訴えるのは考えにくいとのこと。そもそも描いてくれたら嬉しいので、ファンを攻撃するようなこと(訴える等)はしないよね、という話。とはいえ、「著作権を相続した遺族の方から訴えられるかも」という意見もでていました。

【個人的な感想】赤松さんの「描いてくれたらうれしいもん!」という熱い一言はグッとくるものがありました。著作権を相続した遺族が訴えるかも、という話はたしかに懸念事項ですね。著作権著作者人格権(譲渡できない)と財産的な権利(譲渡できる)に分けられるはずで、遺族が著作権を相続する場合は「財産的な権利」のみになるのかなと思います(ここらへんちゃんと理解できてない...)。そうすると先にあげた「『著作権者の利用能力に影響を与える場合』に限って著作権非親告罪を適用」というケースに当てはまりそうな心配がありますね。

現状での著作線侵害のセーフ、アウトの線引きはどこ?

中川さんがコミケの島めぐりをして、どの辺りが危ないかについての解説。一見してアウト、セーフが判別できるものがあり、判断基準は「著作権者の絵をそのまま使っているか」。「絵が似ているか」で、例えば、TVのキャプチャをそのまま使っているのはマズいとのこと。絵が似ているかについては、似ていなかったらそもそも著作権侵害が成り立たないよね、という話。つまるところ絵のウマい・ヘタ論になってしまうも、著作権侵害だ!と言われた場合に、その判断(絵が似ている似ていない)を警察等の機関が正しく行えるかは疑問が残る。

【個人的な感想】確かに、絵が似ている、似ていないを第三者が判断する状況下で正しく判断してもらえるかは心配の種として残りますね。例えば、リゼ(ご注文はうさぎですか?)と胡桃(がっこうぐらし!)は同じキャラでしょ、と言われるとどう説明したものやら...。

二次創作小説について

二次創作小説については、マンガと比べて著作権侵害と判断されにくい。作品のキャラクターやアイディアを借りてくるだけであれば著作権侵害ではない、しかし、作品内のセリフをそのまま使うとアウト。「引用」の範囲であると判断できそうだけれど、先の例にあった本の表紙にTVのキャプチャを使うのはマズい。

ガイドラインもの」について

初音ミク艦これ等に見られる二次利用許諾の形態、いわゆる「ガイドラインもの」については、(当然ではあるが)ガイドラインに従って同人作品を作るのはなんら問題無い。このガイドラインものに加え、著作者への確認の制度をルール化するのが良い方法かもしれない。

【個人的な感想】ガイドラインによる二次利用許諾という方法は賛成ですね。すでに同人マークで二次創作と同人誌の配布の許諾意思を原作者が明示的に示すという流れがあります。ただ、原作者が二次創作OKとしていても、出版社が許可しない、という状況もありえるのだろうか?という疑問が新たに湧いてきてしまいました。このあたりは、「原作者、出版社、許諾形態」の形で星取り表があったりすると分かりやすいのかなと思います。

我々はどうするべきなのか

TPPで著作権の扱いが変わることによる、「(創作活動の)萎縮への対策」と「第三者の悪意ある通報への対策」を考えないといけないという話。TPP著作権条項にまつわる問題点については、政府側でも問題は共有されつつあるとのこと。

【個人的な感想】国会中継とかはあまり見たりしない(逆に国会中継を欠かさず観る!という人はいるのだろうか...)のですが、このメモをまとめるにあたり、国会議事録検索システムの存在を知りました。定期的に議事録内を「著作権 TPP」といったキーワードで検索することで、議論の状況を追跡できそうです。

この話をしている途中で、山田太郎さんから飛び入りでの補足があり、TPPの非親告罪化については大丈夫だと思う、政府が本当に規制したいのはエログロやゲームの暴力表現である、という補足説明があった。

【個人的な感想】SFCストIIで2コンのスタートボタンを押したのか!と思うくらいの飛び入りっぷりでした。

我々はどうすべきなのかについては、TPPの著作権条項により、即座にコミケ潰れるようなことはないが、安心しすぎるのも良くない。また、ひとつのジャンルに限らず見て行くのが大事という話がされていました。加えて、若い人も政治に関心をもった方がいいよね、という(良い最終回だった的な)話の締めくくりになっていました。

【個人的な感想】個人的に政治に関心をという話は大仰かなと思うところもありますが、著作権まわりの現状とこれからについては、同人活動に関わるなら少なくとも自分の意見を持てるくらいじゃないと足元が危ういという気がしています。そのためには現状の理解が必要で、こういったトークイベントはとても勉強になります。

まとめ

このトークイベントにより、TPPの著作権条項の議論のポイント、現状についてある程度把握できました。これを踏まえて今後の議論や法律面から著作権の話、創作界隈にどう影響してくるのかを掘り下げて理解して行ければ良いなと思います。

参考URL

「ポケットミクちゃんに何かいろいろさせる会(その8)」開催レポート

「ポケットミクちゃんに何かいろいろさせる会(その8)」開催レポート

ポケットミクちゃんに何かいろいろさせる会(その8)を開催しました。定期的に開催しているにも関わらず、ぜんぜん開催レポートをまとめていませんでしたが、得られた知見をまとめておかないとすぐ忘れてしまうので今回から開催レポートを書いてみようと思います。

リモート開催してみた

ポケミク会は都内のコワーキングスペースで開催しているのですが、都内は距離的に参加がキビしいという声もあり、今回からリモート接続しつつポケミク会、というのを試してみました。

えいやでSkypeを使ってみたところ、リモートから接続はできるのですが、以下のようなハマり所がありました。

  • リモートから同時に接続できるのは1人だけ
    • 後から接続した人の電話を取ると、既に接続していた人は保留状態になってしまう
  • Webカメラの性能が良すぎる(HD画質など)と、ネットワークの帯域を圧迫してしまう
    • 画質の低いWebカメラの方が良さそうです...。

特に同時接続1人だけという制限はキツいため、次回以降はGoogleハングアウトで試す予定です。こちらは同時8人での通話が可能とのことです(次回に向けのての準備はそのあたりの確認からですね)。

ポケミクで歌わせる際のTips

今回のポケミク会では初参加の方がいて、ポケミクを演奏する際の疑問がいくつか出ていました。すっかり忘れていた部分もあったので、備忘録を兼ねてメモしてみます。音を伸ばしたりする歌わせ方の時のノウハウです。

「ばーい」と歌わせたい場合

  • 歌詞は「ばい」と入力する
  • リボンを「ばー」の所で引っ張る
    • リボンの上側を通してから次の音に入れる
    • リボンの上側が滑らかに音が変化するので自然(?)に聞こえる
      • ただし、音が離れすぎているような場合はもう少し工夫が必要そう

「とぉーく」と歌わせたい場合

  • 「ぉ」を入力しなくてもOK
  • 「とぉー」でスタイラスをタッチ、リボンの上を次の音まで滑らせて「く」で(ダブルクリックっぽく)軽くタップする

Firefox + WebMIDI API

Web MIDI APIのサポート状況が厚いこともあり、ポケミクアプリはGoogle Chromeのみでの動作となっています。他のブラウザでのWeb MIDI APIのサポート(実装)状況はどうなんでしょうね?という話があり、ざっとググってみたところ、FirefoxでWeb MIDI APIを実装しようぜ!的な話はあるみたいです。が、「でもまあ、優先度は低いよね」というちょっと残念な流れのようです。

MIDI規格周りの話

MIDI規格周りの話題も出ていて、なんでもBluetoothを利用してMIDIデータをやり取りする規格があるとのことです。ちょっとまだよく分かってないので、とりあえずはググって得られたリンク集を貼っておきます...。

まとめ

ポケットミクちゃんに何かいろいろさせる会(その8)を開催してみました。開催の記録を残しておかないと、せっかくの知見が頭から揮発してしまうので、次回以降はせっせとメモを取るようにしようと思います。

Linuxカーネルもくもく会#6に参加してきました

Linuxカーネルもくもく会#6に参加してきました。この会は各自Linuxソースコードの気になる箇所を読み進めたり、Linuxに関する何かをもくもくと進める会です。

cgroup周りのソースコードを読み進めてみているので、読んでみた内容のメモをまとめてみます。linux-3.17-rc3のソースコードを対象にしています。

start_kernel()からcgroup_init_early()を読んでみる

まずはLinuxカーネルのエントリポイントであるinit/main.c:start_kernel()から読み進めてみます。

init/main.c:
 500 asmlinkage __visible void __init start_kernel(void)
 501 {
 ...
 519         cgroup_init_early();
 ...

cgroup_*()な関数群で最初に呼ばれているのはkernel/cgroup.c:cgroup_init_early()です。関数コメントによると、OS起動時にcgroupの初期化とearly initが必要なサブシステムの初期化を行うようです。

kernel/cgroup.c:
4895 /**
4896  * cgroup_init_early - cgroup initialization at system boot
4897  *
4898  * Initialize cgroups at system boot, and initialize any
4899  * subsystems that request early init.
4900  */
4901 int __init cgroup_init_early(void)
4902 {
4903         static struct cgroup_sb_opts __initdata opts;
4904         struct cgroup_subsys *ss;
4905         int i;
4906
4907         init_cgroup_root(&cgrp_dfl_root, &opts);
4908         cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;
4909
4910         RCU_INIT_POINTER(init_task.cgroups, &init_css_set);
4911
4912         for_each_subsys(ss, i) {
4913                 WARN(!ss->css_alloc || !ss->css_free || ss->name || ss->id,
4914                      "invalid cgroup_subsys %d:%s css_alloc=%p css_free=%p name:id=%d:%s\n",
4915                      i, cgroup_subsys_name[i], ss->css_alloc, ss->css_free,
4916                      ss->id, ss->name);
4917                 WARN(strlen(cgroup_subsys_name[i]) > MAX_CGROUP_TYPE_NAMELEN,
4918                      "cgroup_subsys_name %s too long\n", cgroup_subsys_name[i]);
4919
4920                 ss->id = i;
4921                 ss->name = cgroup_subsys_name[i];
4922
4923                 if (ss->early_init)
4924                         cgroup_init_subsys(ss, true);
4925         }
4926         return 0;
4927 }

引数cgrp_dfl_rootはグローバル変数で同じソースファイル内で宣言されています。

kernel/cgroup.c:
 139 /*
 140  * The default hierarchy, reserved for the subsystems that are otherwise
 141  * unattached - it never has more than a single cgroup, and all tasks are
 142  * part of that cgroup.
 143  */
 144 struct cgroup_root cgrp_dfl_root;

変数cgrp_dfl_rootのデータ型である、struct cgroup_rootを見てみます。

include/linux/cgroup.h:
264 /*
265  * A cgroup_root represents the root of a cgroup hierarchy, and may be
266  * associated with a kernfs_root to form an active hierarchy.  This is
267  * internal to cgroup core.  Don't access directly from controllers.
268  */
269 struct cgroup_root {
270         struct kernfs_root *kf_root;
271
272         /* The bitmask of subsystems attached to this hierarchy */
273         unsigned int subsys_mask;
274
275         /* Unique id for this hierarchy. */
276         int hierarchy_id;
277
278         /* The root cgroup.  Root is destroyed on its release. */
279         struct cgroup cgrp;
280
281         /* Number of cgroups in the hierarchy, used only for /proc/cgroups */
282         atomic_t nr_cgrps;
283
284         /* A list running through the active hierarchies */
285         struct list_head root_list;
286
287         /* Hierarchy-specific flags */
288         unsigned int flags;
289
290         /* IDs for cgroups in this hierarchy */
291         struct idr cgroup_idr;
292
293         /* The path to use for release notifications. */
294         char release_agent_path[PATH_MAX];
295
296         /* The name for this hierarchy - may be empty */
297         char name[MAX_CGROUP_ROOT_NAMELEN];
298 };

kernel/cgroup.c:init_cgroup_root()は必要なデータ構造の初期化を行っているだけのようなので、ざっくりと眺める感じで次の処理を読み進めてみます。

kernel/cgroup.c:
1596 static void init_cgroup_root(struct cgroup_root *root,
1597                              struct cgroup_sb_opts *opts)
1598 {
1599         struct cgroup *cgrp = &root->cgrp;
1600
1601         INIT_LIST_HEAD(&root->root_list);
1602         atomic_set(&root->nr_cgrps, 1);
1603         cgrp->root = root;
1604         init_cgroup_housekeeping(cgrp);
1605         idr_init(&root->cgroup_idr);
1606
1607         root->flags = opts->flags;
1608         if (opts->release_agent)
1609                 strcpy(root->release_agent_path, opts->release_agent);
1610         if (opts->name)
1611                 strcpy(root->name, opts->name);
1612         if (opts->cpuset_clone_children)
1613                 set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags);
1614 }

init_cgroup_root()の直後で何やらフラグを設定しています。

kernel/cgroup.c:
4901 int __init cgroup_init_early(void)
4902 {
 ...
4907         init_cgroup_root(&cgrp_dfl_root, &opts);
4908         cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;

CSS_NO_REFはinclude/linux/cgroup.hでenum定義されており、このCSS(Cgroup Subsys State)には参照カウンタが無いことを示しているみたいです(「参照カウンタを持たない」という意味かも)。

include/linux/cgroup.h:
 96 /* bits in struct cgroup_subsys_state flags field */
 97 enum {
 98         CSS_NO_REF      = (1 << 0), /* no reference counting for this css */
 99         CSS_ONLINE      = (1 << 1), /* between ->css_online() and ->css_offline() */
100         CSS_RELEASED    = (1 << 2), /* refcnt reached zero, released */
101 };

cgroup_init_early()を読み進めて行きます。関数の後半にはfor_each_subsys()のループがあり、何やらsubsys的なものを順次処理しているようです。

kernel/cgroup.c:
4901 int __init cgroup_init_early(void)
4902 {
 ...
4912         for_each_subsys(ss, i) {
 ...
4920                 ss->id = i;
4921                 ss->name = cgroup_subsys_name[i];
4922
4923                 if (ss->early_init)
4924                         cgroup_init_subsys(ss, true);
4925         }
 ...

for_each_subsus()はfor文に展開されるマクロだと予想できるのですが、実際どうなっているか見てみます。

kernel/cgroup.c:
 376 /**
 377  * for_each_subsys - iterate all enabled cgroup subsystems
 378  * @ss: the iteration cursor
 379  * @ssid: the index of @ss, CGROUP_SUBSYS_COUNT after reaching the end
 380  */
 381 #define for_each_subsys(ss, ssid)                                       \
 382         for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT &&                \
 383              (((ss) = cgroup_subsys[ssid]) || true); (ssid)++)

上記で参照しているCGROUP_SUBSYS_COUNT、cgroup_subsys_idはenum値なので、最後にCGROUP_SUBSYS_COUNTが付いています。

 43 /* define the enumeration of all cgroup subsystems */
 44 #define SUBSYS(_x) _x ## _cgrp_id,
 45 enum cgroup_subsys_id {
 46 #include <linux/cgroup_subsys.h>
 47         CGROUP_SUBSYS_COUNT,
 48 };
 49 #undef SUBSYS

cgroup_init_early()の4925行目のcgroup_init_subsys()、こちらはcgroup_subsys_name[]で配列になっています。

kernel/cgroup.c:
 125 /* generate an array of cgroup subsystem pointers */
 126 #define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys,
 127 static struct cgroup_subsys *cgroup_subsys[] = {
 128 #include <linux/cgroup_subsys.h>
 129 };
 130 #undef SUBSYS
 131
 132 /* array of cgroup subsystem names */
 133 #define SUBSYS(_x) [_x ## _cgrp_id] = #_x,
 134 static const char *cgroup_subsys_name[] = {
 135 #include <linux/cgroup_subsys.h>
 136 };
 137 #undef SUBSYS

linux/cgroup_subsys.hの中身を見てみます。

linux/cgroup_subsys.h:
  1 /*
  2  * List of cgroup subsystems.
  3  *
  4  * DO NOT ADD ANY SUBSYSTEM WITHOUT EXPLICIT ACKS FROM CGROUP MAINTAINERS.
  5  */
  6 #if IS_ENABLED(CONFIG_CPUSETS)
  7 SUBSYS(cpuset)
  8 #endif
  9
 10 #if IS_ENABLED(CONFIG_CGROUP_SCHED)
 11 SUBSYS(cpu)
 12 #endif
 ...

kernel/cgroup.c:SUBSYS()マクロで値が列挙されています。簡単なサンプルコードで展開結果を確認してみます。

$ cat tmp.c 
#define SUBSYS(_x) [_x ## _cgrp_id] = #_x,
SUBSYS(cpu)
$ gcc -E tmp.c 
...
[cpu_cgrp_id] = "cpu",

微妙にややこしいのですが、上記のcpu_cgrp_idはlinux/cgroup_sybsys.h(こっちはヘッダファイル)でenum宣言された値です。

linux/cgroup_subsys.hをテンプレートのように利用し、*cgroup_sybsys[]とenum cgroup_syubsys_idを再定義したSUBSYS()マクロで作るという方法になっています。cgroupサブシステムの列挙を一つのファイルで完結させたいという気持ちは分かるのですが、知らずに読むと混乱してしまいます...。

気を取り直してcgroup_init_early()の続きを読み進めてみます。

kernel/cgroup.c:
4901 int __init cgroup_init_early(void)
4902 {
 ...
4912         for_each_subsys(ss, i) {
 ...
4920                 ss->id = i;
4921                 ss->name = cgroup_subsys_name[i];
4922
4923                 if (ss->early_init)
4924                         cgroup_init_subsys(ss, true);
4925         }
4926         return 0;
4927 }

再度for_each_subsys()、変数ssに入るのはcgroup_sybsys[]ですね。

 381 #define for_each_subsys(ss, ssid)                                       \
 382         for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT &&                \
 383              (((ss) = cgroup_subsys[ssid]) || true); (ssid)++)

cgroup_subsys[]の中身を見てみます。サブシステムごとに関数ポインタを渡せるみたいですが、この段階ではidとnameの設定とearly_initが真の場合にcgroup_init_subsys()を呼ぶだけですね。

include/cgroup/cgroup.h:
623 struct cgroup_subsys {
624         struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css);
625         int (*css_online)(struct cgroup_subsys_state *css);
626         void (*css_offline)(struct cgroup_subsys_state *css);
627         void (*css_free)(struct cgroup_subsys_state *css);
628         void (*css_reset)(struct cgroup_subsys_state *css);
629
630         int (*can_attach)(struct cgroup_subsys_state *css,
631                           struct cgroup_taskset *tset);
632         void (*cancel_attach)(struct cgroup_subsys_state *css,
633                               struct cgroup_taskset *tset);
634         void (*attach)(struct cgroup_subsys_state *css,
635                        struct cgroup_taskset *tset);
636         void (*fork)(struct task_struct *task);
637         void (*exit)(struct cgroup_subsys_state *css,
638                      struct cgroup_subsys_state *old_css,
639                      struct task_struct *task);
640         void (*bind)(struct cgroup_subsys_state *root_css);
641
642         int disabled;
643         int early_init;
 ...
660         /* the following two fields are initialized automtically during boot */
661         int id;
662 #define MAX_CGROUP_TYPE_NAMELEN 32
663         const char *name;

続けてcgroup_init_subsys()も読み進めようと思ったのですが、このあたりでLinuxもくもく会の終了時刻に。続きは次回ですね。

kernel/cgroup.c:
4845 static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)
4846 {
4847         struct cgroup_subsys_state *css;
4848
4849         printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name);
...

まとめ

Linuxカーネルもくもく会に参加し、cgroupソースコードを読み進めてみました。次回のもくもく会でもcgroup周りを読んでみようと思います。