sstea備忘録

日々のPCライフのメモ、備忘録、及びそれに類推する何か。
記載内容に間違い等を発見された場合はコッソリ教えてください... → sstea<a>aol.jp (<a> を @ に変えてください)


Linux

Raspberry Pi に起こしてもらう

今さらながらですが、Raspberry Pi をアラーム(目覚まし)として使おうと思います。
音も鳴るようにしましたし、VPN サーバーを構築して常時電源入れたまんまになったので。。。

◆アラームとして求める要件
"mpd play" を cron に登録、、、でアラームとしては最低限、機能するんですが、それだけだとおもしろくないので。
時間になったらお知らせしてくれるのは当然として、下記三点を満たすものを目指します。

・好きな音をアラーム音として指定可能。
・最初は控えめな音量で、徐々に音量が大きくなる。
・一定時間経てば勝手に鳴り止む。
(ひょっとしたら、その場にいないかもしれないですし。鳴りっぱなしだと、近所迷惑なので。)

◆mp3 ファイルの再生
アラーム音としては、一旦 mp3 ファイルのみを考えます。

まずは mp3 ファイルを再生するために必要なパッケージをインストールします。
$ sudo apt-get install mpg123

mpg123 コマンドは指定フレーム分だけ再生する、とかがオプション指定で出来るのでそれを利用します。
(↓おまけ1にて補足)
ただ、フレーム指定だと直感的に分かりにくいので、下記のようなラッパーのシェルを作りました。

mp3play.sh
#!/bin/bash

n_option=
k_option=

mp3_file=$1
time=$(($2))
offset=$(($3))

if [ ! -f "${mp3_file}" ]; then
    echo "no such file : ${mp3_file}"
    exit 1
fi

# 30s is about 1150frame
if [ ${time} -ne 0 ]; then
    n_option="-n $((${time} * 38))"
fi
if [ ${offset} -ne 0 ]; then
    k_option="-k $((${offset} * 38))"
fi

mpg123  ${n_option} ${k_option} "${mp3_file}" &> /dev/null

使用方法としては、
・第一引数にmp3 ファイル
・第二引数に再生時間(秒数指定、0 なら最後まで再生)※
・第三引数に再生オフセット(秒数指定、0 なら最初から再生)※
を指定する感じです。
※ 数字以外、または省略時は 0 と解釈されます。
→ "Usage: mp3play.sh mp3-file [time] [offset]"

再生オフセットを微調整することでいきなりサビの部分から再生が出来ます。

◆音量の変更

alarm.sh
#!/bin/bash

volume=$(($1))
period=$(($2))

prev_volume=`amixer get PCM | sed -n "s/.*\[\(.*\)%\].*/\1/gp" | head -n 1`

if [ ${volume} -ne 0 ]; then
    amixer set PCM ${volume}% > /dev/null
else
    volume=${prev_volume}
fi

shift 2
/home/pi/local/bin/mp3play.sh "$@" &

if [ ${period} -ne 0 ]; then
    while [ ${volume} -lt 100 ]; do
        sleep ${period}
        volume=$((${volume} + 5))
        amixer set PCM ${volume}% > /dev/null
    done
fi

wait

if [ ${volume} -ne ${prev_volume} ]; then
    amixer set PCM ${prev_volume}% > /dev/null
fi

音量を変更するので、実行前の音量を prev_volume に退避し、終了時に元に戻しています。
第三引数以降は、上記で作った mp3play.sh にそのまま渡して、実行します。
その後、一定秒数ごとに 5% ずつボリュームアップさせ、100% までアップさせたら mp3play.sh が終了するのを待ち合わせます。

使用方法としては、
・第一引数に音量(%指定、0 ならその時のシステム音量)※
・第二引数にボリュームアップ間隔(秒数指定、0 ならボリュームアップしない)※
・第三引数にmp3 ファイル
・第四引数に再生時間(秒数指定、0 なら最後まで再生)※
・第五引数に再生オフセット(秒数指定、0 なら最初から再生)※
※ 数字以外、または省略時は 0 と解釈されます。
を指定する感じです。
→ "Usage: alarm.sh volume period mp3-file [time] [offset]"

◆スケジューリング登録
$ cd /home/pi/local/bin/
$ curl -O http://sstea.blog.jp/raspi/script/mp3play.sh
$ curl -O http://sstea.blog.jp/raspi/script/alarm.sh
$ chmod 755 mp3play.sh alarm.sh
スクリプトを取得し、実行権限を付与します。

crontab コマンドで末尾に処理を追加します。
$ crontab -e
月曜日から金曜日の7時30分に実行させる設定が下記です。
ボリューム10%からスタートし、2秒ごとにボリュームアップ、60秒後に停止、という引数で alarm.sh を実行させます。hoge.mp3 は任意のファイルを指定してください。
    : (略)
30 7 * * 1-5 /home/pi/local/bin/alarm.sh 10 2 "/home/pi/hoge.mp3" 60 0

◆アラームの停止
現状、途中で止める方法がありません。(鳴り止むのを待つしかないです。)
どうしても止めたい場合は、
$ killall mpg123
とでもしてください。
(んな事しなくても、電源抜いたらさすがに止まりますけどね。)

◆おまけ1

mp3 ファイルの再生には mpg123 コマンドでもいいですが、mpg321 コマンドでも可能です。
どっちを使ってもやりたいことは可能と思います。オプションが微妙に違いますが。

自分の環境では mpg123 コマンドのインストールには 316kB 必要でした。
top コマンドでCPU使用率を見ていると大体 14% 程度といったところでした。

一方、mpg321 コマンドのインストールにはその他のパッケージのインストールも必要で合計 5025kB 必要でした。
top コマンドでCPU使用率を見ていると大体 9% 程度といったところでした。

自分はディスク容量を取り、mpg123 の方を使用する事にしました。
ちなみに mpd で再生した場合のtopコマンドでのCPU使用率も14%程度でした。参考までに。

Raspberry Pi を VPN サーバーにする、改

前回に引き続き、Raspberry Pi のVPN サーバーの設定を行ないます。

◆VPN 経由で Raspberry Pi 上の web サーバーにローカルアクセス出来ない問題

前回までで VPN 接続は出来るようになりましたが、前回環境ではなぜか VPN 経由では Raspberry Pi 上の web サーバーにローカルアクセスIPアドレスでアクセス出来ませんでした。

いろいろ Google 先生に問い合わせてみると、
「Linux および Solaris オペレーティングシステムでは、仮想 HUB (VPN) の内側からローカルブリッジ先のLANカードから LAN への通信は行うことが出来ますが、ローカルブリッジしている LAN カード自体に対して通信することはできません。これは Linux カーネルの制限事項です。」
という情報を Google 先生から頂きました。

つまり、Linux ではこれは仕様という事。。。
回避策は、、、物理的にLANカードもう一個挿す事。。。

何とか、ソフトだけで回避はできないのか!?
→ できるみたいです!!


何かブリッジの設定をすればいいっぽいみたいですが、すいません。正直、よく分かってないです。

http://www.petapico.biz/ラジ館petapicoshop/softether-vpnで外から-irmagician-を制御してエアコンをつける/

のページの情報が凄く役立ちました。先人の知恵に感謝です。


以下の手順で解決しました。

◆ローカルブリッジの設定

必要パッケージをインストールします。
$ sudo apt-get install bridge-utils

/etc/network/interfaces
を修正します。
auto eth0
iface eth0 inet manual


auto br0
iface br0 inet static
  address 192.168.0.100
  netmask 255.255.255.0
  network 192.168.0.0
  broadcast 192.168.0.255
  gateway 192.168.0.1
  bridge_ports eth0
  bridge_maxwait 10


前回作った /etc/init.d/vpnserver に下記を追加します。
        : (略)
vpn_start() {
    ${DAEMON} start
    sleep 2
    tap=`ip tuntap | cut -f1 -d:`
    brctl addif br0 ${tap}

}
        : (略)

上記を追加した part2 版が↓です。
$ curl -O http://sstea.blog.jp/raspi/script/vpnserver2 > vpnserver
$ chmod 755 vpnserver
$ sudo mv vpnserver /etc/init.d/

◆tapデバイスの設定

Windows 側で「SoftEther VPN サーバー管理マネージャ」を立ち上げます。
前回の (F-2)  ~ (F-4) の部分の設定を変えます。

メインウィンドウから「ローカルブリッジ設定」ボタンを押下し、「ローカルブリッジ設定」ウィンドウを出します。
「ローカルブリッジの削除」ボタンで前回追加した設定は一旦、削除します。

(F-2)' [新しいローカルブリッジの定義]->[仮想 HUB] を選択
(F-3)' [作成する種類] で「新しい tap デバイスとのブリッジ接続」を選択
(F-4)' [新しい tap デバイス名] を入力
(F-5)' 「ローカルブリッジを追加」ボタンを押下


これで、VPN 経由でも Raspberry Pi 上の web サーバーにアクセス出来るようになりました。

これからは Raspberry Pi を VPN サーバーとして常時稼動させようと思いますので、どれほどの省電力効果があるか分かりませんが、オーバークロック設定は None に戻しておこうと思います。

