distrolessを試す。

軽量なコンテナを作るにはdistrolessのイメージをベースにするのがよいらしい。 distrolessは、Googleが管理しているイメージで、Google Container Registryで管理されているっぽい。

github.com

当たり前だけどdockerhubで検索してもでてこない。

とりあえず、nextのコンテナで試してみた。

FROM node:14-bullseye AS build-env

ADD . /app
WORKDIR /app
RUN npm install
RUN npm run build

FROM gcr.io/distroless/nodejs:14
WORKDIR /app
COPY --from=build-env /app /app

EXPOSE 3000
CMD ["node_modules/.bin/next", "start"]

ちょっとめんどくさいけどできた。

どのくらい軽いか比較してみた。

  • node:14-bullseye: 1.24GB
  • node:14-bullseye-slim: 411MB
  • gcr.io/distroless/nodejs:14 : 340MB

bullseye-slimと比較すると71MB削減、この差分に価値を見出すかどうかかなぁ。

JNIのはじめの一歩

たまに必要になるJNI。ノート替わりにメモしておく。

例えばこんな感じのSample.javaコードを用意して、ここからヘッダファイルを生成する。

public class Sample {
  public static native int readValue();
}
javac Sample.java -h .

javacのヘルプみればわかるけど、-hの後ろにはヘッダファイルを生成するディレクトリを指定する。

生成されたヘッダファイルSample.hは以下。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Sample */

#ifndef _Included_Sample
#define _Included_Sample
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Sample
 * Method:    readValue
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_Sample_readValue
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

このヘッダファイルをincludeしたcのコードは以下。

#include "Sample.h"

JNIEXPORT jint JNICALL Java_Sample_readValue
  (JNIEnv *env, jclass clazz) {
    return 13;
}

ヘッダファイルでは引数の変数名が省略されているので、コピペする場合は引数名を決めること。

とりあえずgccコンパイルする。(最近はAmazon Correttoをつかっているので。)

gcc -I/usr/lib/jvm/java-11-amazon-corretto/include -I/usr/lib/jvm/java-11-amazon-corretto/include/linux sample.c -shared -o libSample.so

このとき-sharedを指定して、-oで共有オブジェクト.soとしてコンパイルする必要がある。(そりゃそうだ)

あとは、Javaコードからsoをロードする必要がある。とりあえずはstaticブロックに追加して再コンパイルする。

public class Sample {
  static {
    System.loadLibrary("Sample");
  }
  public static native int readValue();
}

で、これは普通に呼び出せる。

public static class Main {
  public static void main(String[] args) {
    System.out.printf("readValue: %d\n", Sample.readValue());
  }
}

上記のコードをMain.javaとすると以下でコンパイルする。

javac Main.java

soをロードするにはjava.library.pathをパスを指定する必要があるので実行は以下。

java -Djava.library.path=. Main

実行結果は以下となる。

readValue: 13

これで最低限のJNIが呼び出せるようになった。

ちなみにWindowsの場合は.soが.dllになったりするのでコンパイルオプションなど取り扱いが変わる。

「12ステップで作る組み込みOS自作入門」の写経を終えた。

とりあえず本の内容は写経し終えた。

「ゼロから~」ので汎用OSの自作の入門は終えていたけど、組み込みOSの自作ということで学びが多かった。

ma38su.hatenablog.com

できあがるもの、という意味ではecho コマンド一つなのだけれど、個々の説明もかなり詳しくサクサク読み進められた。 派手さはないが、教材としての学びはとても大きいと思った。これ発売当初に気づいてやっておければよかったと思う。。。 ただまぁ、この本を読んだからといってH8マイコンのマニュアルが読めるかというとなかなか難しくて。。。日本語なのにね。

「12ステップで作る組み込みOS」を始めた。

メモ。

WSL2はUSBが認識できないので、VirtualBoxにUbuntu20.04をセットアップした。 セルフコンパイラのインストール。とりあえずバージョンとか意識せずにインストールしてみた。

apt install binutils make gcc curl

ロスコンパイラのインストール。

binutils

curl -LO http://kozos.jp/books/makeos/binutils-2.21.tar.gz
tar zxvf binutils-2.21.tar.gz
cd binutils-2.21
./configure --target=h8300-elf --disable-nls --disable-werrorz
make
sudo make install

gcc いろいろパッチ充てる必要があった。

curl -LO http://kozos.jp/books/makeos/gcc-3.4.6.tar.gz
tar zxvf gcc-3.4.6.tar.gz
cd gcc-3.4.6
curl -LO http://kozos.jp/books/makeos/patch-gcc-3.4.6-gcc4.txt
patch < patch-gcc-3.4.6-gcc4.txt
curl -LO http://kozos.jp/books/makeos/patch-gcc-3.4.6-x64-h8300.txt
patch < patch-gcc-3.4.6-x64-h8300.txt
./configure --target=h8300-elf --disable-nls --disable-threads --disable-shared --enable-languages=c --disable-werror
make
sudo make install

