SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|

2008-07-03(Thu) Fedora9、SSDチューン

  エラく間があいてしまったが、それはエラくハマっていたせいである。今日は、その辺を含め、安物のコンパクトフラッシュでもそれなりに快適なSSD環境を構築すべく、試行錯誤した結果を報告する。

  まず、問題を明らかにすると、MLCタイプのフラッシュメモリは……

  「書き込みが超絶対的に遅い」

  ……というのがネックである。特にチョットずつアチコチに書き込む、という処理が苦手で、これは避けがたい事実である。一方でSLCタイプのフラッシュは、ほとんどHDDと遜色ない性能を発揮するので、連続書き込みのアドバンテージが際だつことから、あらゆる面でHDDを圧倒、という印象になる。

  しかし、安さにつられて、その辺の事情を知らずに買っちまったんだから仕方ない。そこで……

  「ソフトウェアでどうにかする」

  ……ことにするのであった。そんなもん、どうにもならんだろッ!! ……と、思うだろうが、さにあらず。Linuxならカーネル自体をチューニングすることで、かなり体感速度を緩和できるハズなのだ。

  近年のOSには、もれなくディスクへの書き出しを遅延する機能が備わっている。例えば、文書を保存、とかやって、保存完了、と画面に出ても、その時点ではディスクには書き込まれていないのだ。OSは、書き込まなければならない内容をメモリ上に覚えておいて、ワザと、しばらく放置しておくのであった。

  この動作は、保存した直後に、誤字に気づいて修正し、再保存されるような状況に効果を発揮する。修正後の内容を一度だけ書き込みば済むからだ。また、ファイルが削除される場合にも有効だ。書き込み自体をやめてしまうことができる。

  なお、Windowsはどうかしらんが、Linuxでは「覚えてある、書き込まなければならない内容」を5秒ごとにチェックして、書き込み要求から30秒以上経過していたら、実際に書き込む、が基本動作である。

  また、書き込みが連続したため「覚えてある、書き込まなければならない内容」が増えすぎたり、もっと別の用途にメモリを使いたくなったりすると、その場で書き込みが行われたりもする、例外的な処理もある。

  さらに、まだこの先に、効率よく書き込むために、場所の近いヤツをまとめて書き込もうとしてしばらく待つ、なんて機能のもあるので、一概には言えないが、ここで問題が明るみに出るワケだ。

  つまり「ドカンと書き込むと、30秒後に、実際にドカンと書き込まれる」ということだ。書き込み能力の低いメディアにドカンと書くと、長時間、書き込み以外の仕事ができなくなってしまい、結果として、読みたい人が待たされてしまうことになる。書くのと違って、読めないと処理が先に進まない。これが、レスポンスの低下として表面化する。

  しかし、ここに工夫の余地がある。「ドカンと書き込んでも、実際はジョロジョロと書き込む」ようにすればいいのである。そうすれば、常に読み込みができる余地を残せるわけだ。

  ここで、スルドいLinuxマスターは「そんなコトできたっけ?」と思うだろうが、普通はそんなコトはできない。しかし、オープンソースなら、改造してしまえばいいのだ。つまりは、オリジナルカーネルをビルドしてしまうのである。

  具体的には「mm/page-writeback.c」の改造である。dirty(ディスクに未書き出し)ページをゆっくりwriteback(書き出し)するようにする。一度にwritebackするページ数はMAX_WRITEBACK_PAGESという定数で1024に設定されているが、これをprocファイルシステムから可変化してしまう。

  なお「5秒ごとにチェック、30秒以上経過」も「dirty増えすぎ」もpdflushというカーネルデーモンが処理するが、前者はkupdateモード、後者はbdflushモードと呼ばれる。

  kupdateモードは、dirtyページの存在時間(dirty_expire_centisecs:デフォルト30秒)を超えたことを契機にwritebackする。

  一方で、bdflushモードは、dirtyページのメモリに占める割合が一定(dirty_background_ratio:デフォルト5%)を超えた状態を契機にwritebackを開始する。

  それ以外にも、dirtyページのメモリに占める割合が一定(dirty_ratio:デフォルト10%)を超えた状態を契機にwritebackを開始するものもある。この処理はbalance_dirty_pages関数で行われるので、以後、この動作をbalanceモードと呼ぶ。

  各々、MAX_WRITEBACK_PAGESを最大処理ページ数としてwritebackするが、dirtyページが多い場合は即座にwritebackが繰り返される。書き込みが混雑している(congestion)と判断された場合、繰り返しごとに(HZ/10:0.1秒)ずつ、ウェイトが入るものの、これだけではジョロジョロとした書き込みにはならず、やはりドカンという感じになってしまう。

  以上を踏まえ、最初から備わっているパラメータは以下のとおり。

dirty_writeback_centisecs(500): kupdate, bdflushモードの起動周期(1/100sec)
dirty_expire_centisecs(3000): dirtyページの存在時間(1/100sec)(kupdateの動作契機)
dirty_background_ratio(5): bdflushモードの動作契機(%)
dirty_ratio(10): balanceモードの動作契機(%)

  ここからは、オイラのパッチを当ててコンパイルした場合に使えるもの。

kupdate_max_writeback_pages(1024): kupdateモードの最大処理数(Page)(MAX_WRITEBACK_PAGESの可変化)
bdflush_max_writeback_pages(1024): bdflushモードの最大処理数(Page)(MAX_WRITEBACK_PAGESの可変化)
 
kupdate_writeback_once(0): kupdateモードの連続writeback抑制(Boolean)
bdflush_writeback_once(0): bdflushモードの連続writeback抑制(Boolean)
 
kupdate_congestion_wait(100): kupdateモードの書き込み混雑時のウェイト(ms)
bdflush_congestion_wait(100): bdflushモードの書き込み混雑時のウェイト(ms)
balance_congestion_wait(100): balanceモードの書き込み混雑時のウェイト(ms)

  以下は、動作モニタリング用。

kupdate_call_count: kupdateモードの起動回数
bdflush_call_count: bdflushモードの起動回数
balance_call_count: balanceモードの起動回数
 