◆おまけ1
Xperia Z3 には以下のAndroidアプリを入れています。

・WOL用アプリ
Fing - Network Tools

LAN内のマシンを自動的に検出してくれるので、MACアドレスとか入力しなくてもよくてお手軽です。

・リモートデスクトップ用アプリ
Splashtop 2 Remote Desktop

リモートデスクトップでアクセスしたいマシンに Splashtop Streamer をインストールしておく必要はありますが、軽い動画くらいなら普通にリモートデスクトップ経由で見れるくらいのポテンシャルを持ってます。(音も鳴ります)

外出先から VPN 接続し、上記アプリを使用して PC の電源を入れてリモートデスクトップで操作、という事が普通にできてます。

◆おまけ2
Windowsマシンでの動画再生には VLC メディアプレイヤーを使ってるんですが、Splashtop のリモートデスクトップ経由で動画再生した時に、VLC のウィンドウ内が真っ黒で動画が見えないことがありました。

VLC の[ツール]->[設定]->[ビデオ]->[ディスプレイ]->[出力] を「Direct3Dビデオ出力」に設定することで、表示されるようになりました。

Raspberry Pi を VPN サーバーにする

今回は、外出先から家のPCにアクセスするために Raspberry Pi を VPN(Virtual Private Network) サーバーにしてみたいと思います。
SotfEther VPN というOSSのVPNソフトがありますので、それを今回は使用してみる事にします。

以下の要件を満たす事が可能な環境を構築する事を目指します。

・PCの電源が落ちていれば外出先から電源投入できる(Wake On LAN)。
・外出先からPCにスマホのリモートデスクトップアプリでアクセスできる。
・外出先から前回作ったwebブラウザで Raspberry Pi を操作できる。
・セキュリティとかあんまり詳しくないので、そのあたりは良きに計らってくれる。

◆VPN(SoftEther) サーバーソフトのインストール
https://ja.softether.org/4-docs/1-manual/7/7.3 を参考にインストールしました。

http://www.softether-download.com/ja.aspx から SoftEther VPN Server の ARM EABI 版をダンロードし、展開します。
(現時点の最新は v4.15-9546-beta-2015.04.05 でした)
$ wget http://jp.softether-download.com/files/softether/v4.15-9546-beta-2015.04.05-tree/Linux/SoftEther_VPN_Server/32bit_-_ARM_EABI/softether-vpnserver-v4.15-9546-beta-2015.04.05-linux-arm_eabi-32bit.tar.gz

$ tar xvzf softether-vpnserver-v4.15-9546-beta-2015.04.05-linux-arm_eabi-32bit.tar.gz
$ cd vpnserver/
$ make

後は、何回か「1」を押してもらえればいいです。。


公式のマニュアルでは vpnserver は /usr/local/vpnserver に移動させていますが、自分は /home/pi/local/vpnserver に移動しておきます。(個人的な趣味です。)

公式マニュアルに従って、root のみ実行可能なように権限変更を行なっておきます。
$ mv vpnserver /home/pi/local/vpnserver

$ cd /home/pi/local/vpnserver/
$ chmod 600 *
$ chmod 700 vpncmd vpnserver

$ cd ../
$ sudo chown -R root:root vpnserver/
vpncmd の check コマンドで動作チェックできます。
$ cd /home/pi/local/vpnserver/
$ sudo ./vpncmd

◆VPN のスタートアップスクリプトを登録

公式のスタートアップスクリプトやその他諸々を参考に書きました。

/etc/init.d/vpnserver
#!/bin/sh
### BEGIN INIT INFO
# Provides:       vpnserver
# Required-Start: $local_fs $network
# Required-Stop:  $local_fs $network
# Default-Start:  2 3 4 5
# Default-Stop:   0 1 6
# Description:    SoftEther VPN Server
### END INIT INFO

DAEMON=/home/pi/local/vpnserver/vpnserver
LOCK=/var/lock/vpnserver

vpn_start() {
    ${DAEMON} start
}

vpn_stop() {
    ${DAEMON} stop
}

test -x ${DAEMON} || exit 0

case "$1" in
start)
    vpn_start
    touch ${LOCK}
    ;;
stop)
    vpn_stop
    rm ${LOCK}
    ;;
restart)
    vpn_stop
    sleep 3
    vpn_start
    ;;
*)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
esac

exit 0

上記を実行権限を与えて、 /etc/init.d/ 直下に移動し、ブート時に自動起動するように登録します。
$ curl -O http://sstea.blog.jp/raspi/script/vpnserver
$ chmod 755 vpnserver

$ sudo mv vpnserver /etc/init.d/
$ sudo chkconfig --add vpnserver

VPNサービスを開始します。再起動でもいいです。ブート時に自動起動するので。
$ sudo /etc/init.d/vpnserver start

◆VPNの設定
VPNの設定は Windows 側から行なえるので楽でした。

http://www.softether-download.com/ja.aspx から「SoftEther VPN Server Manager for Windows」をダンロードし、windows マシンにインストールし、管理ツールを実行します。

(A-1) 「新しい接続設定」ボタンを押下
(A-2) 「新しい接続設定の作成」ウィンドウが出るので、[接続設定名], [ホスト名] をそれぞれ入力し、[OK]
(A-3) 「接続」ボタンを押下
ss_softether01
ss_softether02
(B-1) メインのウィンドウ(「VPN サーバー管理マネージャ」)が出るので、「仮想 HUB の作成」ボタンを押下
(B-2) 「仮想 HUB の新規作成」ウィンドウが出るので、[仮想 HUB 名] を入力し、[OK]
ss_softether03
ss_softether04

(C-1) メインウィンドウに戻り、「仮想 HUB の管理」ボタンを押下
(C-2) 適宜「ログ保存設定」を設定(変更しなくても可)
(C-3) 「仮想 HUB の管理」ウィンドウが出るので、「ユーザーの管理」ボタンを押下
ss_softether05

(D-1) 「ユーザーの管理」ウィンドウが出るので、「新規作成」ボタンを押下
(D-2) [ユーザーの新規作成]ウィンドウが出るので、[ユーザー名], [パスワード] を入力し、[OK]
ss_softether06
ss_softether07

(E-1) メインウィンドウに戻り、「IPsec / L2TP 設定」ボタンを押下
(E-2) 「IPsec / L2TP / EtherIP / L2TPv3 設定」ウィンドウが出るので、[L2TP サーバー機能を有効にする(L2TP over IPsec)] をチェック
(E-3) [IPsec 共通設定]->[IPsec 事前共有鍵] を入力し、[OK]
ss_softether08

(F-1) メインウィンドウに戻り、「ローカルブリッジ設定」ボタンを押下
(F-2) 「ローカルブリッジ設定」ウィンドウが出るので、[新しいローカルブリッジの定義]->[仮想 HUB] を選択
(F-3) [作成する種類] で「物理的な既存の LAN カードとのブリッジ接続」を選択
(F-4) 「ローカルブリッジを追加」ボタンを押下
ss_softether09

(G-1) メインウィンドウに戻り、「ダイナミック DNS 設定」ボタンを押下
(G-2) 「ダイナミック DNS 設定」ウィンドウが出るので、[設定の変更]->[ダイナミック DNS ホスト名の変更] を入力
(G-3) 「上記の DNS ホスト名に変更する」ボタンを押下
ss_softether10

◆ルーターの設定
外出先から Raspberry Pi にアクセスするためには、ルーターのポートを開放しておく必要があります。
L2TP/IPSec を使用する場合、500(UDP) と4500(UDP) の2つのポートを開放する必要があります。

ルータによって「ポートフォワーディング」とか「ポートマッピング」とかで設定できるはずです。
ちなみに、うちにあるルーター(CG-WLR300NM)の場合は、[詳細設定] ->[バーチャル・サーバ(ポート開放)] で設定しました。

だいたいのルーターは設定後、ルーターを再起動させれば反映されると思います。

◆VPN接続テスト
スマホ側からVPN接続できるか確認してみます。
下記は Xperia Z3 の場合の設定例です。

(1) [設定]->[Wi-Fi] を OFF (同一LAN 内の場合)

(2) [設定]->[その他の設定]->[VPN] に移動

(3) [VPNプロフィールの追加]
     「名前」を適宜入力
     「タイプ」に [L2TP/IPSec PSK] を設定
     「サーバーアドレス」に (G-2) で入力したホスト名を入力
     「IPSec事前共有鍵」に (E-3) で入力した事前共有鍵を入力
ss_xperia_vpn01

(4) 追加したVPNプロフィールに接続
      「ユーザー名」、「パスワード」に (D-2) で入力したユーザー名、パスワードを入力し、接続


VPN接続が正常に行なえたなら、リモートデスクトップ、WOLもアプリ入れればローカルIPアドレスで可能と思います。
※ただし現段階では、VPN 経由での前回作ったwebブラウザ経由での Raspberry Pi 操作ができません。

ちょっと長くなってきたので、今回はここまでです。
次回、上記課題の解決を試みます。

Raspberry Pi をスマホで操作する

以前にジョイスティックで Raspberry Pi の操作を試みましたが、よく考えたら「スマホで操作できたらそれでいいんじゃない?」(半年前にXperia Z3を買ったので。)と思いましたので、今度はスマホでの Raspberry Pi の操作にチャレンジしたいと思います。

スマホ(Xperia Z3)からウェブブラウザ経由で Raspberry Pi のリブート等の操作ができる事を目指します。

ちなみに操作画面のイメージは↓こんな感じです。
(Xperia Z3からドルフィンブラウザでアクセス)
ss_xperia_cmd01

このあたりについては専門外なのでカッコいいUIの操作画面とかまで求められても、自分には無理です。
動くことをもって良しとしてください。

◆apache, php のインストール
まずは必要なパッケージをインストールします。
$ sudo apt-get install apache2 php5

以前に /var/log/ 以下はRAMディスクを使用するようにしていましたので、/etc/init.d/init-ramdisk に下記を追記しておきます。
mkdir -p /var/log/apache2/

◆Raspberry Pi 操作用 html & php

google 先生に訊きながらいろいろ参考にして作ってみましたが、正直、自分は素人なので、無駄な事や一般的でない事等、いろいろイケてない事とかしてるかもしれません。。。ご容赦を。

command.html
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta http-equiv="Content-Script-Type" content="text/javascript" />
  <meta http-equiv="Content-Style-Type" content="text/css" />
  <script type="text/javascript">
//<![CDATA[
function createXMLHttpRequest() {
  if (window.XMLHttpRequest) {
    return new XMLHttpRequest();
  } else {
    return new ActiveXObject("Microsoft.XMLHTTP");
  }
}

function entryClickEvent(id, handler) {
  var obj = document.getElementById(id);
  if (window.addEventListener) {
    obj.addEventListener("click", handler, false);
  } else {
    obj.attachEvent("onclick", handler);
  }
}

function doCommand(command) {
  var request = createXMLHttpRequest();
  request.open("POST", "command.php", true);
  request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  request.send("cmd=" + encodeURIComponent(command));
}

function doCommandConfirm(command)
{
  ret = confirm("本当に実行してもよろしいですか?\n" + command);
  if (ret == true) {
    doCommand(command);
  }
}

function command1() {
  doCommand("mpc prev");
}
function command2() {
  doCommand("mpc toggle");
}
function command3() {
  doCommand("mpc next");
}
function command4() {
  doCommandConfirm("sudo shutdown -r now");
}
function command5() {
  doCommandConfirm("sudo shutdown -h now");
}

window.onload = function() {
  entryClickEvent('btn1', command1);
  entryClickEvent('btn2', command2);
  entryClickEvent('btn3', command3);
  entryClickEvent('btn4', command4);
  entryClickEvent('btn5', command5);
}
//]]>
  </script>
  <title>command</title>
</head>
<body>
  <center>
  <input type="button" id="btn1" value="<<" style="width:30%; height:50%; font-size:35px" />
  <input type="button" id="btn2" value="mpc" style="width:30%; height:50%; font-size:35px" />
  <input type="button" id="btn3" value=">>" style="width:30%; height:50%; font-size:35px" />
  <input type="button" id="btn4" value="reboot" style="width:45%; height:20%; font-size:40px" />
  <input type="button" id="btn5" value="shutdown" style="width:45%; height:20%; font-size:40px" />
</body>
</html>

command.php
<?php
$command = filter_input(INPUT_POST, "cmd");
if ($command) {
  shell_exec($command);
}
?>
php スクリプトで html から渡された文字列をシェルコマンドとして実行します。
html ファイルのシェルコマンドの部分を他のコマンドに置き換える事で、簡単に改造できると思います。

※外部に公開するwebサーバーでは導入しないでください

$ curl -O http://sstea.blog.jp/raspi/web/command.html
$ curl -O http://sstea.blog.jp/raspi/web/command.php
$ sudo mv command.html /var/www/
$ sudo mv command.php /var/www/


http://192.168.0.100/command.html にブラウザでアクセスし、ちゃんと動くかテストしてみてください。
PCからでも、スマホからでもいけると思います。
(reboot と shutdown はまだ出来ません。)

# ↑IPアドレス部分は raspberry pi のIPアドレスを指定して下さい。

自分の Xperia Z3 では上記アドレスをブックマークに登録し、ホーム画面にショートカットを作る事で、ワンタッチですぐにアクセス可能にしています。

◆php から sudo コマンドを実行できるようにする
今のままでは、sudo を含むシェルコマンドは実行できませんので、reboot, shutdown が出来ません。
これを実行できるようにします。

$ sudo visudo
 で /etc/sudoers の末尾に以下を追記しておきます。一応、実行可能なコマンドを絞っておきます。
www-data ALL=(ALL) NOPASSWD: /sbin/shutdown
(実行可能コマンドはコンマで区切ることで、複数コマンドを指定可能です。)

これで Raspberry Pi の再起動、シャットダウンもスマホから可能になりました。
ただし、現状は LAN 内からしか操作はできません。
VPN(Virtual Private Network) を構築すれば、LAN 外からでも Raspberry Pi を操作可能になります。


◆おまけ1
以前に作った talk.sh を html ファイルのシェルコマンドの部分に指定して、ボタンを押したら喋るようにしようと思ったのですが、最初はうまく動きませんでした。

sudo 付ければちゃんと喋ってくれましたので、権限関連の問題かと思っていろいろ試していたところ、www-data に権限を追加することで、sudo 無しでも無事に喋ってくれるようになりました。
$ sudo usermod -G audio -a www-data

Raspberry Pi のカーネルカバレッジ情報を取得する

◆GCOVカーネル
Linux にはカバレッジ計測する gcov コマンドというものがあります。
しかし、一般的な gcov でカバレッジ計測が可能なのはユーザー空間のプログラムです。
(厳密には gcov コマンド自体は、
  カバレッジ計測用のコンパイルオプションでコンパイルされたプログラムの
  実行結果を収集、可視化するためのものですが。)

Linux カーネルはユーザー空間のプログラムではないので、
一般的な gcov コマンド等ではカバレッジ計測できないのですが、
それを行なってしまうのがGCOVカーネルなのです。

元々は LTP (Linux Test Project) のサイトからGCOVカーネル用のパッチをダウンロードして適用する必要があったのですが、いつのまにか本家の Linux に取り込み済みのようです。

# 3.18 以降なら ARM にも対応しています。
# (3.17 以前でも本質的には対応されてます。 Kconfig メニュー上表示されませんが。)

◆カーネルカバレッジデータのサンプル
どんな感じのデータが取得できるのかは、まずは見てもらった方が早いと思います。
下記は自分が raspberry pi でGCOVカーネル(3.19.3) を実際に動かして取得したデータです。

  GCOVカーネルカバレッジデータの出力サンプル


arch/arm/lib/uaccess_with_memcpy.c の __copy_to_user_memcpy() の部分を
↓下記に抽出しました。
if 文などの分岐単位でカバレッジ計測されます。 
左にその行の実行回数が表示されています。

ss_rpi_gcov01

◆GCOVカーネルのビルド
では実際のGCOVカーネルのビルド方法です。

"make menuconfig" により、
CONFIG_GCOV_KERNEL と CONFIG_GCOV_PROFILE_ALL を有効にし、
Linux カーネルをビルドします。(Linux カーネルのビルド方法詳細はここを参照)
    General setup  --->
      GCOV-based kernel profiling  --->
      [*] Enable gcov-based kernel profiling
      [*] Profile entire Kernel

           Specify GCOV format (Autodetect)  --->
# CONFIG_GCOV_KERNEL のメニューは CONFIG_DEBUG_FS を有効にしないと表示されません。

参考までに、本家 LTP の説明ページ。
http://ltp.sourceforge.net/coverage/gcov.php

(実際動かしてみると、結構重くなってましたので、あくまでプロファイリング用のカーネルと割り切りましょう。)

◆lcov コマンドのインストール
GCOVカーネルで取得したカバレッジデータを
上記サンプルのように html 化して出力してくれる lcov というツール(?)があります。

ホスト環境に lcov コマンドをインストールしておきます。
(Linux カーネルをクロスビルドしているホスト側です。構築方法詳細はここを参照)

$ git clone https://github.com/linux-test-project/lcov.git
$ cd lcov/
$ sudo make install
で、lcov 1.11 がインストールできると思います。

または、下記でもいいです。
$ curl -L -O http://downloads.sourceforge.net/ltp/lcov-1.11.tar.gz
$ tar xvzf lcov-1.11.tar.gz
$ cd lcov-1.11/
$ sudo make install

いつもの apt-get でもインストールされたんですが、
バージョンが最新(1.11)ではなく1.10でしたので、
上記のどちらかを推奨します。
$ sudo apt-get install lcov

◆カーネルカバレッジデータの取得

GCOVカーネルをビルドしたなら、そのカーネルでRaspberry Pi を実際に動かしてみます。

/sys/kernel/debug/gcov/ 以下にカバレッジデータが蓄積されていますので、
そのデータを取得し、ホスト環境に持って行きます。
$ sudo mount -t debugfs nodev /sys/kernel/debug/
$ sudo cp -a /sys/kernel/debug/gcov/home/mint/raspi/linux-3.19.y ./gcov-data
$ sudo tar cvjf gcov.tar.bz2 gcov-data/
※ピンク色の部分はクロスビルドした環境によって異なります。

