20210322のLinuxに関する記事は5件です。

ターミナルでディレクトリ内の先頭ファイルを開く

発端

大量のファイルがあるディレクトリから, なんでもいいから
1個ファイルを覗きたいというときがあります.
例えば機械学習の大量のアノテーションファイルがあるディレクトリで,
なんでもいいから1個覗いてみて形式を見たいとか.
ls.png
lsして大量に表示されるの嫌ですよね.
llwc.png
ディレクトリの中に17126ものファイルが...

そんなときのワンライナー

$ vim `ls | head -n 1`

コマンドの説明

まず, vimが嫌という人は他のエディタでどうぞ. lessとかでもいいかもしれません.

`コマンド`

これで, ``の中身をコマンドとして処理し, 結果を出力させます. これをvimに渡します.

ls | head -n 1

lsした結果を, headにパイプで渡し, -nオプションで1行だけ抽出します.

もし実行権限がついていて, ファイル名に*がついちゃってるときは

$ vim `ls | head -n 1 | sed s/\*//`

のようにsedで取り除きましょう. chmodしたほうが早そうですが.

さらに, ディレクトリ内にディレクトリが含まれていて, lsしたら最初にディレクトリが表示されてしまうよってときは

$ vim `ls -F | grep -v / | head -n 1`

のようにします.
- ls -F : ディレクトリ名に / がつくようになる
- grep -v : ヒットしたもの以外を検索結果に出す

結論

こうなると, このコマンドを打ったほうが早いのか直接ファイル名を打ったほうが早いのかわかりませんね.
今回はエディタでしたが, 他のアプリでも使えます.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

勉強メモ18_「yum -y install」で何かツールを実行して「Another app is currently holding the yum lock; waiting for it to exit...」のエラーがでてしまい、インストールがうまくできない場合

0 はじめに

件名の通り、「yum -y install ツール名」で「Another app is currently holding the yum lock; waiting for it to exit...」が出てしまい、インストールできない場合

1 事象(今回はhttpdをインストール)

下記、実行すると、「Another app is currently holding the yum lock; waiting for it to exit...」により、インストールできない

[rinchome@localhost ~]$ su - root
[root@localhost ~]# yum -y install httpd

読み込んだプラグイン:fastestmirror, langpacks
ロックファイル /var/run/yum.pid が存在します: PID 7341 として別に実行されていま す。
Another app is currently holding the yum lock; waiting for it to exit...
 他のアプリケーション: PackageKit
   メモリー: 166 M RSS (592 MB VSZ)
    開始   : Mon Mar 22 13:17:11 2021 - 13:42 秒経過
    状態   : スリープ中、PID: 7341
Another app is currently holding the yum lock; waiting for it to exit...
 他のアプリケーション: PackageKit
   メモリー: 166 M RSS (592 MB VSZ)
    開始   : Mon Mar 22 13:17:11 2021 - 13:44 秒経過
    状態   : スリープ中、PID: 7341
Another app is currently holding the yum lock; waiting for it to exit...
 他のアプリケーション: PackageKit
   メモリー: 166 M RSS (592 MB VSZ)
    開始   : Mon Mar 22 13:17:11 2021 - 13:46 秒経過
    状態   : スリープ中、PID: 7341

2 対処方法

[root@localhost ~]# rm -f /var/run/yum.pid
[root@localhost ~]# yum clean all

3 再度実行(httpdをインストール)

[root@localhost ~]# yum -y install httpd

インストール:
  httpd.x86_64 0:2.4.6-97.el7.centos

依存性関連をインストールしました:
  apr.x86_64 0:1.4.8-7.el7                     apr-util.x86_64 0:1.5.2-6.el7
  httpd-tools.x86_64 0:2.4.6-97.el7.centos     mailcap.noarch 0:2.1.41-2.el7

完了しました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cgminerをsystemdで管理する方法

はじめに

ASICマイニング用のマイニングツールcgminerを、systemdで管理する方法です。

cgminerはフォアグラウンドで動作させる必要があり、そのままではバックグラウンドで動作させる事はできません。
今回はscreenを介して、バックグラウンドで動作させます。
併せて、systemdによりサービスとして管理させます。

