Linux kernel 3.9 の新機能 SO_REUSEPORT を試してみる
Linux kernel 3.9 のマージウィンドウでは SO_REUSEPORT というソケットオプションがマージされました。
Merge branch 'soreuseport' · c617f39 · torvalds/linux · GitHub
これは同一ポートに複数のリスナー(listen ソケット)が bind できるようになるというもので、Webサーバなど単一のポートに多くのコネクションが来るようなワークロードで、複数プロセスでうまく負荷分散ができるようになるそうです。
(これまでだと一人がaccept()してそれぞれの worker に渡すというようなモデルがありましたが、これでは accept() する部分がボトルネックになってしまいがちでした。)
BSDでは元々 SO_REUSEPORT オプションはあったようですが、マルチキャスト通信で使うもののようです。Linux のこの実装は目的が違うので少し混乱を招きそうですが…
まずは面白そうな機能なので実際に試してみました。
準備
カーネルは最新のものをgitで取ってきました。
# uname -r 3.9.0-rc2+
お試しプログラム
お手軽に試すため Python で書きました。
10プロセスが同一ポートに bind して、誰が accept したかを表示するものです。
#!/usr/bin/env python import sys, socket, time from multiprocessing import Process PORT = 8000 NR_LISTENERS = 10 SO_REUSEPORT = 15 def listener_work(num): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, SO_REUSEPORT, 1) # set SO_REUSEPORT s.bind(("", PORT)) s.listen(5) while True: conn, addr = s.accept() print '%2d: accepted!' % num ret = conn.recv(16) conn.close() def server(): processes = [] for i in xrange(NR_LISTENERS): p = Process(target=listener_work, args=(i,)) p.start() processes.append(p) for p in processes: p.join() def client(): while True: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((sys.argv[1], PORT)) s.sendall("a" * 16) s.close() time.sleep(1) def main(): if '-s' in sys.argv: server() else: client() if __name__ == '__main__': main()
実行結果
クライアント側
# ./reuseport.py <サーバのIP>
サーバ側
# ./reuseport.py -s 0: accepted! 2: accepted! 3: accepted! 4: accepted! 0: accepted! 6: accepted! 1: accepted! 3: accepted! 9: accepted! 5: accepted! :
SO_REUSEPORT を使うことで、10プロセスが同一ポートに bind できました。
また、accept する人も少しムラはありますがそこそこうまく分散されているようです。
実際に効果があるかを見るにはもっと高負荷でないと分からないですが、
まずは動くことが確認できました。
最近は TCP のプロトコルを改良するようなパッチが色々とマージされていて面白いですね。
TCP Fast Open を試してみる
Linux カーネル 3.6 では TCP Fast Open (TFO) という機能がマージされたそうです。詳しくは以下の URL に記載がありますが、一度接続したクライアントは TCP の 3way-handshake を簡略化してコネクションをオープンできる(SYN にデータを載せられる)という機能のようです。
TCP Fast Open: expediting web services [LWN.net]
面白そうな機能なので実際に使ってみて、使い方をまとめてみました。
準備
カーネル
kernel 3.6 ではまだクライアント側の機能しかマージされていないので、さらに新しいカーネルを使います(3.7 でサーバ側の機能もマージされるようです)。今回は git で取ってきた最新 (12250d843e8489ee00b5b7726da855e51694e792) をビルドして使いました。
TFO を利用したソケットプログラム
TFO を使うにはサーバ側、クライアント側それぞれで配慮が必要です。
具体的には
- サーバ側はソケットオプション TCP_FASTOPEN (23) をセットする
- クライアント側は connect(2)/send(2) の代わりに MSG_FASTOPEN (0x20000000) フラグをつけた sendto(2) を使う
以下の様なテストプログラムを使いました。
クライアントは(TFO Cookies をもらうため)一度通常のコネクションで通信し、二回目に TFO を使って通信しています。
#!/usr/bin/env python import sys, socket PORT = 8000 TCP_FASTOPEN = 23 MSG_FASTOPEN = 0x20000000 def server(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_TCP, TCP_FASTOPEN, 5) # set TCP_FASTOPEN option s.bind(("", PORT)) s.listen(5) while True: conn, addr = s.accept() ret = conn.recv(1024) while len(ret) > 0: print ret ret = conn.recv(1024) conn.close() s.close() def client(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((sys.argv[1], PORT)) s.sendall("1. First connection.") s.close() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # sendto with MSG_FASTOPEN flag s.sendto("2. Sending data by using TCP fast open!!!", MSG_FASTOPEN, (sys.argv[1], PORT)) s.close() def main(): if '-s' in sys.argv: server() else: client() if __name__ == '__main__': main()
試してみる
今回はサーバ/クライアントを一台のループバックでやっています。
サーバ側
# ./TFO_test.py -s 1. First connection. 2. Sending data by using TCP fast open!!!
クライアント側
# ./TFO_test.py localhost
これだけ見てもよく分からないので tcpdump も見てみます。
(TFO を使ってコネクションを確立している部分のダンプです。)
ポイントは
- クライアントは SYN パケットに TFO Cookies とデータを載せて送信している
- SYN パケットにデータが載っているため、今回のテストプログラムでは 3way-handshake 終了後に即 FIN が送られてソケットがクローズされている(データを別で送らない分パケットのやり取りが通常のコネクションより一回少ない。)
IP localhost.42887 > localhost.irdmi: Flags [S], seq 914856703:914856744, win 43690, options [mss 65495,sackOK,TS val 182325 ecr 0,nop,wscale 6,Unknown Option 254f9896c02881aefd4e7c0], length 41 IP localhost.irdmi > localhost.42887: Flags [S.], seq 2336617123, ack 914856745, win 43690, options [mss 65495,sackOK,TS val 182325 ecr 182325,nop,wscale 6], length 0 IP localhost.42887 > localhost.irdmi: Flags [.], ack 1, win 683, options [nop,nop,TS val 182325 ecr 182325], length 0 IP localhost.42887 > localhost.irdmi: Flags [F.], seq 1, ack 1, win 683, options [nop,nop,TS val 182326 ecr 182325], length 0 IP localhost.irdmi > localhost.42887: Flags [F.], seq 1, ack 2, win 683, options [nop,nop,TS val 182326 ecr 182325], length 0 IP localhost.42887 > localhost.irdmi: Flags [.], ack 2, win 683, options [nop,nop,TS val 182326 ecr 182326], length 0
たぶんこれで TFO が使えていると思います。
netstat で見ると以下のカウンタも上がっていました。
# netstat -s | grep TCPFastOpen TCPFastOpenActive: 7 TCPFastOpenPassive: 9 TCPFastOpenCookieReqd: 2
これが Web で使われるようになれば応答性が良くなって幸せになれそうですね。
scapy でソケット通信
最近の高級な言語を使ったネットワークプログラミングではソケットすら意識しなくてもプログラムができてしまいますが、実際にどんな仕組みで通信されているかを知ることは重要です。
そこで scapy という任意のパケットを生成するソフトを使ってパケットを一つ一つ作成し、echo サーバとTCP で通信してみたいと思います。
scapy のインストール
scapy は Ubuntu ではパッケージになっているので apt で入りますが、CentOS6 ではパッケージになっていなかったので自分で取ってきて入れました。
# wget http://www.secdev.org/projects/scapy/files/scapy-latest.tar.gz # tar xvf scapy-latest.tar.gz # cd scapy-2.1.0 # ./setup.py install
scapy の簡単な使い方
scapy は python のモジュールですが、インストールすると専用コマンドも一緒にインストールされます。
# scapy Welcome to Scapy (2.1.0) >>>
試しに www.google.com 宛の ICMP Echo リクエスト (ping) のパケットを作ってみます。
>>> ping=IP(dst='www.google.com')/ICMP() >>> ping.show() ###[ IP ]### version= 4 ihl= None tos= 0x0 len= None id= 1 flags= frag= 0 ttl= 64 proto= icmp chksum= None src= 192.168.11.76 dst= Net('www.google.com') \options\ ###[ ICMP ]### type= echo-request code= 0 chksum= None id= 0x0 seq= 0x0
IP() や ICMP() でそれぞれのレイヤを作成でき、それを "/" で繋ぐとパケットになります。
パケットの中身は show() で見ることができます。
パラメータで指定しなかった chksum や ttl などの部分はうまく作ってくれるので非常に楽です。
それではこの ping パケットを実際に送信してみます。
>>> reply = sr1(ping) Begin emission: .......................Finished to send 1 packets. ...............................* Received 55 packets, got 1 answers, remaining 0 packets <IP version=4L ihl=5L tos=0x0 len=28 id=51106 flags= frag=0L ttl=44 proto=icmp chksum=0x2f8 src=64.233.183.105 dst=192.168.11.7> >>> reply.show() ###[ IP ]### version= 4L ihl= 5L tos= 0x0 len= 28 id= 51106 flags= frag= 0L ttl= 44 proto= icmp chksum= 0x2f8 src= 64.233.183.105 dst= 192.168.11.76 \options\ ###[ ICMP ]### type= echo-reply code= 0 chksum= 0x0 id= 0x0 seq= 0x0
sr1() はパケットを送信し、戻ってきたパケットを返すメソッドです。
返ってきたパケットを見ると、確かに echo-reply が戻ってきていることがわかります。
echo サーバと TCP 通信
今回は echo サーバとソケットをオープンしてデータを送信し、ソケットをクローズするところまでやってみます。
TCP の状態遷移図は以下のページに載っています。
@IT:連載 基礎から学ぶWindowsネットワーク 第16回 信頼性のある通信を実現するTCPプロトコル(3) 2.TCPの状態遷移図
サーバ側の準備
サーバ側は netcat を使ってポート 30000 を開いて待っておきます。
# nc -l 30000
このサーバに対して、scapy を使ってパケットを送りつけることでソケット通信を行います。
今回の環境ではサーバの IP は 192.168.11.1 です。
クライアント側の準備
実際にパケットを送る前にまずは以下のコマンドで、RST パケットの送信を抑止しておきます。
クライアント側は実際にはソケットがないため、サーバからのリプライに対して OS が RST を送ってしまうためです。
# iptables -A OUTPUT -p tcp --tcp-flags RST RST -d <サーバのIP> -j DROP
今回の環境ではクライアント側の IP は 192.168.11.76 です。
ソケットのオープン
まずは 3way ハンドシェイクでセッションを確立します。
SYN を送って返ってきた SYN+ACK に対して ACK を送ります。
>>> ip=IP(dst='192.168.11.1') >>> tcp=TCP(sport=50000,dport=30000,seq=100)
dst にはサーバの IP を指定し、dport には nc で待ち受けているポートを指定します。
sport, seq は適当です。
>>> tcp.flags = 'S' >>> SYN=ip/tcp >>> SYNACK=sr1(SYN) Begin emission: ..............................Finished to send 1 packets. ...* Received 34 packets, got 1 answers, remaining 0 packets
SYN フラグを立てたパケットを送信して SYN+ACK を受け取ります。
この時点でのサーバの状態は TCP 状態遷移図でいうところの SYN_RECV です。
(サーバで netstat をすると状態が確認できます。)
>>> tcp.seq += 1 >>> tcp.ack = SYNACK.seq + 1 >>> tcp.flags = 'A' >>> send(ip/tcp) . Sent 1 packets.
返ってきた SYN+ACK に対して ACK を返します。
これでサーバの状態は ESTABLISHED になり、セッションが確立できました。
データ送信
セッションができたので実際のデータを送信します。
>>> payload="Hello world!" >>> DATA=ip/tcp/payload >>> ACK=sr1(DATA) Begin emission: ...............................Finished to send 1 packets. ..* Received 34 packets, got 1 answers, remaining 0 packets >>> tcp.seq += len(payload)
サーバの nc を実行したプロンプトを見てみると、"Hello world!" が表示されているはずです。
送信したデータのサイズ分シーケンス番号を進めておきます。
ソケットのクローズ
ソケットをクローズするために FIN フラグを立てたパケットを送ります。
>>> tcp.flags = 'FA' >>> FINACK=sr1(ip/tcp) Begin emission: ............................Finished to send 1 packets. ..* Received 31 packets, got 1 answers, remaining 0 packets >>> FINACK.show() ###[ IP ]### version= 4L ihl= 5L tos= 0x0 len= 40 id= 65164 flags= DF frag= 0L ttl= 64 proto= tcp chksum= 0xa4a5 src= 192.168.11.1 dst= 192.168.11.76 \options\ ###[ TCP ]### sport= 30000 dport= 50000 seq= 788872184 ack= 114 dataofs= 5L reserved= 0L flags= FA window= 5840 chksum= 0x5d75 urgptr= 0 options= {}
サーバは何も送信するデータがないため、即時 FIN を返してきます。
サーバの状態は LAST_ACK です。
>>> tcp.seq += 1 >>> tcp.ack = FINACK.seq + 1 >>> tcp.flags = 'A' >>> send(ip/tcp)
最後に ACK を返してやることでソケットは正常にクローズされます。
これでひと通りの TCP 通信ができました。
scapy を使うと色々なことができそうなのでもっと使ってみようと思います。
Ubuntu を BeyondRAID 環境へ移行する (2/2)
ファイルサーバデータの移行
OS 領域と同様に、以下のような流れで行います。
BeyondRAID 環境を構築します。
- MDRAID 構築
- LVM、ファイルシステム構築
- 既存データをコピー
- 既存ディスクの RAID 組み込み
MDRAID 構築
3つのディスクを使う部分は RAID5、2つのディスクを使う部分は RAID1 にします。
データ移行のため、まずは2つのディスクを使って RAID5、1つのディスクで RAID1 を構成します。
# mdadm -C -l5 -n3 -e1 --assume-clean /dev/md2 /dev/sdb4 /dev/sdc5 missing # mdadm -C -l1 -n2 -e1 --assume-clean /dev/md3 /dev/sdb5 missing
LVM、ファイルシステム構築
2つの md デバイスをまとめて一つの LVM ボリュームにします。
# pvcreate /dev/md2 # pvcreate /dev/md3 # vgcreate BeyondRAIDVG0 /dev/md2 /dev/md3 # lvcreate -n BeyondRAIDLV0 -l 324317 BeyondRAIDVG0 ☆PEの数 (324317) は vgdisplay で確認出来ます # mkfs -t ext4 /dev/mapper/BeyondRAIDVG0-BeyondRAIDLV0
既存データをコピー
rsync でコピーします。
800GB ほどデータがあったので一晩かかりました。
# mount /dev/mapper/BeyondRAIDVG0-BeyondRAIDLV0 /mnt # rsync -aSv /opt/oldfiles /mnt/ # vim /etc/fstab ☆新しい領域を使うように fstab を編集
既存ディスクの RAID 組み込み
これで既存ディスク (sda) のデータはすべて新しいディスクに移行できたので、パーティションを切りなおして RAID に組み込みます。
# parted -s /dev/sda p モデル: ATA Hitachi HDT72101 (scsi) ディスク /dev/sda: 1000GB セクタサイズ (論理/物理): 512B/512B パーティションテーブル: msdos 番号 開始 終了 サイズ タイプ ファイルシステム フラグ 1 32.3kB 510MB 510MB primary ext3 boot 2 511MB 1512MB 1001MB primary 3 1512MB 20.0GB 18.5GB primary raid 4 20.0GB 1000GB 980GB extended lba 5 20.0GB 400GB 380GB logical raid 6 400GB 1000GB 600GB logical raid # mdadm --add /dev/md1 /dev/sda4 # mdadm --add /dev/md2 /dev/sda5 # mdadm --add /dev/md3 /dev/sda6
RAID1 への write 性能は遅いディスクに引っ張られる(と思う)ので、1TB(sda) と 2TB(sdb) のディスクで RAID1 を構成するように変更します。
# mdadm --fail /dev/md1 /dev/sdc4 ☆sdc4 を外すと自動的に sda4 が RAID1 に組み込まれる # mdadm --remove /dev/md1 /dev/sdc4 # mdadm --add /dev/md1 /dev/sdc4 ☆sdc4 はスペアに
最終構成
余っていた 1TB のパーティションも RAID なしの LVM ボリュームにしました。
# cat /proc/mdstat md4 : active raid1 sdb6[0] 976753528 blocks super 1.0 [2/1] [U_] md3 : active raid1 sda6[2] sdb5[0] 586048600 blocks super 1.0 [2/2] [UU] md2 : active raid5 sda5[3] sdc5[1] sdb4[0] 742358656 blocks super 1.0 level 5, 64k chunk, algorithm 2 [3/3] [UUU] md0 : active raid1 sdb1[2] sdc1[0] 498676 blocks super 1.0 [2/2] [UU] md1 : active raid1 sdc3[0](S) sdb3[2] sda3[3] 18054072 blocks super 1.0 [2/2] [UU] unused devices: <none> # lvs -o +devices LV VG Attr LSize Origin Snap% Move Log Copy% Convert Devices BeyondRAIDLV0 BeyondRAIDVG0 -wi-ao 1.24t /dev/md2(0) BeyondRAIDLV0 BeyondRAIDVG0 -wi-ao 1.24t /dev/md3(0) TMPLV0 NoRAIDVG0 -wi-a- 931.50g /dev/md4(0) Ubuntu10.04 OSImageVG0 -wi-ao 17.21g /dev/md1(0) # df -h ファイルシステム サイズ 使用 残り 使用% マウント位置 /dev/mapper/OSImageVG0-Ubuntu10.04 17G 4.9G 12G 30% / none 1.7G 364K 1.7G 1% /dev none 1.7G 1.4M 1.7G 1% /dev/shm none 1.7G 488K 1.7G 1% /var/run none 1.7G 0 1.7G 0% /var/lock none 1.7G 0 1.7G 0% /lib/init/rw /dev/sda1 471M 55M 392M 13% /boot /dev/mapper/BeyondRAIDVG0-BeyondRAIDLV0 1.3T 715G 469G 61% /opt/share /dev/mapper/NoRAIDVG0-TMPLV0 917G 200M 871G 1% /opt/tmplv
Ubuntu を BeyondRAID 環境へ移行する (1/2)
最近はディスクも安くなってきたので、既存の1ディスクで運用しているファイルサーバ (Ubuntu) を RAID 環境に移行してみたいと思います。
複数の容量の異なるディスクで冗長性をもたせるには BeyondRAID というやりかたがおもしろそうです。すでに同じことをされている方もいますが、既存の環境を壊さずに移行するというところにこだわってやってみたいと思います。
nakanote blog
UbuntuでDrobo風なNASを作る
環境
- Ubuntu 10.04
移行後
- OS領域は RAID1、ファイル置き場は BeyondRAID で冗長性と拡張性を持たせる
- 400GB, 1TB, 2TB のディスク3つで冗長性を持たせた環境を構築
- ファイルシステムも ext4 に移行
Drives | sdc | sda | sdb | | 400 GB | 1 TB | 2 TB | --------- | sdb6 | (1TB) -> TMP領域(RAID なし) --------- ------------------- | sda5 | sdb5 | RAID1 (2 x 600GB) -> md3 (600GB) ------------------- ---------------------------- | sdc4 | sda4 | sdb4 | RAID5 (3 x 378GB) -> md2 (756GB) ---------------------------- ---------------------------- | sdc3 | sda3 | sdb3 | RAID1 (2 x 20GB + 1 spare) -> /: md1 (20GB) ---------------------------- ---------------------------- | sdc2 | sda2 | sdb2 | Swap (3 x 1GB) -> 個別にマウント (3GB) ---------------------------- ---------------------------- | sdc1 | sda1 | sdb1 | (3 x 512MB) -> /boot: (512MB) ----------------------------
OS 領域の移行
まずは sda に入っていた OS 領域を RAID1 環境に移行します。
以下のような流れで行います。
- パーティション設定
- MDRAID 構築
- LVM、ファイルシステム構築
- 既存 OS 領域をコピー
- 新しい OS 領域から起動
パーティション設定
4k セクタ (AFT) 対応に注意しながらパーティションを切ります。
最近の parted はアラインされていないと警告が出るようになっていました。
- 2TB ディスクは 4k セクタ (AFT) なのでパーティションのアラインメントに注意する
- 開始は 2048 セクタから(Win7 標準?)
- 2TB ディスクは GPT で(Linuxは MBR でも GPT でもどちらでもブートできるようです)
sdc (400GB) のパーティション設定 # parted /dev/sdc (parted) mklabel msdos (parted) mkpart primary 2048s 512M (parted) mkpart primary 512M 1512M (parted) mkpart primary 1512M 20G (parted) mkpart extended 20G -1 (parted) mkpart logical 20G -1 (parted) set 1 raid on (parted) set 3 raid on (parted) set 5 raid on (parted) print モデル: ATA ST3400820AS (scsi) ディスク /dev/sdc: 400GB セクタサイズ (論理/物理): 512B/512B パーティションテーブル: msdos 番号 開始 終了 サイズ タイプ ファイルシステム フラグ 1 1049kB 512MB 511MB primary ext3 raid 2 512MB 1512MB 1000MB primary linux-swap(v1) 3 1512MB 20.0GB 18.5GB primary raid 4 20.0GB 400GB 380GB extended lba 5 20.0GB 400GB 380GB logical raid
sdb (2TB) のパーティション設定 # parted /dev/sdb (parted) mklabel gpt (parted) mkpart /boot 2048s 512M (parted) mkpart swap 512M 1512M (parted) mkpart OSImageVG0 1512M 20G (parted) mkpart BeyondRAID0 39063552s 781422591s ☆小さいディスクにセクタ単位で合わせます (parted) mkpart BeyondRAID1 781422592s 1953520063s ☆小さいディスクにセクタ単位で合わせます (parted) set 1 raid on (parted) set 3 raid on (parted) set 4 raid on (parted) set 5 raid on (parted) print モデル: ATA WDC WD20EARS-00M (scsi) ディスク /dev/sdb: 2000GB セクタサイズ (論理/物理): 512B/512B パーティションテーブル: gpt 番号 開始 終了 サイズ ファイルシステム 名前 フラグ 1 1049kB 512MB 511MB ext3 /boot raid 2 512MB 1512MB 1000MB swap 3 1512MB 20.0GB 18.5GB OSImageVG0 raid 4 20.0GB 400GB 380GB BeyondRAID0 raid 5 400GB 1000GB 600GB BeyondRAID1 raid
MDRAID 構築
OS 領域は RAID1 で構築します。
/boot も RAID1 にするつもりでしたが、grub2 のバグ?でできなかったので、ブートに利用する sda1 は単体にしました。
(sdb1 と sdc1 で RAID1 にして、手動で sda1 のバックアップを取ることにしました。)
Bug #527401 “grub-installer fails to install on a raid1 array” : Bugs : “grub2” package : Ubuntu
# mdadm -C -l1 -n2 -e1 --assume-clean /dev/md0 /dev/sdb1 /dev/sdc1 # mdadm -C -l1 -n2 -e1 --assume-clean /dev/md1 /dev/sdb3 /dev/sdc3 # mdadm --detail --scan >> /etc/mdadm/mdadm.conf # cat /proc/mdstat Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10] md1 : active raid1 sdb3[2] sdc3[0] 18054072 blocks super 1.0 [2/2] [UU] md0 : active raid1 sdb1[2] sdc1[0] 498676 blocks super 1.0 [2/2] [UU] (initrd も更新しないと新しい OS 領域からブート出来なかったのでここで更新しておきます。) # mv /boot/initrd.img-2.6.32-24-generic{,.org} # mkinitramfs -o /boot/initrd.img-2.6.32-24-generic `uname -r`
LVM、ファイルシステム構築
MD の上に LVM を構築して ext4 でフォーマットします。
# pvcreate /dev/md1 # vgcreate OSImageVG0 /dev/md1 # lvcreate -n Ubuntu10.04 -L 17.21g OSImageVG0 # lvs -o +devices LV VG Attr LSize Origin Snap% Move Log Copy% Convert Devices Ubuntu10.04 OSImageVG0 -wi-ao 17.21g /dev/md1(0) # mkfs -t ext4 /dev/mapper/OSImageVG0-Ubuntu10.04
既存 OS 領域をコピー
既存の OS 領域をコピーするために CDROM などから Linux を起動します。
私は Ubuntu 10.10 Server を USB メモリブートで利用したのですが、rsync が入っていなくて面倒だったので他のものにした方がいいかもしれません。(Desktop 版では mdadm と lvm2 が入っていないので Server にしたのですが。)
rsync で OS 領域をコピー # mount /dev/sda5 /mnt/0 ☆古い OS 領域をマウント # mount /dev/mapper/OSImageVG0-Ubuntu10.04 /mnt/1 # rsync -av /mnt/0/ /mnt/1/ ☆OS 領域を丸ごとコピー # vim /mnt/1/etc/fstab ☆ fstab を新しい OS 領域 (/dev/mapper/OSImageVG0-Ubuntu10.04) に変更
新しい OS 領域からブートするため、新しい grub エントリを追加します。
/etc/grub.d/40_custom に以下を追加します。(uuid は適宜変更してください。)
menuentry 'Ubuntu, with Linux 2.6.32-27-generic' --class ubuntu --class gnu-linux --class gnu --class os { recordfail insmod ext2 set root='(hd0,1)' search --no-floppy --fs-uuid --set aaaa-bbbb-cccc-dc5be58cec49 linux /vmlinuz-2.6.32-27-generic root=/dev/mapper/OSImageVG0-Ubuntu10.04 ro apparmor=0 initrd /initrd.img-2.6.32-27-generic }
/etc/default/grub を編集して grub の選択画面が出るようにします。
#GRUB_HIDDEN_TIMEOUT=0 ☆コメントアウト #GRUB_HIDDEN_TIMEOUT_QUIET=true ☆コメントアウト GRUB_HIDDEN_TIMEOUT_QUIET=false ☆追加 GRUB_TIMEOUT=3 ☆変更
新しい OS 領域から起動
初回のみ新しい OS 領域からブートするため、ブート時の OS 選択画面で先ほど追加したエントリを選択します。
以降自動的に新しい OS 領域からブートするようにするため、grub.cfg を更新しておきます。
# update-grub2
ルートパーティションが新しい LVM ボリュームになっていたら移行完了です。
# df ファイルシステム 1K-ブロック 使用 使用可 使用% マウント位置 /dev/mapper/OSImageVG0-Ubuntu10.04 17763668 5046296 11815024 30% / none 1764460 344 1764116 1% /dev none 1769508 0 1769508 0% /dev/shm none 1769508 344 1769164 1% /var/run none 1769508 0 1769508 0% /var/lock none 1769508 0 1769508 0% /lib/init/rw none 17763668 5046296 11815024 30% /var/lib/ureadahead/debugfs /dev/sda1 482214 56211 401104 13% /boot
IS01のカーネルをビルドしてみた
IS01のアップデートがないことが確定してしまい残念な限りです。
ところで、この IS01 は android ということもあり、カーネルが OSS として公開されているので、せっかくなのでビルドしてみました。
準備
まずは必要なものをダウンロードしてきます。
(参考:AndroidエミュレータでLinuxカーネルをデバッグ!! (1/3) − @IT MONOist)
- カーネルソース
- クロスコンパイラ
- git://android.git.kernel.org/platform/prebuilt.git
- .config
- IS01本体の/proc/config.gzから持ってきます。
その他必要に応じて開発環境のパッケージなど。
以下のように展開します。
# mkdir android # cd android # tar zxvf /path/to/kernel.tar.gz # git clone git://android.git.kernel.org/platform/prebuilt.git # cp /path/to/config.is01 /kernel/.config
カーネルソースの修正
そのままではコンパイルエラーが出たのでソースを少し修正しました。
--- arch/arm/mach-msm/sh_sleepcheck.c.orig 2010-11-19 00:00:57.000000000 +0900 +++ arch/arm/mach-msm/sh_sleepcheck.c 2010-11-19 00:01:08.000000000 +0900 @@ -24,7 +24,7 @@ #include <linux/wakelock.h> #include <linux/pm.h> #include <mach/msm_rpcrouter.h> -#include <smd_private.h> +#include "smd_private.h" #include <sharp/sh_sleepcheck.h> /* ==========================================================================================
コンパイル
後はコンパイルをするだけです。
# cd kernel # make ARCH=arm CROSS_COMPILE=../prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi- : OBJCOPY arch/arm/boot/Image Kernel: arch/arm/boot/Image is ready Building modules, stage 2. : # ls -l arch/arm/boot/Image -rwxr-xr-x 1 nobody nobody 5338336 2010-11-19 00:06 arch/arm/boot/Image
(たぶん)これでできました。
実行
実機にはインストールできないので、エミュレータで実行しようとしたのですがうまくできませんでした。
(出来上がった Image を -kernel オプションで指定すればいけるかと思ったのですが…)
android はあまり詳しくないのでこれ以上分からず、このあたりであきらめることにしました。
DSC-HX5V で撮った動画をDLNA を利用し PS3 で見る
Cyber-shot HX5V では AVCHD または MP4 形式で動画を撮ることができます。
撮った動画は HDMI ケーブルで繋ぐといった方法で見ることができますが、せっかくなので DLNA を利用して PS3 で見られるようにしてみました。
(DLNA クライアントの機能があれば PS3 意外でも見られるはずです。)
DLNA サーバの設定
利用する DLNA サーバはMediaTomb - Free UPnP MediaServerです。
サーバは Ubuntu 10.04 (x86_64) なので、apt で入れたもの (mediatomb-0.12.0~svn2018-6ubuntu2) を使いました。
迷うところはほぼ無かったのですが、m2ts と mp4 のファイルをそのままの設定では認識しなかったので、/etc/mediatomb/config.xml を以下のように修正。
--- config.xml.old 2010-05-16 18:10:19.000000000 +0900 +++ config.xml 2010-05-16 17:58:27.000000000 +0900 @@ -82,6 +82,8 @@ <map from="mka" to="audio/x-matroska"/> <!-- Uncomment the line below for PS3 divx support --> <map from="avi" to="video/divx"/> + <map from="m2ts" to="video/mpeg"/> + <map from="mp4" to="video/mp4"/> <!-- Uncomment the line below for D-Link DSM / ZyXEL DMA-1000 --> <!-- <map from="avi" to="video/avi"/> --> </extension-mimetype> @@ -101,8 +103,8 @@ <treat mimetype="audio/x-wav" as="pcm"/> <treat mimetype="audio/L16" as="pcm"/> <treat mimetype="video/x-msvideo" as="avi"/> - <treat mimetype="video/mp4" as="mp4"/> - <treat mimetype="audio/mp4" as="mp4"/> + <!-- <treat mimetype="video/mp4" as="mp4"/> + <treat mimetype="audio/mp4" as="mp4"/> --> <treat mimetype="application/x-iso9660" as="dvd"/> <treat mimetype="application/x-iso9660-image" as="dvd"/> <treat mimetype="video/x-matroska" as="mkv"/>
これで PS3 で認識する mediatomb から動画が見られました。
同じ SONY 同士ということもあってか、動画の形式などに悩ませられることもなく見ることが出来ています。
mediatomb ではディレクトリを監視するよう設定することも出来るので、あらかじめ動画を取り込むディレクトリを監視しておくことで、デジカメから動画ファイルを取り込むだけで自動的に DLNA 経由で公開され、PS3 で見ることが出来るようになります。
DLNA は便利でよいですね。