◆カーネルカバレッジデータのhtml化

ところ変わって、こちらはホスト環境側になります。
KERNEL_DIR はクロスビルドした Linux カーネルのディレクトリです。
$ export KERNEL_DIR=/home/mint/raspi/linux-3.19.y
$ export CROSS_COMPILE=arm-linux-gnueabihf-
Raspberry Pi 上で取得した gcov.tar.bz2 を適当なディレクトリに展開します。
$ mkdir gcov-tmp/
$ cd gcov-tmp/
$ tar xvjf gcov.tar.bz2


クロス環境での lcov の使い方がいまいちよく分からなかったので、
この辺りは自分がトライ&エラーで試した結果になります。
なのでもっとスマートな方法があるかもしれないです。

カバレッジデータとソースコードを同ディレクトリに置くために、取得データをカーネルツリーにコピーします。
(lcov のオプションで別ディレクトリのままでもいけそうな気もするのですが。)
$ cp -a gcov-data/* ${KERNEL_DIR}/

クロス用の gcov を指定し、lcovコマンドを叩きます。output-file、output-directory の名前は何でもいいです。
$ lcov --capture --directory ${KERNEL_DIR}/ --output-file coverage.info --gcov-tool ${CROSS_COMPILE}gcov
$ genhtml coverage.info --output-directory out
genhtml により out ディレクトリ下に html が生成されますので、
out ディレクトリ下の index.html をブラウザで開いてください。

参考までに、本家 LTP の説明ページ。あっさりし過ぎ。。。
http://ltp.sourceforge.net/coverage/lcov.php

Raspbian(2015-02)のLinuxカーネルを差し替える

前記事で新バージョン(2015-02-16-raspbian-wheezy)の Raspbian をインストールしたので、Linuxカーネルも差し替えたいと思います。
以下、以前に書いた、この辺を見ながらクロスビルドしました。

微妙に手順変わってます。。。

◆カーネル/ローダブルモジュールのビルド

カーネルのクロスビルドには以前作った環境を使います。
以下、ホスト環境での操作になります。

git からLinuxカーネルのソースを取得します。
$ cd /home/mint/raspi/
$ git clone https://github.com/raspberrypi/linux.git
$ cd linux/
$ git checkout -b rpi-3.19.y remotes/origin/rpi-3.19.y
現時点の最新カーネルバージョンは 3.19.1 でした。(もうすぐ 4.0 になりますね。)

Raspberry Pi 実機上から取得したカーネルコンフィグをベースに、デバッグ関連、プロファイル関連のコンフィグを無効化したカーネルコンフィグ(これ)を使用します。(機能的にはデフォルトを維持してます。圧縮形式は lz4 に変更してますが。)

$ export ARCH=arm
$ export CROSS_COMPILE=arm-linux-gnueabihf-
$ cd /home/mint/raspi/linux/

上記のカスタマイズしたコンフィグを.config として linux ディレクトリ直下に置きます。
$ curl http://sstea.blog.jp/raspi/kbuild/kconfig-rpi-3.19.1 > .config
デフォルトのコンフィグを使用する場合は↓のようにしてもいいです。
$ make bcmrpi_defconfig

カーネルをビルドします。(コア数に応じて -j2 とかお好みで付けてあげてください)
$ make dtbs zImage modules
$ make modules_install INSTALL_MOD_PATH=../out/  INSTALL_MOD_STRIP=--strip-unneeded

前までは zImage を kernel.img にリネームするだけで問題ありませんでしたが、
dtb 使うようになってから、もう一工程、下記作業が必要になったようです。
(ここめっちゃ悩みました。詳細は↓おまけ1参照。
  mkknlimg は "git clone https://github.com/raspberrypi/tools.git" で取ってきた中にあります。)
$ cd /home/mint/raspi/
$ tools/mkimage/mkknlimg --dtok linux/arch/arm/boot/zImage out/kernel.img
dtb ファイル(bcm2708-rpi-b-plus.dtb)もコピーしておきます。
$ cp -p linux/arch/arm/boot/dts/bcm2708-rpi-b-plus.dtb out/

dtbファイルとkernel.img とローダブルモジュール郡を圧縮し、Raspberry Pi に持っていきます。
(まあ、このあたりは好きな方法で持っていってもらえればいいんですが。)
$ tar cvjf new.tar.bz2 out/

$ scp -p new.tar.bz2 pi@192.168.0.100:~
# IPアドレス部分は Raspberry Pi のIPアドレスを指定

◆クロスビルドしたLinuxカーネルへの差し替え
以下、Raspberry Pi 側での操作になります。

dtbファイルとカーネルイメージを差し替えます。
$ tar xvjf new.tar.bz2

$ sudo cp out/bcm2708-rpi-b-plus.dtb /boot/
$ sudo cp out/kernel.img /boot/
新しいカーネルバージョンのローダブルモジュールを展開します。
$ sudo cp -a out/lib/firmware/* /lib/firmware/
$ sudo cp -a out/lib/modules/3.19.1+/ /lib/modules/
リブートし、新しいカーネルの起動確認を行ないます。
$ reboot

もはや不要になったファイルは削除しておこうと思います。(必須ではないです)
$ sudo rm -rf /lib/modules/3.18.9+/ /lib/modules/3.18.9-v7+/
$ sudo rm -rf /boot.bak/

使用しない dtb ファイルや カーネルイメージファイルも削除していいですよね?
(自分が使用しているのは Raspberry Pi Model B+ モデルですので、下記ファイルは使用していません。)
$ sudo rm /boot/bcm2708-rpi-b.dtb /boot/bcm2709-rpi-2-b.dtb /boot/kernel7.img
$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/root        3671884 1723412   1749112  50% /
devtmpfs          219996       0    219996   0% /dev
tmpfs              44836     452     44384   2% /run
tmpfs               5120       0      5120   0% /run/lock
tmpfs              89660       0     89660   0% /run/shm
/dev/mmcblk0p1     57288   10200     47088  18% /boot

◆おまけ1

以前のように、クロスビルドしたlinuxカーネルの zImage ファイルを kernel.img とリネームだけして、Raspberry Pi のカーネルを差し替えてみたんですが、どうも dtb ファイルを認識していないように思います。
ブートログにちらほらエラーも見えますし。
/boot/bcm2708-rpi-b-plus.dtb ファイルは確かに存在するのですが。
(ブートログは /var/log/messages で確認できます。)
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Initializing cgroup subsys cpuacct
[    0.000000] Linux version 3.19.1+ (mint@Mint) (gcc version 4.8.3 20140106 (prerelease) (crosstool-NG linaro-1.13.1-4.8-2014.01 - Linaro GCC 2013.11) ) #1 PREEMPT Sat Mar 14 22:50:58 JST 2015
[    0.000000] CPU: ARMv6-compatible processor [410fb767] revision 7 (ARMv7), cr=00c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
[    0.000000] Machine: BCM2708
[    0.000000] cma: Reserved 8 MiB at 0x1b800000
[    0.000000] Memory policy: Data cache writeback
            :

試しに、/boot/config.txt の末尾に
device_tree=bcm2708-rpi-b-plus.dtb
と追加し、リブートしてみたところ、
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Initializing cgroup subsys cpuacct
[    0.000000] Linux version 3.19.1+ (mint@Mint) (gcc version 4.8.3 20140106 (prerelease) (crosstool-NG linaro-1.13.1-4.8-2014.01 - Linaro GCC 2013.11) ) #1 PREEMPT Sat Mar 14 22:50:58 JST 2015
[    0.000000] CPU: ARMv6-compatible processor [410fb767] revision 7 (ARMv7), cr=00c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
[    0.000000] Machine model: Raspberry Pi Model B Plus
[    0.000000] cma: Reserved 8 MiB at 0x1b800000
[    0.000000] Memory policy: Data cache writeback
            :
dtb ファイル認識してますね。。。

どうして、デフォルトのカーネルイメージは config.txt に何も記載がなくても、ちゃんと正しい dtb ファイルを認識できてるんでしょうか?
カーネルバージョンの差か、コンフィグ変えたからか、とか考えていろいろ試してみたんですが、どうも関係なさそうです。
カーネルイメージに何か情報を付与してんじゃねーの?と思って、オリジナルのkernel.imgをバイナリエディタで開いてみると、、、
末尾に何か付いてますね。。。文字列が読めます。
圧縮イメージなのに読める文字列があるって事は、圧縮後に付与されたって事です。

zImageファイルはImageファイルを圧縮して自己展開プログラムを先頭にくっつけたファイルで、自分が知る限り末尾にこんな情報はくっつかないので、外部ツールなりでzImageにこの情報を付与する加工を加えているように思います。

カーネルイメージ生成手順、何か増えてんのか?と思って https://github.com/raspberrypi/tools の中をいろいろ見てると mkknlimg とかいうそれらしいのがありました。

で、コレ使って kernel.img 作ってみると、、、

$ mkknlimg --dtok zImage kernel.img
config.txt を弄らなくても、いけましたー。

◆おまけ2
CONFIG_KALLSYMS を無効化しようとして毎回、迷子になるのでメモ。

[1] CONFIG_LATENCYTOP と CONFIG_FTRACE を無効化

  Kernel hacking  --->
  [ ] Latency measuring infrastructure
  [ ] Tracers  ----

[2] CONFIG_KPROBES を無効化
  General setup  --->
  [ ] Kprobes

[3] KALLSYMS_ALL がようやく変更可能になるので無効化
  General setup  --->
  -*- Configure standard kernel features (expert users)  --->
  [ ]   Load all symbols for debugging/ksymoops

◆おまけ3
dtb (device tree blob)
ファイルは dts (device tree source)ファイルから生成されますが、dtb ファイルから dts ファイルに戻す事もできます。
Linux ツリーの中に dtc (device tree compiler)が入っています。

下記、コマンドで dtb ファイルから dts ファイルを取得できます。
$ cd /home/mint/raspi/
$ linux/scripts/dtc/dtc -I dtb -O dts ./bcm2708-rpi-b-plus.dtb > ./out.dts

演算子/errno/シグナル/システムコール 一覧表

プログラム、Linux 関連の各種テーブル早見表。

Linux カーネルのソースコードは↓下記サイトで確認可能。
  https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/

  https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/

表の作成は↓下記サイトにお世話になりました。
http://styleme.jp/tool/xls2html/

◆演算順序(C言語)

優先度 演算子 説明 評価順序
1 ( ) 括弧
[ ] 配列要素
. メンバ参照演算子
-> アロー演算子
2 & アドレス演算子
* 間接演算子
~ ビット反転
! 論理否定
+ プラス符号
- マイナス符号
++ インクリメント
-- デクリメント
sizeof サイズ
(cast) キャスト
3 * 乗算
/ 除算
% 剰余
4 + 加算
- 減算
5 << 左シフト
>> 右シフト
6 < より小さい
<= 以下
> より大きい
>= 以上
7 == 等しい
!= 等しくない
8 & ビット論理積
9 ^ ビット排他的論理和
10 | ビット論理和
11 && 論理積
12 || 論理和
13 ? : 三項演算子
14 = 代入演算子
*=
/=
%=
+=
-=
<<=
>>=
^=
&=
|=
15 , コンマ演算子

◆Linux errno
linux 3.19.3 の下記ファイルを参照し、作成。
- include/uapi/asm-generic/errno-base.h
- include/uapi/asm-generic/errno.h
- include/linux/errno.h

エラー番号 マクロ
コメント
1 EPERM Operation not permitted
2 ENOENT No such file or directory
3 ESRCH No such process
4 EINTR Interrupted system call
5 EIO I/O error
6 ENXIO No such device or address
7 E2BIG Argument list too long
8 ENOEXEC Exec format error
9 EBADF Bad file number
10 ECHILD No child processes
11 EAGAIN Try again
12 ENOMEM Out of memory
13 EACCES Permission denied
14 EFAULT Bad address
15 ENOTBLK Block device required
16 EBUSY Device or resource busy
17 EEXIST File exists
18 EXDEV Cross-device link
19 ENODEV No such device
20 ENOTDIR Not a directory
21 EISDIR Is a directory
22 EINVAL Invalid argument
23 ENFILE File table overflow
24 EMFILE Too many open files
25 ENOTTY Not a typewriter
26 ETXTBSY Text file busy
27 EFBIG File too large
28 ENOSPC No space left on device
29 ESPIPE Illegal seek
30 EROFS Read-only file system
31 EMLINK Too many links
32 EPIPE Broken pipe
33 EDOM Math argument out of domain of func
34 ERANGE Math result not representable
35 EDEADLK Resource deadlock would occur
36 ENAMETOOLONG File name too long
37 ENOLCK No record locks available
38 ENOSYS Function not implemented
39 ENOTEMPTY Directory not empty
40 ELOOP Too many symbolic links encountered
EAGAIN(11) EWOULDBLOCK Operation would block
42 ENOMSG No message of desired type
43 EIDRM Identifier removed
44 ECHRNG Channel number out of range
45 EL2NSYNC Level 2 not synchronized
46 EL3HLT Level 3 halted
47 EL3RST Level 3 reset
48 ELNRNG Link number out of range
49 EUNATCH Protocol driver not attached
50 ENOCSI No CSI structure available
51 EL2HLT Level 2 halted
52 EBADE Invalid exchange
53 EBADR Invalid request descriptor
54 EXFULL Exchange full
55 ENOANO No anode
56 EBADRQC Invalid request code
57 EBADSLT Invalid slot
EDEADLK(35) EDEADLOCK
59 EBFONT Bad font file format
60 ENOSTR Device not a stream
61 ENODATA No data available
62 ETIME Timer expired
63 ENOSR Out of streams resources
64 ENONET Machine is not on the network
65 ENOPKG Package not installed
66 EREMOTE Object is remote
67 ENOLINK Link has been severed
68 EADV Advertise error
69 ESRMNT Srmount error
70 ECOMM Communication error on send
71 EPROTO Protocol error
72 EMULTIHOP Multihop attempted
73 EDOTDOT RFS specific error
74 EBADMSG Not a data message
75 EOVERFLOW Value too large for defined data type
76 ENOTUNIQ Name not unique on network
77 EBADFD File descriptor in bad state
78 EREMCHG Remote address changed
79 ELIBACC Can not access a needed shared library
80 ELIBBAD Accessing a corrupted shared library
81 ELIBSCN .lib section in a.out corrupted
82 ELIBMAX Attempting to link in too many shared libraries
83 ELIBEXEC Cannot exec a shared library directly
84 EILSEQ Illegal byte sequence
85 ERESTART Interrupted system call should be restarted
86 ESTRPIPE Streams pipe error
87 EUSERS Too many users
88 ENOTSOCK Socket operation on non-socket
89 EDESTADDRREQ Destination address required
90 EMSGSIZE Message too long
91 EPROTOTYPE Protocol wrong type for socket
92 ENOPROTOOPT Protocol not available
93 EPROTONOSUPPORT Protocol not supported
94 ESOCKTNOSUPPORT Socket type not supported
95 EOPNOTSUPP Operation not supported on transport endpoint
96 EPFNOSUPPORT Protocol family not supported
97 EAFNOSUPPORT Address family not supported by protocol
98 EADDRINUSE Address already in use
99 EADDRNOTAVAIL Cannot assign requested address
100 ENETDOWN Network is down
101 ENETUNREACH Network is unreachable
102 ENETRESET Network dropped connection because of reset
103 ECONNABORTED Software caused connection abort
104 ECONNRESET Connection reset by peer
105 ENOBUFS No buffer space available
106 EISCONN Transport endpoint is already connected
107 ENOTCONN Transport endpoint is not connected
108 ESHUTDOWN Cannot send after transport endpoint shutdown
109 ETOOMANYREFS Too many references: cannot splice
110 ETIMEDOUT Connection timed out
111 ECONNREFUSED Connection refused
112 EHOSTDOWN Host is down
113 EHOSTUNREACH No route to host
114 EALREADY Operation already in progress
115 EINPROGRESS Operation now in progress
116 ESTALE Stale file handle
117 EUCLEAN Structure needs cleaning
118 ENOTNAM Not a XENIX named type file
119 ENAVAIL No XENIX semaphores available
120 EISNAM Is a named type file
121 EREMOTEIO Remote I/O error
122 EDQUOT Quota exceeded
123 ENOMEDIUM No medium found
124 EMEDIUMTYPE Wrong medium type
125 ECANCELED Operation Canceled
126 ENOKEY Required key not available
127 EKEYEXPIRED Key has expired
128 EKEYREVOKED Key has been revoked
129 EKEYREJECTED Key was rejected by service
130 EOWNERDEAD Owner died
131 ENOTRECOVERABLE State not recoverable
132 ERFKILL Operation not possible due to RF-kill
133 EHWPOISON Memory page has hardware error
512 ERESTARTSYS
513 ERESTARTNOINTR
514 ERESTARTNOHAND restart if no handler..
515 ENOIOCTLCMD No ioctl command
516 ERESTART_RESTARTBLOCK restart by calling sys_restart_syscall
517 EPROBE_DEFER Driver requests probe retry
518 EOPENSTALE open found a stale dentry
519
-

520
-

521 EBADHANDLE Illegal NFS file handle
522 ENOTSYNC Update synchronization mismatch
523 EBADCOOKIE Cookie is stale
524 ENOTSUPP Operation is not supported
525 ETOOSMALL Buffer or request is too small
526 ESERVERFAULT An untranslatable error occurred
527 EBADTYPE Type not supported by server
528 EJUKEBOX Request initiated, but will not complete before timeout
529 EIOCBQUEUED iocb queued, will get completion event


◆シグナル
linux 3.19.3 の下記ファイルを参照し、作成。
- include/uapi/asm-generic/signal.h

シグナル番号 マクロ 備考
1 SIGHUP
2 SIGINT
3 SIGQUIT
4 SIGILL
5 SIGTRAP
6 SIGABRT / SIGIOT
7 SIGBUS
8 SIGFPE
9 SIGKILL マスク不可
10 SIGUSR1
11 SIGSEGV
12 SIGUSR2
13 SIGPIPE
14 SIGALRM
15 SIGTERM
16 SIGSTKFLT
17 SIGCHLD
18 SIGCONT
19 SIGSTOP マスク不可
20 SIGTSTP
21 SIGTTIN
22 SIGTTOU
23 SIGURG
24 SIGXCPU
25 SIGXFSZ
26 SIGVTALRM
27 SIGPROF
28 SIGWINCH
29 SIGIO /*SIGLOST*/
SIGIO(29) SIGPOLL
30 SIGPWR
31 SIGSYS / SIGUNUSED

◆Linux システムコール
linux 3.19.3 の下記ファイルを参照し作成。
- arch/arm/include/uapi/asm/unistd.h

システムコール番号 シンボル
コメント
0 restart_syscall
1 exit
2 fork
3 read
4 write
5 open
6 close
7 - 7 was sys_waitpid
8 creat
9 link
10 unlink
11 execve
12 chdir
13 time
14 mknod
15 chmod
16 lchown
17 - 17 was sys_break
18 - 18 was sys_stat
19 lseek
20 getpid
21 mount
22 umount
23 setuid
24 getuid
25 stime
26 ptrace
27 alarm
28 - 28 was sys_fstat
29 pause
30 utime
31 - 31 was sys_stty
32 - 32 was sys_gtty
33 access
34 nice
35 - 35 was sys_ftime
36 sync
37 kill
38 rename
39 mkdir
40 rmdir
41 dup
42 pipe
43 times
44 - 44 was sys_prof
45 brk
46 setgid
47 getgid
48 - 48 was sys_signal
49 geteuid
50 getegid
51 acct
52 umount2
53 - 53 was sys_lock
54 ioctl
55 fcntl
56 - 56 was sys_mpx
57 setpgid
58 - 58 was sys_ulimit
59 - 59 was sys_olduname
60 umask
61 chroot
62 ustat
63 dup2
64 getppid
65 getpgrp
66 setsid
67 sigaction
68 - 68 was sys_sgetmask
69 - 69 was sys_ssetmask
70 setreuid
71 setregid
72 sigsuspend
73 sigpending
74 sethostname
75 setrlimit
76 getrlimit Back compat 2GB limited rlimit
77 getrusage
78 gettimeofday
79 settimeofday
80 getgroups
81 setgroups
82 select
83 symlink
84 - 84 was sys_lstat
85 readlink
86 uselib
87 swapon
88 reboot
89 readdir
90 mmap
91 munmap
92 truncate
93 ftruncate
94 fchmod
95 fchown
96 getpriority
97 setpriority
98 - 98 was sys_profil
99 statfs
100 fstatfs
101 - 101 was sys_ioperm
102 socketcall
103 syslog
104 setitimer
105 getitimer
106 stat
107 lstat
108 fstat
109 - 109 was sys_uname
110 - 110 was sys_iopl
111 vhangup
112 - 112 was sys_idle
113 syscall syscall to call a syscall!
114 wait4
115 swapoff
116 sysinfo
117 ipc
118 fsync
119 sigreturn
120 clone
121 setdomainname
122 uname
123 - 123 was sys_modify_ldt
124 adjtimex
125 mprotect
126 sigprocmask
127 - 127 was sys_create_module
128 init_module
129 delete_module
130 - 130 was sys_get_kernel_syms
131 quotactl
132 getpgid
133 fchdir
134 bdflush
135 sysfs
136 personality
137 - 137 was sys_afs_syscall
138 setfsuid
139 setfsgid
140 _llseek
141 getdents
142 _newselect
143 flock
144 msync
145 readv
146 writev
147 getsid
148 fdatasync
149 _sysctl
150 mlock
151 munlock
152 mlockall
153 munlockall
154 sched_setparam
155 sched_getparam
156 sched_setscheduler
157 sched_getscheduler
158 sched_yield
159 sched_get_priority_max
160 sched_get_priority_min
161 sched_rr_get_interval
162 nanosleep
163 mremap
164 setresuid
165 getresuid
166 - 166 was sys_vm86
167 - 167 was sys_query_module
168 poll
169 nfsservctl
170 setresgid
171 getresgid
172 prctl
173 rt_sigreturn
174 rt_sigaction
175 rt_sigprocmask
176 rt_sigpending
177 rt_sigtimedwait
178 rt_sigqueueinfo
179 rt_sigsuspend
180 pread64
181 pwrite64
182 chown
183 getcwd
184 capget
185 capset
186 sigaltstack
187 sendfile
188 - 188 reserved
189 - 189 reserved
190 vfork
191 ugetrlimit SuS compliant getrlimit
192 mmap2
193 truncate64
194 ftruncate64
195 stat64
196 lstat64
197 fstat64
198 lchown32
199 getuid32
200 getgid32
201 geteuid32
202 getegid32
203 setreuid32
204 setregid32
205 getgroups32
206 setgroups32
207 fchown32
208 setresuid32
209 getresuid32
210 setresgid32
211 getresgid32
212 chown32
213 setuid32
214 setgid32
215 setfsuid32
216 setfsgid32
217 getdents64
218 pivot_root
219 mincore
220 madvise
221 fcntl64
222 - 222 for tux
223 - 223 is unused
224 gettid
225 readahead
226 setxattr
227 lsetxattr
228 fsetxattr
229 getxattr
230 lgetxattr
231 fgetxattr
232 listxattr
233 llistxattr
234 flistxattr
235 removexattr
236 lremovexattr
237 fremovexattr
238 tkill
239 sendfile64
240 futex
241 sched_setaffinity
242 sched_getaffinity
243 io_setup
244 io_destroy
245 io_getevents
246 io_submit
247 io_cancel
248 exit_group
249 lookup_dcookie
250 epoll_create
251 epoll_ctl
252 epoll_wait
253 remap_file_pages
254 - 254 for set_thread_area
255 - 255 for get_thread_area
256 set_tid_address
257 timer_create
258 timer_settime
259 timer_gettime
260 timer_getoverrun
261 timer_delete
262 clock_settime
263 clock_gettime
264 clock_getres
265 clock_nanosleep
266 statfs64
267 fstatfs64
268 tgkill
269 utimes
270 arm_fadvise64_64
271 pciconfig_iobase
272 pciconfig_read
273 pciconfig_write
274 mq_open
275 mq_unlink
276 mq_timedsend
277 mq_timedreceive
278 mq_notify
279 mq_getsetattr
280 waitid
281 socket
282 bind
283 connect
284 listen
285 accept
286 getsockname
287 getpeername
288 socketpair
289 send
290 sendto
291 recv
292 recvfrom
293 shutdown
294 setsockopt
295 getsockopt
296 sendmsg
297 recvmsg
298 semop
299 semget
300 semctl
301 msgsnd
302 msgrcv
303 msgget
304 msgctl
305 shmat
306 shmdt
307 shmget
308 shmctl
309 add_key
310 request_key
311 keyctl
312 semtimedop
313 vserver
314 ioprio_set
315 ioprio_get
316 inotify_init
317 inotify_add_watch
318 inotify_rm_watch
319 mbind
320 get_mempolicy
321 set_mempolicy
322 openat
323 mkdirat
324 mknodat
325 fchownat
326 futimesat
327 fstatat64
328 unlinkat
329 renameat
330 linkat
331 symlinkat
332 readlinkat
333 fchmodat
334 faccessat
335 pselect6
336 ppoll
337 unshare
338 set_robust_list
339 get_robust_list
340 splice
341 arm_sync_file_range sync_file_range2
342 tee
343 vmsplice
344 move_pages
345 getcpu
346 epoll_pwait
347 kexec_load
348 utimensat
349 signalfd
350 timerfd_create
351 eventfd
352 fallocate
353 timerfd_settime
354 timerfd_gettime
355 signalfd4
356 eventfd2
357 epoll_create1
358 dup3
359 pipe2
360 inotify_init1
361 preadv
362 pwritev
363 rt_tgsigqueueinfo
364 perf_event_open
365 recvmmsg
366 accept4
367 fanotify_init
368 fanotify_mark
369 prlimit64
370 name_to_handle_at
371 open_by_handle_at
372 clock_adjtime
373 syncfs
374 sendmmsg
375 setns
376 process_vm_readv
377 process_vm_writev
378 kcmp
379 finit_module
380 sched_setattr
381 sched_getattr
382 renameat2
383 seccomp
384 getrandom
385 memfd_create
386 bpf
387 execveat

