コグノスケ


link 未来から過去へ表示  link 過去から未来へ表示(*)

link もっと前
2023年11月28日 >>> 2023年12月28日
link もっと後

2023年11月28日

glibcのclone_threadが使うシステムコール

目次: C言語とlibc

RISC-V向けglibcの実装を眺めていたところ、2.37と2.38でスレッドを作成する関数(__clone_internal関数)が使っているシステムコールが変わっていることに気づきました。

スレッドを作成するcloneシステムコールにはいくつか亜種がありますが、大抵のアーキテクチャではcloneとclone3が実装されています。cloneは引数を値で渡します。clone3は構造体へのポインタと構造体のサイズを渡します。cloneの引数は非常に多く、しかも昔より増えているような気がします……。今後の拡張も考えれば引数の個数に制限がある値渡しよりも、構造体のポインタを渡したほうが合理的ですね。

実装は全アーキテクチャ共通で、下記のような感じです。struct clone_argsがclone3に渡す構造体です。__clone3_internal()はcl_argsを直接clone3システムコールに渡します。__clone_internal_fallback()はcl_argsの各フィールドをバラバラにしてcloneシステムコールに渡します。

glibc-2.38のcloneの実装

// glibc/sysdeps/unix/sysv/linux/clone-internal.c

int
__clone_internal (struct clone_args *cl_args,
		  int (*func) (void *arg), void *arg)
{
#ifdef HAVE_CLONE3_WRAPPER
  int saved_errno = errno;
  int ret = __clone3_internal (cl_args, func, arg);    //★★SYS_clone3を呼ぶ
  if (ret != -1 || errno != ENOSYS)
    return ret;

  /* NB: Restore errno since errno may be checked against non-zero
     return value.  */
  __set_errno (saved_errno);
#endif

  return __clone_internal_fallback (cl_args, func, arg);    //★★SYS_cloneを呼ぶ
}

これまで(2.37まで)のglibcのRISC-V向け実装でclone3システムコールが使われていなかった理由は、有効/無効のスイッチが無効側に設定されていたからです。スイッチとなるマクロ定義はsysdep.hというヘッダにあります。

clone3を使うか使わないかのスイッチ(RISC-V向け)

// glibc/sysdeps/unix/sysv/linux/riscv/sysdep.h

# define HAVE_CLONE3_WRAPPER		1

2.37まではHAVE_CLONE3_WRAPPERが未定義で、2.38ではHAVE_CLONE3_WRAPPERの定義が追加されました。以上が__clone_internal()が使っているシステムコールが変わる仕組みでした。良くできてます。

編集者:すずき(2023/11/30 22:46)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年11月29日

newlib-4.3.0 for RISC-V 32bitのバグ

目次: RISC-V
目次: C言語とlibc

RISC-V用のツールチェーンを更新しているときに気づいたバグです。

現在の時刻を取得するgettimeofday()というAPIがあります。newlib-4.1.0ではSYS_gettimeofdayを使っていましたが、newlib-4.3.0ではSYS_clock_gettime64を使うように変更されました。が、これがバグっていました。

newlib-4.3.0のgettimeofday()の実装

// newlib-cygwin/libgloss/riscv/sys_gettimeofday.c

/* Get the current time.  Only relatively correct.  */
int
_gettimeofday(struct timeval *tp, void *tzp)
{
#if __riscv_xlen == 32
  struct __timespec64
  {
    int64_t tv_sec;         /* Seconds */
# if BYTE_ORDER == BIG_ENDIAN
    int32_t __padding;      /* Padding */
    int32_t tv_nsec;        /* Nanoseconds */
# else
    int32_t tv_nsec;        /* Nanoseconds */
    int32_t __padding;      /* Padding */
# endif
  };
  struct __timespec64 ts64;
  int rv;
  rv = syscall_errno (SYS_clock_gettime64, 2, 0, (long)&ts64, 0, 0, 0, 0);
  tp->tv_sec = ts64.tv_sec;
  tp->tv_usec = ts64.tv_nsec * 1000;    //★★計算式を間違えている、* 1000ではなく / 1000が正しい★★
  return rv;
#else
  return syscall_errno (SYS_gettimeofday, 1, tp, 0, 0, 0, 0, 0);
#endif
}

見ての通り、gettimeofdayは結果を秒(tv_sec)とマイクロ秒(tv_usec)のペアで返します。clock_gettime64は秒とナノ秒で結果を返してきますので、ナノ秒→マイクロ秒へ変換する必要があります。しかし悲しいことにナノ秒→マイクロ秒の変換コードがバグっており、マイクロ秒の値がかなり大きな値(本来1usなのに1msになってしまう(訂正: 1nsなのに1msになってしまう))になってしまいます。

長らく放置され、今日直っていた

実装変更がnewlibに入ったのは約2年前(2021年4月13日、commit id: 20d008199)でした。結構時間が経っていますね。先ほど紹介したgettimeofdayの実装はRISC-V 32bit向けの時しか使わないので、他のアーキを使っている開発者の皆様がバグに気づかなかったのだろうと思われます。

バグったコミット
commit 20d00819984058e439cfe40818f81d7315c89201
Author: Kito Cheng <kito.cheng@sifive.com>
Date:   Tue Apr 13 17:33:03 2021 +0800

    RISC-V: Using SYS_clock_gettime64 for rv32 libgloss.

     - RISC-V 32 bits linux/glibc didn't provide gettimeofday anymore
       after upstream, because RV32 didn't have backward compatible issue,
       so RV32 only support 64 bits time related system call.

     - So using clock_gettime64 call instead for rv32 libgloss.

このバグは既に下記のコミットで修正されています。

バグが修正されたコミット
commit 5f15d7c5817b07a6b18cbab17342c95cb7b42be4
Author: Kuan-Wei Chiu <visitorckw@gmail.com>
Date:   Wed Nov 29 11:57:14 2023 +0800

    RISC-V: Fix timeval conversion in _gettimeofday()

    Replace multiplication with division for microseconds calculation from
    nanoseconds in _gettimeofday function.

    Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>

