d.sunnyone.org
sunnyone.org

ページ

2011-11-30

Solaris/x86のスタック領域がなぜ低位に確保されるのか?

藤原さんの「理解した気分になる Solaris Internals "Chapter 9: Virtual Memory" 総ざらえ」の疑問
x86 のスタック領域がアドレス低位に確保されるのは、 Solaris 固有の話?それとも他の OS でも共通の、CPU 固有の話?
の答え、ちょっと調べた感じだと、どうも歴史的なもののようです。
(質問の回答としては、Solaris固有ではないが、CPU固有といえば固有、他のOSでも共通かどうかは不明というところでしょうか。)

まず話を聞いていない人向けに書くと、Solaris/SPARC(32bit,64bit)やSolaris/x64はそんなことないのに、
Solaris/x86だけ、スタックが妙に低いアドレス(0x8048000)から伸びているけどなんぞ、というお話です。
cf. Solaris Internalsの図

このアドレスがキーなのかと思って検索してみたらどうもそうのようで、このアドレスは
ELFのエントリポイントの模様。

リンカとかローダの話ですかと思って手元の「Linkers & Loaders」を調べてみたら、まさにそれっぽい記述を見つけました。
ELFがa.outと異なるのは、オブジェクトを0番地付近ではなくアドレス空間の中程にロードして、スタックがテキストセグメントの下位から成長し、ヒープがデータセグメントの終端から成長するようにすることによって、全体として利用するアドレス空間が比較的小さく済むようにしている点である。386システムでは、テキストのベースアドレスは0x08048000で、テキストの前の0x08000000以降の部分に十分に大きなスタックを確保できると同時に、ほとんどのプログラムでは第2レベルのページテーブルは1つだけで済む(386においては、1つの第2レベルテーブルで0x00400000のアドレスをマップできる)。
決めた時代的には、ここに置くとアドレスがいい感じだったということのようですね。

なおこれを決めたのは「Re: is not 0x8048000 too wasteful?--about virtual address layout」によると、
There is folklore that 0x08048000 once was STACK_TOP (that is, the stack
grew downwards from near 0x08048000 towards 0) on a port of *NIX to i386
that was promulgated by a group from Santa Cruz, California. This was
when 128MB of RAM was expensive, and 4GB of RAM was unthinkable.
ということで、1983年のSCOでしょうか。

ちなみに、Linux/x86 (2.6)は上記場所にスタックはできないようです。


なお、もっと詳しい方がいればブログなりでフォローいただければ修正します :)

ちなみにこの「Linkers & Loaders」、ひさびさに読み始めてたのですが、本を見ると2001年初版。出た当初に買ったはず(だいぶ色あせてる)なので、10年後にまた読むとは。前知識なしで読むと全然わからない(10年前がそうだった)ですが、最近のx86やらOSやらの勉強でようやくわかるようになってきました。
Amazonの評価はよくないですが、これだけ一通り書いてあってもし読みやすい本があれば是非知りたい感じです。

2011-11-11

GNOME ShellのFavorites Barに好きなプログラムを追加する

GNOME Shellのアクティビティ・オーバービューの左側にあるバー(Favorites Bar?)に、
メニューにでてこないアプリケーション(~/binに入っているやつとか)を登録するには、
手で.desktopファイルを作って(!)メニューに出せばいいようだ...

手順。ここではV2Cというアプリを例にする。

1. 適当な.desktopファイルを/usr/local/share/applicationsからコピる
cp -p /usr/share/applications/firefox.desktop ~/.local/share/applications/v2c.desktop
(Firefoxはいろいろ書いてあってあまりいい例ではないかも)

2. コピってきた.desktopファイルを編集する
vi ~/.local/share/applications/v2c.desktop


3. アクティビティ・オーバービューでNameに書いた名前を入力し、アイコンが表示されたらFavorites Barにドラッグアンドドロップ

これで登録できる。でてこなかったらAlt+F2と「r」でいったんGNOME Shellを再起動すればいいかも。

参考:http://askubuntu.com/questions/40382/how-to-add-a-folder-to-the-favorites-bar-in-gnome-shell

2011-11-01

strlen(...) > 0とstrlen(...) != 0はどちらが速いか?