kupdate_writeback_count: kupdateモードのwriteback処理回数
bdflush_writeback_count: bdflushモードのwriteback処理回数
balance_writeback_count: balanceモードのwriteback処理回数

  この機能を追加するのが以下のパッチ。一応、kernel-2.6.25.6-55.fc9.src.rpm用。linux-2.6-add-writeback-throttle.patchというパッチ名で保存しておこう。

diff -U 3 -r linux-2.6.25.i386.org/drivers/mmc/core/mmc.c linux-2.6.25.i386/drivers/mmc/core/mmc.c
--- linux-2.6.25.i386.org/drivers/mmc/core/mmc.c	2008-04-17 11:49:44.000000000 +0900
+++ linux-2.6.25.i386/drivers/mmc/core/mmc.c	2008-07-03 12:31:22.000000000 +0900
@@ -509,6 +509,7 @@
 	mmc_claim_host(host);
 	if (!mmc_host_is_spi(host))
 		mmc_deselect_cards(host);
+	printk(KERN_INFO "mmc_core: deselected mmc card for suspend.\n");
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
 }
@@ -527,7 +528,9 @@
 	BUG_ON(!host->card);
 
 	mmc_claim_host(host);
+/*	msleep(1000);	*/
 	err = mmc_init_card(host, host->ocr, host->card);
+	printk(KERN_INFO "mmc_core: reinitialised mmc card for resume. state=%d.\n", err);
 	mmc_release_host(host);
 
 	if (err) {
diff -U 3 -r linux-2.6.25.i386.org/drivers/mmc/core/sd.c linux-2.6.25.i386/drivers/mmc/core/sd.c
--- linux-2.6.25.i386.org/drivers/mmc/core/sd.c	2008-04-17 11:49:44.000000000 +0900
+++ linux-2.6.25.i386/drivers/mmc/core/sd.c	2008-07-03 12:31:08.000000000 +0900
@@ -571,6 +571,7 @@
 	mmc_claim_host(host);
 	if (!mmc_host_is_spi(host))
 		mmc_deselect_cards(host);
+	printk(KERN_INFO "mmc_core: deselected sd card for suspend.\n");
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
 }
@@ -589,7 +590,9 @@
 	BUG_ON(!host->card);
 
 	mmc_claim_host(host);
+/*	msleep(1000);	*/
 	err = mmc_sd_init_card(host, host->ocr, host->card);