シェルスクリプト/Makefile/LKM 等サンプル

各種プログラム、Makefile サンプル。

◆シェルサンプル
shell/test.sh
#!/bin/bash

# ./test.sh 1 "2 3" 4

TMPFILE=/tmp/test

init() {
        if [ -f ${TMPFILE} ]; then
                rm -f ${TMPFILE}
        elif [ -e ${TMPFILE} ]; then
                exit 1
        fi

        touch ${TMPFILE}
        if [ $? -ne 0 ]; then
                exit 1
        fi
}

main() {
        local arg
        local i=0

        for arg in "$@"; do
                echo ${arg} >> ${TMPFILE}
                list[${i}]=${arg}
                i=$((${i} + 1))
        done

        i=0
        while [ ${i} -lt ${#list[@]} ]; do
                echo ${list[${i}]}
                i=$((${i} + 1))
        done

        list2=(`cat ${TMPFILE}`)
        for i in "${list[@]}"; do
                echo ${i}
        done

        while read line; do
                echo ${line}
        done < ${TMPFILE}
}

init
main "$@"

◆Makefileサンプル
csrc/Makefile

CC=gcc
CFLAGS ?= -O2 -Wall
LDFLAGS ?=

OBJS=main.o
TARGET=test

.PHONY: all clean
all: $(TARGET)

$(TARGET): $(OBJS)
        $(CC) -o $@ $^ $(LDFLAGS)

debug:
        $(MAKE) -C . CFLAGS="$(CFLAGS) -DDEBUG"

.c.o:
        $(CC) -c $< $(CFLAGS)

clean:
        $(RM) $(TARGET) $(OBJS)
上記サンプル↓
$ curl -O http://sstea.blog.jp/raspi/csrc/Makefile

csrc/main.c

#include <stdio.h>

#ifdef DEBUG
#define DEBUG(...)    printf("<DEBUG> " __VA_ARGS__)
#else /* !DEBUG */
#define DEBUG(...)    do { } while (0)
#endif /* !DEBUG */

int main(int argc, char **argv)
{
    DEBUG("hello\n");
    return 0;
}

◆Linux LKM(ローダブルカーネルモジュール)サンプル
kbuild/Makefile (KDIR は場合によっては任意に変更)
obj-y := test/

KDIR ?= /lib/module/$(shell uname -r)/build
PWD := $(shell pwd)

EXTRA_CFLAGS =

modules:
    make -C $(KDIR) M=$(PWD) V=1 modules

clean:
    make -C $(KDIR) M=$(PWD) V=1 clean
上記サンプル↓
$ curl -O http://sstea.blog.jp/raspi/kbuild/Makefile

kbuild/test/Makefile
obj-m:= test.o

test-objs := test_base.o
test-objs に.oファイルを複数指定することで、複数ファイルから1つのローダブルカーネルモジュール(.koファイル)を作成可能。

kbuild/test/test_base.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

static int __init test_init(void)
{
    printk("test_init\n");
    return 0;
}
module_init(test_init);

static void __exit test_exit(void)
{
    printk("test_exit\n");
}
module_exit(test_exit);

Raspberry Pi のカーネルをクロスビルドする

前回までは設定をいじってましたが、今回はLinuxカーネルを差し替えます。

こちらの記事で最新のRaspbianのカーネルを差し替えてますので、そちらも参考までに。

◆VirtualBox上でクロスコンパイルする

特にカーネルを差し替える必要はないのかもしれませんが、自己満足コンフィグレーションの最適化のため、何となく自家製Myカーネルに差し替えたいと思います。愛着もわきますし。

Raspberry Piのターゲット上でカーネルをビルドをすることも可能ですが、カーネルビルドするにはRaspberry Piでは非力で、とても時間がかかってしまうため、VirtualBox上に構築したLinux Mintでクロスコンパイルしようと思います。

VirtualBox上にLinux Mintの構築方法はこちらを参照。

以下はVirtualBox上のLinux Mintでの操作です。

◆Raspberry Pi用クロスコンパイラを取得する
コンパイラもカーネルソースもgitで公開されています。

  GitHub (https://github.com/raspberrypi/)

gitがインストールされていない場合は、インストールしておきます。
$ sudo apt-get install git

まずはTerminalを起動し、適当な作業ディレクトリを作成します。
以降、作業ディレクトリを ${HOME}/raspi/ として記述します。
$ cd ${HOME}/raspi/
$ git clone https://github.com/raspberrypi/tools.git

取得してきた中を見ると tools/arm-bcm2708/ に以下の4つのツールチェーンがあります。
- arm-bcm2708-linux-gnueabi
- arm-bcm2708hardfp-linux-gnueabi
- gcc-linaro-arm-linux-gnueabihf-raspbian
- gcc-linaro-arm-linux-gnueabihf-raspbian-x64

どれを使うべきか、、、
自分は 32bit版 Linux Mint をインストールしているので 64bit版はそもそも実行できません。
これで残りの候補は3つ。(64bitマシンならこれ一択でいい気もします)
それぞれの gcc で --version を実行した結果が下記です。
$ arm-bcm2708-linux-gnueabi/bin/arm-bcm2708-linux-gnueabi-gcc --version
arm-bcm2708-linux-gnueabi-gcc (crosstool-NG 1.15.2) 4.7.1 20120402 (prerelease)
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ arm-bcm2708hardfp-linux-gnueabi/bin/arm-bcm2708hardfp-linux-gnueabi-gcc --version
arm-bcm2708hardfp-linux-gnueabi-gcc (crosstool-NG 1.15.2) 4.7.1 20120402 (prerelease)
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc (crosstool-NG linaro-1.13.1-4.8-2014.01 - Linaro GCC 2013.11) 4.8.3 20140106 (prerelease)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
gcc-linaro-arm-linux-gnueabihf-raspbian がGCCバージョン4.8.3 で一番新しいので、これを使うことにします。
(安易ですが、OSSは最新であれば最新ほどいい、が自分のモットーですので。)

.bashrc の最後に
PATH=${HOME}/raspi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:${PATH}
とでも付け加えて、パスを通しておきます。

# 新しいTerminalを使うか ". ~/.bashrc" と実行しないと、まだ反映はされていませんので注意。

◆Raspberry Pi用Linuxカーネルのソースコードを取得
$ cd ${HOME}/raspi/
$ git clone https://github.com/raspberrypi/linux.git
それなりに時間かかると思います。

◆Raspberry Pi用のカーネルコンフィグを最適化する
カーネルコンフィグは Raspberry Pi 上で
$ sudo zcat /proc/config.gz > raspi.config
とすれば、今 Raspberry Pi で動いているLinuxカーネルのカーネルコンフィグが取得できます。

今回は自分がデフォルトのコンフィグをベースに変更を加えたこのコンフィグを使用します。
カーネルバージョンは 3.12.31 です。

# もっと最新のカーネルがいいという方はもっと↓を参照。

変更点は下記の通りです。
- デバッグ関連のコンフィグを無効化
(ダンプ 吐かれても別に原因解析する気ないなー、と思ったのでCOREDUMP、GDB、KPROBEとか全部切ってます。)
- プロファイル関連のコンフィグを無効化
- IPv6 の無効化(元々ローダブルモジュールだったのでそのままでもいいのですが)
- ROOT_NFS の無効化
- カーネル圧縮形式をlz4に変更
- ログバッファサイズとかその他、何点か微修正

カーネルイメージの省サイズ化、及びオーバーヘッド軽減を目的としたものなので、デフォルトのコンフィグから機能的には変わっていないはずです。(nfs root を使用する場合を除き)

上記コンフィグを.config として linux ディレクトリ直下に置きます。
$ curl http://sstea.blog.jp/raspi/kbuild/kconfig-rpi-3.12.31 > .config

◆Raspberry Pi用のカーネルをビルド(再構築)する
今回、カーネル圧縮形式をlz4にしているので下記をインストールしておきます。
$ sudo apt-get install liblz4-tool

ついでに、カーネルビルドに必要なパッケージが未インストールなら入れておきます。
$ sudo apt-get install build-essential libncurses5-dev

環境変数を設定します。
$ export ARCH=arm
$ export CROSS_COMPILE=arm-linux-gnueabihf-
カーネルコンフィグを変えたい場合は変えます。(変えないならmenuconfigは不要です。)
$ cd ${HOME}/raspi/linux/
$ make menuconfig
カーネルをビルドします。
$ make
カーネルモジュールのビルド&インストールを行います。(インストール先はテンポラリなのでどこでもいいです。../mod-3.12.31である必要はありません。)
$ make modules
$ make modules_install INSTALL_MOD_PATH=../mod-3.12.31/  INSTALL_MOD_STRIP=--strip-unneeded

ビルドが完了したなら、linux/arch/arm/boot/zImage がカーネルイメージになります。
これを kernel.img とリネームし、Raspberry Pi の /boot/kernel.img と置換することで新カーネルに差し替えることが出来ます。

ちなみに、元のカーネルイメージはgzip圧縮形式で 3.3MBほどでした。
今回のカーネルイメージはlz4圧縮形式で3.0MBほどで、シュリンク出来ています。
(同じgizp圧縮形式なら2.6MBまで縮みましたが、展開速度が高速なlz4を採用しています。)

あと、${HOME}/raspi/mod-3.12.31/ というのが出来ているはずなのでこの中の lib/modules/3.12.31+/ をRaspberry Pi の /lib/modules/ 以下に置いてあげるのも忘れずに。

ちなみに、../mod-3.12.31/lib/firmware/ にもいろいろファイルが出来ているのですが、
Raspberry Pi の /lib/firmware/ にあるファイルと一つも被っていないのが謎。
たぶんなくてもいい気もしますが、念のために ../mod-3.12.31/lib/firmware/ 以下のファイルも Raspberry Pi の /lib/firmware/ 以下にコピーしておくことにします。

◆Raspberry Pi用のチャキチャキの最近カーネルを使用する
と、ここで終わってもいいのですが、せっかくビルドまでして自家製カーネルを使うのだから、最新バージョンのカーネルを使用したいと思います。
現在の最新バージョンは 3.17.2 ですので、3.17.2 のカーネルにしましょう。

git でLinuxカーネルのソースを取得する所までは同じです。
(または、3.12.31 のツリーが不要なら make distclean してそれを流用しましょう。)
$ cd ${HOME}/raspi/linux/
$ git checkout -b rpi-3.17.y remotes/origin/rpi-3.17.y

3.17.2 のカーネルコンフィグにはこのコンフィグを使用します。
デフォルトコンフィグからの変更点は 3.12.31 の時と同様です。

ビルド方法等は 3.12.31 と同様です。

カーネルイメージを差し替えて、リブート後 Raspberry Pi 上で
$ uname -r
とし、3.17.2 と表示されていれば成功です。

◆カーネルイメージのファイル名を変更する
今回はカーネルを自家製カーネルに置き換えました。
ファームアップデート等を行なうと、本家のカーネルイメージに置き換えられてしまいます。

カーネルイメージのファイル名をデフォルト(kernel.img) から変更することで、これを回避することができます。

/boot/config.txt に下記を追記します。
$ kernel=kernel-3.17.img

カーネルイメージのファイル名をリネームします。
$ sudo mv /boot/kernel.img /boot/kernel-3.17.img

◆おまけ1
カーネルのオーバーヘッド軽減を目指してカーネルコンフィグをいじりましたが、効果は出ているのでしょうか?

printk で表示できる時間はカーネル起動からの時間のため、カーネル省サイズ下によるブートローダーの展開時間削減の効果は残念ながら正確な確認方法がありません。(体感できるレベルでなければ)

カーネル起動後であればカーネルログで確認できますので、今回の確認方法としてはカーネルログのrootfsのマウントまでの時間("EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)"とういうログの出力時間)を指標としたいと思います。
以前の記事でquietの効果確認を行なったのと同じ方法です。)

オリジナルconfig
raspberrypi kernel: [   13.000215] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
変更config
raspberrypi kernel: [   12.670055] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)

一応オリジナルより早くなってそうです。体感できないと思いますが。
小さなことからコツコツと!!

◆おまけ2

initramfsを使用してみます。

# 結局使用しない構成に戻しましたが。

現状のブートシーケンスはbootloader→kernel→rootfsの順に起動しますが、
bootloader→kernel→initramfs→rootfsの順に起動させる事ができます。

このinitramfsで先に起動させておきたいデーモン等やモジュールのロード等を済ませれば、rootfsの起動スクリプトの処理が遅かろうが関係ありません。
$ sudo apt-get install initramfs-tools
$ sudo update-initramfs -c -k $(uname -r)
で /boot/ 以下に initrd.img-3.17.2+ というファイルが出来ているはずです。

/boot/config.txt に以下を追記すれば、initramfsを使用したブートシーケンスになります。
initramfs initrd.img-3.17.2+
ただこのままだと、純粋にinitramfsを経由するようになった分だけ起動時間が少し遅くなっただけでメリットはありません。

initramfsの中身をいじくるためにこのファイルを VirtualBox の Linux Mint 上に持っていきます。
以下は ${WORKDIR} にファイルを持ってきたとして記載します。

ファイル名を変更し、ディレクトリを作成しておきます。
$ mv initrd.img-3.17.2+ initrd.img-3.17.2+.orig.gz
$ mkdir -p ${WORKDIR}/initramfs/
展開します。
$ cd ${WORKDIR}/initramfs/
$ gunzip -c -9 ../initrd.img-3.17.2+.orig.gz | cpio -i -d -H newc
お好きにいじくってください。

満足したら、再びinitramfsイメージにします。
$ cd ${WORKDIR}/initramfs/
$ find . | cpio -o -H newc | gzip > ../initrd.img-3.17.2.+
これを raspberry pi の /boot/ 以下においてあげてください。


次回はいったんこの環境をバックアップしようと思います。

VirtualBox 上の Linux Mint の初期設定を行う

前回、Linux Mintのインストールまでを行ないましたので、今回は初期設定を行ないます。

◆Linux Mintをインストールして最初に行う事


以下、アカウント名 mint として書いています。

(1) Linux MintへのGUEST ADDITIONSのインストール
  ホスト側とのクリップボードの共有や共有フォルダ等、仮想環境としての利便性を高めるために、GUEST ADDITIONSをインストールします。
  VirtualBoxのウィンドウメニューから[デバイス][Guest AdditionsのCDイメージを挿入]を選択。

  左下のMenuから Terminal を起動し、以下を実行。
  (Terminal は今後もお世話になるので、パネルに追加しておいてもいいと思います。)
$ cd /media/mint/VBOXADDITIONS_4.3.18_96516/
$ sudo ./VBoxLinuxAdditions.run
  完了したなら、デスクトップ上の VBOXADDITIONS_4.3.18_96516 のアイコンを右クリックし「取り出す」を選択します。
  (/media/mint/VBOXADDITIONS_4.3.18_96516/ 下にいる場合は取り出しに失敗します。
    cd で適当な所に移動してください。)

  デフォルトでは、共有フォルダは root:vboxsf 権限が設定されており、一般ユーザからはアクセス出来ません。
  それでは不便極まりないので、vboxsf グループにユーザアカウントを追加します。
$ sudo gpasswd -a mint vboxsf
  ここまで完了したなら、一旦リブートします。

(Windows の C:\tools\VirtualBox\shared/ と Linux Mint の /media/sf_shared/ にてファイルのやりとりが出来るようになります。)

(2) プロキシ設定(使用しないなら不要、社内ネットワーク内とかならたぶん要る)
  左下のMenuから[コントロールセンター][ネットワークのプロキシ]を選択し、適宜設定。

  .bashrc あたりに追記。
export ftp_proxy="http://プロキシIPアドレス:ポート番号/"
export http_proxy="http://プロキシIPアドレス:ポート番号/"
export https_proxy="http://プロキシIPアドレス:ポート番号/"

apt のプロキシ設定
  /etc/apt/apt.conf.d/80proxy を新規作成
Acquire::ftp::proxy "ftp://プロキシIPアドレス:ポート番号/";
Acquire::http::proxy "http://プロキシIPアドレス:ポート番号/";
Acquire::https::proxy "https://プロキシIPアドレス:ポート番号/";


プロキシ経由でgitを使う場合の追加設定
  .bashrc あたりに追記。
export GIT_PROXY_COMMAND=/usr/local/bin/git-proxy.sh

  /usr/local/bin/git-proxy.sh を新規作成。
#!/bin/sh
corkscrew プロキシIPアドレス ポート番号 $1 $2


  実行権限付与と、corkscrew のインストール。
$ sudo chmod 755 /usr/local/bin/git-proxy.sh
$ sudo apt-get install corkscrew


(3) apt 関連の設定
  左下のMenuから[コントロールセンター][ソフトウェアソース]を選択。

  ミラーのMain, Base をそれぞれ日本のサーバーに変更します。

  [キャッシュをアップデート]ボタンを押す。

sudo apt-get upgrade
  で最新パッケージに更新。

(4) 日本語化(特に英語で問題なければ不要)
日本語表示
  以下を実行。
$ wget -q http://packages.linuxmint-jp.net/linuxmint-ja-archive-keyring.gpg -O- | sudo apt-key add -
$ sudo wget http://packages.linuxmint-jp.net/sources.list.d/linuxmint-ja.list -O /etc/apt/sources.list.d /linuxmint-ja.list
  Linux Mint 17 MATE の場合
$ sudo apt-get install mint-gnome-ja --install-recommends
(LMDEの場合)
$ sudo apt-get install mint-lmde-ja --install-recommends

日本語入力

$ sudo apt-get install ibus-mozc

(5) おまけ(個人的追加設定。お好みで)
Terminal のログが消えないようにする
  Terminal のウィンドウメニュー[編集][プロファイル設定]を選択。
  [スクロール]タブの[スクロールバックのサイズ]にある「無制限にする」をチェック

無操作時に画面がブラックアウトしないようにする
  左下のMenuから[コントロールセンター][スクリーンセーバー]を選択。
  「アイドル状態になったらスクリーンセーバーを起動する」をオフ。

  左下のMenuから[コントロールセンター][電源管理]を選択。
  アクション、ディスプレイのスリープを「しない」に。

やっぱり emacs のインストール
$ sudo apt-get install emacs

プライバシーポリシー
当サイトでは、Googleを含む第三者配信事業者や広告ネットワークが配信する広告を掲載しています。
当サイトでは、アクセス情報に基づいた適切な広告表示、及びトラフィックデータ収集のためにCookieを使用しています。
ユーザーはCookieの無効化により、これらの情報の提供を拒否する事ができます。
スポンサーリンク
記事検索
スポンサーリンク