うまい文にならなかったが、貴重な結果なので以下にまとめておく。

Loquiでtopicを空にしようとしてもできない、というバグを発見して対応を施した際、
strlen(str)は>0がよいか? != 0がよいか?という話になり、今のご時世問題になるわけのない、
「どちらが速いのか」が話題になった(というかした)
(バグは対策済み:対応内容はこちら https://bugs.launchpad.net/loqui/+bug/689164/comments/3)

結論としては、出力結果が同じになるので、速度は変わらない。しかし、変わらない理由が驚きであった。

話題となるのはこの部分:


上記の部分が入ったソース(irc_message.c)をgcc -Sでアセンブルし、if文のところを抜き出した結果がこちら:


前述の通り、strlen(...) > 0をstrlen(...) != 0にしても、出力結果は変わらないのだが、問題は
出力結果。strlenをコールしていないどころか、これではstr[0] != '\0'のようである。

strlen()の定義を追うと、eglibc-2.13/sysdeps/i386/i486/bits/string.hにあった:


このコードが使われている感じではないので、実際に使われているのはおそらくgccのbuiltin...
どうやらgccがstrlen(...) > 0自体を解釈しているようである。驚くべきgcc.

ちなみに、-O0でも一緒であるが、-fno-builtinをつけるとちゃんとstrlenをコールしてくれる:


まぁ、それでもsize_tがunsignedだったので、結局>0と!=0は出力結果は同じなのであったが。

2011-08-30

OpenIndianaでvirtionetドライバを使ってみる

KVM (CentOS 5.x)の上でOpenIndianaを動かしているが、どうもネットワークがおかしい。
KVMのpara virtualドライバであるところのvirtioドライバがあればきっといいのだろうと思って探してみると、それっぽいものを発見。
https://github.com/grigale/solaris-virtio

一昨日pkgコマンドを触ったばかりの人間には妙にハードルが高いが、さっそく入れてみる。
Linuxでいうところのmakeしてinsmodするくらいのノリでいけるんだろうと期待。

まずコンパイラがないので入れる。きっとSun Studioとやらが必要だ。

$ pkg search sun studio

sunstudio12u1というのが見つかったので、インストール。

$ pkg install sunstudio12u1

ccコマンドが使えるようになった。

そしてコンパイル。

$ tar zxvf grigale-solaris-virtio-07c22ec.tar.gz # Solarisのくせにzオプションが使える

$ cd grigale.../usr/src/uts/common/io/virtio/

$ make

$ make install


あっさり/usr/kernel/drv/amd64とかに入った感。

しかしドライバをシステムに認識させないといけない。
http://kohju.justplayer.com/Tips_Solaris_OpenSolarisOS_forlinuxuser_adddevice.html

virtioのNICのIDがなんだかわからなかった(prtconfの結果を読むのが面倒だった)ので
他のLinuxマシンでlspciするというチートを使った。
# lspci

~~

00:03.0 Ethernet controller: Red Hat, Inc Virtio network device

~~

# lspci -n

~~

00:03.0 0200: 1af4:1000

~~

きっとpci1af4,1000でいけるに違いない。
# sudo /usr/sbin/add_drv -n -v -i "pci1af4,1000" virtionet

Reboot client to install driver.

Driver (virtionet) installed.

リブートしろと言われたのでリブートする。

$ sudo dladm show-link

LINK CLASS MTU STATE BRIDGE OVER

e1000g0 phys 1500 up -- --

e1000g1 phys 1500 up -- --

virtionet0 phys 1500 unknown -- --


キター!

$ sudo ifconfig virtionet0 plumb

ifconfig: cannot plumb virtionet0: Could not open DLPI link


だめでした。

ちなみにprtconf -vの当該部分


2011-08-28

OpenIndianaを148から151にアップグレードする

OpenIndianaを148から151にアップグレードする手順です。

まずはpkg publisherで確認すると、openindiana.orgのURIがhttp://pkg.openindiana.org/dev/になっています。


これを、pkg set-publisherでdevのところをdev-ilにして、pkg image-updateします。
すると、package/pkgが古いと言われるので、指示通り更新します。


package/pkgを更新すると、pkg image-updateできるようになります。
終わったら、再起動します。


アップグレードされました。


2011-07-25

Vyattaのwebproxy (squid) に好きな設定を加える

VyattaのCLIから設定できない内容をsquidに設定したいときは、/etc/squid3/local.confに書けば
次回コンフィグ生成以降に使用できるようになる。

本体のコンフィグと別管理になっちゃうけど仕方ないかなという感じ。
vlan databaseみたいなもんだと思えば。

2011-06-27

図解 grub.cfg 生成 (GRUB2 設定ファイルができるまで)

squeezeを初期インストールしたらgrub2になってしまった。
いつもインストールするとLinux起動パラメータからquietを外すのが好みなんだけど、grub2はmenu.lstじゃないので設定を変更するのが一苦労。結局、/etc/default/grubの環境変数を変更すればいいことがわかったんだけど、また同じようなことをしないといけない予感がしたので呼び出しを図にしておいた。

以下がupdate-grubを叩いてから、grub2の設定ファイルであるgrub.cfgが生成されるまでの動きである。
grub.d以下はdistributionによってだいぶ違うんだと思う。


しかしフリーの良いUML書くツールないかな。上のはAmaterasUMLで、結構書きやすいけど機能が少なくて常用するのはちょっとなぁという感じ。それなりに書きやすくてExcelにエクスポートできれば完璧なんだけど...

2011-06-26

virt-installでDebian (squeeze)をコンソールインストールする

virt-installでDebian (squeeze)をシリアルコンソール(=KVMだとvirsh consoleで出てくるやつ)経由で
インストールするコマンド。diskとかはそれっぽく変えないとダメよ。console=tty0はいらないかも。


今までvirt-managerからGUIでインストールしてたけど、この程度なら
コマンドのほうが繰り返し使えて便利。CとEnglishしか選べなかったから日本語は
おそらく使えないんだと思うけど、まぁいつものインストーラが使えるから、いつもの
選択肢に答えればOK.

2011-05-24

Rutyテンプレートエンジン

使ってみてからLiquidテンプレートエンジンはメソッドコールが全くできないという
弱点が発覚。意図した特徴だけど全くできないのはちょっとつらい。

似たテンプレートエンジンRutyというのがあった。
http://ruty.rubyforge.org/documentation/ Ruty Documentation
こっちは使える。指示してあげないといけないけど...

コード例:
require 'rubygems'
require 'ruty'

class HogeHoge
  def get_var
    "VARVAR"
  end
  
  def ruty_safe?(name)
    return [:get_var].include?(name)
  end
end

items = [ { :name => "The Oldest Book", :author => 'Kota' },
          { :name => "Yet Another Book", :author => 'Ryohei' },
          { :name => "The Lost Book ><", :author => 'Mikako' } ]
title = "Book List"

tmpl = Ruty::Template.new(DATA.read)
puts tmpl.render({:title => title, :items => items, :is_ok => true, :hogehoge => HogeHoge.new })

__END__
{% if is_ok %}
<h1>{{ title }}</h1>
{% endif %}
<table>
{% for item in items %}
  <tr><td>{{ item.name|escape }}</td><td>{{ item.author|escape }}</td>
{% endfor %}
</table>

hoge: {{ hogehoge.get_var }}

インストールはgem install rutyで。

Liquid テンプレートエンジン

最近DjangoやらKayやらを勉強してDjangoのテンプレートやらJinja2やらを見て
Rubyにも似たようなものないかなと思ったらあった。
http://www.liquidmarkup.org/ Liquid

検索しても全然日本語がひっかからないので、サンプルを紹介。

使い方は簡単。
テンプレート文字列をLiquid::Template.parseして、データを与えてrenderすればよいのだ。

テンプレートの基本文法はこんな感じ:
{{ 変数 }} : 変数出力
{{ 変数 | フィルタ }} : フィルタ適用
{% if XXX %} ... {% endif %} : if分岐
{% for XXX in LIST %} ... {% endfor %} : forループ

コード例(Bloggerでインデント面倒だから崩れちゃってるけど):
require 'rubygems'
require 'liquid'

items = [
{ 'name' => "The Oldest Book", 'author' => 'Kota' },
{ 'name' => "Yet Another Book", 'author' => 'Ryohei' },
{ 'name' => "The Lost Book", 'author' => 'Mikako' } ]
title = "Book List"

tmpl = Liquid::Template.parse(DATA.read)
puts tmpl.render({'title' => title, 'items' => items})

__END__
<h1>{{ title|escape }}</h1>
<table>
{% for item in items %}
<tr><td>{{ item.name|escape }}</td><td>{{ item.author|escape }}</td>
{% endfor %}
</table>

ちなみにインストールはgem install liquidでOK.

Template::Toolkitスキーなのですばらしい。erb使いでもちょっとした設定なんかにいかが。

参考:Mini reviews of 19 Ruby template engines

2011-05-06

OMRON BY50SをUbuntu 11.04で使用する(2) - チューニング

前回のupscの出力を見てもらえるとわかるが、UPSを使用する上で重要な充電率が出てこない。
"Q1"コマンドでは充電率を教えてもらえないので、仕方がない。オフィシャルなソフトウェア
だとうまいことやっているのかもしれない。

ここでどうするかというと、フル充電時の電圧と、使い切った時の電圧を書くと、割り算して
出してくれる仕組みがあるので、利用する。

<調べ方>
# upsc by50s battery.voltage
12.40
これをフル充電時と使い切ったときに確認する。
シャットダウン試験のときに調べればいいと思う。

<設定方法>
/etc/nut/ups.confの[by50s]エントリに以下を追記
default.battery.voltage.high = xx.x
default.battery.voltage.low = yy.y
うちでは13.6, 10.6ぐらいだったので、high: 13.6, low: 11とした。
こう設定すると、以下の通りでてくるようになる。

# upsc by50s
battery.charge: 54
battery.voltage: 12.40
battery.voltage.high: 13.6
battery.voltage.low: 11
(略)

これで充電率がわかるようになったので、だいぶよくなった。
ランタイム時間を記録するruntimecalオプションもあるが、そこまでいらないかなと
思ってやっていない。

OMRON BY50SをUbuntu 11.04で使用する

BY50Sはコンパクトながら正弦波出力、しかも価格は20k以下となかなか良いUPS.
Linuxも対応しているということで買ってみた。

しかし、distributionの縛りが緩そうで、わりと汎用に使えそうなSimple Shutdown Softwareは、
本当にシンプルで、できることはシャットダウンくらい。情報取得をしてみたかったので、
今回はあえてNUT (Network UPS Tools)を使ってみた。
(ちゃんとした環境ではSimple Shutdown SoftwareかPowerAct Proを使用することをおすすめします。
ちゃんと動かなくても誰も助けてくれないです。)

できた後にやったので、途中経過など不足している部分があるかもしれないです。
(結論だけ書いているので、ドライバ探しとかの経緯はまったく書いてない)

<概要>
・NUT 2.6系を使った場合の説明
・ドライバはblazer_usb, subdriverにはipponを指定する。
・よりよい情報を得るには電圧等を読んで設定に記録する(別エントリ)
・Ubuntu 11.04の場合であるが、他のdistribution / OSでも似たような感じと思われる。
以上。このエントリは実はこれだけでいいかも。

<インストール>
# apt-get install nut

<NUTの設定>
/etc/nutに設定ファイルがある。いじるのは、
モードを司るnut.conf, 機器の「ドライバ」の設定を行うups.conf, ドライバとクライアントの
やりとりをするupsdの設定upsd.conf, upsdの接続ユーザ設定のupsd.users, UPSの状態を
モニタリングするupsmon.confとなる。

設定内容は以下のとおり


nut.conf
MODE=standalone

ups.conf
[by50s]
driver = blazer_usb
port = /dev/usb/hiddev0
desc = "Omron UPS"
vendorid = 0590
productid = 0081
subdriver = ippon

vendorid, productidはlsusbの結果を読む
# lsusb
(略)
Bus 005 Device 002: ID 0590:0081 Omron Corp.
(略)

portは実は関係ないので、なんでもいい。port = autoにしておくといいかもみたいなことを
マニュアルに書いてあった。

upsd.users
[upsmon]
password = [password]
upsmon master


upsmon.conf
MONITOR by50s@localhost 1 upsmon [password] master

<udevの設定>
nutユーザでUPSデバイスが見えるように設定する。
/lib/udev/rules.d/52-nut-usbups.rulesが用意されるので、他の
行に合わせて以下を書く。
# Omron BY50S - blazer_usb
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", \
ATTRS{idVendor}=="0590", ATTRS{idProduct}=="0081", \
MODE="0664", GROUP="nut"

このファイルはパッケージアップグレードで上書きされてしまうかもしれないので、
53-nantyara.rulesを用意して書くほうがいいかも。

このルールの書き方はudevのバージョンによって違うので、他のdistributionでは
気をつける。

終わったら、
# /etc/init.d/udev restart
しておく。

<動作確認>
まずはドライバの確認。
$ sudo ./blazer_usb -a by50s -DDD
Network UPS Tools - Megatec/Q1 protocol USB driver 0.03 (2.6.0)
0.000000 debug level is '3'
(略)
0.290799 Trying to match device
0.290819 Device does not match - skipping
0.290844 Checking device (0590/0081) (003/002)
0.312918 - VendorID: 0590
0.312957 - ProductID: 0081
0.312975 - Manufacturer: OMRON
0.312993 - Product: BY50S
0.313010 - Serial Number: unknown
0.313028 - Bus: 003
0.313045 Trying to match device
0.313130 Device matches
0.315981 Trying megatec protocol...
0.319910 send: Q1
0.564929 read: (103.1 103.9 102.6 000 50.0 13.0 43.4 00101000
0.565172 Status read in 1 tries
0.565195 Supported UPS detected with megatec protocol
(略)
0.565216 Battery runtime will not be calculated (runtimecal not set)
0.568909 send: Q1
0.820932 read: (103.1 103.9 102.8 000 50.0 13.0 43.4 00101000
0.821185 dstate_init: sock /var/run/nut/blazer_usb-by50s open on fd 5
0.824906 send: Q1
1.076905 read: (103.2 103.9 102.8 000 50.0 13.0 43.4 00101000
2.826914 send: Q1
3.076932 read: (103.1 103.9 102.6 000 50.0 13.0 43.4 00101000
4.827914 send: Q1
5.076929 read: (103.2 103.9 102.6 000 50.0 13.0 43.4 00101000
(略)
Ctrl+Cで終了。send: Q1でread: (100... が読めたらOK. Vendor informationがNAKとかいうのは気にしない。Operation not permittedみたいなのが出たら、オプションに-u rootをつけてみて成功したら、
権限問題の可能性が高い。udevの設定やらを疑うこと。そうでなかったら残念。

ほぼ同じことであるが、以下を実行してエラーが出ないことを確認する。
$ sudo upsdrvctl start
Network UPS Tools - UPS driver controller 2.6.0
Network UPS Tools - Megatec/Q1 protocol USB driver 0.03 (2.6.0)
Supported UPS detected with megatec protocol
Battery runtime will not be calculated (runtimecal not set)

あとは/etc/init.d/nut startなりで起動すればOK.
upsd, upsmonプロセスが動いていることを確認する。

upscというコマンドを使うと、情報が取れる。
# upsc by50s
battery.voltage: 12.30
beeper.status: disabled
device.type: ups
driver.name: blazer_usb
driver.parameter.pollinterval: 2
driver.parameter.port: /dev/usb/hiddev0
driver.parameter.productid: 0081
driver.parameter.subdriver: ippon
driver.parameter.vendorid: 0590
driver.version: 2.6.0
driver.version.internal: 0.03
input.frequency: 49.9
input.voltage: 102.9
input.voltage.fault: 102.7
output.voltage: 102.3
ups.delay.shutdown: 30
ups.delay.start: 180
ups.load: 32
ups.productid: 0081
ups.status: OL BYPASS
ups.temperature: 57.0
ups.type: offline / line interactive
ups.vendorid: 0590

upslogというコマンドもただ記録するだけだが便利。

あとは電源試験をすればOK. デフォルトでは、Low Battery状態になると、
shutdownシーケンスに入ってくれる。普通に使う分にはこれでOK.

上記の設定方法は、公式のUser Manualにまとまっているので、参照のこと。
Configuration notesセクションの内容。
http://www.networkupstools.org/docs/user-manual.chunked/ar01s06.html
あとはドライバのman page. 今回はblazer_usb(8).

情報取得のチューニングについては、次回記載。