動作確認した環境

以下の環境で動作を確認しています。
環境に応じて、適宜読み替えて下さい。

$ sudo cat /etc/debian_version 
10.7

$ uname -a
Linux debian 4.19.0-10-686-pae #1 SMP Debian 4.19.132-1 (2020-07-24) i686 GNU/Linux

$ cgminer --version
cgminer 4.11.1-wrk

$ cgminer -n
 [2021-03-22 13:43:40.226] USB all: found 5 devices - listing known devices
.USB dev 0: Bus 1 Device 3 ID: 0403:6015
  Manufacturer: 'GekkoScience'
  Product: 'NewPac Bitcoin Miner'                    
 [2021-03-22 13:43:40.231] 1 known USB devices      

cgminerは、以下のリポジトリをcloneし、hand makeしました。

以下、cgminerが動作する状況を前提とします。

cgminerをscreenで動かす・止める

以下のシェルスクリプトで、screenで、デタッチした状態で起動させます。
cgminer自身はroot権限を必要としない為、実行は一般ユーザーで問題ありません。

cgminer.screen.sh
#!/bin/sh

MINER="/usr/local/bin/cgminer"
SERVER="<Pool Addr>"
ADDR="<Your Wakllet Address>"
WORKER="<Your Worker Name>"
PASS="x"
SCREEN="cgminer"

screen -S ${SCREEN} \
${MINER} \
-o ${SERVER} \
-u ${ADDR}.${WORKER} 

実行すると、screen内でcgminerが起動します。
image.png

Ctrl+a, Ctrl+dで、デタッチし、プロセスを確認します。

$ screen -ls
There is a screen on:
        3979.cgminer    (03/22/2021 01:53:44 PM)        (Detached)
1 Socket in /run/screen/S-mika.

再度アタッチするには、以下のコマンドを実行します。
$ screen -S cgminer -r

以降、screen使いの諸兄においてはいつもの事と思われますので、screenそのものの使い方は省略します。

cgminerを停止する

デタッチされた状態からcgminerを停止するには、プロセスにqを送信します。

$ screen -S cgminer -X stuff "q"

この直後、screen -lsを実行すると、該当のプロセスが終了している事が確認できます。

$ screen -ls
No Sockets found in /run/screen/S-mika.

以上で、cgminerをscreenで運用する事が出来るようなりました。出来るようになりました。

systemdでサービス化する

ここまでの内容を踏まえ、cgminer + screenを、systemdで管理させるようにします。

実行用のシェルスクリプトを用意する

本番の実行用シェルスクリプトを用意します。
今回、実行するユーザーはmika:mika、ホームディレクトリは/home/mika/とします。

/home/mika/bin/cgminer.sh
#!/bin/sh

MINER="/usr/local/bin/cgminer"
SERVER="<Pool Addr>"
ADDR="<Your Wakllet Address>"
WORKER="<Your Worker Name>"
PASS="x"

${MINER} \
-o ${SERVER} \
-u ${ADDR}.${WORKER} 

さいごに、スクリプトに実行権限を付けておきましょう。

$ chmod +x ./cgminer.sh

systemdにサービスを追加する

rootに昇格後、以下のファイルを作成し、systemdにサービスとして追加します。

/etc/systemd/system/cgminer.service
[Unit]
Description=cgminer
After=network-online.target

[Service]
User=mika
Group=mika
TimeoutStopSec=90
WorkingDirectory=/home/mika/bin
ExecStart=/usr/bin/screen -Dm -S cgminer /home/mika/bin/cgminer.sh
ExecStop=/usr/bin/screen -S cgminer -X stuff "q"
Restart=always

[Install]
WantedBy=multi-user.target

追加が終わったら、サービスをリロードします。

# systemctl daemon-reload

サービスを起動する

サービスをstart後、statusを見ると、以下のように動作している事が確認できます。

