目次: Linux
昨日(2015年3月5日の日記参照)に引き続き、何となく自分ではわかっているつもりでしたが、いざ変更しようとしたら全然わかっていなかったSysV initの話です。
まずはSysV initの動きのおさらいから。
前提: ディストリビューションは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に起動を任せる
さて、お忘れの方も多いかと思いますが、本題は「initの処理に何か足したい」でした。
もしCONCURRENCY=noneつまり直列版のinitを使っている場合、initに何か足す方法は非常に簡単です。今まで見てきたように、/etc/rcS.d以下にスクリプトファイルを足せば、/etc/init.d/rcSが実行してくれます。
しかしCONCURRENCY=makefileつまり並列版のinitを使っている場合、/etc/rcS.d以下にスクリプトファイルを足すだけでは実行されません。なぜかというとstartparというコマンドが一枚噛んでいるためです。
このstartparというコマンドには2つのモードがあります。
NAME
startpar - start runlevel scripts in parallel
SYNOPSIS
startpar [-p par] [-i iorate] [-t timeout] [-T global_timeout] [-a arg]
prg1 prg2 ... ★1
startpar [-p par] [-i iorate] [-t timeout] [-T global_timeout] -M [
boot|start|stop] ★2
一つは ★1側の、渡された引数を実行するCONCURRENCY=noneに近い動作をするモード(引数に -Mを指定しない)です。
もう一つは ★2側の、別の設定ファイルからスクリプトの依存関係を得て、出来る限り並列に実行する(引数に -Mを指定する)モードです。
さて /etc/init.d/rcでは、startparの2つモードのうち、どちらが使われていたでしょうか?覚えている人はスゴい暗記力です。私も含めて忘れてしまった方のために、もう一度コードを見ます。
#
# 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
簡単ですね。別の設定ファイルを見る(-Mを指定する)モードです。しかもよく見るとstartup() に渡されているスクリプト名($2以降の引数)は無視されています。だから /etc/rcS.d/ にスクリプトを足すだけでは、実行されなかったのです。
ではstartparに何か処理を追加するには、何をどうしたら良いのか?と言う話です。こういうときはまずstartparのマニュアルを見てみます。
The -M option switches startpar into a make(1) like behaviour. This
option takes three different arguments: boot, start, and stop for read-
ing .depend.boot or .depend.start or .depend.stop respectively in the★1
directory /etc/init.d/. By scanning the boot and runlevel directories
in /etc/init.d/ it then executes the appropriate scripts in parallel.
FILES
/etc/init.d/.depend.boot
/etc/init.d/.depend.start
/etc/init.d/.depend.stop
SEE ALSO
init(8) insserv(8). ★2
★1の説明を見るに、-Mはboot, start, stopのうちどれか1つ引数を取って、実際の動作は .depend.boot or .depend.start or .depend.stopで決まりますよ、というようなことが書いてあります。親切なことにFILESの章にファイルのフルパスまで書いてくれています。
これら3つの .depend.XXXXファイルを更新すれば良い、ということがわかりましたが、どうやって更新するのかが書いていなくて困ってしまいます。とりあえず★2のSEE ALSOの章にあるinsservという奴が新顔で怪しいので、マニュアルを見ます。
INSSERV(8) INSSERV(8)
NAME
insserv - boot sequence organizer using LSB init.d script dependency
information
(...略...)
FILES
/etc/insserv.conf
configuration file for insserv which defines the LSB System
Facilities.
(...略...)
/etc/init.d/.depend.boot,
/etc/init.d/.depend.start,
/etc/init.d/.depend.stop
The make(1) like dependency files produced by insserv for boot‐
ing, starting, and stopping with the help of startpar(8). ★1
コマンドの説明はちょっと抽象的でわかりづらいですが、FILESの ★1の部分を見る限り、探し求めていた物に間違いないでしょう。
1. /etc/rcS.d/ にS20script.shのような名前のスクリプトを追加する 2. startpar -Mを使っている場合は、insservを実行して設定ファイルを更新する
まとめてしまえば短いものですが、これもinitの仕組みの一端だと思うと、中々に感慨深いもんがありますね。
この記事にコメントする
目次: 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と書いてありました。
今のところ、任意のファイル名をアップロードする仕組みはなく、セキュリティホールにならなかったのが不幸中の幸いです。
教訓としては「昔から使っている設定だから動く」は成り立っても「昔から使っている設定だから安全」は成り立たない、ということですね。むしろ何も理解せずにコピペすると危険と言える良い例だったと思います。
この記事にコメントする
| < | 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 | - | - | - | - |
25年10月15日
25年10月18日
22年5月5日
25年10月19日
23年4月11日
06年4月22日
25年10月17日
25年10月6日
25年10月13日
20年10月23日
25年10月12日
20年8月29日
19年1月13日
18年10月13日
18年9月3日
18年8月20日
18年7月23日
18年7月22日
18年10月14日
18年11月10日
wiki
Linux JM
Java API
2002年
2003年
2004年
2005年
2006年
2007年
2008年
2009年
2010年
2011年
2012年
2013年
2014年
2015年
2016年
2017年
2018年
2019年
2020年
2021年
2022年
2023年
2024年
2025年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: