目次: Linux
何となく自分ではわかっているつもりでしたが、いざ変更しようとしたら全然わかっていなかった initの話です。
突然ですがLinuxマシンのブート時にやりたいことが増えた場合、どうやって変えたら良いでしょうか?SysV initなら /etc/inittabか /etc/init.d/rcSを変えれば?とサラりと答えられる方は、きっとかなり詳しい方ですね。
せっかく書くので、初めて見る人でもわかるように……とまでは言いませんが、Linuxのブート時には何が実行されるのか?を1つずつ見ていこうと思います。
Linuxカーネルは起動が終わると、PID 1のプロセスとして、下記リストを上から順に実行しようとします(参考: linux/init/main.cのkernel_init() 関数)。
通常の利用ではinit= を指定しないと思いますので、/sbin/initが実行されるはずです。Debianでは(※)これがSystem V initを指しています。
(※)Debianに限らず昔はSysV initが多かったのですが、最近はUbuntuのupstart、Gentoo Linuxのopenrc、Fedora Coreのsystemdのように、SysV init以外のinitも増えています。設定の方法が皆違うので、同時に使うと混乱します……。
SysV initは /etc/inittabという設定ファイルに従って動作します。このinittabというファイルにはrunlevelがいくつのときは、こういう動作をしなさい、という命令が羅列されています。
ちなみにrunlevelとはSysV initの動作モードのことで、大抵は0〜6, Sの8つのモードがあります。実際に使われるのは3〜4個(0: マシン停止、1: シングルユーザ、2や3: デフォルト、6: マシン再起動)だったりしますけど。デフォルトのrunlevelはディストリビューションによって違います。たぶん2が多いんじゃないかな。
例としてDebian GNU/Linux 7.8(Wheezy) の /etc/inittabを見てみます。
# The default runlevel.
id:2:initdefault: ★デフォルトのrunlevel★
# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS★runlevelに無関係に、システムブート中に実行される。★
(...略...)
l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2★runlevel 2なら、ここが実行される★
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
(...略...)
Debianの場合、まず最初に /etc/init.d/rcSが実行され、その後、デフォルトrunlevelの2に対応する /etc/init.d/rc 2が実行される、ということがわかります。ちなみに /etc/init.d/rcSは、下記のような実装です。
#! /bin/sh
#
# rcS
#
# Call all S??* scripts in /etc/rcS.d/ in numerical/alphabetical order
#
exec /etc/init.d/rc S
すなわち、initは/etc/init.d/rc Sと /etc/init.d/rc 2を実行しているだけです。ここまでの話をまとめておくと、
前提: ディストリビューションはDebian GNU/Linux 7.8(Wheezy) 1. Linux Kernel起動終盤にkernel_init() が /sbin/initを起動 2. /sbin/initは /etc/inittabを見る 3. /etc/inittabにはinitへの指示が書いてある 3-1. デフォルトのrunlevelは2である 3-2. ブート時に /etc/init.d/rcSを実行せよ 3-2-1. /etc/init.d/rcSは /etc/init.d/rc Sを起動する 3-3. runlevel 2なら /etc/init.d/rc 2を実行せよ
こうなります。割とシンプルですよね。
簡単に言えば/etc/rcXXXX.dディレクトリの中に入っている初期化スクリプトを全部実行するランチャーです。XXXXの部分にはrcの第一引数に指定したrunlevelが入ります。例えばrc Sであれば /etc/rcS.d/* が、rc 2であれば /etc/rc2.d/* が実行されます。
スクリプトの先頭から中程にかけて色々と環境変数を設定していますが、注目すべきは、CONCURRENCYという変数です。rcはこのCONCURRENCYという変数の値によって大きく動作が変わります。
#
# Check if we are able to use make like booting. It require the
# insserv package to be enabled. Boot concurrency also requires
# startpar to be installed.
#
CONCURRENCY=makefile
test -s /etc/init.d/.depend.boot || CONCURRENCY="none"
test -s /etc/init.d/.depend.start || CONCURRENCY="none"
test -s /etc/init.d/.depend.stop || CONCURRENCY="none"
if test -e /etc/init.d/.legacy-bootordering ; then
CONCURRENCY="none"
fi
if ! test -e /proc/stat; then
if [ "$(uname)" = "GNU/kFreeBSD" ] ; then
# startpar requires /proc/stat
mount -t linprocfs linprocfs /proc
fi
fi
startpar -v > /dev/null 2>&1 || CONCURRENCY="none"
条件を洗っていくと、下記のいずれかが成立するときCONCURRENCY=noneとなり、いずれも成立しなければCONCURRENCY=makefileとなることがわかります。
次にCONCURRENCYの役割について調べるため、rcXXXX.d内のスクリプトを実行するためのstartup() という関数を見てみます。
#
# Start script or program.
#
case "$CONCURRENCY" in
makefile|startpar|shell) # startpar and shell are obsolete
CONCURRENCY=makefile
log_action_msg "Using makefile-style concurrent boot in runlevel
$runlevel"
startup() {
eval "$(startpar -p 4 -t 20 -T 3 -M $1 -P $previous -R $
runlevel)" ★1
(略)
none|*)
startup() {
action=$1
shift
scripts="$@"
for script in $scripts ; do
$debug "$script" $action★2
done
}
;;
色々書いてありますが、簡単に言うと
CONCURRENCY=makefileであれば、startparを起動(★1)し、
CONCURRENCY=noneであればstartup() に渡された引数を一つずつ起動する(★2)、という作りです。
次に呼び出し側も見てみます。
# Now run the START scripts for this runlevel.
# Run all scripts with the same level in parallel
CURLEVEL=""
for s in /etc/rc$runlevel.d/S* ★1
do
# Extract order value from symlink
level=${s#/etc/rc$runlevel.d/S}
level=${level%%[a-zA-Z]*}
if [ "$level" = "$CURLEVEL" ]
then
continue
fi
CURLEVEL=$level
SCRIPTS=""
for i in /etc/rc$runlevel.d/S$level* ★2
do
[ ! -f $i ] && continue
(...略...)
SCRIPTS="$SCRIPTS $i" ★3
done
startup $ACTION $SCRIPTS★4
done
まず ★1にある * のパターン展開一発で、起動するべきスクリプトが「起動順に列挙」されます。
理由は2つで、/etc/rcS.d中のファイルは全て「S + 2桁の数字 + 名前.sh」という規則でファイル名が付けられていること、なおかつ、bashは * の展開結果をアルファベット順にソートする仕様(下記を参照)だからです。
パス名展開 -fオプションが指定されていなければ、単語分割を行った後にbashはそれぞ れの単語が *, ?, [ を含んでいるかどうか調べます。 これらの文字のいずれ かが見つかると、その単語は パターン とみなされ、 パターンにマッチする ファイル名を アルファベット順にソートしたリストに置換されます。 マッチ ...
次に ★2から ★3の間の処理で、同じレベルのスクリプト名を列挙して全て連結し、★4でstartup() にスクリプト名を渡して実行します。ちなみにrunlevel Sおよび2のときのACTION変数には "start" が入っています。
例えばS10zzz.sh S20aaa.shとS20bbb.shとS30ccc.shがあったとしますと、
このような順にstartup() が呼ばれることになります。
前提: ディストリビューションはDebian GNU/Linux 7.8(Wheezy) 1. Linux Kernel起動終盤にkernel_init() が /sbin/initを起動 2. /sbin/initは /etc/inittabを見る 3. /etc/inittabにはinitへの指示が書いてある 3-1. デフォルトのrunlevelは2である 3-2. ブート時に /etc/init.d/rcSを実行せよ 3-2-1. /etc/init.d/rcSは /etc/init.d/rc Sを起動する 3-3. runlevel 2なら /etc/init.d/rc 2を実行せよ ↑↑↑↑↑ 以上、おさらい1の部分 ↑↑↑↑↑ 4. 下記がいずれか一つでも成立すればCONCURRENCY=none成立しなければCONCURRENCY=makefile - /etc/init.d/.depend.bootが存在しないか、空ファイル - /etc/init.d/.depend.startが存在しないか、空ファイル - /etc/init.d/.depend.stopが存在しないか、空ファイル - /etc/init.d/.legacy-bootorderingが存在する - startpar -vコマンドが失敗する 5. /etc/rcXXXX.d/S* をアルファベット順に実行 CONCURRENCY=noneならば、単に実行するだけ、 CONCURRENCY=makefileならば、startparに起動を任せる
PC Watch - Intel、3G/LTE統合型SoC「Atom x3 C3000」シリーズを読んで。
個人的にはIntelのSoCは携帯端末向けと言いつつ、PCとしても使えます!という作りにした方が輝くと思いますが、その路線も、単純に攻めるのはもう限界とも思います。
というのもPCとして使おうとすると、どうしてもある程度の画面サイズ(出力手段)と、キーボード(入力手段)が欲しくなるからです。
Baytrailが8〜 インチタブレットを捉えたので、単純に考えれば次に狙うのは7インチ以下のタブレットですが、7インチの画面はPCとしては小さすぎますし、キーボードも端末の倍近いサイズになって非常に格好悪いです。
出力手段は、画面を折るとか重ねるとか何とかするにしても、入力手段だけはキーボード以外でキーボード以上の正確さと速さを持つものを見つけないと、ミニ画面PCを実現するのは、ちょいと厳しそうですね…。
メモ: 技術系の話はFacebookから転記しておくことにした。
目次: Linux
自作ARMエミュレータのアイドル状態を実装して、何もしていないのにCPU時間をバカ食いしていた問題を解決しました。
ARM9コアをアイドル状態に移行させるには、mcr命令を使ってコプロセッサ15の特定のレジスタにストアします。具体的に言うとmcr 15, 0, r0, cr7, cr0, {4}(opcode_1:0, crn:7, rd:0, opcode_2:4, crm:0)です。ただ、これだけ実行すれば良いものでもないので、詳細はLinuxのarch/arm/mm/proc-arm926.Sのcpu_arm926_do_idleという関数が参考になります。
アイドル状態に入るとCPUは停止し、デバイスから割り込みが掛かると再開します。実装する前は簡単かなーと思っていたのですが、やってみると意外と大変でした。
今まで割り込み回りの実装をサボっていて、CPUからデバイスの割り込みステータスを全力でポーリングするだけのやっつけ処理しか実装しておらず、デバイスからCPUに何か通知する仕組みが全くなかったためです。
どう見ても自業自得ですね。うん。
新会社に転籍しましたのでプロフィール更新しました。新会社の発足日にも関わらず、公式サイトが未だにお葬式状態なのですが、どういうことなの…。
2015年3月1日をもってソシオネクストに転籍しました。さよならパナソニック。
ソシオネクストは、富士通セミコンダクターとパナソニックのSLSI設計部門を合併させ、ファブレス(工場部門は既に分離済み)として再出発した会社です。
従業員は人事、経理などを除いてほぼ全員が転籍者です。出向と違い、元の会社に戻ることはありません。資本は富士通(4割)、パナソニック(2割)、日本政策投資銀行(4割)がソシオネクストに出資しています。
しかし、いずれの会社とも連結対象ではありませんので、赤字になっても救いの手はありません。反面、親会社の意向を気にせず自由に投資できます。たぶん…。
メモ: 技術系?の話はFacebookから転記(※)しておくことにした。
目次: 自宅サーバー
Twitterで知ったサイト ApacheのAddHandlerはセキュリティ上の懸念から使用すべきではない を読んでいたら、家に置いているサーバの設定に思い切り当てはまっていました。
このサイトにも .cgiファイルを使っている箇所があり、Apacheの設定ファイルには当然のごとくAddHandler cgi-script .cgiと書いてありました。
今のところ、任意のファイル名をアップロードする仕組みはなく、セキュリティホールにならなかったのが不幸中の幸いです。
教訓としては「昔から使っている設定だから動く」は成り立っても「昔から使っている設定だから安全」は成り立たない、ということですね。むしろ何も理解せずにコピペすると危険と言える良い例だったと思います。
ソニー - ハイレゾ対応ウォークマン(R)に最適。高音質再生のためのメモリーカードを読んで。
せっかく輻射ノイズを測ったのなら、グラフの単位や、計測条件くらい書いてくれても良いのでは。
高級オーディオって、人を騙して高い物を売りつけるように見えたり、オカルトっぽく感じてしまうんですよねー。
メモ: 技術系の話はFacebookから転記しておくことにした。
< | 2015 | > | ||||
<< | < | 03 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | - | - | - | - |
合計:
本日: