買ったのは1年ぐらい前で未開封のまま放置していたのですが…。
人生ハイド(隠れて目立たないように生きている意)担当の @nakochi です。
アドベントカレンダーをノリで2枠も取ったものの、なにを書くか深く考えていなかったので、前日になって一年ほど前に購入した milk-V Duo を引っ張り出してきました。
milk-V Duo (64MB) とは
超格安シングルボードコンピュータの一種です。本体(画像では白いボードの上に載ってる茶色っぽいボード)であればみんな大好き秋月電子通商さんにて1,420円ほどで購入できてしまいます。
この安さだけでもすごいのですが、もう一つの注目ポイントとしてCPUアーキテクチャが今最もアツいRISC-Vという点があります。
他、スペックとしては以下のようになっています:
- SoC: CVITEK CV1800B
- CPU1: RISC-V C906@1GHz for Linux
- CPU2: RISC-V C906@700MHz for FreeRTOS
- RAM: 64MB(!)
他、256MBモデルもあり、こちらの場合は文字通りRAMが256MBに増えるほか、なんとARMコア(Cortex-A53@1GHz)まで内蔵されているようです(ただしRISC-Vのメインコアとは排他)。わりと意味がわからなく、発表当初は界隈が若干ざわついたりしていました。
とりあえず起動してみた
公式イメージもあるのですが、今回はフォーラムに投稿されていたDebianイメージで起動してみました。ソースコードもしっかり公開されているとはいえ、試される際は十分注意してご利用ください。当環境ではメインのネットワークとは完全に切り離した(大元の回線契約ごと違う)環境にて遊んでおります。
$ ssh root@10.255.255.7
> root@10.255.255.7 password: riscv
> The programs included with the Debian GNU/Linux system are free software;
> the exact distribution terms for each program are described in the
> individual files in /usr/share/doc/\*/copyright.
> Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
> permitted by applicable law.
$ root@milkv-duo:~# apt update
> Get:1 https://mirror.sjtu.edu.cn/debian-ports sid InRelease [74.2 kB]
> Get:2 https://mirror.sjtu.edu.cn/debian-ports unstable InRelease [74.2 kB]
> Get:3 https://mirror.sjtu.edu.cn/debian-ports unreleased InRelease [50.0 kB]
> Get:4 https://mirror.sjtu.edu.cn/debian-ports experimental InRelease [74.3 kB]
> Fetched 273 kB in 8s (32.6 kB/s)
> Reading package lists... Done
> Building dependency tree... Done
> Reading state information... Done
> All packages are up to date.
$ root@milkv-duo:~# apt install nginx
> Reading package lists... Done
> Building dependency tree... Done
> Reading state information... Done
> E: Unable to locate package nginx
$ root@milkv-duo:~# free -h
> total used free shared buff/cache available
> Mem: 54Mi 24Mi 6.7Mi 76Ki 27Mi 30Mi
> Swap: 255Mi 4.2Mi 251Mi
IPはご家庭のルーターのDHCP払い出し状況等などから該当するIPを探して接続します(例では10.255.255.7)。デフォルトパスワードは riscv
です。
リポジトリがデフォルトで中国内ミラーに向いているのが気になりますが、それ以上に主流なパッケージがあまりない状況です。
Webサーバにしてみたい
深く考えずに記事を書き、深く考えずに題材を決め、深く考えずにmilk-Vを起動したため、明確になにをしたいみたいなものがありません。そもそもタイトルが「買ってみた」なのでタイトルは現時点でも達成できているわけで、買ってみた&起動してみたで終わる手もありますが、それだとおもしろくない。
なので、ひとまずWebサーバとして一枚のWebサイトを表示できるようにしてみたいと思います。Linuxが動作するデバイスの我流 Hello, world
的な感じです。
本来であればWebサーバはNGINXをインストールしてしまえば解決なのですが、上で試した通りNGINXパッケージがデフォルトだとありません。
NGINXをビルドする、他の方がビルドしたバイナリを探してくる、あるいはlighttpd(ライティ)などをビルドするなど様々な方法がありますが、今回はそれらすべて面倒だったため時間がかかりそうだったため、GoでWebサーバを書いてクロスコンパイルします。
コード例
雑実装です。HTTPライブラリとして gorilla/mux を利用しています。
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"strconv"
"github.com/gorilla/mux"
)
var serverSettings struct {
FileDir string `json:"directory"`
Port int `json:"port"`
}
func main() {
var fileName string
flag.StringVar(&fileName, "s", "./settings.json", "Configurations File Path")
flag.Parse()
err := readSettings(fileName)
if err != nil {
fmt.Printf("%v\n", err)
}
routingRequests(serverSettings.Port)
}
func readSettings(fileName string) (err error) {
jsonDef := []byte(`{"directory":"./data","port":80}`)
rawData, err := os.ReadFile(fileName)
if err != nil {
json.Unmarshal(jsonDef, &serverSettings)
return fmt.Errorf("file error")
}
err = json.Unmarshal(rawData, &serverSettings)
if err != nil {
json.Unmarshal(jsonDef, &serverSettings)
return fmt.Errorf("format error")
}
return nil
}
func routingRequests(port int) {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/{request}", requestHandler)
log.Fatal(http.ListenAndServe(":"+strconv.Itoa(port), router))
}
func requestHandler(w http.ResponseWriter, r *http.Request) {
request := mux.Vars(r)["request"]
raw, err := os.ReadFile(serverSettings.FileDir + "/" + request)
if err != nil {
res, _ := json.Marshal(map[string]string{"error": err.Error()})
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotFound)
w.Write(res)
} else {
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
w.Write(raw)
}
}
コンパイル例
GOOSでOS種別、GOARCHでアーキテクチャを設定します。かなり幅広いOSとアーキテクチャに対応しており、雑アプリを書くならやっぱりGolangかRustだなとなるなどしました。
$ GOOS=linux GOARCH=riscv64 CGO_ENABLED=0 go build -o binary -a -tags netgo -installsuffix netgo --ldflags '-extldflags "-static"' main.go
設定ファイルの用意(しなくてもデフォルト値があるので大丈夫)
{
"directory": "./data",
"port": 80
}
起動する
起動させる際はがんばってmilk-Vにバイナリを転送し(ぼくはSDカードのbootに突っ込み、milk-V側からマウントして移動させました)、 ./binary
のように直接実行で起動できます。
まとめ
なんだかよくわからない駄文になってしまいましたが、まとめると以下のような感じです。
- RISC-V関連はまだ発展途上なのでさわるだけでもかなりつらさがあり、おもしろい
- Golangはクロスコンパイルや静的ビルドが相当簡単にでき、ビルド周りだけは本当に優秀
ありがとうございました。