最初の一回しかやらないので、うっかり忘れやすいのでメモ。
VirtualBox を便利に使うためにやっておくべき設定。
GuestAddiions はインストールしておく。
・ホスト⇔ゲスト間でコピー&ペースト
設定 -> 一般 -> クリップボードの共有 で双方向 を選択
・ホスト⇔ゲスト間でフォルダ共有
設定 -> 共有フォルダー
で追加。
自動マウントにチェックを入れると /media/以下にマウントされた。
特に /etc/fstab などゲストOS側での設定は不要でした。
(ゲストOSがUbuntuの場合に確認)
2012年10月16日火曜日
2012年10月10日水曜日
LinuxカーネルのMakefileを解析する その8
前回からまたまた間があきました。
今回の話題は O= を付けた時の動作についてです。
ソースを見ていて、あれっ、どうやって動いてるんだっけ?という部分がありましたので記載しておきます。
例えば、Kernel のソースツリーで
$ make O=dir/to/store/output/files/ defconfig vmlinux
とやってみると、
ソースツリーを一切汚すことなく、
dir/to/store/output/files/ に出力結果を置いてくれます。
トップの Makefile の動きは 「LinuxカーネルのMakefileを解析する その1」で解説しました。
今回注目するのは script/Makefile.build です。
例えば、*.c から *.o を生成するサフィックスルールを見てみます。
Makefile.build の300行目過ぎにそれがあります。
今回の話題は O= を付けた時の動作についてです。
ソースを見ていて、あれっ、どうやって動いてるんだっけ?という部分がありましたので記載しておきます。
例えば、Kernel のソースツリーで
$ make O=dir/to/store/output/files/ defconfig vmlinux
とやってみると、
ソースツリーを一切汚すことなく、
dir/to/store/output/files/ に出力結果を置いてくれます。
トップの Makefile の動きは 「LinuxカーネルのMakefileを解析する その1」で解説しました。
今回注目するのは script/Makefile.build です。
例えば、*.c から *.o を生成するサフィックスルールを見てみます。
Makefile.build の300行目過ぎにそれがあります。
# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
$(call cmd,force_checksrc)
$(call if_changed_rule,cc_o_c)
これは $(src)/%.c から $(obj)/%.o を生成してくれるルールを表しています。
Makefile.build の先頭に
src := $(obj)
がありますので、src と obj は同じディレクトリを指しています。
O= 指定がないときは、それでいいのですが、O= 指定ありの時は、 *.c と違うディレクトリに *.o を作りたいわけです。
obj と src が同じなのに、どうやって動いてるんだろう??としばらく悩みましたが、その答えはトップの Makefile の 150行目過ぎにある
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
export srctree objtree VPATH
にあります。
VPATHに $(srctree) が追加されています。
VPATHというのは、make がサフィックスルールにおいて検索するパスを指定するものです。
Cコンパイラでいうところの -I オプションみたいなものといういえばいいでしょうか。
$(obj)/*.o を生成するときに、 $(src)/*.c を探しますが、見つかりません。
次にVPATHで指定されたパスを探しにいき、$(srctree)/$(src)/*.c を使うわけです。
VPATHというのは、私は使わない方がいいと思っている機能なので(どのファイルをコンパイルしているかわかりにくくなる)、うっかり失念しておりました。
2012年10月9日火曜日
make 3.80 と make 3.81 の違い
経験的に以下のような書き方ができることは知っていたけど、
これが許されるのはGNU make version 3.81以降のようだ。
case文みたく、重なりがないことが明らかな場合は以下のようにする方が見やすそうだ。
ifeq ($(FOO),a)
$(warning FOO is a)
else ifeq ($(FOO),b)
$(warning FOO is b)
else ifeq ($(FOO),c)
$(warning FOO is c)
else ifeq ($(FOO),d)
$(warning FOO is d)
endif
make version 3.80 の場合、上記は以下のようにエラーになってしまう。
Makefile:5: FOO is a
Makefile:6: `else' 疑似命令の後ろに無関係な文字列があります
Makefile:8: `else' 疑似命令の後ろに無関係な文字列があります
Makefile:8: *** 一つの条件部につき一つしか `else' を使えません。中止。
ポータビリティのことを考えるなら、以下のように書いた方がよさそうだ。
ifeq ($(FOO),a)
$(warning FOO is a)
else
ifeq ($(FOO),b)
$(warning FOO is b)
else
ifeq ($(FOO),c)
$(warning FOO is c)
else
ifeq ($(FOO),d)
$(warning FOO is d)
endif
endif
endif
endif
しかしこれだと、インデントがないと、わけがわからなくなるな。
ネストが深くなると大変そうだ。
case文みたく、重なりがないことが明らかな場合は以下のようにする方が見やすそうだ。
ifeq ($(FOO),a)
$(warning FOO is a)
endif
ifeq ($(FOO),b)
$(warning FOO is b)
endif
ifeq ($(FOO),c)
ifeq ($(FOO),c)
$(warning FOO is c)
endif
ifeq ($(FOO),d)
ifeq ($(FOO),d)
$(warning FOO is d)
endif
ARM クロスコンパイラをビルドする
普通は Menter Graphics (旧CodeSourcery)からビルド済みのコンパイラをダウンロードする人が多いと思いますが。。
arm-linux-gnueabi-gcc をソースからビルドしてみました。物好きですな。
まずは各種ソースの tarball を入手します。
入手元を併記します。
(1) binutils-2.21.1.tar.bz2 - http://www.gnu.org/software/binutils/ から入手
(2) gcc-4.6.3.tar.bz2 - http://gcc.gnu.org/ から入手
(3) gmp-4.3.2.tar.bz2 - http://gcc.gnu.org/ から入手 (infrastructure の下)
(4) mpc-0.8.1.tar.gz - http://gcc.gnu.org/ から入手 (infrastructure の下)
(5) mpfr-2.4.2.tar.bz2 - http://gcc.gnu.org/ から入手 (infrastructure の下)
(6) linux-3.0.45.tar.bz2 - http://www.kernel.org/ から入手
(7) eglibc-2.13.tar.bz2 - http://www.eglibc.org/ から入手
(7) は公式サイトでtarball を配布してないようなので、
$ svn co svn://svn.eglibc.org/branches/eglibc-2_13 eglibc-2.13
のようにチェックアウトし、
$ tar jcf eglibc-2.13.tar.bz2 eglibc-2.13
のようにして、固めておきました。
ビルドの仕方は
eglibc/libc/EGLIBC.cross-building
に書いてあるので、それに沿ってやるだけなのですが、
ステップ数が多いので、シェルスクリプトにしたものを最後につけておきます。
最後のシェルスクリプトを例えば、 install_gcc.sh などのファイル名で保存し、上記7個の tarball と同じディレクトリに置く。
$ ./install_gcc.sh
で実行するだけ。
同じディレクトリの arm-built-from-src の中に一式出来上がる。
速いマシンだと1時間以内に完了します。
$ ./install_gcc.sh -T /path/to/install/directory
のようにインストール先を指定するオプションも用意してあること。
(補足1)
make 3.82 だとEGLIBC のビルドでコケるので、 make 3.81 を使う。
(「make 3.82 glibc」でググるとこのあたりのことが出てくる。)
(補足2)
eglibc のビルドに GNU awk が必要。
最近のUbuntuだとデフォルト mawk が入っていたりするので apt-get install gawk で入れる。
(補足3)
eglibc はあえて古めの 2.13 を使っている。
2.14 以降だと、rpc/rpc.h などのヘッダーがなくなっていて、あとあと面倒なので。
「glibc rpc/rpc.h」でググるとこのあたりの事情が出てくるが、ちゃんと追いかけてません。
(補足4)
gcc を解凍したディレクトリで
$ contrib/download_prerequisites
とすると、gmp, mpfr, mpc をダウンロードしてくれるが、
ここでは、最初にまとめてtarball を準備しておく方法をとった。
--- シェルスクリプトここから ---
arm-linux-gnueabi-gcc をソースからビルドしてみました。物好きですな。
まずは各種ソースの tarball を入手します。
入手元を併記します。
(1) binutils-2.21.1.tar.bz2 - http://www.gnu.org/software/binutils/ から入手
(2) gcc-4.6.3.tar.bz2 - http://gcc.gnu.org/ から入手
(3) gmp-4.3.2.tar.bz2 - http://gcc.gnu.org/ から入手 (infrastructure の下)
(4) mpc-0.8.1.tar.gz - http://gcc.gnu.org/ から入手 (infrastructure の下)
(5) mpfr-2.4.2.tar.bz2 - http://gcc.gnu.org/ から入手 (infrastructure の下)
(6) linux-3.0.45.tar.bz2 - http://www.kernel.org/ から入手
(7) eglibc-2.13.tar.bz2 - http://www.eglibc.org/ から入手
(7) は公式サイトでtarball を配布してないようなので、
$ svn co svn://svn.eglibc.org/branches/eglibc-2_13 eglibc-2.13
のようにチェックアウトし、
$ tar jcf eglibc-2.13.tar.bz2 eglibc-2.13
のようにして、固めておきました。
ビルドの仕方は
eglibc/libc/EGLIBC.cross-building
に書いてあるので、それに沿ってやるだけなのですが、
ステップ数が多いので、シェルスクリプトにしたものを最後につけておきます。
最後のシェルスクリプトを例えば、 install_gcc.sh などのファイル名で保存し、上記7個の tarball と同じディレクトリに置く。
$ ./install_gcc.sh
で実行するだけ。
同じディレクトリの arm-built-from-src の中に一式出来上がる。
速いマシンだと1時間以内に完了します。
$ ./install_gcc.sh -T /path/to/install/directory
のようにインストール先を指定するオプションも用意してあること。
(補足1)
make 3.82 だとEGLIBC のビルドでコケるので、 make 3.81 を使う。
(「make 3.82 glibc」でググるとこのあたりのことが出てくる。)
(補足2)
eglibc のビルドに GNU awk が必要。
最近のUbuntuだとデフォルト mawk が入っていたりするので apt-get install gawk で入れる。
(補足3)
eglibc はあえて古めの 2.13 を使っている。
2.14 以降だと、rpc/rpc.h などのヘッダーがなくなっていて、あとあと面倒なので。
「glibc rpc/rpc.h」でググるとこのあたりの事情が出てくるが、ちゃんと追いかけてません。
(補足4)
gcc を解凍したディレクトリで
$ contrib/download_prerequisites
とすると、gmp, mpfr, mpc をダウンロードしてくれるが、
ここでは、最初にまとめてtarball を準備しておく方法をとった。
--- シェルスクリプトここから ---
#!/bin/bash # version gmp=gmp-4.3.2 mpfr=mpfr-2.4.2 mpc=mpc-0.8.1 binutils=binutils-2.21.1 gcc=gcc-4.6.3 eglibc=eglibc-2.13 linux=linux-3.0.45 # constant variables tarballtop=$(pwd) worktop=$(pwd) tools=$worktop/arm-built-from-src target=arm-none-linux-gnueabi sysroot=$tools/$target/libc linux_arch=arm # # helper functions # msg () { echo ";" echo ";" echo ";;;;; $1 ;;;;;" echo ";" echo ";" } usage () { cat <&2 usage: install_gcc.sh [-t tarballtop] [-w worktop] [-T tools] [targets...] Valid targets are: binutils Build and install binutils gcc1 Build and install the first gcc linuxhdr Install Linux kernel headers eglibchdr Install EGLIBC headers gcc2 Build and install the second gcc eglibc Build and install eglibc gcc3 Build and install the third gcc all Do all of above in above order If no target is given, "install_gcc all" shall be run. EOF } optcheck () { if [ -z $2 ] || [ $(echo $2 | cut -c 1) != "/" ]; then echo "error: specify absolute pass for $1 option" exit 1 fi } unpack () { # search *.tar.gz, *.tar.bz2, *.tar.xz, *.tgz, *.tbz2 *.tar # in this order. # If found, the tarball shall be uncompressed. for suffix in tar.gz tar.bz2 tar.xz tgz tbz2 tar; do tarball=$tarballtop/$1.$suffix if [ -f $tarball ]; then case $suffix in tar.gz) taropt=-zxf ;; tar.bz2) taropt=-jxf ;; tar.xz) taropt=-Jxf ;; tgz) taropt=-zxf ;; tbz2) taropt=-jxf ;; tar) taropt=-xf ;; *) echo "error: something wrong. fix me." >&2 ; exit 1 ;; esac echo "uncompressing $tarball ..." tar $taropt $tarball return fi done echo "error: tarball not found. Abort." >&2 exit 1 } gcc_prerequisites () { # $1: relative or absolute path to gcc source directory # sanity check if [ -z $1 ]; then echo "error: gcc_prerequisites was called without args" >&2 exit 1 fi gccdir=$1 if [ ! -d $gccdir ]; then echo "error: directory \'$gccdir\' not found" exit 1 fi # cd $gccdir # ./contrib/download_prerequisites # Instead of doing above, do following: unpack $gmp unpack $mpfr unpack $mpc mv $gmp $mpfr $mpc $gccdir/ ln -s $gmp $gccdir/gmp ln -s $mpfr $gccdir/mpfr ln -s $mpc $gccdir/mpc } install_binutils () { srcdir=$binutils objdir=obj-binutils cd $worktop rm -rf $srcdir $objdir unpack $srcdir mkdir -p $objdir cd $objdir $worktop/$srcdir/configure \ --target=$target \ --prefix=$tools \ --with-sysroot=$sysroot make make install cd $worktop rm -rf $srcdir $objdir } install_gcc1 () { srcdir=$gcc objdir=obj-gcc1 cd $worktop rm -rf $srcdir $objdir unpack $srcdir gcc_prerequisites $srcdir mkdir -p $objdir cd $objdir $worktop/$srcdir/configure \ --target=$target \ --prefix=$tools \ --without-headers --with-newlib \ --disable-shared --disable-threads --disable-libssp \ --disable-libgomp --disable-libmudflap \ --disable-libquadmath \ --enable-languages=c PATH=$tools/bin:$PATH make PATH=$tools/bin:$PATH make install cd $worktop rm -rf $srcdir $objdir } install_linuxhdr () { srcdir=$linux cd $worktop rm -rf $srcdir unpack $srcdir cd $srcdir make headers_install \ ARCH=$linux_arch CROSS_COMPILE=$target- \ INSTALL_HDR_PATH=$sysroot/usr cd $worktop rm -rf $srcdir } install_eglibchdr () { srcdir=$eglibc objdir=obj-eglibc-headers cd $worktop rm -rf $srcdir $objdir unpack $srcdir mv $srcdir/ports $srcdir/libc mkdir -p $objdir cd $objdir BUILD_CC=gcc \ CC=$tools/bin/$target-gcc \ CXX=$tools/bin/$target-g++ \ AR=$tools/bin/$target-ar \ RANLIB=$tools/bin/$target-ranlib \ $worktop/$srcdir/libc/configure \ --prefix=/usr \ --with-headers=$sysroot/usr/include \ --host=$target \ --disable-profile --without-gd --without-cvs --enable-add-ons make install-headers install_root=$sysroot \ install-bootstrap-headers=yes make csu/subdir_lib mkdir -p $sysroot/usr/lib cp csu/crt1.o csu/crti.o csu/crtn.o $sysroot/usr/lib $tools/bin/$target-gcc -nostdlib -nostartfiles -shared -x c /dev/null \ -o $sysroot/usr/lib/libc.so cd $worktop rm -rf $srcdir $objdir } install_gcc2 () { srcdir=$gcc objdir=obj-gcc2 cd $worktop rm -rf $srcdir $objdir unpack $srcdir gcc_prerequisites $srcdir mkdir -p $objdir cd $objdir $worktop/$srcdir/configure \ --target=$target \ --prefix=$tools \ --with-sysroot=$sysroot \ --disable-libssp --disable-libgomp --disable-libmudflap \ --disable-libquadmath \ --enable-languages=c PATH=$tools/bin:$PATH make PATH=$tools/bin:$PATH make install cd $worktop rm -rf $srcdir $objdir } install_eglibc () { srcdir=$eglibc objdir=obj-eglibc cd $worktop rm -rf $srcdir $objdir unpack $srcdir mv $srcdir/ports $srcdir/libc mkdir -p $objdir cd $objdir BUILD_CC=gcc \ CC=$tools/bin/$target-gcc \ CXX=$tools/bin/$target-g++ \ AR=$tools/bin/$target-ar \ RANLIB=$tools/bin/$target-ranlib \ $worktop/$srcdir/libc/configure \ --prefix=/usr \ --with-headers=$sysroot/usr/include \ --host=$target \ --disable-profile --without-gd --without-cvs --enable-add-ons PATH=$tools/bin:$PATH make PATH=$tools/bin:$PATH make install install_root=$sysroot cd $worktop rm -rf $srcdir $objdir } install_gcc3 () { srcdir=$gcc objdir=obj-gcc3 cd $worktop rm -rf $srcdir $objdir unpack $srcdir gcc_prerequisites $srcdir mkdir -p $objdir cd $objdir $worktop/$srcdir/configure \ --target=$target \ --prefix=$tools \ --with-sysroot=$sysroot \ --enable-__cxa_atexit \ --disable-libssp --disable-libgomp --disable-libmudflap \ --enable-languages=c,c++ make make install cd $worktop rm -rf $srcdir $objdir } # stop immediately if an error occurres set -e # process options while [ $# -gt 0 ] ; do case "$1" in -t) optcheck $1 $2; shift; tarballtop=$1 ;; -w) optcheck $1 $2; shift; worktop=$1 ;; -T) optcheck $1 $2; shift; tools=$1; sysroot=$tools/$target/libc ;; -*) (echo error: $1: unknown option ; echo ) >&2 ; usage ; exit 1 ;; *) break;; esac shift done if [ $# -eq 0 ] ; then set all fi # process targets while [ $# -gt 0 ] ; do case "$1" in binutils) msg binutils ; install_binutils ;; gcc1) msg gcc1 ; install_gcc1 ;; linuxhdr) msg linuxhdr ; install_linuxhdr ;; eglibchdr) msg eglibchdr ; install_eglibchdr ;; gcc2) msg gcc2 ; install_gcc2 ;; eglibc) msg eglibc ; install_eglibc ;; gcc3) msg gcc3 ; install_gcc3 ;; all) set binutils gcc1 linuxhdr eglibchdr gcc2 eglibc gcc3; continue ;; *) (echo error: $1: unknown target ; echo ) >&2 ; usage ; exit 1;; esac shift done
2012年10月4日木曜日
tftp サーバー on Ubuntu12.04.1
Ubuntu 12.04.1 に tftp サーバーを入れた時のメモ。
$ sudo apt-get install tftpd
設定ファイルが /etc/xinetd.d/tftp にあるはず、、なのに何も作ってくれてない。
そこで、 RHEL マシンから設定ファイルを持ってきた。
/etc/xinetd.d/tftp
# default: off
# description: The tftp server serves files using the trivial file transfer \
# protocol. The tftp protocol is often used to boot diskless \
# workstations, download configuration files to network-aware printers, \
# and to start the installation process for some operating systems.
service tftp
{
disable = no
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s /var/lib/tftpboot
per_source = 11
cps = 100 2
flags = IPv4
}
一箇所ハマった点。
tftp はデフォルトではクライアントからの put での新規ファイル作成はできないようになっている。
(すでに存在するファイルへの上書きはできる。)
そこで、以下のように -c をつけておくと、新規作成もできるはず、、
server_args = -c -s /var/lib/tftpboot
RHEL ではこれでうまくいったが、Ubuntu だと、 -c がついていると、get も put も 「Transfer timed out」になってしまって、さっぱり動きません。
セキュリティ上の配慮なんでしょうか。。
というわけで、新規作成はあきらめることにしました。
とりあえず一番やりたいことは、 u-boot からの kernel ダウンロードだから、get さえできれば、ひとまずはOKです。
$ sudo apt-get install tftpd
設定ファイルが /etc/xinetd.d/tftp にあるはず、、なのに何も作ってくれてない。
そこで、 RHEL マシンから設定ファイルを持ってきた。
/etc/xinetd.d/tftp
# default: off
# description: The tftp server serves files using the trivial file transfer \
# protocol. The tftp protocol is often used to boot diskless \
# workstations, download configuration files to network-aware printers, \
# and to start the installation process for some operating systems.
service tftp
{
disable = no
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s /var/lib/tftpboot
per_source = 11
cps = 100 2
flags = IPv4
}
一箇所ハマった点。
tftp はデフォルトではクライアントからの put での新規ファイル作成はできないようになっている。
(すでに存在するファイルへの上書きはできる。)
そこで、以下のように -c をつけておくと、新規作成もできるはず、、
server_args = -c -s /var/lib/tftpboot
RHEL ではこれでうまくいったが、Ubuntu だと、 -c がついていると、get も put も 「Transfer timed out」になってしまって、さっぱり動きません。
セキュリティ上の配慮なんでしょうか。。
というわけで、新規作成はあきらめることにしました。
とりあえず一番やりたいことは、 u-boot からの kernel ダウンロードだから、get さえできれば、ひとまずはOKです。
ntpdate と ntp daemon on Ubuntu
Ubuntu 12.04.1 LTS で ntp daemon をインストールした。
Ubuntu では標準で ntpdate がインストールされている。
両者の違いは以下のような感じと思っている。
・ntpdate
実行したときにワンショットで、すぐにサーバーの時刻と同期。
・ntp daemon
起動してもすぐには同期しない。
長期的にずれないようにうまく保ってくれるもの。どういうアルゴリズムなのかはよく知らない。
デフォルトでは、ネットワークがつながった時に
/etc/network/if-up.d/ntpdate から
/usr/sbin/ntpdate-debian
が自動実行されるようになっていると思っている(たぶん)。
ntpdate-debian は簡単なシェルスクリプトなので、中身見てみると、
/etc/default/ntpdate
を設定にして、ntpdate を実行しているようだ。
サーバー名はデフォルトで
NTPSERVERS="ntp.ubuntu.com"
を見るようだ。
会社などプロキシなどの問題がある場合、NTPSERVERSのところを会社指定のntpサーバに書きかえれば、同期してくれるようになった。
ntp daemon はデフォルトで入っていないので、
$ sudo apt-get install ntp
でインストールする必要がある。
/etc/ntp.conf が設定ファイルなので
server のところを必要に応じて書き換える。
ところで、ntpdate と ntp daemon が両方入っていたらどうなるんでしょう。。
ntp daemon がすでに起動しているときに、ntpdate を実行しようとすると
the NTP socket is in use, exiting
とエラーが出て、実行できない。
一方、
システムツール -> システム設定 -> 日付と時刻
で「ネットワーク時刻」をオフからオンに切り替えたときや、
新たにネットワークがつながったときは、
ntp daemon が NTP socket をつかむ前に ntpdate が実行されるように見えます。
よって、ntpdate が最初にガツンと合わせ、その後は ntp daemon がずれないように維持してくれるように見えました。
Ubuntu では標準で ntpdate がインストールされている。
両者の違いは以下のような感じと思っている。
・ntpdate
実行したときにワンショットで、すぐにサーバーの時刻と同期。
・ntp daemon
起動してもすぐには同期しない。
長期的にずれないようにうまく保ってくれるもの。どういうアルゴリズムなのかはよく知らない。
デフォルトでは、ネットワークがつながった時に
/etc/network/if-up.d/ntpdate から
/usr/sbin/ntpdate-debian
が自動実行されるようになっていると思っている(たぶん)。
ntpdate-debian は簡単なシェルスクリプトなので、中身見てみると、
/etc/default/ntpdate
を設定にして、ntpdate を実行しているようだ。
サーバー名はデフォルトで
NTPSERVERS="ntp.ubuntu.com"
を見るようだ。
会社などプロキシなどの問題がある場合、NTPSERVERSのところを会社指定のntpサーバに書きかえれば、同期してくれるようになった。
ntp daemon はデフォルトで入っていないので、
$ sudo apt-get install ntp
でインストールする必要がある。
/etc/ntp.conf が設定ファイルなので
server のところを必要に応じて書き換える。
ところで、ntpdate と ntp daemon が両方入っていたらどうなるんでしょう。。
ntp daemon がすでに起動しているときに、ntpdate を実行しようとすると
the NTP socket is in use, exiting
とエラーが出て、実行できない。
一方、
システムツール -> システム設定 -> 日付と時刻
で「ネットワーク時刻」をオフからオンに切り替えたときや、
新たにネットワークがつながったときは、
ntp daemon が NTP socket をつかむ前に ntpdate が実行されるように見えます。
よって、ntpdate が最初にガツンと合わせ、その後は ntp daemon がずれないように維持してくれるように見えました。
登録:
投稿 (Atom)