+	printk(KERN_INFO "mmc_core: reinitialised sd card for resume. state=%d.\n", err);
 	mmc_release_host(host);
 
 	if (err) {
diff -U 3 -r linux-2.6.25.i386.org/drivers/mmc/host/sdhci.c linux-2.6.25.i386/drivers/mmc/host/sdhci.c
--- linux-2.6.25.i386.org/drivers/mmc/host/sdhci.c	2008-04-17 11:49:44.000000000 +0900
+++ linux-2.6.25.i386/drivers/mmc/host/sdhci.c	2008-07-03 07:46:09.000000000 +0900
@@ -1184,7 +1184,7 @@
 	if (!chip)
 		return 0;
 
-	DBG("Suspending...\n");
+	printk(KERN_INFO DRIVER_NAME ": Suspending...\n");
 
 	for (i = 0;i < chip->num_slots;i++) {
 		if (!chip->hosts[i])
@@ -1221,7 +1221,7 @@
 	if (!chip)
 		return 0;
 
-	DBG("Resuming...\n");
+	printk(KERN_INFO DRIVER_NAME ": Resuming...\n");
 
 	pci_set_power_state(pdev, PCI_D0);
 	pci_restore_state(pdev);
diff -U 3 -r linux-2.6.25.i386.org/include/linux/sysctl.h linux-2.6.25.i386/include/linux/sysctl.h
--- linux-2.6.25.i386.org/include/linux/sysctl.h	2008-04-17 11:49:44.000000000 +0900
+++ linux-2.6.25.i386/include/linux/sysctl.h	2008-07-03 08:02:36.000000000 +0900
@@ -205,6 +205,25 @@
 	VM_PANIC_ON_OOM=33,	/* panic at out-of-memory */
 	VM_VDSO_ENABLED=34,	/* map VDSO into new processes? */
 	VM_MIN_SLAB=35,		 /* Percent pages ignored by zone reclaim */
+
+	VM_BDFLUSH_MAX_WRITEBACK_PAGES=36,
+	VM_KUPDATE_MAX_WRITEBACK_PAGES=37,
+
+	VM_BALANCE_CALL_COUNT=38,
+	VM_BDFLUSH_CALL_COUNT=39,
+	VM_KUPDATE_CALL_COUNT=40,
+
+	VM_BALANCE_WRITEBACK_COUNT=41,
+	VM_BDFLUSH_WRITEBACK_COUNT=42,
+	VM_KUPDATE_WRITEBACK_COUNT=43,
+
+	VM_BALANCE_CONGESTION_WAIT=44,
+	VM_BDFLUSH_CONGESTION_WAIT=45,
+	VM_KUPDATE_CONGESTION_WAIT=46,
+
+	VM_BDFLUSH_WRITEBACK_ONCE=47,
+	VM_KUPDATE_WRITEBACK_ONCE=48,
+	VM_KUPDATE_SYNC_SUPERS=49,
 };
 
 
diff -U 3 -r linux-2.6.25.i386.org/include/linux/writeback.h linux-2.6.25.i386/include/linux/writeback.h
--- linux-2.6.25.i386.org/include/linux/writeback.h	2008-04-17 11:49:44.000000000 +0900
+++ linux-2.6.25.i386/include/linux/writeback.h	2008-07-03 08:02:29.000000000 +0900
@@ -105,6 +105,25 @@
 extern int block_dump;
 extern int laptop_mode;
 
+extern int bdflush_max_writeback_pages;
+extern int kupdate_max_writeback_pages;
+
+extern int balance_call_count;
+extern int bdflush_call_count;
+extern int kupdate_call_count;
+
+extern int balance_writeback_count;
+extern int bdflush_writeback_count;
+extern int kupdate_writeback_count;
+
+extern int balance_congestion_wait;
+extern int bdflush_congestion_wait;
+extern int kupdate_congestion_wait;
+
+extern int bdflush_writeback_once;
+extern int kupdate_writeback_once;
+extern int kupdate_sync_supers;
+
 extern int dirty_ratio_handler(struct ctl_table *table, int write,
 		struct file *filp, void __user *buffer, size_t *lenp,
 		loff_t *ppos);
diff -U 3 -r linux-2.6.25.i386.org/kernel/sysctl.c linux-2.6.25.i386/kernel/sysctl.c
--- linux-2.6.25.i386.org/kernel/sysctl.c	2008-07-02 15:58:31.000000000 +0900
+++ linux-2.6.25.i386/kernel/sysctl.c	2008-07-03 08:06:14.000000000 +0900
@@ -951,6 +951,118 @@
 		.proc_handler	= &proc_dointvec_userhz_jiffies,
 	},
 	{
+		.ctl_name	= VM_BDFLUSH_MAX_WRITEBACK_PAGES,
+		.procname	= "bdflush_max_writeback_pages",
+		.data		= &bdflush_max_writeback_pages,
+		.maxlen		= sizeof(bdflush_max_writeback_pages),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_KUPDATE_MAX_WRITEBACK_PAGES,
+		.procname	= "kupdate_max_writeback_pages",
+		.data		= &kupdate_max_writeback_pages,
+		.maxlen		= sizeof(kupdate_max_writeback_pages),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_BALANCE_CALL_COUNT,
+		.procname	= "balance_call_count",
+		.data		= &balance_call_count,
+		.maxlen		= sizeof(balance_call_count),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_BDFLUSH_CALL_COUNT,
+		.procname	= "bdflush_call_count",
+		.data		= &bdflush_call_count,
+		.maxlen		= sizeof(bdflush_call_count),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_KUPDATE_CALL_COUNT,
+		.procname	= "kupdate_call_count",
+		.data		= &kupdate_call_count,
+		.maxlen		= sizeof(kupdate_call_count),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_BALANCE_WRITEBACK_COUNT,
+		.procname	= "balance_writeback_count",
+		.data		= &balance_writeback_count,
+		.maxlen		= sizeof(balance_writeback_count),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_BDFLUSH_WRITEBACK_COUNT,
+		.procname	= "bdflush_writeback_count",
+		.data		= &bdflush_writeback_count,
+		.maxlen		= sizeof(bdflush_writeback_count),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_KUPDATE_WRITEBACK_COUNT,
+		.procname	= "kupdate_writeback_count",
+		.data		= &kupdate_writeback_count,
+		.maxlen		= sizeof(kupdate_writeback_count),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_BALANCE_CONGESTION_WAIT,
+		.procname	= "balance_congestion_wait",
+		.data		= &balance_congestion_wait,
+		.maxlen		= sizeof(balance_congestion_wait),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_BDFLUSH_CONGESTION_WAIT,
+		.procname	= "bdflush_congestion_wait",
+		.data		= &bdflush_congestion_wait,
+		.maxlen		= sizeof(bdflush_congestion_wait),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_KUPDATE_CONGESTION_WAIT,
+		.procname	= "kupdate_congestion_wait",
+		.data		= &kupdate_congestion_wait,
+		.maxlen		= sizeof(kupdate_congestion_wait),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_BDFLUSH_WRITEBACK_ONCE,
+		.procname	= "bdflush_writeback_once",
+		.data		= &bdflush_writeback_once,
+		.maxlen		= sizeof(bdflush_writeback_once),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_KUPDATE_WRITEBACK_ONCE,
+		.procname	= "kupdate_writeback_once",
+		.data		= &kupdate_writeback_once,
+		.maxlen		= sizeof(kupdate_writeback_once),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
+		.ctl_name	= VM_KUPDATE_SYNC_SUPERS,
+		.procname	= "kupdate_sync_supers",
+		.data		= &kupdate_sync_supers,
+		.maxlen		= sizeof(kupdate_sync_supers),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+	{
 		.ctl_name	= VM_NR_PDFLUSH_THREADS,
 		.procname	= "nr_pdflush_threads",
 		.data		= &nr_pdflush_threads,
diff -U 3 -r linux-2.6.25.i386.org/kernel/sysctl_check.c linux-2.6.25.i386/kernel/sysctl_check.c
--- linux-2.6.25.i386.org/kernel/sysctl_check.c	2008-04-17 11:49:44.000000000 +0900
+++ linux-2.6.25.i386/kernel/sysctl_check.c	2008-07-03 08:07:24.000000000 +0900
@@ -136,6 +136,25 @@
 	{ VM_VDSO_ENABLED,		"vdso_enabled" },
 	{ VM_MIN_SLAB,			"min_slab_ratio" },
 
+	{ VM_BDFLUSH_MAX_WRITEBACK_PAGES,	"bdflush_max_writeback_pages" },
+	{ VM_KUPDATE_MAX_WRITEBACK_PAGES,	"kupdate_max_writeback_pages" },
+
+	{ VM_BALANCE_CALL_COUNT,	"balance_call_count" },
+	{ VM_BDFLUSH_CALL_COUNT,	"bdflush_call_count" },
+	{ VM_KUPDATE_CALL_COUNT,	"kupdate_call_count" },
+
+	{ VM_BALANCE_WRITEBACK_COUNT,	"balance_writeback_count" },
+	{ VM_BDFLUSH_WRITEBACK_COUNT,	"bdflush_writeback_count" },
+	{ VM_KUPDATE_WRITEBACK_COUNT,	"kupdate_writeback_count" },
+
+	{ VM_BALANCE_CONGESTION_WAIT,	"balance_congestion_wait" },
+	{ VM_BDFLUSH_CONGESTION_WAIT,	"bdflush_congestion_wait" },
+	{ VM_KUPDATE_CONGESTION_WAIT,	"kupdate_congestion_wait" },
+
+	{ VM_BDFLUSH_WRITEBACK_ONCE,	"bdflush_writeback_once" },
+	{ VM_KUPDATE_WRITEBACK_ONCE,	"kupdate_writeback_once" },
+	{ VM_KUPDATE_SYNC_SUPERS,	"kupdate_sync_supers" },
+
 	{}
 };
 
diff -U 3 -r linux-2.6.25.i386.org/mm/page-writeback.c linux-2.6.25.i386/mm/page-writeback.c
--- linux-2.6.25.i386.org/mm/page-writeback.c	2008-04-17 11:49:44.000000000 +0900
+++ linux-2.6.25.i386/mm/page-writeback.c	2008-07-03 08:18:33.000000000 +0900
@@ -44,6 +44,25 @@
  */
 #define MAX_WRITEBACK_PAGES	1024
 
+int bdflush_max_writeback_pages = 1024;
+int kupdate_max_writeback_pages = 1024;
+
+int balance_call_count = 0;
+int bdflush_call_count = 0;
+int kupdate_call_count = 0;
+
+int balance_writeback_count = 0;
+int bdflush_writeback_count = 0;
+int kupdate_writeback_count = 0;
+
+int balance_congestion_wait = HZ/10;
+int bdflush_congestion_wait = HZ/10;
+int kupdate_congestion_wait = HZ/10;
+
+int bdflush_writeback_once = 0;
+int kupdate_writeback_once = 0;
+int kupdate_sync_supers = 1;
+
 /*
  * After a CPU has dirtied this many pages, balance_dirty_pages_ratelimited
  * will look to see if it needs to force writeback or throttling.
@@ -366,6 +385,8 @@
 
 	struct backing_dev_info *bdi = mapping->backing_dev_info;
 
+	balance_call_count++;
+
 	for (;;) {
 		struct writeback_control wbc = {
 			.bdi		= bdi,
@@ -408,6 +429,7 @@
 		 */
 		if (bdi_nr_reclaimable) {
 			writeback_inodes(&wbc);
+			balance_writeback_count++;
 			pages_written += write_chunk - wbc.nr_to_write;
 			get_dirty_limits(&background_thresh, &dirty_thresh,
 				       &bdi_thresh, bdi);
@@ -436,7 +458,7 @@
 		if (pages_written >= write_chunk)
 			break;		/* We've done our duty */
 
-		congestion_wait(WRITE, HZ/10);
+		congestion_wait(WRITE, balance_congestion_wait);
 	}
 
 	if (bdi_nr_reclaimable + bdi_nr_writeback < bdi_thresh &&
@@ -558,6 +580,8 @@
 		.range_cyclic	= 1,
 	};
 
+	bdflush_call_count++;
+
 	for ( ; ; ) {
 		long background_thresh;
 		long dirty_thresh;
@@ -569,14 +593,17 @@
 			break;
 		wbc.more_io = 0;
 		wbc.encountered_congestion = 0;
-		wbc.nr_to_write = MAX_WRITEBACK_PAGES;
+		wbc.nr_to_write = bdflush_max_writeback_pages;
 		wbc.pages_skipped = 0;
 		writeback_inodes(&wbc);
-		min_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
+		bdflush_writeback_count++;
+		if (bdflush_writeback_once)
+			break;
+		min_pages -= bdflush_max_writeback_pages - wbc.nr_to_write;
 		if (wbc.nr_to_write > 0 || wbc.pages_skipped > 0) {
 			/* Wrote less than expected */
 			if (wbc.encountered_congestion || wbc.more_io)
-				congestion_wait(WRITE, HZ/10);
+				congestion_wait(WRITE, bdflush_congestion_wait);
 			else
 				break;
 		}
@@ -633,7 +660,10 @@
 		.range_cyclic	= 1,
 	};
 
-	sync_supers();
+	kupdate_call_count++;
+
+	if(kupdate_sync_supers)
+		sync_supers();
 
 	oldest_jif = jiffies - dirty_expire_interval;
 	start_jif = jiffies;
@@ -644,15 +674,18 @@
 	while (nr_to_write > 0) {
 		wbc.more_io = 0;
 		wbc.encountered_congestion = 0;
-		wbc.nr_to_write = MAX_WRITEBACK_PAGES;
+		wbc.nr_to_write = kupdate_max_writeback_pages;
 		writeback_inodes(&wbc);
+		kupdate_writeback_count++;
+		if (kupdate_writeback_once)
+			break;
 		if (wbc.nr_to_write > 0) {
 			if (wbc.encountered_congestion || wbc.more_io)
-				congestion_wait(WRITE, HZ/10);
+				congestion_wait(WRITE, kupdate_congestion_wait);
 			else
 				break;	/* All the old data is written */
 		}
-		nr_to_write -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
+		nr_to_write -= kupdate_max_writeback_pages - wbc.nr_to_write;
 	}
 	if (time_before(next_jif, jiffies + HZ))
 		next_jif = jiffies + HZ;

  カーネルのコンパイル手順は、以下の通り。

raven.itline.jp:/usr/src # rpm -ivh kernel-2.6.25.6-55.fc9.src.rpm 
raven.itline.jp:/usr/src # cp linux-2.6-add-writeback-throttle.patch redhat/SOURCES/

  パッチを当てる旨をSPECファイルに追記。一応、リビジョンを勝手にひとつ上げておく。55が引き算の結果だったとは、なかなか気づかなかった。

raven.itline.jp:/usr/src/redhat/SPECS # diff -c kernel.spec kernel.my56.spec 
*** kernel.spec	2008-06-10 21:54:24.000000000 +0900
--- kernel.my56.spec	2008-07-01 21:30:12.000000000 +0900
***************
*** 21,27 ****
  # works out to the offset from the rebase, so it doesn't get too ginormous.
  #
  %define fedora_cvs_origin 619
! %define fedora_build %(R="$Revision: 1.674 $"; R="${R%% \$}"; R="${R##: 1.}"; expr $R - %{fedora_cvs_origin})
  
  # base_sublevel is the kernel version we're starting with and patching
  # on top of -- for example, 2.6.22-rc7-git1 starts with a 2.6.21 base,
--- 21,27 ----
  # works out to the offset from the rebase, so it doesn't get too ginormous.
  #
  %define fedora_cvs_origin 619
! %define fedora_build %(R="$Revision: 1.675 $"; R="${R%% \$}"; R="${R##: 1.}"; expr $R - %{fedora_cvs_origin})
  
  # base_sublevel is the kernel version we're starting with and patching
  # on top of -- for example, 2.6.22-rc7-git1 starts with a 2.6.21 base,
***************
*** 689,694 ****
--- 689,696 ----
  # get rid of imacfb and make efifb work everywhere it was used
  Patch2600: linux-2.6-merge-efifb-imacfb.patch
  
+ Patch3000: linux-2.6-add-writeback-throttle.patch
+ 
  %endif
  
  BuildRoot: %{_tmppath}/kernel-%{KVERREL}-root
***************
*** 1254,1259 ****
--- 1256,1263 ----
  # get rid of imacfb and make efifb work everywhere it was used
  ApplyPatch linux-2.6-merge-efifb-imacfb.patch
  
+ ApplyPatch linux-2.6-add-writeback-throttle.patch
+ 
  # ---------- below all scheduled for 2.6.24 -----------------
  
  # END OF PATCH APPLICATIONS

  先の、サスペンドする度にSDカードのパーティションテーブルがブッ壊れる問題に対する修正も加える。

raven.itline.jp:/usr/src # vi redhat/SOURCES/config-generic
 
raven.itline.jp:/usr/src/redhat/SOURCES # diff -c config-generic.org config-generic
*** config-generic.org	2008-05-20 17:19:52.000000000 +0900
--- config-generic	2008-07-03 12:47:22.000000000 +0900
***************
*** 104,110 ****
  CONFIG_MMC_BLOCK_BOUNCE=y
  CONFIG_SDIO_UART=m
  # CONFIG_MMC_DEBUG is not set
! # CONFIG_MMC_UNSAFE_RESUME is not set
  CONFIG_MMC_BLOCK=m
  CONFIG_MMC_RICOH_MMC=m
  CONFIG_MMC_SDHCI=m
--- 104,110 ----
  CONFIG_MMC_BLOCK_BOUNCE=y
  CONFIG_SDIO_UART=m
  # CONFIG_MMC_DEBUG is not set
! CONFIG_MMC_UNSAFE_RESUME=y
  CONFIG_MMC_BLOCK=m
  CONFIG_MMC_RICOH_MMC=m
  CONFIG_MMC_SDHCI=m

  ちなみに、冒頭の「エラくハマっていた」のは、ここでスケベ心を出して、このconfig-genericの修正も、パッチファイルに含めてしまおうとした結果であった。パッチは「config-generic等を元に、最終的なconfigが生成された『後』で当たる」のである。つまり、どんなにガンバってもCONFIG_MMC_UNSAFE_RESUMEはセットされないのである。

  よって、config-genericは「直接に書き換える」こと。オイラはこれに気づくまで、何度もSDカードのパーティションテーブルをぶっ壊しては、復旧しては、mmcドライバにデバッグ行を追加しては、カーネルのコンパイルを繰り返しては……と、地獄を這い回りまくっていた。おかげで、ファイルシステムに詳しくなり、mmcドライバの処理の大半が頭に入ってしまったぞ。

  なにはともあれ、コンパイル。

raven.itline.jp:/usr/src # time rpmbuild -ba --target=i686 --with baseonly --without debuginfo /usr/src/redhat/SPECS/kernel.my56.spec

  カーネルのコンパイルには時間がかかるので、実際のチューニングは明日回し。では。


2008-07-14(Mon) Fedora9、ハーバードチューン

  「実際のチューニングは明日回し」などとして長々と引っ張りつつ、結局は、ぜんぜん違う結論に至っていたりする。

  まず、オイラが使っているSSDというか、コンパクトフラッシュの性能を考察する。Linuxを動かしている都合上、Windows用のベンチマークアプリであるCrystalDiskMarkの結果を引用するのはおかしいのだが、同アプリはフラッシュメディアの性能を測る上で事実上の標準となっており、Web上から結果が入手しやすいから仕方ない。

  オイラが使っているコンパクトフラッシュは、ビット単価がほぼ最安で、PC用SSDとしては……

  画像の説明

  「不動の地雷評価

  ……を築いている「A-DATA Speedy 16G」である。Web上にて、以下の結果を発見した。

Sequential Read : 14.871 MB/s
Sequential Write : 7.124 MB/s
Random Read 512KB : 14.885 MB/s
Random Write 512KB : 1.806 MB/s
Random Read 4KB : 7.859 MB/s
Random Write 4KB : 0.018 MB/s

  まずは、Read。このコンパクトフラッシュはUltra DMAモードをサポートしていない。最高でMultiword DMAのmode2までだ。このモードでの理論上の最高転送速度は16.6MB/s。よって、Readの15MB/s弱というのは、フラッシュの性能ではなく、帯域の性能による制限ということになる。

  しかしながら、Random Read 4KBの7.859 MB/sは、ハードディスクにとっては「ドギモを抜く」性能である。最新の高速HDD「VelociRaptor」でも1MB/sがやっとだからだ。動く部位(ヘッド)のないフラッシュならではのスコアといえよう。

  このスコアは、以下のように認識するのが適切である。

7.859MB/s * 1024 / 4KB = 2012times/s = 0.50ms

  秒間2000回シークでき、シークタイムは0.5msである、と。これは、平均シークタイム5ms程度が「物理的限界」であるハードディスクの、軽く10倍の性能である。最底辺のフラッシュでさえだ。恐ろしく高性能といえよう。

  確かに、Sequential Readの絶対値は速くはないものの、通常、アプリの立ち上げ等では、HDDがガリガリいうことからも、Random Readが大きくモノをいう。ベッタリと数百メガのムービー等を外部にコピーする場合などを除けば、Read面ではそこそこ十分な性能といえよう。

  次に、Write。同様に「回数」の見地から性能を測ってみる。

0.018MB/s * 1024 /   4KB = 4.608times/s
1.806MB/s * 1024 / 512KB = 3.612times/s

  4KBの書き込みと512KBの書き込み「回数」には大差がない。量的には100倍以上の差があるのに、である。そこで、もっとドカンと書き込んでいるSequential Writeの結果から、どこまでが「1回」なのかを求めてみる。

7.124MB/s * 1024 / 3.612times/s = 2019.65KB/s

  露骨な結果が出た。2048KB。つまり、2MBまでが1回であろう。逆に言うと、一度に2MBを書き込むのがもっとも効率がよく、4KBずつ書き込むと「秒間5ページを書き込むことができない」ということである。たったの5ページ。こいつぁキッツい制限といえよう。

  ちょっと脱線するが、チマタでは8GBのコンパクトフラッシュに比較し、16GBのものが性能が低い、という評価が出回っているが、これは構造上、書き込み単位が倍になっているためと思われる。書き込み単位が倍になれば、Random Writeの書き込み速度が半分になるのも道理であろう。

  なんにせよ、OSの動作がもっさりとしてしまう要因はココにあるワケだ。通常、処理を行う際には何にをするにもReadが伴う。デバイスがWriteにかかりっきりになっていれば、Readは遅れ、それは処理の遅延、要するにもっさりに直結するのである。

  前回も書いたが、Linuxを含む近年のOSではデバイスに対して書き込みを遅延する。いまこの手にデータがなければ一歩も処理を先に進めることができないReadと違い、Writeなんてのは余剰なメモリに溜め込んでおいてヒマな時にやればいいという思想である。

  そこで私は、前回「遅延分をジョロジョロと書き込む」改造と、ReadをWriteに優先させまくる性格を持つI/Oスケジューラであるdeadlineを組み合わせ、デバイスがWriteにかかりっきりになる状態を防ぐべく、チューニングを繰り返したのである……んが、結論から言うと、どうにもできなかった。

  というのも、Read面ではそこそこ十分な性能といえる15MB/sだが、秒間2回の書き込みがあると、理論上の性能は半分に、秒間4回の書き込みがあると、理論上はゼロに、もっというと、秒間8回の書き込みがあると、2秒間の遅延が発生してしまう。

  上述のとおり、デバイスへの要求は「回数」の観点で制限し、帯域を保護すべきだが、オイラが改造を加えたpage-writeback.cのレイヤでは「ページ(4KB)」単位での制御しかできない。だからといって、最低ラインである秒間4回に配慮して、kupdate_max_writeback_pagesに2、dirty_writeback_centisecsに100を指定して毎秒書き込みを指定すると、100MBの書き込みを処理し終えるのに、2時間弱もかかってしまう。

  しかも、この場合、この間ずっとReadの性能は半分になるばかりか、連続書き込みが抑制されてしまうので、フラッシュの弱点である書き込み回数が爆発的に増加してしまう。まったくの逆チューンだ。

  このヘンが判明したあたりで、いーかげん、レスポンスの悪さに嫌気が差してきたので、8GBでx300なコンパクトフラッシュを買い「お金で解決」してしまおうかとも思ったのだが、それはそれで負けを認めるようで悔しい……そこで、新たに別の手段として、ReadデバイスとWriteデバイスを分割するという手を思いついた。

  問題は、チョロチョロとしたWriteでさえ、デバイスのRead帯域を大幅に圧迫してしまうため、あらゆるページインが遅延してしまうという問題である。だが、それならば、Writeしがちな領域を別のデバイスに割り振ってしまい、結果的にWriteをゼロにしてしまえばいいのではないか?

  ちょっと大げさではあるが、これはハーバードアーキテクチャ化といえるだろう。事実「命令アクセスとデータアクセスを分離することで高速化できる」というのは、ハーバードアーキテクチャの利点のひとつであり、それはまさに今回の狙いそのものである。

  具体的には、別途装備してある16GBのSDカードに/varと/tmpを移動してしまう。既にSDカードは/home領域に使っているが、その用途であればOS自体の挙動に伴ってReadが発生する可能性は低い。

  早速、シングルユーザモードに落ちて、/varと/tmpをSDカード側に移動する。/varと/tmpを押さえつけるのが目的ではないので、mountのbind機能を使って元の位置にぶら下げる。/etc/fstabには、以下のようなエントリを加えた。

/mnt/mmc/home   /home   none    bind    0       0
/mnt/mmc/tmp    /tmp    none    bind    0       0
/mnt/mmc/var    /var    none    bind    0       0

  さて、結果はというと……

  「いままでのもっさり感はなんだったんだッ!?」

  ……というくらいに、飛躍的に体感レスポンスが向上した。これは悔しまぎれでも、大げさでもない。事実、速いコンパクトフラッシュを買う必要性を感じなくなってしまった。これはスゴい。

  ただ、せっかくなので、基本的な書き出しチューンは施しておく。正直、上述の「読み書き分離」が、あまりに功を奏したので、気持ち程度であり、値は適当だ。もしかすると、デチューンしているかもしれない。

  まず、ditry(ディスクに未書き出し)ページの溜め込み割合を引き上げ、定期的な書き出しチェック頻度を下げつつ、書き出し遅延を拡大する。

# vi /etc/sysctl.conf
vm.dirty_ratio = 80
vm.dirty_background_ratio = 50
vm.dirty_writeback_centisecs = 1500
vm.dirty_expire_centisecs = 9000
 
# sysctl -p

  さらに、I/Oスケジューラの溜め込み数も引き上げる。これによって、連続した書き込み要求を、コマンド一発で済ませる可能性が上がるはず。

# echo 8192 > /sys/block/mmcblk0/queue/nr_requests

  I/Oスケジューラのタイプをdeadlineに変更する。deadlineスケジューラは、Write要求を遅延し、Read要求を優先する性格のスケジューラである。deadlineスケジューラのパラメータであるfifo_batchは、連続する要求をキューに押し込む限界数、writes_starvedは、Write要求を差し置いてRead要求を優先する限界数で、これらは増加した。その他のread_expire, write_expire, front_mergesはデフォルトのままとした。

# echo deadline > /sys/block/mmcblk0/queue/scheduler
# echo 128 > /sys/block/mmcblk0/queue/iosched/fifo_batch
# echo 8 > /sys/block/mmcblk0/queue/iosched/writes_starved

  /sysには、/procにおけるsysctl.confのような設定機構はなさそうだ。再起動時に再設定されるように、/etc/rc.localに以下を書き加えておく。

echo 8192 > /sys/block/mmcblk0/queue/nr_requests
echo deadline > /sys/block/mmcblk0/queue/scheduler
echo 128 > /sys/block/mmcblk0/queue/iosched/fifo_batch
echo 8 > /sys/block/mmcblk0/queue/iosched/writes_starved

  なお、起動デバイスである/dev/sdaはデフォルトのままとした。もはや、このデバイスは、ほぼ読み込み専用になっており、巧みなスケジューリングは必要としないはず。

  念のため、cfq, anticipatory, deadline, noopで起動時間を計測してみたが、ログイン画面が出るまでの時間は、どれも63±1秒。回転部を持たないフラッシュメモリにとって、Readには一切の小細工は必要なさそうである。

  以上、地雷なコンパクトフラッシュでもRandom Readは、ハードディスクの10倍。ほぼ読み出し専用に使えば、書き換え寿命に対する不安も払拭である。まさに「地雷とハサミは使いよう」といえよう。

  いやぁ、回り道したものの、ひさびさに有意義な試行であった。ではまた。


2008-07-23(Wed) Fedora9、iiimecfチューン

  つーわけで、emacsである。オイラのemacsとの付き合いは電脳倶楽部から始まった……と、一見意味不明ながら、これでピンときてしまう、オイラと同じ「その筋」な人は決して少なくないに違いないと思うので、あえて説明は略す。

  言うまでもなく、emacsは至高の環境である。んが、ガンガンと日本語を打つには、チューンが必須である。そもそも、ATOKを導入しておきながら、emacsからロクに使えないのは罪である。

  そりゃ、別にemacs上でフツーにATOKを起動したって日本語は打てる。んが、その場合、2ストロークキーを使う際、イチイチATOKをオフにする必要が生じ、実質、使い物にならない。キーボードの上に何層にもバラ撒いてある大量の機能を体で使いこなしてこそemacsを使う価値がある。決してマウスなんかでメニューを触わってはイカんのである。

  ここは、emacsに備わっているIMインタフェイスに、直接ATOKを接続しなくてはならない。そして、そのためのiiimecfである。

  iiimecfに関しては、以前にTERATERM環境から快適に使えるよう、いろいろチューンした経緯がある。今回はそれを下敷きにしつつ、もう少し向こう側まで、細かいチューンを進める。

  まずは、iiimecfをダウンロード。前回は0.73を導入し、その環境は現在も現役であるが、今回は、いくつかの不具合が修正されている0.75を利用させてもらう。まずは展開。

# tar xvfz IIIMECF-0.75.tar.gz
# cd iiimecf

  先のチューンの成果を導入する。

diff -c lisp.org/iiimcf.el lisp/iiimcf.el
*** lisp.org/iiimcf.el	2007-12-09 08:54:54.000000000 +0900
--- lisp/iiimcf.el	2008-06-05 12:54:41.000000000 +0900
***************
*** 284,289 ****
--- 284,305 ----
  
  (defvar iiimcf-keycode-spec-alist
    `((13 10 0)
+ 
+ 	(11 37 65535)	; Ctrl + k
+ 	(12 39 65535)	; Ctrl + l
+ 	(14 40 65535)	; Ctrl + n
+ 
+ 	(21 117 65535)	; Ctrl + u
+ 	(9 118 65535)	; Ctrl + i
+ 	(15 119 65535)	; Ctrl + o
+ 	(16 120 65535)	; Ctrl + p
+ 
+ 	(2 38 65535)	; Ctrl + b
+ 	(6 32 65535)	; Ctrl + f
+ 	(7 27 65535)	; Ctrl + g
+ 
+ 	(1 36 65535)	; Ctrl + a
+ 
      (32 32)
      ,@(mapcar #'(lambda (x) (list x x 0))
  	      (iiimcf-numseq 1 31))

  前回よりちょっとだけ増えている気がするのは、気のせいではない。では、改めてバイトコンパイルする。

# emacs -q --no-site-file -batch -l iiimcf-comp.el

  emacs-22.2-4.fc9だと、多少、コンパイルエラーが出るが、私が使った限りでは、特に問題は出ていない。そのままインストール。

# mkdir /usr/share/emacs/site-lisp/iiimecf
# cp lisp/* /usr/share/emacs/site-lisp/iiimecf

  個人設定である「.emacs」に設定を加える。

;; .emacs
 
;;; uncomment this line to disable loading of "default.el" at startup
;; (setq inhibit-default-init t)
 
;; enable visual feedback on selections
;(setq transient-mark-mode t)
 
;; default to better frame titles
(setq frame-title-format
      (concat  "%b - emacs@" (system-name)))
 
;; default to unified diffs
(setq diff-switches "-u")
 
;; always end a file with a newline
;(setq require-final-newline 'query)
 
;;; uncomment for CJK utf-8 support for non-Asian users
;; (require 'un-define)
 
(global-set-key "\C-h" 'delete-backward-char)
(global-set-key "\C-z" 'scroll-down)
(global-set-key "\C-u" 'undo)
(global-set-key "\C-]" 'call-last-kbd-macro)
 
(menu-bar-mode nil)
(column-number-mode t)
(setq blink-matching-paren nil)
 
(setq default-tab-width 4)
(setq default-fill-column 64)
(setq text-mode-hook 'turn-on-auto-fill)
 
(setq make-backup-files nil)
(setq auto-save-default nil)
 
;; ATOK X3 for Linux
(setq iiimcf-server-control-hostlist '("unix:/tmp/.iiim-mitsu/:0.0"))
(require 'iiimcf-sc)
(setq iiimcf-server-control-default-language "ja")
(setq iiimcf-server-control-default-input-method "atokx3")
(setq default-input-method 'iiim-server-control)
(define-key global-map "\C-j" (lambda ()
    (interactive)
    (if current-input-method (inactivate-input-method))
    (toggle-input-method)))
(define-key global-map "\C-o" (lambda ()
    (interactive)
    (inactivate-input-method)))
(setq iiimcf-UI-input-method-title-format "<ATOK:%s>")
(setq iiimcf-UI-preedit-use-face-p "window-system")

  前回との差は、IMの起動キーを「Ctrl+J」でオン「Ctrl+O」でオフに変更したこと。結局「変換」で通常のATOKのオン/オフ、「Ctrl+変換」でemacsのATOKのオン/オフ、などとやってると、なまじ操作が近い分、混乱を招くだけであった。そこで、思い切って、オンとオフのキーを分離し、指の方を慣らすことにした。

  それと、TCPでなく、既存のunixドメインソケット経由でiiimdに接続するようにした。これで、別途iiimdを立ち上げる必要がなくなった。

  また、preedit-use-face-pにwindow-systemを与えた。これをすると、変換中の状態が色で表示されるようになり、断然見やすい。これはTERATERM経由でも有効だ。

  んが、イザ使い始めると問題発生。全角の記号である「■★…」等が半角扱いになって表示が乱れてしまう。表示上の問題だけであれば、何とか我慢できないこともないが、この文字を出そうとするとiiimecfがコケてしまうからキツい……■だぁって使いたいぃ〜、●もぉ出したいぃ〜、きらっ★ 流星にぃ〜♪……てな感じで打てなければ戦争が終わらない。

  どうも、emacs22からはMule-UCSというUTF8を扱うモジュールが不要になり、代わりに内蔵のutf-translate-cjkというモジュールを使うようになっており、それで起動が速くなった反面、微妙な動作の違いが問題として表面化しているということらしい。

  結論から言うと、以下の設定を.emacsに追加すればいいらしい。出典はココ

(utf-translate-cjk-set-unicode-range
   '((#x00a2 . #x00a3) ; 
     (#x00a7 . #x00a8) ; 
     (#x00ac . #x00ac) ; 
     (#x00b0 . #x00b1) ; 
     (#x00b4 . #x00b4) ; 
     (#x00b6 . #x00b6) ; 
     (#x00d7 . #x00d7) ; 
     (#X00f7 . #x00f7) ; 
     (#x0370 . #x03ff) ; Greek and Coptic
     (#x0400 . #x04FF) ; Cyrillic
     (#x2000 . #x206F) ; General Punctuation
     (#x2100 . #x214F) ; Letterlike Symbols
     (#x2190 . #x21FF) ; Arrows
     (#x2200 . #x22FF) ; Mathematical Operators
     (#x2300 . #x23FF) ; Miscellaneous Technical
     (#x2500 . #x257F) ; Box Drawing
     (#x25A0 . #x25FF) ; Geometric Shapes
     (#x2600 . #x26FF) ; Miscellaneous Symbols
     (#x2e80 . #xd7a3) (#xff00 . #xffef)))

  これでほとんどの記号が問題なく扱えるようになる。一部ダメだった記号があったような気もするが、忘れた。忘れる程度の記号ということなので問題ない。たぶん。

  と、記号の問題が片づいたところで、ラスボスの登場である。それは、IMがオンの状態だと、Shift+Spaceで半角スペースが打てないという問題。

  ブログ等、ブラウザでは基本的にプロポーショナルなフォントで文章を読むので大きな問題はないが、メール等では閲覧環境が等幅であることも少なくなく(我がmaveもそうだ)、その場合の読みやすさに配慮して、オイラは半角英単語の左右には半角スペースを入れることにしている。んが、IMをオフにしないと半角スペースが打てない環境だとすると、これはやってられないほど面倒である。個人的には大問題だ。

  前回はTERATERMとiiimecfの両方の改造によって、どうにかこの動作を実現したが、今回はどうしてもダメ。相当の時間、iiimecfのソースをイジっては、試行錯誤を繰り返したのだが、ニッチもサッチもどころか、ヨッチもゴッチも(以下略)いかないったらない。

  結局、またもやオープンソースの強みを発動してしまった。やや、究極の手段だが「作者の方に相談する」である。状況を説明し、どうにかヒントだけでも、とお願いしたところ、結局、ズバリの修正方法を教えてもらってしまった。方法は以下。

diff -c lisp.org/iiimcf.el lisp/iiimcf.el
*** lisp.org/iiimcf.el	2007-12-09 08:54:54.000000000 +0900
--- lisp/iiimcf.el	2008-06-05 12:54:41.000000000 +0900
***************
*** 565,570 ****
--- 581,587 ----
  	 (char (logior (lsh (car kchar) 16)
  		       (cdr kchar)))
  	 key)
+     (if (and (= 1 mod) (= char 32)) (setq kmod '(0 . 0)))
      (cond ((/= (car kcode) 0) nil)
  	  ((setq key (iiimcf-iiim-keycode-pre-translate
  		      (cdr kcode)))

  でもって、.emacsに以下を追加。

(global-set-key [?\S- ] 'iiimcf-server-control-keyforward)

  作者の方によると、実装方法としてあまり美しくないということで、本家の開発ツリーにはこのパッチは取り込まれない可能性があるそうだ。いうなれば「iiimecf先生のパッチが読めるのはSVX日記だけ!!」という状態といえよう。うっしっし。

  ただし、これにはまだ続きがある。言ってなかったが、オイラはemacsを-nw付きで常用している。つまり「別にGUIウィンドウを開く」のではなく「端末画面内にそのまま表示」させているのである。これで何か問題があるかというと、実はかなりの問題があって……

  Shiftキーが押されていることがemacsに伝わりゃしねぇ

  ……のであった。これでは、如何にiiimecf側で対処してもらっても、どうすることもできない。当初は、どうにかShiftの押下を伝える方法があるのではないか、と軽く考えていたのだが、どうやらどうにも無理らしい。

  こーなると、emacs -nwを使うのをアキらめるしかない。これに関して言えば、決して「GUIウィンドウで使う」にヤブサカでないのだが……

  なんじゃこのまるっちいフォントは!?

  ……やっぱ、ヤブサカっすわ。フォントが複数使えるエディタは「マルチフォントエディタ」だが、これじゃ「まるっちいフォントエディタ」である。そもそもオイラは、アンチエイリアスフォントが今ほど一般的でなかった頃からの、筋金入りのOsakaアンチエイリアンである。今さら、ビットマッピンガーには戻れないのだ。決して。

  emacsの「伝統を大事にした実装」は素晴らしいと思うが、反面、UTF8といい、フォントといい、取り込むタイミングがちょっと後手に回っている感じだよなぁ……

  ……などと、ボヤくヒマがあったら、情報収集だ。同じコトを考えているヤツは大抵いるハズなのだ……と、あったッ!! XftGnuEmacsだ。

  というわけで、次回はFedora9、××チューン最終回である。