コミットの日付を見てびっくりしたのですが、なんと今日のコミットです。きっと世界のどこかで私と同じようなことを調べ、なんじゃこりゃー?!とバグを見つけて直した人が居たんでしょう。やー、奇遇ですね……。

編集者:すずき(2023/12/04 00:39)

コメント一覧

  • 大山恵弘さん(2023/12/02 18:53)
    すずきさんのX(旧Twitter)へのポストを見て気づいた可能性もあるかもしれないと思っています.
    私はOSの時間管理に興味があるので時々gettimeofdayでエゴサーチをかけますが,そのエゴサーチで私はすずきさんのこのポストに気づきました.
    そして,これはすごいバグがみつかったと思っていたところでした.

    今回のfixを施したのは日本語を理解しない人かもしれませんが,当該ポストの画像を見れば何がまずいかはどの国の人にも一目瞭然だと思います.
  • すずきさん(2023/12/03 00:35)
    大山先生、お久しぶりです。コメントありがとうございます。

    gettimeofdayでエゴサーチ……!!さすがです、私はやったことがありませんでした。
    私がバグに気づいてnewlibのリポジトリをチェックしたとき、既に修正されていたので、本当にほぼ同時に気づいた(向こうの方が修正&ML投稿の時間分だけ早いですが)のだろうと思います。偶然ってすごいです。
  • hdkさん(2023/12/03 18:49)
    >(本来1usなのに1msになってしまう)

    1 nsが1 msになる、ですかね。本来の値の範囲を大幅に超えて桁溢れもおこしてしまいそうなバグですね...
  • すずきさん(2023/12/04 00:38)
    あ、そうか。1nsですね。ありがとうございます。
open/close この記事にコメントする



2023年12月1日

musl libcのpthread_barrier_wait()の実装 その1、Owner

目次: C言語とlibc

誰得かわかりませんが、musl libc 1.2.4のpthread_barrier_wait()の実装と動作の概要です。このAPIはバリア同期を実現するためのAPIで、POSIXという規格の一部です。バリア同期はある地点に指定した数のスレッドが全員到達するまで、全スレッドを待機させる同期機構です。

バリアに到達したスレッドがNスレッド(N >= 2)あるとすると、大きく分けて3つの役割に分かれます。役割名は私が適当に付けました。正式な名前はあるのかな?

  • Owner: 最初にバリアに突入してきたスレッド(1スレッド)
  • Last: 最後にバリアに突入してきたスレッド(1スレッド)
  • Others: OwnerでもLastでもないスレッド全て(0〜N-2スレッド、平たく言えば3スレッド以上のバリアのときだけ存在する)

いっぺんに説明すると意味不明なので、順番に説明します。最初はOwnerからです。

Owner

自分がOwnerかどうか判定する条件はバリア変数の_b_instがNULLであることです。Ownerのやることはインスタンスを作成しバリア変数(pthread_barrier_t)に設定することと、バリアから一番「最後」に脱出してインスタンスを破棄することです。インスタンスはmuslの用語ですかね?それはさておいてインスタンスは1回のバリア同期に必要なパラメータがおかれた場所です。

インスタンスの必要性を簡易に説明するのは難しいですね……バリア変数は使いまわされるからです。具体例で言うと、1回目のバリアからスレッドが脱出している途中で、先に脱出した他のスレッドが2回目のバリアに到達する、というケースが発生します。もしバリアのカウンタ値などをバリア変数に置いてしまうと、1回目のバリアの処理と2回目のバリアの処理が混ざって正常に処理できなくなります。

Ownerに関連するコードは下記のとおりです。

pthread_barrier_wait()のOwner関連のコード

// musl/src/thread/pthread_barrier_wait.c