# systemctl start cgminer
# systemctl status cgminer
● cgminer.service - cgminer
   Loaded: loaded (/etc/systemd/system/cgminer.service; disabled; vendor preset: enabled)
   Active: active (running) since Mon 2021-03-22 14:15:45 JST; 2s ago
 Main PID: 7699 (screen)
    Tasks: 15 (limit: 4175)
   Memory: 2.2M
   CGroup: /system.slice/cgminer.service
           ├─7699 /usr/bin/SCREEN -Dm -S cgminer /home/mika/bin/cgminer.sh
           ├─7700 /bin/sh /home/mika/bin/cgminer.sh
           └─7701 /usr/local/bin/cgminer -o stratum+tcp://sa256.example.com:8888 -u ...

Mar 22 14:15:45 debian systemd[1]: Started cgminer.

サービスを停止する

先と同様に、サービスをstopすると、cgminerおよびscreenが停止している事が確認できます。
メインプロセスが戻り値1で終了しているので異常のように思えますが、正常です。
(子プロセスがいきなりexitしてしまったので、Failと思っているようです)

root@debian:~# systemctl stop cgminer 
root@debian:~# systemctl status cgminer
● cgminer.service - cgminer
   Loaded: loaded (/etc/systemd/system/cgminer.service; disabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Mon 2021-03-22 14:18:03 JST; 1s ago
  Process: 7699 ExecStart=/usr/bin/screen -Dm -S cgminer /home/mika/bin/cgminer.sh (code=exited, status=1/FAILURE)
  Process: 8165 ExecStop=/usr/bin/screen -S cgminer -X stuff q (code=exited, status=0/SUCCESS)
 Main PID: 7699 (code=exited, status=1/FAILURE)
    Tasks: 0 (limit: 4175)
   Memory: 316.0K
   CGroup: /system.slice/cgminer.service

Mar 22 14:15:45 debian systemd[1]: Started cgminer.
Mar 22 14:18:03 debian systemd[1]: Stopping cgminer...
Mar 22 14:18:03 debian systemd[1]: cgminer.service: Main process exited, code=exited, status=1/FAILURE
Mar 22 14:18:03 debian systemd[1]: cgminer.service: Failed with result 'exit-code'.
Mar 22 14:18:03 debian systemd[1]: Stopped cgminer.

自動起動を設定する

Linux Serverを普段使いされている諸兄においては、いつもの事と思われますので、詳細は省略します。

# systemctl enable cgminer
# systemctl start cgminer

おわりに

設定とか、例外処理とか、いろいろと端折りましたが、個人でも普段使いならこの程度で問題ない気がします。
DockerコンテナからUSBパススルーとかすると面白かったのかも知れませんが、そこまでの気力もないので、このあたりで。

おまけ1 - デバイスの認識

USB ASICであるGekkoScience NEWPACは、OS上からは以下のように認識されます。

$ lsusb 
Bus 001 Device 003: ID 0403:6015 Future Technology Devices International, Ltd Bridge(I2C/SPI/UART/FIFO)

WindowsではドライバをCOMポートからWinUSBに変更する必要がありましたが、Linuxの方では、その辺りよしなにしてくれるようです。

おまけ2 - SHA256について

BItCoinで使用されている暗号化アルゴリズムSHA256ですが、現状ASIC Minerを使用した業者により大量のハッシュレートが提供されており、20GHs程度ではお金になりません。苦笑
今回、同じ暗号化アルゴリズムを採用しているすすコインを掘らせております。

正直ネタコインの域を超えない感じは否めませんが、技術的興味を満たすには十分でしょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CDK を用いた Linux 仮想サーバーの構築

概要

前回は EC2 ダッシュボードの GUI に従って インスタンス(Linux 仮想サーバー)を起動した。今回は東京大学計数工学科で2020年度S1/S2タームに開講された"システム情報工学特論" を参考に、クラウド開発キット(Cloud Development Kit : CDK) というソフトウェア開発プラットホームを用いてインスタンスを起動する手順を紹介する。講義資料の手順に従うために必要な環境設定を中心に解説する。

環境

  • MacBook Air (Retina, 13-inch, 2018)
  • macOS Big Sur (Version 11.2.3)
  • メモリ 16 GB

CDK とは

CDK は CloudFormation を構築するソフトウェア開発プラットホームであり、現在は TypeScript(JavaScript)、Python、Java、C#(.NET)の 4 言語でサポートされている。本項では AWS 無料アカウントを用いて Python プラットホームによる開発に取り組む。CDK の使い方は、こちらのワークショップに従ってアプリを作成することで効率的に習得することもできる。

CDK を用いた Linux 仮想サーバーの構築

STEP1: AWS CLI のインストール

前回のように AWS の各種設定・操作はマネージメントコンソールから視覚的に行うこともできる。一方で AWS 専用の CLI(コマンドラインインターフェース)を用いることで各種設定・操作を管理・自動化することもできる。
AWS CLI は aws から始まる一連のコマンドであり、様々なサービスを迅速に利用することができる。AWS CLI をローカルにインストールする方法はこちらに記載されているが、前回構築した Linux 仮想サーバーに AWS CLI は標準搭載されているため今回はこちらを利用する。前回の要領で Linux 仮想サーバーに SSH 接続してみよう。

STEP2: VScode と SSH 接続

テキストエディタの中でも VSCode は拡張機能で SSH 接続が可能になるため、AWS の開発に利用されることが多い。公式でも AWS のコードエディタとして VSCode が推奨されている。日本語ではこちらに詳しくまとめられている。VSCode で SSH 接続拡張機能をインストールし、前回作成したキーペアを用いて SSH 接続しよう。
これにより仮想サーバーのコード編集がリモートで可能になる。

STEP3: AWS CDK のインストール

AWS CDK は npm を用いてインストールする。まずはこちらを参考に npm をインストールし、続けてこちらに従って CDK をインストールする。

npm インストールのために nvm をインストールする。

ターミナル
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash

nvmを有効にする。

ターミナル
$ . ~/.nvm/nvm.sh

nvmnpm をインストールする。

ターミナル
$ nvm install node

npm で CDK をインストールする。

ターミナル
$ npm install -g aws-cdk

CDK のバージョンが正しく確認できれば正常にインストールされている。

ターミナル
$ cdk --version
1.94.1 (build 60d8f91)

STEP4: AWS シークレットキーの取得・登録

AWS CLI や AWS CDK を用いてアプリ開発に取り組む場合、ユーザー認証にシークレットキーが必要である。こちらに従ってシークレットキーを取得しよう。

まずユーザー名を指定し、「アクセスの種類」で「プログラムによるアクセス」にチェックを入れる。
スクリーンショット 2021-03-22 11.08.07.png

次に「アクセス許可の設定」で「既存のポリシーを直接アタッチ」を選択し、「ポリシー名」で「AdministratorAccess」にチェックを入れる。

スクリーンショット 2021-03-22 11.08.39.png

その他の設定は行わず「確認」ボタンを押すとアクセスキー ID および「シークレットアクセスキー」が得られる。このページから移動するとアクセスキーは二度と得られなくなるため .csv でダウンロードして保存しておこう。

スクリーンショット 2021-03-22 11.09.27.png

AWS CLI を用いてシークレットキーを登録しよう。aws configure というコマンドで登録できる。

ターミナル
$ aws configure
AWS Access Key ID [None]: アクセスキーID
AWS Secret Access Key [None]: シークレットアクセスキー
Default region name [None]: ap-northeast-1
Default output format [None]: json

ここで登録した内容は .aws/config および .aws/credentials に書き込まれる。先ほど SSH 接続した VSCode で確認してみるとよい。
スクリーンショット 2021-03-22 12.36.31.png

configure の内容は環境変数にも代入しておく。以下の内容は ~/.bash_profile に書きこおくと毎回代入しなくても済むようになる。同様に VSCode で編集してもよいだろう。

ターミナル
$ export AWS_ACCESS_KEY_ID=アクセスキーID
$ export AWS_SECRET_ACCESS_KEY=シークレットアクセスキー
$ export AWS_DEFAULT_REGION=ap-northeast-1

シークレットキーの取得・登録を行わずに AWS CDK を利用しようとすると以下のエラーが表示されるため注意。

ターミナル
Unable to resolve AWS account to use. It must be either configured when you define your CDK or through the environment

STEP5: ソースコードのダウンロード

AWS CDK を用いて Linux 仮想サーバーを構築する際に必要なソースコードをダウンロードする。今回は東京大学計数工学科で2020年度S1/S2タームに開講された"システム情報工学特論" の Hands-on を参考に取り組む。git をインストールし、続けてソースコードをダウンロードしよう。

ターミナル
$ sudo yum install git
$ git clone https://gitlab.com/tomomano/intro-aws.git

これを実行するとソースコードや講義資料を含む intro-aws/ が得られる。フォルダの中身を VSCode で確認しておこう。今回は intro-aws/handson/01-ec2 で作業を行う。
スクリーンショット 2021-03-22 12.42.33.png

STEP6: EC2 インスタンスの起動

こちらを参考に EC2 インスタンスを起動する。
まずは Python3 をインストールし、venv で仮想環境を構築する。

ターミナル
$ cd intro-aws/handson/01-ec2
$ sudo yum install python3 -y
$ python3 -m venv .env
$ source .env/bin/activate
$ pip install -r requirements.txt

次に AWS CLI を用いてキーペアを作成する。前回は EC2 ダッシュボードの GUI で作成したが、このようにコマンドでも実行できる。KEY_NAMEは自由に設定してよい。

ターミナル
$ export KEY_NAME="HirakeGoma"
$ aws ec2 create-key-pair --key-name ${KEY_NAME} --query 'KeyMaterial' --output text > ${KEY_NAME}.pem
$ mv HirakeGoma.pem ~/.ssh/
$ chmod 400 ~/.ssh/HirakeGoma.pem

CDK で仮想サーバーをデブロイし、SSH 接続する。
以下のように表示されればインスタンス起動が成功している。

ターミナル
$ cdk deploy -c key_name="HirakeGoma"
$ ssh -i ~/.ssh/HirakeGoma.pem ec2-user@3.113.6.36

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/

今回開発したアプリの概要は講義資料を参考にしてください。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Raspberry PiでDevice Treeを読んでみる

Raspberry Pi 4を使って、Device Treeを読んでみます。

このページではRaspberry Pi 4を題材に、Device Treeの読み方を3種類紹介します。1つ目はLinux kernelのソースツリー上で読む方法、2つ目はビルドしたバイナリー (Device Tree blob) から読む方法、3つ目はOS起動後のランタイムで読む方法です。2つ目と3つ目の方法はどちらも一旦Device Treeソースに戻して読む方法を紹介します。

なお、筆者が使用している環境はRaspberry Pi 4 Model BのRAM 8GBモデル、Raspberry Pi OSの64-bit版です。Raspberry Pi OSで使用しているLinux kernelのソースコードは https://github.com/raspberrypi/linux からダウンロードできます。

参考:
Device Tree (eLinux Wiki)
Linux and the Deivce Tree (The Linux Kernel Documentation)

ソースツリー上で読んでみる

Device Treeは、システムに搭載されたデバイスのうち、OSが検出する仕組みを持っていないデバイスを記述しておく手段の一つです。主に組込み向けのLinuxで採用されていて、Raspberry Pi OSでもDevice Treeを採用しています。

Linux kernelのソースツリー上の、Device Treeの格納場所は arch/<各アーキテクチャ名>/boot/dts/ の下です。Raspberry Pi OSは正式版の32-bitと、評価版の64-bitのものがありますが、それぞれ'arm'と'arm64'でアーキテクチャのディレクトリが別になっています。

まずは64-bit版のDevice Treeソースを見てみます。ファイルはRaspberry Piが採用しているチップのベンダーであるBroadcom用のディレクトリ arch/arm64/boot/dts/broadcom ディレクトリの下にあります。ここには、Coretex-a53を採用して64-bit動作可能になったRasberry Pi 2-B (v1.2)、3-A+、3-Bおよび3-B+、Coretex-a57を採用した4-Bや、新たに発表されているRaspberry Pi 400のものが格納されています。
ちなみに、32-bit版の arch/arm/boot/dts の下はチップベンダー毎のディレクトリ階層になっておらず、ちょっとカオスな状態になっています。ファイル名の接頭辞 (broadcomなら'bcmxxx')を見ると判別できます。

Raspberry Pi 4用のarch/arm64/boot/dts/broadcom/bcm2711-rpi-4.dtsの中を見てみます。

#include "../../../../arm/boot/dts/bcm2711-rpi-4-b.dts"

1行だけです。LinuxのDevice Treeソースでは、Cソースと同様に#includeを使って他のDevice Treeソースやヘッダファイルをインクルードしたり、マクロの定数を使用したりできます。ここでは32-bit版の"bcm2711-rpi-4-b.dts"をインクルードしているだけですが、これは32-bit版のソースと共通化するための施策だと思います。

ということで、64-bit版向けにビルドする場合であっても、Device Treeのソースは32-bit版の物だけを見ればよさそうです。arch/arm/boot/dts/bcm2711-rpi-4-b.dtsを見ていきます。

// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
#include "bcm2711.dtsi"
#include "bcm2835-rpi.dtsi"

#include <dt-bindings/reset/raspberrypi,firmware-reset.h>

/ {
    compatible = "raspberrypi,4-model-b", "brcm,bcm2711";
    model = "Raspberry Pi 4 Model B";
...

このファイルでも、別のファイルをインクルードしています。

Device Treeの基本的な書式

Device Treeの書式について、詳しくはDevice Tree Usage (eLinux Wiki)などを参考にしてください。ここではざっくりと眺める上で必要な最低限のことだけを記載します。

Device Treeの基本的な構造は単純です。ノードと呼ぶ中括弧{}で囲われた中に、キー・バリュー型のプロパティが列挙されています。

ノード名 {
    プロパティ名 = プロパティの値;
    ...
};

ノードは階層構造を持たせることもできます。一番外側のノードはルートノード/と呼びます。

/ {
  親ノード {
    子ノード1 {
    };
    子ノード2 {
    };
  };
};

オーバーライド

また、ルートノード/の外側に、&で始まるノードが書かれている場合があります。これは既に定義済みのノードへの参照です。これまでに見た通り、LinuxのDevice Treeソースでは他ファイルをインクルードすることができますが、インクルードされたファイルを追っていくと、同じノード名・同じプロパティ名が定義されていることに気がつくかと思います。このとき、そのノードのプロパティは後から記述された値によってオーバーライドされます。

/{
    あるノード {
        プロパティ名 = プロパティ値;
    };
    ...
};
...
&あるノード {
        プロパティ名 = オーバーライドする値; 
};

例として、LEDのノードを見てみます。ノード名はledsです。
ledsノードはbcm2711-rpi-4-b.dtsの2箇所 (ここここ)、bcm2711-rpi-4-b.dtsがインクルードしているbcm2835-rpi.dtsiに1箇所 (ここ) 記述があります。インクルードの順序関係から、最初に解釈されるノード記述はbcm2835-rpi.dtsi、それをbcm2711-rpi-4-b.dtsの記述でオーバーライドした後、さらに同ファイルの&leds {...};の値がオーバーライドしています。

bcm2835-rpi.dtsi

    leds {
        compatible = "gpio-leds";

        act {
            label = "ACT";
            default-state = "keep";
            linux,default-trigger = "heartbeat";
        };
    };

bcm2711-rpi-4-b.dts

    leds {
        act {
            gpios = <&gpio 42 GPIO_ACTIVE_HIGH>;
        };

        pwr {
            label = "PWR";
            gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;
            default-state = "keep";
            linux,default-trigger = "default-on";
        };
    };
...
&leds {
    act_led: act {
        label = "led0";
        linux,default-trigger = "mmc0";
        gpios = <&gpio 42 GPIO_ACTIVE_HIGH>;
    };

    pwr_led: pwr {
        label = "led1";
        linux,default-trigger = "default-on";
        gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;
    };
};

ビルドされたときには、ledsの子ノードであるactノードのプロパティlabelの値は"led0"、pwrノードのプロパティlabelの値は"led1"になります。

Note: 同じノード名のを記述した場合、後から書いたものでプロパティがオーバーライドされる。例えば同系統のボードで搭載デバイスに共通するものがある場合はdtsiにし、各ボードのdtsからインクルードしたうえで細かい差異はオーバーライドすることでDevice Treeを構造化できる。
image.png
image.png

バイナリーから読んでみる

Raspberry Pi上でLinux kernelをビルドする手順や必要なパッケージについては、公式ページのビルド手順を参考にしてください。Linuxソースツリー上でDevice Treeのみをコンパイルするには、ソースツリーのトップディレクトリ下でmake dtbsを叩きます。

$ export ARCH=arm64       # クロス環境・Raspberry Pi OSの32-bit版で行う場合は実行する
$ make bcm2711_defconfig  # このコマンドはまだ実行していなければ実行する
$ make dtbs

arch/arm64/boot/dts/broadcom/ ディレクトリおよびarch/arm64/boot/dts/overlays/ ディレクトリのソースがビルドされ、Device Treeのバイナリー (Device Tree blob)が生成されます。このうち、Raspberry Pi 4で使用するのはarch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtbです。これを/bootの下におけばLinux起動時にブートローダがロードしてくれるのですが、今回はこれをソースに戻して読めるようにしてみます。

Device Treeをソースに戻すにはDevice Treeコンパイラdtcを使います。dtcはソースからバイナリー、バイナリーからソースのどちらの変換もできるツールです。詳しい使い方はman dtcを参照してください。主に使うオプションは下記です。

使い方: dtc <オプション> <入力ソース>
-I <入力形式>
入力ソースの形式を指定する。下記3つが選択可能。
dts -- Device Treeソース形式 (テキスト形式)
dtb -- Device Tree blob 形式 (バイナリー形式)
fs -- /proc/device-tree スタイルのディレクトリ (※後述)
-O <出力形式>
コンパイル後の形式を指定する。下記3つが選択可能。
dts, dtb, asm (アセンブラ命令の形式)
-s
ノード、プロパティ名をソートして出力する

なお、入力ソースは引数で指定するか、パイプを使って標準入力で入力できます。出力は-oでファイル名を指定しない限りは標準出力になります。

Device Treeバイナリーからソースへと変換する例:

$ dtc -I dtb -O dts -s -q arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb | head
/dts-v1/;

/memreserve/    0x0000000000000000 0x0000000000001000;
/ {
    #address-cells = < 0x02 >;
    #size-cells = < 0x01 >;
    compatible = "raspberrypi,4-model-b\0brcm,bcm2711";
    interrupt-parent = < 0x01 >;
    model = "Raspberry Pi 4 Model B";

…私の環境だとなんかWarningがいっぱい出ましたが、とりあえず無視します。-qオプションをつけるとWarningが抑止されます。

ランタイムで読んでみる

OS起動後に、どのようなDevice Treeで起動したのかを確認します。
dtcのオプションにある通り、/proc/device-treeなどのファイルシステムを入力としてDevice Treeソースへ変換することができます。/proc/device-treeは、OSが読み込んだDevice Treeの情報を確認できるようにしているAPIです。

/proc/device-treeの実体は /sys/firmware/devicetree/base です。

$ ls -l /proc/device-tree
lrwxrwxrwx 1 root root 29 Mar 13 02:49 /proc/device-tree -> /sys/firmware/devicetree/base

ディレクトリの構造を見てみると、Device Treeのノードとプロパティの構造に対応した構造になっているのがわかるかと思います。例として、ledsディレクトリの下はこのようになっています。

$ cd /sys/firmware/devicetree/base/leds/
$ tree
.
├── act
│   ├── default-state
│   ├── gpios
│   ├── label
│   ├── linux,default-trigger
│   ├── name
│   └── phandle
├── compatible
├── name
├── phandle
└── pwr
    ├── default-state
    ├── gpios
    ├── label
    ├── linux,default-trigger
    ├── name
    └── phandle

/sys/firmware/devicetree/base/leds/act/labelの値を見てみると、Device Treeソースので設定されたlabelプロパティの値"led0"が読めることがわかります。

$ cat act/label
led0

上記の例は文字列のプロパティ値のためcatコマンドで読むことができますが、整数のプロパティはhexdumpを使えば16進の値として読むことができます。

dtcで/proc/device-treeをDevice Treeソースに戻してみます。-I fsオプションを付け、引数に/proc/device-treeを指定して実行します。コンパイルに成功すると、バイナリーからコンパイルしたときと同様に、Device Treeソースとして読めるようになります。

/proc/device-treeをDevice Treeソースに戻す例

$ dtc -I fs -O dts -s -q /proc/device-tree | head
/dts-v1/;

/ {
    #address-cells = < 0x02 >;
    #size-cells = < 0x01 >;
    compatible = "raspberrypi,4-model-b\0brcm,bcm2711";
    interrupt-parent = < 0x01 >;
    memreserve = < 0x3b400000 0x4c00000 >;
    model = "Raspberry Pi 4 Model B Rev 1.4";
    serial-number = "10000000ff852c78";

例えば組込み機器開発中のデバッグ中に「あれ、なんか挙動がおかしいけどDevice Tree間違えたんだろか?」という状況で、起動に実際に使われたプロパティを確認したいこともあるかと思います。そんな場合でも、ランタイムでDevice Treeを読む方法を知っていると役に立つかと思います。

余談

ランタイムで読まないといけないケース、というのがあり、例えばRaspberry Pi 4の一部のプロパティ値は、ブートローダによって書き換えられてしまうものがあるようです。Device Treeソースを見てみると、memoryノードに下記のようなコメントが付いています。

    /* Will be filled by the bootloader */
    memory@0 {
        device_type = "memory";
        reg = <0 0 0>;
    };

memoryノードは搭載されているRAMのアドレスやサイズを記載しておくノードですが、上記の通り中身が全て0です。Raspberry Pi 4はRAM 2GB/4GB/8GBのモデルがあり、1つのDevice Treeで全てのモデルをカバーすることができません。Raspberry Piのブートローダはクローズドソースなので中で何をやっているのかは確認できませんが、上記コメントから察するに、おそらくブートローダが起動時にモデルの判別を行い、memoryノードのアドレスやサイズを書き換えるのでしょう。

実際に読み込まれたmemoryノードのプロパティregの値をコンパイルして確かめてみます。

$ dtc -I fs -O dts -q /proc/device-tree | grep "memory@0" -A 3
    memory@0 {
        device_type = "memory";
        reg = < 0x00 0x00 0x3b400000 0x00 0x40000000 0xbc000000 0x01 0x00 0x80000000 0x01 0x80000000 0x80000000 >;
    };

regはメモリマップドデバイスのアドレスとサイズを設定するためのプロパティで、書式はreg = <address1 length1 [address2 length2] [address3 length3] ... >のように、アドレスとサイズが一対となったタプルのリストとして表現されます (書式の詳細はこちらを参照してください)。
タプルの解釈に必要な#address-cells#size-cellsの値はルートノード/をみると、

    #address-cells = <2>;
    #size-cells = <1>;

となっているため、上記regの値より、下表のようにアドレス・サイズが設定されていることがわかります。

address length
0x00000000_00000000 0x3b400000
0x00000000_40000000 0xbc000000
0x00000001_00000000 0x80000000
0x00000001_80000000 0x80000000

設定されているRAM領域は4つあり、サイズ合計 0x1F7400000(16) Byte = 8443133952(10) Byte = 8052 MBとなり、約8GB分が設定されているようです。

他にもDevice Tree Overlayという仕組みで書き換えられるケースがあります。ブートローダによって書き換えられてしまうケースはRaspberry Piが特殊なだけな気もしますが、使用している環境によっては実際に設定される値をあらかじめ知る手段が無い場合があるので、そんなときにもランタイムでDevice Treeを読む方法が役に立つでしょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む