4章終了時点でのブートローダののMakefileは以下。XMODEMはkz_xmodemを利用した。

PREFIX  = /usr/local
ARCH    = h8300-elf
BINDIR  = $(PREFIX)/bin
ADDNAME = $(ARCH)-

AR      = $(BINDIR)/$(ADDNAME)ar
AS      = $(BINDIR)/$(ADDNAME)as
CC      = $(BINDIR)/$(ADDNAME)gcc
LD      = $(BINDIR)/$(ADDNAME)ld
NM      = $(BINDIR)/$(ADDNAME)nm
OBJCOPY = $(BINDIR)/$(ADDNAME)objcopy
OBJDUMP = $(BINDIR)/$(ADDNAME)objdump
RANLIB  = $(BINDIR)/$(ADDNAME)ranlib
STRIP   = $(BINDIR)/$(ADDNAME)strip

H8WRITE = ../../tools/h8write/kz_h8write

H8WRITE_SERDEV = /dev/ttyUSB0

OBJS  = vector.o startup.o main.o \
        lib.o serial.o xmodem.o

TARGET = kzload

CFLAGS = -Wall -mh -nostdinc -nostdlib -fno-builtin \
         -I. -g -Os -DKZLOAD

LFLAGS = -static -T ld.scr -L.

.SUFFIXES: .c .o
.SUFFIXES: .s .o

all: $(TARGET)

$(TARGET): $(OBJS)
        $(CC) $(OBJS) -o $(TARGET) $(CFLAGS) $(LFLAGS)
        cp $(TARGET) $(TARGET).elf
        $(STRIP) $(TARGET)

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

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

$(TARGET).mot: $(TARGET)
        $(OBJCOPY) -O srec $(TARGET) $(TARGET).mot

image: $(TARGET).mot

write: $(TARGET).mot
        sudo chmod o+rw $(H8WRITE_SERDEV)
        $(H8WRITE) -3069 -f20 $(TARGET).mot $(H8WRITE_SERDEV)

clean:
        rm -f $(OBJS) $(TARGET) $(TARGET).elf $(TARGET).mot

monitor:
        sudo chmod o+rw $(H8WRITE_SERDEV)
        cu -l $(H8WRITE_SERDEV)

load:
        sudo chmod o+rw $(H8WRITE_SERDEV)
        ../../tools/kz_xmodem/kz_xmodem defines.h /dev/ttyUSB0

インフィニティnumpyの演習221問やり終えた。

Twitter眺めてたら見つけた、インフィニティnumpy。numpyは、ちょっとはわかるけど、ちゃんとわかってなかったので、ちょうどよさそうと思いやってみた。 一か月くらいかかった気はするけど、夏季休暇期間中にやり終えることができた。

booth.pm

1章

基礎。この辺はさすがにさくさくできる。

2章

リスト。 パフォーマンスの考察などはためになる。 演習は簡単。応用もそんなに詰まらずにできる。

Q19からQ28の「魔王と勇者のデータサイエンス」は、 問題文が長いので一度飛ばしたが、やってみたら特に詰まらずにできた。

3章

行列。 stackとconcatenateの違い、axisの使い方あたり重要。 演習も変な難問があるわけではないので、そんなに詰まらずにできる範囲だと思った。

4章

画像処理。 3章までの応用。画像処理のアルゴリズム的な部分ではまることが何度か。numpy力はある程度身についている気がするけど、型の扱いとかたまにミスって試行錯誤はしてしまう。。。 Q84からQ91は、CMYK関係なので、印刷とかしないならいいかなーと、Q84だけやって一旦パスした。(あとでやる)

あとは紙の書籍だと白黒なので、カラーの電子書籍を見ながらやるほうがよいかも。

5章

動画生成。 画像から一次元増やしただけではあるけれど、 reshapeとか、ブロードキャストとか新しいテクニックが頻出する。

感想など

numpyならではのfor文なしで行列演算(テンソル)で処理するのは、GLSLでフラグメントシェーダ書くのと結構似ている部分があった気がする。 と思って久々にGLSL書いたら、もっと制約きつかった。

こつこつやっていきたい。

「ゼロからの自作OS入門」を一巡した。

一応、1行1行確認しながら写経して30章まで終えた。ただし、画像ビューアとテキストエディタは省略したけど。

つぶやいたら、uchanさんからもコメントもらってさらに達成感++。感想など残しておく。