int pthread_barrier_wait(pthread_barrier_t *b)
{
	int limit = b->_b_limit;
	struct instance *inst;

	/* Trivial case: count was set at 1 */
	if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;

	/* Process-shared barriers require a separate, inefficient wait */
	if (limit < 0) return pshared_barrier_wait(b);

	/* Otherwise we need a lock on the barrier object */
	while (a_swap(&b->_b_lock, 1))
		__wait(&b->_b_lock, &b->_b_waiters, 1, 1);
	inst = b->_b_inst;

	/* First thread to enter the barrier becomes the "instance owner" */
	if (!inst) {
		struct instance new_inst = { 0 };
		int spins = 200;
		b->_b_inst = inst = &new_inst;
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		while (spins-- && !inst->finished)
			a_spin();
		a_inc(&inst->finished);
		while (inst->finished == 1)
			__syscall(SYS_futex,&inst->finished,FUTEX_WAIT|FUTEX_PRIVATE,1,0) != -ENOSYS
			|| __syscall(SYS_futex,&inst->finished,FUTEX_WAIT,1,0);
		return PTHREAD_BARRIER_SERIAL_THREAD;
	}

	//...

各所に工夫が散りばめられていて全部説明すると30分くらい説明が必要な気がしますが、細かい部分は省いてシーケンスの例を1つだけ示せば下記のようになります。


pthread_barrier_wait()のOwnerシーケンスの一例

特徴的というか個人的に感心した点は、インスタンスをローカル変数で確保していることです。変数のアドレスは一般的にはスレッドのスタック領域の一部になることが多いでしょう。ローカル変数の特徴として、

  • 生存期間は関数実行している間だけ
  • 他のスレッドと干渉しない

1つ目の特徴はmusl libcのバリア実装に限って言えば問題ありません。Ownerが必ず最後にバリア同期APIを脱出するように実装が工夫されていて、壊れたローカル変数に他のスレッドがアクセスする状況が発生しないからです。

2つ目の特徴はうまく活かしています。ローカル変数はバリアに一番最初にたどり着いたスレッド(= Owner)が、他スレッドと干渉せずインスタンスを確保できる簡単かつ高速な方法です。mallocのようなヒープでも目的は達成できますが、速度的に不利でしょう。面白い実装ですね。

続きはまた今度。

編集者:すずき(2023/12/04 02:43)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月2日

musl libcのpthread_barrier_wait()の実装 その2、LastとOthers

目次: C言語とlibc

前回の続きです。musl libc 1.2.4のpthread_barrier_wait()の実装と動作の概要です。バリアに到達したスレッドがNスレッド(N >= 2)あるとすると、大きく分けて3つの役割に分かれます。役割名は私が適当に付けました。正式な名前はあるのかな?

  • Owner: 最初にバリアに突入してきたスレッド(1スレッド)
  • Last: 最後にバリアに突入してきたスレッド(1スレッド)
  • Others: OwnerでもLastでもないスレッド全て(0〜N-2スレッド、平たく言えば3スレッド以上のバリアのときだけ存在する)

いっぺんに説明すると意味不明なので、順番に説明します。今回はLastとOthersです。

LastとOthers(到達側)

自分がLastかどうか判定する条件はバリア変数の_b_instがNULL以外であり、インスタンスのカウントがバリア同期するスレッド数と一致(= 最後に到達した1スレッド)することです。Lastのやることはバリア変数(pthread_barrier_t)に設定されたインスタンスを消すことと、バリア同期を待っているOthersを起こすことです。

Lastがこの時点でb->_b_instをNULLにしていること、ローカル変数instを使っていてb->_b_instを使わないことには理由があって、次のバリア処理とのオーバーラップと関係しています。同時に説明するとややこしいので、次回ご説明しようと思います。

OwnerとLast以外は全部Othersスレッドとなります。OthersのやることはLastが来るまで待つことです。

LastとOthersの到達側に関連するコードは下記のとおりです。

pthread_barrier_wait()のLastとOthersの到達側のコード

// musl/src/thread/pthread_barrier_wait.c

int pthread_barrier_wait(pthread_barrier_t *b)
{
	int limit = b->_b_limit;
	struct instance *inst;

	/* Trivial case: count was set at 1 */
	if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;

	/* Process-shared barriers require a separate, inefficient wait */
	if (limit < 0) return pshared_barrier_wait(b);

	/* Otherwise we need a lock on the barrier object */
	while (a_swap(&b->_b_lock, 1))
		__wait(&b->_b_lock, &b->_b_waiters, 1, 1);
	inst = b->_b_inst;

	/* First thread to enter the barrier becomes the "instance owner" */
	if (!inst) {
		//★★Ownerのスレッドの処理は省略(その1をご覧ください)★★
	}

	/* Last thread to enter the barrier wakes all non-instance-owners */
	if (++inst->count == limit) {
		//★★Lastのスレッドはこちら★★
		b->_b_inst = 0;
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		a_store(&inst->last, 1);
		if (inst->waiters)
			__wake(&inst->last, -1, 1);
	} else {
		//★★Othersのスレッドはこちら★★
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		__wait(&inst->last, &inst->waiters, 0, 1);
	}

細かい部分は省いてシーケンスの例を1つだけ示せば下記のようになります。


pthread_barrier_wait()のLastとOthersの到達側シーケンスの一例

見たままなので解説することがないですね。全員でインスタンスのカウントを+1して、最後のスレッドLastだけが特殊な処理を行います。

LastとOthers(脱出側)

脱出側のやることは単純ですが役割分担が少しややこしいです。到達側と同様にLastとOthersに役割が分かれますが、到達側のLast = 脱出側のLastとは限らないからです。

脱出時のLastになる条件はインスタンスのカウント値が1であることです。到達時にLastであったかOthersであったかは無関係です。脱出時のLastのやることは、インスタンスのfinishedを+1して待機中のOwnerスレッドを再開させることです。

脱出時のOthersになる条件はLastではない、インスタンスのカウント値が1以外であることです。

LastとOthersの脱出側に関連するコードは下記のとおりです。

pthread_barrier_wait()のLastとOthersの脱出側のコード

// musl/src/thread/pthread_barrier_wait.c

int pthread_barrier_wait(pthread_barrier_t *b)
{
	int limit = b->_b_limit;
	struct instance *inst;

	//★★略★★

	/* Last thread to exit the barrier wakes the instance owner */
	if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
		__wake(&inst->finished, 1, 1);

	return 0;
}

細かい部分は省いてシーケンスの例を1つだけ示せば下記のようになります。


pthread_barrier_wait()のLastとOthersの脱出側シーケンスの一例

到達側のLastスレッドの処理では待機していたOthersスレッド達を全員再開させ、Lastも処理を再開します。するとLast + Othersスレッド全てがいっぺんに脱出側の処理を開始します。先ほど説明したとおり、どのスレッドが脱出側のLastになるかは運次第です。

実装の特徴はアトミックアクセスですかね。a_fetch_add(x, -1)はポインタxの指す先をアトミックに-1して、返り値でxの以前の値を返す関数です……といわれてもわかりにくいですよね。4スレッド(Owner, Others1, Others2, Last)の場合を書きましょうか。Ownerスレッドはカウント値を+1しないので、脱出処理開始時のカウント値は4 - 1 = 3です。

スレッド-1したあとのカウント値a_fetch_add()の返り値
Others 123
Others 212
Last 01

ちなみにアトミックアクセス以外の方法(if文とカウント値の変更など)では正常に動作しません。判定と値変更の間に他のスレッドが処理を行う可能性があるからです。

続きはまた今度。

編集者:すずき(2023/12/04 03:49)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月3日

musl libcのpthread_barrier_wait()の実装 その3、インスタンス

目次: C言語とlibc

参考: 図を書くために使ったlink PlantUMLのコードです。

前回の続きです。musl libc 1.2.4のpthread_barrier_wait()の実装と動作の概要です。バリアに到達したスレッドがNスレッド(N >= 2)あるとすると、大きく分けて3つの役割に分かれます。役割名は私が適当に付けました。正式な名前はあるのかな?

  • Owner: 最初にバリアに突入してきたスレッド(1スレッド)
  • Last: 最後にバリアに突入してきたスレッド(1スレッド)
  • Others: OwnerでもLastでもないスレッド全て(0〜N-2スレッド、平たく言えば3スレッド以上のバリアのときだけ存在する)

今回はインスタンスがなぜローカル変数として確保されるかを紹介します。1回目と2回目のバリアが重なるところがポイントです。

スレッドの役割は変わる

各スレッドはOwnerかLastかOthersになりますが、役割は毎回のバリア同期処理ごとに変わります。例えばバリアとバリアの間の処理時間が同じだとすると、あるバリアのOwnerは次のバリアではLastになる可能性が高いです。Ownerはバリアから最後に脱出しますので、その間に他のスレッドの処理が進んで次のバリア同期処理のOwnerになるからです。

3スレッドあって下記のように役割が変化する場合を考えてみます。

  • スレッド1: Owner1, Last2: 1回目Owner、2回目Last
  • スレッド2: Others1, Owner2: 1回目Others、2回目Owner
  • スレッド3: Last1, Others2: 1回目Last、2回目Others

更に考えてみるとバリアとバリアの間の処理時間が非常に短い場合、1回目のバリアのOwner(スレッド1、Owner1, Last2)がバリアを脱出する前に、違うスレッドが2回目のバリアのOwner(スレッド2、Others1, Owner2)となってバリアに到達している可能性があります。シーケンス例は下記のようになります。


インスタンスが1つしかない場合のシーケンス例

もしバリアのインスタンスをグローバル変数などに確保した場合、1回目のバリアのOwner(スレッド1、Owner1, Last2)によるインスタンスの破棄と、2回目のバリアのOwner(スレッド2、Others1, Owner2)のインスタンスの初期化がぶつかって、お互いに内容を壊しあうため正常に動作しません。

この問題を解決にはインスタンスをOwnerスレッド固有の領域に置くと良いです。1回目のバリアのOwnerスレッドと、2回目のバリアのOwnerスレッドがそれぞれ別の領域に置けば、お互いに壊し合うことがなくなります。

ローカル変数はスレッド固有の領域

スレッドごとの固有の領域として使えるのは、

TLS(Thread Local Storage) ヒープから確保したメモリ(mallocなど) ローカル変数

があります。インスタンスを各Ownerスレッド固有の領域においたとき、2回分のバリア動機処理のシーケンス例は下記のようになります。


インスタンスがスレッドごとにある場合のシーケンス例

バリアのインスタンスは複数スレッド間で共有する領域です。ローカル変数を複数スレッド間で共有すると、ローカル変数が破棄されたときに問題が発生するので、ヒープを使うことが多いでしょう。

しかし前回説明したようにバリア同期処理の場合はローカル変数の寿命をうまく制御できるため、複数スレッド間で共有しても問題が起きません。インスタンスをローカル変数に確保すると、ヒープに比較して高速なメモリ割当が可能です。

編集者:すずき(2023/12/09 16:19)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月10日

fno-builtinによるlibc関数呼び出し最適化の抑制

目次: GCC

GCCはlibcの関数呼び出しを別の関数呼び出しに置き換える最適化(名前がわからないので以降「libc関数呼び出し最適化」と呼びます)を行います。例を挙げると、

  • 1文字出力するprintf関数→putchar関数
  • 行末に改行がある文字列を出力するprintf関数→puts関数

時には勝手にlibcの関数呼び出しを置き換えてほしくないこともあると思います。libc関数呼び出し最適化を抑えることができるコンパイラオプションfno-builtinを指定したときの挙動についてメモしておきます。

GCCのバージョンは下記のとおりです。

GCCのバージョン
$ gcc --version

gcc (Debian 12.2.0-14) 12.2.0
Copyright (C) 2022 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.

テストに使うプログラムは下記の通りです。

テストプログラム

int printf(const char *a, ...);

void _start(void)
{
        printf("\n");
}

特に何も指定しなければ、1文字出力するprintfはputchar呼び出しに置き換えられるはずです。確認のためnostdlibオプションを付けてコンパイルし、リンクエラーを見るとprintfの呼び出しがどの関数に置き換えられたかわかります。

nostdlibでのコンパイル、リンク結果
$ gcc -O2 -g -Wall a.c -nostdlib

/usr/bin/ld: /tmp/cc1D673J.o: in function `_start':
/home/katsuhiro/share/test_gcc_no_builtin/a.c:5: undefined reference to `putchar'
collect2: error: ld returned 1 exit status

リンクエラーは「putcharがない」と出ました。printfの呼び出しがputcharの呼び出しに置き換えられたことがわかりますね。次はfno-builtinオプションを指定します。

fno-builtin、nostdlibでのコンパイル、リンク結果
$ gcc -O2 -g -Wall a.c -nostdlib -fno-builtin

/usr/bin/ld: /tmp/cc6JbRD2.o: in function `_start':
/home/katsuhiro/share/test_gcc_no_builtin/a.c:5: undefined reference to `printf'
collect2: error: ld returned 1 exit status

リンクエラーは「printfがない」と出ました。fno-builtinオプションによってprintfからputcharへの置き換えが抑制されたことがわかります。

fno-builtinの仕様なのか?

動作結果を見る限りfno-builtinオプションでlibc関数呼び出し最適化が抑制されましたが、この挙動がオプションの仕様か?と聞かれると正直良くわかりません

仕様はGCCのマニュアル(Other Builtins (Using the GNU Compiler Collection (GCC)))に書かれていて、説明によれば残りのビルトイン関数(printfを含む)は最適化のために提供されている("The remaining functions are provided for optimization purposes.")とあります。

  • ビルトイン関数を無効にした
  • 最適化後に呼び出すためのビルトイン関数がない
  • libc関数呼び出し最適化そのものが無効になった

私はこのような動作をしているものと推測しています。正しいかどうかは知りません……。

最適化はビルトイン関数に頼る必要はありません。つまりfno-builtinオプションで抑制できないようなlibc関数呼び出し最適化があっても不思議ではありません。今のところそのような例は見つけていませんが、もしあったとすると抑制する方法はあるのでしょうか?うーん??

編集者:すずき(2023/12/11 02:56)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月15日

辞書に書いてある謎の記号

辞書(家にあるのは新明解国語辞典 第四版)を引いていると、語句の漢字表記に○とか▽のような謎の記号が書いてあります。今までずっと意味がわかっていなかったのですが、三省堂が素敵な解説文をサイトに載せてくれていました。

詳細は解説(第29回 ×とか▽とか、これは何だ? - 【 】見出し(漢字表記)についている○▽× - 三省堂 ことばのコラム)を読んでいただければと思いますが、そのなかに▽は音訓表に載っていない読みのこと、と書いてあります。音訓表とはなんぞ?

検索してみると、文化庁が作成している常用漢字の読み方(文化庁 | 国語施策・日本語教育 | 国語施策情報 | 常用漢字表の音訓索引)のことらしいです。

常用漢字の一覧(文化庁 | 国語施策・日本語教育 | 国語施策情報 | 内閣告示・内閣訓令 | 常用漢字表(平成22年内閣告示第2号))を文化庁が作成しているのは知っていたんですけど、読み方まで一覧にしてくれていたんですね。知らなかったなあ……。

編集者:すずき(2023/12/17 23:35)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月21日

Twitterのトラブル

昼頃だったか?Twitterで何かトラブルが起きていたらしくて、初ログインしたときのような画面になっていました。


Twitterのようこそ画面

Twitterの使い始めがどんな画面だったか全く覚えていませんが、たぶんこんな画面なんでしょう。今だと別アカウントを登録しない限り、見る機会がない画面でしょうね。

編集者:すずき(2023/12/22 22:54)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月23日

サンタ - まとめリンク

目次: サンタ

一覧が欲しくなったので作りました。

今後、日記が増えたら追加します。

編集者:すずき(2024/01/04 03:17)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月24日

サンタがマッハ5で街にやってくる(2023年)

目次: サンタ

去年(2022年12月24日の日記参照)と同様に、飛行機の位置をリアルタイムに表示するFlightradar24というサービス(サイトへのリンク)にてサンタが出現しました。クリスマス・イブの日にサンタが出現するのは毎年恒例のお約束演出です。私が見たときはインド上空を飛んでいました。

2021年のサンタは表示された対地速度は40ktsなのに、サンタの位置から計算した速度はマッハ2 = 1300ktsと変な状態でしたが、2023年と今年は対地速度の表示は727kts(1346.4km/h, マッハ1.1くらい)です。


00:32:10時点の位置(緯度27.15451度、経度76.90161度)


00:33:10時点の位置(緯度26.26621度、経度76.42773度)

去年同様、ある程度の時間をあけ(今回は1分間)でどれくらい進んだか計算します。今回も距離の計算には国土地理院のページを使いました、緯度経度から距離を一発で計算してくれて便利です(サイトへのリンク)。


緯度、経度から距離と方位角を計算(国土地理院のサイト)

2地点間の距離は約109.1km、時間差は60秒から計算すると、対地速度は約6,540km/hです。地上でのマッハ5(ただし、サンタが飛んでいる高さ38,000ftsだとマッハ数はもっと高く出る)です。世界最速の飛行機であるロッキードSR-71ブラックバードでさえマッハ3.3といわれているので、サンタさんバチクソ速いですね。

  • 2021年: マッハ2
  • 2022年: マッハ1.3
  • 2023年: マッハ5

推測ですが24日、25日で北極から世界一周してまた北極へ行くために、緯度、経度は対地速度に関係なく適当に変えているんじゃないか?という気がしてきました。別の時間に測ったら全然違う速度になってませんかね?面倒なので検証しませんけど……。来年の速度も気になります。覚えていたらまた計算してみましょう。

編集者:すずき(2024/01/04 03:17)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月25日

クロスビルド用ツールチェーン - なぜか必要なsysroot/usr/lib

目次: GCC

最近はRISC-V向けのクロスコンパイラをビルドすることが多かったのですが、昔を思い出してARM向けも作るか〜と気軽にやってみたらハマりました。

最初にトライしたのはarm-unknown-linux-gnueabi(昔の32bit ARM向け、ARM9とか)で、特に問題なくビルドできました。次にトライしたのはaarch64-unknown-linux-gnu(64bit ARM向け、最近のCortexとか)ですが、リンクエラーが発生してcrt1.oやcrti.oが見つからないと言ってきます。

AArch64向けクロスGCCのリンクエラー
$ aarch64-unknown-linux-gnu-gcc -Wall -O2 -g -static a.c

/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/bin/ld: cannot find crt1.o: No such file or directory
/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/bin/ld: cannot find crti.o: No such file or directory
/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/bin/ld: cannot find -lc: No such file or directory
collect2: error: ld returned 1 exit status

最初はglibcのビルドに失敗したのかと思いましたが、crt1.oはsysroot/usr/lib64ディレクトリの下に存在していました。

glibcのファイル(crt1.o)は存在している
$ find -name crt1.o

./aarch64-unknown-linux-gnu/sysroot/usr/lib64/crt1.o

ファイルが存在しているのに見つからないのは、GCCが自動的に付与する-Lオプションが間違っている可能性大です。-vオプションでGCCのオプションを調べます。

うまくいかないときのGCC -vオプションの出力(一部抜粋)
--sysroot=/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot 
-Bstatic 
-X 
-EL 
-maarch64linux 
crt1.o 
crti.o 
/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/crtbeginT.o 
-L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1 
-L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/lib/../lib64 
-L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib/../lib64     ★★相対パスが入っている★★
-L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/lib 
-L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib 
/tmp/ccQ9tEVu.o 
--start-group 
-lgcc 
-lgcc_eh 
-lc 
--end-group 
/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/crtend.o 
crtn.o

困ったことにsysroot/usr/lib64を指定する-Lオプションがありません。なぜでしょう。

手掛かりになりそうなのは、sysroot/lib64ディレクトリの指定の仕方で、lib/../lib64のように一度libを経由しています。実はsysroot/usr/lib64も同じでsysroot/usr/libを経由している可能性があります。

ディレクトリの構造を調べるとsysroot/libは存在するものの、sysroot/usr/libは存在しませんでした。試しに空のsysroot/usr/libディレクトリを作成し、再度コンパイルするとエラーが解消しました。やったー。エラーだった時と何が違うか知るために-vオプションでGCCのオプションを調べます。

うまくいったときのGCC -vオプションの出力(一部抜粋)
--sysroot=/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot 
-Bstatic 
-X 
-EL 
-maarch64linux 
/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crt1.o 
/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crti.o 
/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/crtbeginT.o 
-L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1 
-L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/lib/../lib64 
-L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib/../lib64 
-L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib/../lib64     ★★やっぱりlib/../lib64になっている★★
-L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/lib 
-L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib 
-L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib 
/tmp/ccLe9HUo.o 
--start-group 
-lgcc 
-lgcc_eh 
-lc 
--end-group 
/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/crtend.o 
/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crtn.o

やはりsysroot/usr/lib64を指定するパスにはsysroot/usr/libが含まれていました。原因は「最終のディレクトリに辿り着く途中のパスが存在しないから」と言えそうです。しかしどうしてGCCが勝手に-Lオプションを消すのかは謎のままで、若干スッキリしません。うーん。

編集者:すずき(2023/12/25 02:08)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月27日

Hello! Zephyr OS!!(2023年版)

目次: Zephyr

Zephyr RTOSのインストール、ビルドの手順が少し変わったので改めて紹介したいと思います。

基本的にはZephyrのGetting Startedに記載の通りですが、実行する上での補足や引っかかるところを説明したいと思います。

Zephyr開発環境のセットアップ

以降、作業するディレクトリは~/workとします。

ZephyrのGetting Startedを順に実行します。既存のPython環境を壊さないようにInstall within virtual environmentの手順を使うことをお勧めします。

依存ツールインストールとPython venv環境の作成

# apt-get install git cmake ninja-build gperf \
    ccache dfu-util device-tree-compiler wget \
    python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \
    make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1

# apt-get install python3-venv

$ cd ~/work/zephyr-sdk-0.16.4/
$ python3 -m venv _venv
$ source ~/work/zephyr-sdk-0.16.4/_venv/bin/activate

Python venvを有効にするとシェルのプロンプトの頭に(_venv)のような環境名が付くようになります。venv用のディレクトリはどこに作成しても良いです。私はZephyr SDKの下に作ることが多いです。作成済みのvenvを再度使うときはsource ~/work/zephyr-sdk-0.16.4/_venv/bin/activateを実行します。

続きを実行しましょう。westを使ってZephyrと周辺ツール群をダウンロードします。

Zephyrと周辺ツール群をダウンロード
$ pip install west
$ cd ~/work
$ west init zephyr

=== Initializing in /home/katsuhiro/work/zephyr
--- Cloning manifest repository from https://github.com/zephyrproject-rtos/zephyr
Cloning into '/home/katsuhiro/work/zephyr/.west/manifest-tmp'...
remote: Enumerating objects: 968377, done.
remote: Counting objects: 100% (17/17), done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 968377 (delta 8), reused 8 (delta 4), pack-reused 968360
Receiving objects: 100% (968377/968377), 600.07 MiB | 20.80 MiB/s, done.
Resolving deltas: 100% (733897/733897), done.
Updating files: 100% (31355/31355), done.
--- setting manifest.path to zephyr
=== Initialized. Now run "west update" inside /home/katsuhiro/work/zephyr.

$ cd ~/work/zephyr
$ west update

(大量にリポジトリがクローンされますのでしばし待ちます)


$ west zephyr-export

Zephyr (/home/katsuhiro/work/zephyr/zephyr/share/zephyr-package/cmake)
has been added to the user package registry in:
~/.cmake/packages/Zephyr

ZephyrUnittest (/home/katsuhiro/work/zephyr/zephyr/share/zephyrunittest-package/cmake)
has been added to the user package registry in:
~/.cmake/packages/ZephyrUnittest


$ pip install -r ~/work/zephyr/zephyr/scripts/requirements.txt

(大量にモジュールがインストールされますのでしばし待ちます)

基本的にはコマンドを実行して待つだけです。たぶん。

SDKをインストールします。アーカイブはGitHubのreleaseページから取得できます。特にこだわりがなければ、現状の最新版である0.16.4を使ってください。

アーカイブがたくさんあって迷うと思いますが、今回はサイズが小さくてインストールするツールチェーンを選択できるzephyr-sdk-0.16.4_linux-x86_64_minimal.tar.xzを使います。

Zephyr SDKのインストール
$ wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.4/zephyr-sdk-0.16.4_linux-x86_64_minimal.tar.xz
$ tar xf zephyr-sdk-0.16.4_linux-x86_64_minimal.tar.xz
$ cd ~/work/zephyr-sdk-0.16.4/

$ ./setup.sh -c -t riscv64-zephyr-elf

Zephyr SDK 0.16.4 Setup

Installing 'riscv64-zephyr-elf' toolchain ...
toolchain_linux-x86 100%[===================>] 105.07M  24.7MB/s 時間 4.3s

All done.

何も引数を付けずにすべてインストールしても良いです(が、結構時間が掛かります)。今回はRISC-V向けのツールチェーンがあれば良いので-t riscv64-zephyr-elfを付けることで時間短縮しました。注意点としては-cオプションを忘れないようにしてください。Zephyrビルド時にエラーが発生します。

次にhosttoolsをインストールします。要らない場合もありますが、今回はQEMUを使って動作確認したいのでインストールしましょう。

hosttoolsのインストール
$ ./zephyr-sdk-x86_64-hosttools-standalone-0.9.sh

Zephyr Yocto Toolchain SDK installer version 0.9
================================================
Enter target directory for SDK (default: /opt/zephyr-sdk/0.9): /home/katsuhiro/work/zephyr-sdk-0.16.4/host
You are about to install the SDK to "/home/katsuhiro/work/zephyr-sdk-0.16.4/host". Proceed [Y/n]? y
Extracting SDK..................done
Setting it up...done
SDK has been successfully set up and is ready to be used.
Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g.
 $ . /home/katsuhiro/work/zephyr-sdk-0.16.4/host/environment-setup-x86_64-pokysdk-linux

上記の作業を全て終えた後のディレクトリ構造はこんな感じです。

Zephyr開発環境のディレクトリ構成
$ tree -L 2 ~/work

/home/katsuhiro/work
|-- zephyr
|   |-- bootloader
|   |-- modules
|   |-- tools
|   `-- zephyr                ★Zephyr RTOSのコード
|-- zephyr-sdk-0.16.4
|   |-- _venv
|   |-- cmake
|   |-- host                  ★hosttools
|   |-- riscv64-zephyr-elf    ★RISC-V用ツールチェーン
|   |-- sdk_toolchains
|   |-- sdk_version
|   |-- setup.sh
|   `-- zephyr-sdk-x86_64-hosttools-standalone-0.9.sh
`-- zephyr-sdk-0.16.4_linux-x86_64_minimal.tar.xz

Zephyr開発環境はセットアップ手順は短くなったとは思うんですけど、中身は複雑になってトラブルが起きたときの解決が困難になった印象です。昔より依存するツールが増えているのかな……?

Zephyr開発環境への入り方

使用したいツールに応じて環境を有効化すると良いです。

Zephyr開発環境への入り方の例
#### Python venv環境を有効化

$ source ~/work/zephyr-sdk-0.16.4/_venv/bin/activate

#### QEMUなどhosttoolsを使う場合、hosttoolsを有効化

$ source ~/work/zephyr-sdk-0.16.4/host/environment-setup-x86_64-pokysdk-linux

#### デバッガなどを直接実行するならばツールチェーンにパスを通す

$ export PATH=~/work/zephyr-sdk-0.16.4/riscv64-zephyr-elf/bin:$PATH

私はいちいち選ぶのが面倒なので、全部有効にしております。

Zephyrのビルドと実行

Zephyr開発環境が正常にセットアップできたかどうか確かめるため、QEMU向けにビルドしましょう。

Zephyrのビルド(west版)
$ cd ~/work/zephyr/zephyr
$ west build -p always -b qemu_riscv64 samples/hello_world/

(ログは省略)

ビルドできたので実行したいところですが、なぜかwestを使ってQEMUで実行する方法が見当たりません。仕方ないのでcmakeでビルド&実行します。こちらのやり方も覚えておいて損はないでしょう……。

Zephyrのビルド(cmake版)
$ cd ~/work/zephyr/zephyr
$ rm -r build
$ cmake -B build -G Ninja -DBOARD=qemu_riscv64 samples/hello_world

Loading Zephyr default modules (Zephyr repository).
-- Application: /home/katsuhiro/work/zephyr/zephyr/samples/hello_world
-- CMake version: 3.22.1
-- Found Python3: /home/katsuhiro/work/zephyr-sdk-0.16.4/_venv/bin/python (found suitable version "3.10.12", minimum required is "3.8") found components: Interpreter
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Zephyr version: 3.5.99 (/home/katsuhiro/work/zephyr/zephyr)
-- Found west (found suitable version "1.2.0", minimum required is "0.14.0")
-- Board: qemu_riscv64
-- ZEPHYR_TOOLCHAIN_VARIANT not set, trying to locate Zephyr SDK
-- Found host-tools: zephyr 0.16.4 (/home/katsuhiro/work/zephyr-sdk-0.16.4)
-- Found toolchain: zephyr 0.16.4 (/home/katsuhiro/work/zephyr-sdk-0.16.4)
-- Found Dtc: /home/katsuhiro/work/zephyr-sdk-0.16.4/host/sysroots/x86_64-pokysdk-linux/usr/bin/dtc (found suitable version "1.6.0", minimum required is "1.4.6")
-- Found BOARD.dts: /home/katsuhiro/work/zephyr/zephyr/boards/riscv/qemu_riscv64/qemu_riscv64.dts
-- Generated zephyr.dts: /home/katsuhiro/work/zephyr/zephyr/build/zephyr/zephyr.dts
-- Generated devicetree_generated.h: /home/katsuhiro/work/zephyr/zephyr/build/zephyr/include/generated/devicetree_generated.h
-- Including generated dts.cmake file: /home/katsuhiro/work/zephyr/zephyr/build/zephyr/dts.cmake
Parsing /home/katsuhiro/work/zephyr/zephyr/Kconfig
Loaded configuration '/home/katsuhiro/work/zephyr/zephyr/boards/riscv/qemu_riscv64/qemu_riscv64_defconfig'
Merged configuration '/home/katsuhiro/work/zephyr/zephyr/samples/hello_world/prj.conf'
Configuration saved to '/home/katsuhiro/work/zephyr/zephyr/build/zephyr/.config'
Kconfig header saved to '/home/katsuhiro/work/zephyr/zephyr/build/zephyr/include/generated/autoconf.h'
-- Found GnuLd: /home/katsuhiro/work/zephyr-sdk-0.16.4/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/12.2.0/../../../../riscv64-zephyr-elf/bin/ld.bfd (found version "2.38")
-- The C compiler identification is GNU 12.2.0
-- The CXX compiler identification is GNU 12.2.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/katsuhiro/work/zephyr-sdk-0.16.4/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc
-- Using ccache: /usr/bin/ccache
-- Configuring done
-- Generating done
-- Build files have been written to: /home/katsuhiro/work/zephyr/zephyr/build


$ ninja -C build

ninja: Entering directory `build'
[1/99] Preparing syscall dependency handling

[2/99] Generating include/generated/version.h
-- Zephyr version: 3.5.99 (/home/katsuhiro/work/zephyr/zephyr), build: zephyr-v3.5.0-3603-g603c3af895b0
[98/99] Linking C executable zephyr/zephyr.elf
Memory region         Used Size  Region Size  %age Used
             RAM:       36140 B       256 MB      0.01%
        IDT_LIST:          0 GB         2 KB      0.00%
Generating files from /home/katsuhiro/work/zephyr/zephyr/build/zephyr/zephyr.elf for board: qemu_riscv64
[99/99] cd /home/katsuhiro/work/zephyr.../zephyr/zephyr/build/zephyr/zephyr.elf


$ ninja -C build run

ninja: Entering directory `build'
[0/1] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: riscv64
*** Booting Zephyr OS build zephyr-v3.5.0-3603-g603c3af895b0 ***
Hello World! qemu_riscv64

実行できました。もしエラーが出る場合は次のトラブルシューティングもご参照ください。

トラブルシューティング

Zephyr SDKセットアップ時に-cオプションを付けない = Zephyr SDK cmake packageのインストールを忘れていると、Zephyrのビルド時に長々とエラーが出て怒られます。

Zephyr SDK cmake packageをインストールしていないと発生するエラー
CMake Error at /home/katsuhiro/work/zephyr/zephyr/cmake/modules/FindZephyr-sdk.c
make:109 (find_package):
  Could not find a package configuration file provided by "Zephyr-sdk"
  (requested version 0.16) with any of the following names:

    Zephyr-sdkConfig.cmake
    zephyr-sdk-config.cmake

  Add the installation prefix of "Zephyr-sdk" to CMAKE_PREFIX_PATH or set
  "Zephyr-sdk_DIR" to a directory containing one of the above files.  If
  "Zephyr-sdk" provides a separate development package or SDK, be sure it has
  been installed.
Call Stack (most recent call first):
  /home/katsuhiro/work/zephyr/zephyr/cmake/modules/FindHostTools.cmake:53 (find_package)
  /home/katsuhiro/work/zephyr/zephyr/cmake/modules/dts.cmake:9 (find_package)
  /home/katsuhiro/work/zephyr/zephyr/cmake/modules/zephyr_default.cmake:129 (include)
  /home/katsuhiro/work/zephyr/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:66 (include)
  /home/katsuhiro/work/zephyr/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate)
  CMakeLists.txt:5 (find_package)

Zephyr SDKのセットアップは2回実行しても問題ないので-cオプションを付けてやり直しましょう。

編集者:すずき(2024/01/05 02:39)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年12月28日

帰省

今年は奥さんの実家である鳥取に帰省しました。

夫婦お互いの実家が近ければそれぞれ3日ずつ滞在などできますが、我が家の場合は結構遠い(奥さんは鳥取、私は北海道)のです……。仕方ないので帰省のルールとして、

  • 正月、GW、お盆の年3回帰省する
  • 夫婦一緒に帰省する
  • 行き先は毎回変える(GW: 鳥取、お盆: 北海道、正月: 鳥取、GW: 北海道、、、のように)

というルールで運用していました。なので鳥取での年越しは2年に1回となりますが、近年はCOVID-19騒ぎで帰れなかったり、自分達もCOVIDに感染したりでルール運用が乱れ、鳥取での年越しのチャンスがありませんでした。

たぶん2019年末以来じゃないかな?つまり4年ぶり?

編集者:すずき(2024/01/05 16:16)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2023年11月28日 >>> 2023年12月28日
link もっと後

管理用メニュー

link 記事を新規作成

<2023>
<<<11>>>
---1234
567891011
12131415161718
19202122232425
2627282930--

最近のコメント5件

  • link 24年6月17日
    すずきさん (06/23 00:12)
    「ありがとうございます。バルコニーではない...」
  • link 24年6月17日
    hdkさん (06/22 22:08)
    「GPSの最初の同期を取る時は見晴らしのい...」
  • link 24年5月16日
    すずきさん (05/21 11:41)
    「あー、確かにdpkg-reconfigu...」
  • link 24年5月16日
    hdkさん (05/21 08:55)
    「システム全体のlocale設定はDebi...」
  • link 24年5月17日
    すずきさん (05/20 13:16)
    「そうですねえ、普通はStandardなの...」

最近の記事3件

  • link 24年6月27日
    すずき (06/30 15:39)
    「[何もない組み込み環境でDOOMを動かす - その4 - 自作OSの組み込み環境へ移植] 目次: RISC-V目次: 独自OS...」
  • link 22年12月13日
    すずき (06/30 15:38)
    「[独自OS - まとめリンク] 目次: 独自OS一覧が欲しくなったので作りました。自作OSの紹介その1 - 概要自作OSの紹介...」
  • link 21年6月18日
    すずき (06/29 22:28)
    「[RISC-V - まとめリンク] 目次: RISC-VSiFive社ボードの話、CoreMarkの話のまとめ。RISC-V ...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 06/30 15:39