「OS自作入門」とか、「作って理解するOS」とかも手元にあるんだけど、実は一巡できたのは今回が初めてだったりする。ただ、今回が楽だったかというと別にそんなことはないかなー。 「ゼロからの」というほど、簡単ではないのである程度意思の強さは必要な気がする。

OS原理というより、x86_64とかUEFIの仕様などを追いかけるので大変だった。CPUとOSって思ってたよりずぶずぶの関係だなぁと感じた。

いろいろ理解は深まったけど、自分で試行錯誤した時間が少ないので身になっているのがどれくらいあるかは不明。 ただ、写経するだけでもかなりの時間を必要としたので、自分で試行錯誤するといくら時間があっても足りないかもなーという感じ。 まぁ、GUI含めて実装されているので、その辺で大変になってるなーとは思った。とはいえCUIのOS作って喜べるひとは少数な気がするから。。。

さて、OS自作して、なんか変わったかといわれると難しいけれど、なんだろ、、OSわかってないかも、というコンプレックスが多少解消されたくらい?まだわかったといえるほどでもないしなー。 ただ、コード書くときにメモリがどうなっているかとかのイメージがもうちょい詳細化された気はする。 仮想メモリ全体の配置とかは、ぼんやり意識しながらコード書くようになった気はする。 ちなみに、Cコンパイラ自作してから、スタック領域とヒープ領域がぼんやり頭に浮かぶようなったのでその影響もある。

それで何かうれしいことあったかというと、、、よくわからないけど。まぁ、それでもいいんじゃないかと思っている。

ma38su.hatenablog.com

Azure PipelinesのWindows VM(vs-win2016),GradleでLaunchableにデータを送る。

公式ドキュメントに書いてある通りだけれど、実際に書いてみたのを残しておく。 docs.launchableinc.com

Javaだし、Windows独自の設定はPATHを通さないといけないくらいだった。

variables:
 LAUNCHABLE_TOKEN: 'v1:xxx/xxx:xxx'

pool:
  vmImage: 'vs2017-win2016'

steps:
- script: |
    python -m pip install --upgrade pip
    set PATH=%PATH%;C:\Users\VssAdministrator\AppData\Roaming\Python\Python37\Scripts
    pip install --user --upgrade launchable~=1.0
    launchable verify || true
  displayName: 'Install dependencies'
- script: |
    set PATH=%PATH%;C:\Users\VssAdministrator\AppData\Roaming\Python\Python37\Scripts
    launchable record build --name Build.BuildId --source src=.
  displayName: 'Sending build data to Launchable'
- task: Gradle@2
  inputs:
    gradleWrapperFile: 'gradlew'
    gradleOptions: '-Xmx3072m'
    javaHomeOption: 'JDKVersion'
    jdkVersionOption: '1.11'
    jdkArchitectureOption: 'x64'
    publishJUnitResults: true
    testResultsFiles: '**/TEST-*.xml'
    tasks: 'build jacocoTestReport'
  displayName: "Gradle build & Jacoco Test Report"
- script: |
    set PATH=%PATH%;C:\Users\VssAdministrator\AppData\Roaming\Python\Python37\Scripts
    launchable record tests --build Build.BuildId gradle ./build/test-results/test/
  displayName: 'Sending data to Launchable'
- task: PublishCodeCoverageResults@1
  inputs:
    codeCoverageTool: 'JaCoCo' # Options: cobertura, jaCoCo
    summaryFileLocation: build/reports/jacoco/test/jacocoTestReport.xml
    #pathToSources: # Optional
    reportDirectory: build/reports/jacoco/test/html # Optional
    #additionalCodeCoverageFiles: # Optional
    failIfCoverageEmpty: false # Optional

最初のscriptでlaunchableをインストールする。ちなみにlaunchableコマンド実行するにはPATHを通す必要がある。これはWindowsの制約かな。。もっとうまい方法があるかもしれないが。

    python -m pip install --upgrade pip
    set PATH=%PATH%;C:\Users\VssAdministrator\AppData\Roaming\Python\Python37\Scripts
    pip install --user --upgrade launchable~=1.0
    launchable verify || true

そのあと、すぐにコードの差分を送る。

    set PATH=%PATH%;C:\Users\VssAdministrator\AppData\Roaming\Python\Python37\Scripts
    launchable record build --name $(Build.BuildId) --source src=.

ビルドとテスト完了後に、Launchableにデータを送る。ここでもPATHを通しておく必要がある。

    set PATH=%PATH%;C:\Users\VssAdministrator\AppData\Roaming\Python\Python37\Scripts
    launchable record tests --build $(Build.BuildId) gradle ./build/test-results/test/

結構データ送らないと使えないみたいで、まだここまで。