コグノスケ


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

link もっと前
2025年10月17日 >>> 2025年10月4日
link もっと後

2025年10月17日

Linux版Firefoxアドレスバーの黒線

目次: ブラウザー/メーラー

LinuxデスクトップマシンのFirefox(140.3.1esr 64bit)をアップデートしたところ、アドレスバーの上下に黒い線が表示されるようになりました。何も害はないですが、目立つなあ。Windows版でこのような線は見たことがありません。


Firefoxのアドレスバー

昨今のブラウザってシステムのデザインはガン無視で、オレオレGUIコンポーネントを描画してるはずで。それでもデザイン統一できないのはバグなのか、Linux版に何か特有の事情があるのか……?

編集者:すずき(2025/10/19 14:53)

コメント一覧

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



2025年10月15日

PipeWireの音切れ問題 - サーバー側の設定確認と反映

目次: ALSA

PipeWireに変えてから音切れがなくなりません。PulseAudioのときも音切れはしてましたが、PipeWireになってから頻度が段違いに多いです。なぜー?今回はサーバー側PipeWireのレイテンシ調整に備えて、設定反映方法と設定確認方法を紹介します。

PipeWireの設定反映、デバッグログを出す方法

設定を反映するにはpipewire-pulse.socket, pipewire-pulse.service, pipewire.socket, pipewire.service, wireplumber.serviceのうち設定を変更したサービスを再起動すれば良いはずです。良くわからないときは全部再起動しましょう。

PipeWireたちの再起動
$ systemctl --user daemon-reload
$ systemctl --user restart pipewire.service


#### もし何かおかしかったら下記もやってみると吉

$ systemctl --user restart pipewire.socket
$ systemctl --user restart wireplumber.service
$ systemctl --user restart pipewire-pulse.service
$ systemctl --user restart pipewire-pulse.socket

デバッグ出力を見たければ、上記の5つのサービスを全部stopしてからPipeWireと仲間たちを直接起動すると良いでしょう。

PipeWireサービスの停止、直接起動
#### サービスを全部止める

$ systemctl --user stop pipewire.socket
$ systemctl --user stop pipewire.service
$ systemctl --user stop wireplumber.service
$ systemctl --user stop pipewire-pulse.socket
$ systemctl --user stop pipewire-pulse.service

#### 直接起動する

$ /usr/bin/pipewire -v
$ /usr/bin/wireplumber
$ /usr/bin/pipewire-pulse -v

ちなみにデバッグログを出すとわかりますが、めちゃくちゃ大量に出ます……私には意味がわかりません。

PipeWireの設定確認

PipeWireは設定確認にはリモート音声再生している状態でpw-topコマンドを起動して確認すると便利です。

PipeWireの状態(変更前)
S   ID  QUANT   RATE    WAIT    BUSY   W/Q   B/Q  ERR FORMAT           NAME
S   30      0      0    ---     ---   ---   ---     0                  Dummy-Driver
S   31      0      0    ---     ---   ---   ---     0                  Freewheel-Driver
S   47      0      0    ---     ---   ---   ---     0                  Midi-Bridge
R   51   2048  48000 594.1us 320.5us  0.01  0.01    1   S24_32 2 48000 alsa_output.platform-es8316-s
R   56   8192  48000 245.6us 253.7us  0.01  0.01    1    S32LE 2 48000  + mpv
S   52      0      0    ---     ---   ---   ---     0                  alsa_input.platform-es8316-so
S   53      0      0    ---     ---   ---   ---     0                  alsa_output.platform-hdmi0-so
S   54      0      0    ---     ---   ---   ---     0                  alsa_output.platform-hdmi1-so
S   55      0      0    ---     ---   ---   ---     0                  alsa_input.platform-hdmiin-so

RATEがサンプリング周波数で、QUANT(※)がバッファサイズのようです。ALSAデバイス側は2048サンプル、PulseAudioモジュール(mpvの話し相手)は8192サンプルです。mpvはpulse-buffer=500を指定したので、500msつまり24000サンプルになるはずですが、なぜか8192です。

(※)QUANT = quantumのこと。バッファサイズを決めるための数値らしく、単位はサンプルだと思われます。PipeWireのドキュメントを見てもquantumについて説明している部分が見当たらなくて困りました。

編集者:すずき(2025/10/19 16:54)

コメント一覧

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



2025年10月13日

PipeWireをPulseAudioサーバーの代わりにする、その2 - 音切れ多発

目次: ALSA

先日(2025年10月11日の日記参照)の設定により、x86_64なPCからROCK 5Bにて動作しているPipeWireに対し、リモートオーディオ再生ができるようになりました。無事動いたまでは良いのですが、PipeWireにしてから音切れがひどくなり困ってしまいました。

クライアント側はx86_64、mplayer v0.40.0(今はmpvですね)+ libpipewire 1.4.8で、サーバー側はaarch64、libpipewire 1.4.2です。

クライアント側の設定

一般的に音が切れるのはクライアントかサーバーかどちらかの音声用のバッファが足りない可能性が高いです。クライアント側の設定を確認するためにmpvに-vオプションを付けて実行します。

mplayer -vの出力結果
$ PULSE_SERVER=192.168.1.x mplayer --audio-device=pulse -v a.mp4

...(略)...

[ao] Trying audio driver 'pulse'
[ao/pulse] requested format: 48000 Hz, stereo channels, floatp
[ao/pulse] Library version: 17.0.0
[ao/pulse] Proto: 35
[ao/pulse] Server proto: 35
[ao/pulse] Channel layouts:
[ao/pulse]  - #fl
[ao/pulse]  - #fr
[ao/pulse]  - #fc
[ao/pulse]  - #lfe
[ao/pulse]  - #bl
[ao/pulse]  - #br
[ao/pulse]  - #flc
[ao/pulse]  - #frc
[ao/pulse]  - #bc
[ao/pulse]  - #sl
[ao/pulse]  - #sr
[ao/pulse]  - #tc
[ao/pulse]  - #tfl
[ao/pulse]  - #tfc
[ao/pulse]  - #tfr
[ao/pulse]  - #tbl
[ao/pulse]  - #tbc
[ao/pulse]  - #tbr
[ao/pulse] result: stereo
[ao/pulse] device buffer: 4800 samples.        ★デバイスバッファが4800/48000 = 0.1秒
[ao/pulse] using soft-buffer of 9600 samples.  ★バッファが9600/48000 = 0.2秒

...

ログからバッファサイズをチェックしたところデフォルト100ms + 200msのようです。変更にはpulse-bufferオプションを使用します。値の単位はミリ秒です。

ログをみてバッファサイズ設定が反映されているか確認
$ PULSE_SERVER=192.168.1.x mplayer --audio-device=pulse --pulse-buffer=1000 -v a.mp4

...

[ao/pulse] result: stereo
[ao/pulse] device buffer: 48000 samples.
[ao/pulse] using soft-buffer of 48000 samples.

...

ひとまず1000msくらいに変更してしばらく様子を見ようと思います。

サーバー側の設定

今回は特に変えていませんが、サーバー側の設定は/usr/share/pipewire/pipewire-pulse.confにあります。設定を変更したらpipewire-pulseを再起動で反映されるはずです。ユーザー権限で動いているのでsudoは不要です。

pipewire-pulseの再起動
$ systemctl --user restart pipewire-pulse

このファイルを書き換えると次のアップデートで上書きされてしまうらしいですが、他の場所にある設定ファイルが見当たらないんですよね。どこにあるんだろう。。。

編集者:すずき(2025/10/16 03:19)

コメント一覧

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



2025年10月12日

ROCK 5BのDebianをアップデート

目次: ARM

Debian StableがTrixieに変わったことをすっかり忘れており、ROCK 5Bをapt-get dist-upgradeしたところ環境が壊れました。ARM SBC(SBC = Single Board Computer)のベンダーがDebianを提供してくれるのは大変ありがたいですが、大抵変なパッケージか変な改造が入っていて年単位で運用すると困ったことが起きがちです。

SBCはアップデートしない派もいるでしょうけど、ネットワークに繋いでいるし可能な限りアップデートを適用したいです。取れる選択肢は下記の二つでしょうか。

Releaseを固定(例えばBookworm)してセキュリティアップデートのみ適用
アップデートの悩みはないもののサポート切れになったときにどうしようもなくなります。
Stableを指定
今回の私のようにStableがバージョンアップしたときに高確率で環境が壊れます。

正直、良し悪しは互角ですね……。

環境を修復

Debianは多少壊れても元に戻せますが、今回は盛大に壊れましてapt --fix-broken installはエラーになります。致命的なのはsystemdとlibsystemd0のバージョン不一致でsystemdが消えたことで、bookwormにも戻してもsystemdがインストールできなくなりました。もしこの状態で再起動したら二度と起動しないでしょう。

systemdがインストールエラーになる
Preparing to unpack .../systemd_257.8-1~deb13u2_arm64.deb ...
Unpacking systemd (257.8-1~deb13u2) over (252.38-1~deb12u1) ...
dpkg: error processing archive /var/cache/apt/archives/systemd_257.8-1~deb13u2_arm64.deb (--unpack):
 trying to overwrite '/usr/lib/systemd/system/serial-getty@.service', which is also in package rockchip-overlay 4.1
dpkg: considering removing systemd in favour of udev ...
dpkg: systemd is not properly installed; ignoring any dependencies on it
dpkg: yes, will remove systemd in favour of udev
Preparing to unpack .../udev_257.8-1~deb13u2_arm64.deb ...
Unpacking udev (257.8-1~deb13u2) over (252.38-1~deb12u1) ...
Removing systemd (252.38-1~deb12u1), to allow configuration of udev (257.8-1~deb13u2) ...
Preparing to unpack .../libudev1_257.8-1~deb13u2_arm64.deb ...
Unpacking libudev1:arm64 (257.8-1~deb13u2) over (252.38-1~deb12u1) ...
Errors were encountered while processing:
 /var/cache/apt/archives/systemd_257.8-1~deb13u2_arm64.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)

エラーメッセージを見るとrockchip-overlayなるパッケージが邪魔しているようです。試行錯誤の結果、

  • trixie → bookwormに戻してapt-get update
  • rockchip-なんちゃらとradxa-なんちゃらパッケージをremove
  • bookworm → trixieに戻してapt-get updateとapt-get dist-upgrade
  • systemdその他の吹き飛んでしまったパッケージをinstall

としたところ、元気に動き始めました。代償として画面が出なくなりましたが、今のところ画面を使っていないので問題ありません。面倒なSDカードイメージの書き直しを避けることができてラッキーでした。

編集者:すずき(2025/10/16 03:14)

コメント一覧

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



2025年10月11日

PipeWireをPulseAudioサーバーの代わりにする

目次: ALSA

最近はPulseAudioがPipeWireに置き換わっているそうです。今はPulseAudioをリモートオーディオ再生機として使っているので、PipeWireで同じことをする方法を紹介します。

やりたいことはLinuxデスクトップからARM SBC上のPulseAudioへオーディオを飛ばすこと(2022年5月27日の日記参照)です。PulseAudioの場合はmodule-native-protocol-tcpを有効にして、TCPでオーディオを送るようにしました。

PipeWireのSinkデバイス設定

まずSinkデバイスの設定を行います。大抵HDMI、ヘッドフォン端子、あるいは別の出力端子があるはずですので、どこから音を鳴らすかを決めます。オーディオデバイスが1つしかない場合は設定不要かもしれませんが、一応チェックしておくと良いと思います。

設定にはWirePlumberの設定ツールwpctlを使います。wpctl statusで現在の様子を見ることができます。

wpctl statusの結果
$ wpctl status

Audio
 ├─ Devices:
 │      48. Built-in Audio                      [alsa]
 │      49. Built-in Audio                      [alsa]
 │      50. Built-in Audio                      [alsa]
 │      51. Built-in Audio                      [alsa]
 │
 ├─ Sinks:
 │      35. Built-in Audio Stereo               [vol: 1.00]
 │      46. Built-in Audio Stereo               [vol: 1.00]
 │  *   56. Built-in Audio Stereo               [vol: 1.00]
 │
 ├─ Sources:
 │      44. Built-in Audio Stereo               [vol: 1.00]
 │  *   57. Built-in Audio Stereo               [vol: 1.00]
 │
 ├─ Filters:
 │
 └─ Streams:

SourcesとSinksのデフォルトデバイスを変更して、音声の入出力先を変更するにはset-defaultを使用します。wpctlはDevice、Source、Sinkの全てがユニークなIDを持っているので、SourceだとかSinkだとか指定する必要はなくて、IDのみ指定します。入出力先を変更後、もう一度statusを見るとSettingsの内容が変わるはずです。

例えば上記のstatusなら、set-defaultに指定できるのはSink系(35, 46, 56)かSource系(44, 57)のIDのうちどれか1つです。もし35か46か56を指定した場合は、IDからSinkデバイスだとわかるのでデフォルトSinkデバイスが変更されます。44か57を指定した場合はデフォルトSourceデバイスが変更されます。直感的じゃない、辛い……。

set-defaultと結果
$ wpctl set-default 56
$ wpctl set-default 57


$ wpctl status

...

Settings
 └─ Default Configured Devices:
         0. Audio/Sink    alsa_output.platform-es8316-sound.stereo-fallback
         1. Audio/Source  alsa_input.platform-es8316-sound.stereo-fallback

余談でWirePlumberが出てくる理由を紹介します。PipeWireは音声の入出力経路を管理しないため、PipeWire以外のソフトウェアをインストールして音声の経路管理しなければならないからです。WirePlumberもPipeWireもこだわりというか思想の強さがにじみ出ていて、私は仲良くなれなさそうです……。

Sinkの確認

動作確認は音を鳴らしてみて鳴れば方法は問いませんが、一例としてALSAを使った方法を紹介します。PipeWireとALSAやV4L2を繋ぐためのモジュールとalsa-utilsをインストールします。

ALSAのテストツールで音が鳴るかテスト
$ sudo apt-get install pipewire-alsa pipewire-audio pipewire-v4l2 alsa-utils

$ speaker-test -c 2 -r 48000 -D pipewire

speaker-test 1.2.14

Playback device is pipewire
Stream parameters are 48000Hz, S16_LE, 2 channels
Using 16 octaves of pink noise
Rate set to 48000Hz (requested 48000Hz)
Buffer size range from 64 to 1048576
Period size range from 32 to 524288
Periods = 4
was set period_size = 12000
was set buffer_size = 48000
 0 - Front Left

左右のスピーカーから交互にノイズ音(サーー)がなれば成功です。鳴らない場合は設定とボリューム(wpctlで確認できます)、もしくはALSAデバイス側のボリューム、ミュート設定の問題も有り得るので、alsamixerで確認してみてください。

PulseAudioモジュールの設定

次はPulseAudioモジュールの設定変更です。PipeWireと一緒にインストールされるはずですが、もし下記の設定ファイルが存在しなければpipewire-pulseをインストールしてください。

PipeWireのPulseAudioモジュール設定
# /usr/share/pipewire/pipewire-pulse.conf

...

pulse.properties = {
    # the addresses this server listens on
    server.address = [
        "unix:native"
        #"unix:/tmp/something"              # absolute paths may be used

        #★★この行を有効にする
        "tcp:4713"                          # IPv4 and IPv6 on all addresses

        #"tcp:[::]:9999"                    # IPv6 on all addresses
        #"tcp:127.0.0.1:8888"               # IPv4 on a single address
        #
        #{ address = "tcp:4713"             # address
        #  max-clients = 64                 # maximum number of clients
        #  listen-backlog = 32              # backlog in the server listen queue
        #  client.access = "restricted"     # permissions for clients
        #}
    ] 

...
PipeWireのPulseAudioモジュール再起動
$ systemctl --user restart pipewire-pulse

設定を変更したら再起動をお忘れなく。

動作確認
#### クライアント側で操作

$ PULSE_SERVER=192.168.1.x speaker-test -c 2 -r 48000 -D pulse

リモートオーディオ再生のクライアント側でPULSE_SERVERにサーバーのIPアドレスを指定して音声を再生します。サーバー側から音が鳴れば成功です。

編集者:すずき(2025/10/14 05:13)

コメント一覧

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



2025年10月6日

makeのデフォルトルールのリンクはLDを使わない

目次: Linux

Makefileの達人には常識かもしれませんが、makeがリンクを行うときのデフォルトルール(LINK.o)が使うコマンドはLDではなくCCです。私はリンク時にldが直接呼ばれるのは見たことがないにも関わらず、ずっとLDが使われると思いこんでいました。

話は以上で終わり……だとつまらないので、実験もしましょう。Makefileとコンパイラの代わりにスクリプトを呼びます。makeはGNU Make 4.4.1を使いました。

実験用のMakefile

CC  = ./cc-dummy
CXX = ./cxx-dummy
CPP = ./cpp-dummy
LD  = ./ld-dummy
CFLAGS   = cflags-dummy
CXXFLAGS = cxxflags-dummy
CPPFLAGS = cppflags-dummy
LDFLAGS  = ldflags-dummy
LOADLIBES = loadlibes-dummy
LDLIBS    = ldlibs-dummy

all: target-c target-cxx

target-c: target-c.o
target-cxx: target-cxx.o
コンパイラの代わりに呼び出すスクリプト
$ cat ./cc-dummy

#!/bin/sh

touch target-c.o


$ cat ./cxx-dummy

#!/bin/sh

touch target-cxx.o

コマンドが見つからないエラーを防ぐため、CCやCXXに指定した名前のスクリプトも作ります。スクリプトではコンパイラがオブジェクトファイル(*.o)を作成する動きのみを模しておけば十分です。

実験

オブジェクトファイルの生成でデフォルトのコンパイルのルール、バイナリファイルの生成でリンクのルールが呼ばれます。このときCCやCXXが使われるか?LDが使われるか?がわかるはずです。実行します。

実行結果
$ make

#### C言語プログラムのコンパイルとリンク、どちらもCCを使う

./cc-dummy cflags-dummy cppflags-dummy  -c -o target-c.o target-c.c
./cc-dummy ldflags-dummy  target-c.o loadlibes-dummy ldlibs-dummy -o target-c


#### C++言語プログラムのコンパイルとリンク、コンパイルはCXX、リンクはCCを使う

./cxx-dummy cxxflags-dummy cppflags-dummy  -c -o target-cxx.o target-cxx.cc
./cc-dummy ldflags-dummy  target-cxx.o loadlibes-dummy ldlibs-dummy -o target-cxx

C言語でもC++言語でもリンクは一緒で、CCを呼び出しました。LDは使われません。

デフォルトルールを見る

オプション-pでmakeのデフォルトルールを見ることができます。"%: %.o"はオブジェクトファイルからバイナリを生成するときに当てはまるデフォルトルールです。コロンの左が生成ファイル、右が依存ファイルです。%はワイルドカードです。

UNIX系の慣例で、実行ファイル名は拡張子なし(例えばhoge)、オブジェクトファイル名は*.o(例えばhoge.o)にします。そのペアにうまく当てはまるルールになっています。

GNU Make 4.4.1のデフォルトルール
# デフォルト
LINK.o = $(CC) $(LDFLAGS) $(TARGET_ARCH)

#...(略)...

%: %.o
#  実行するレシピ (ビルトイン):
	$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

当たり前ですが、実験で見た結果と一緒です。

ソースコードも見る

GNU Makeのソースコードも眺めてみました。おそらくdefault.cがデフォルトルールの定義だと思います。デフォルトルールと一緒です。そりゃそうですね。

makeのソースコードのデフォルトルール

// make/src/default.c

static const char *default_variables[] =
  {
#ifdef VMS

//...

#else /* !VMS */

//...

    "LINK.o", "$(CC) $(LDFLAGS) $(TARGET_ARCH)",
    "COMPILE.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
    "LINK.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
    "COMPILE.m", "$(OBJC) $(OBJCFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
    "LINK.m", "$(OBJC) $(OBJCFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
    "COMPILE.cc", "$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",

//...

ソースコードを見て初めて知ったことが、VAX用のVMSというOSだとCCではなくLDを使うことです。しかし今の時代にVMS使っている人はいないでしょう……たぶん。

編集者:すずき(2025/10/16 03:20)

コメント一覧

  • hdkさん(2025/10/10 08:27)
    ただのHello, worldでも試してみるとわかりますが、標準Cライブラリやスタートアップをリンクするためですよね。ldコマンドを直接使うと全部指定する必要がありますので... VMSはどうなっていたのかわかりませんが。
  • すずきさん(2025/10/10 13:14)
    ですね。ccはもはやコンパイラというよりコンパイル関係のプログラムのランチャーって感じですね。gccはcc1やcc1plusが狭義のコンパイラだと思います。
    VMSはどうなっているのか私もわかりません……。
open/close この記事にコメントする



2025年10月4日

Linuxのprocファイルシステムの実装 - /proc/pid/ioのopenとread

目次: Linux

以前、LinuxのI/O統計情報を得る/proc/[pid]/ioの実装を調べました。あのときは深追いしませんでしたが、/procには/proc/[pid]/以下のファイルから何が読めるのか、pid_entryの配列で指定する仕組みがありました。

tgid_base_stuff(pid_entryの配列)の定義

// fs/proc/base.c

/*
 * Thread groups
 */
static const struct pid_entry tgid_base_stuff[] = {
	DIR("task",       S_IRUGO|S_IXUGO, proc_task_inode_operations, proc_task_operations),
	DIR("fd",         S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations),

//...

#ifdef CONFIG_TASK_IO_ACCOUNTING
	ONE("io",	S_IRUSR, proc_tgid_io_accounting),    //★★★★これ
#endif

なぜこの配列に要素を足すと/proc/[pid]/以下のファイルが定義できるのでしょう?まずはopen/readに限定して調べてみます。調査対象はLinux-5.15です。

/proc/(tgid)/のエントリ

/proc/(tgid)/以下のファイルを定義しているtgid_base_stuffの中身は、DIR()もしくはONE()マクロを使って定義します。ONE()マクロを見てみます。

ONE()マクロの実装

// fs/proc/base.c

#define ONE(NAME, MODE, show)				\
	NOD(NAME, (S_IFREG|(MODE)),			\
		NULL, &proc_single_file_operations,	\
		{ .proc_show = show } )

#define NOD(NAME, MODE, IOP, FOP, OP) {			\
	.name = (NAME),					\
	.len  = sizeof(NAME) - 1,			\
	.mode = MODE,					\
	.iop  = IOP,					\
	.fop  = FOP,					\
	.op   = OP,					\
}

ネストしたマクロはわかりにくいです。何か1つ(ここではio)を展開すると多少はわかりやすいと思います。

ioの定義を展開

// fs/proc/base.c

// ★★★★NODを展開

#define ONE(NAME, MODE, show) {				\
	.name = (NAME),					\
	.len  = sizeof(NAME) - 1,			\
	.mode = (S_IFREG|(MODE)),			\
	.iop  = NULL,					\
	.fop  = &proc_single_file_operations,		\
	.op   = { .proc_show = show },			\
}

static const struct file_operations proc_single_file_operations = {
	.open		= proc_single_open,    //★★open時に呼ばれる関数
	.read		= seq_read,            //★★read時に呼ばれる関数
	.llseek		= seq_lseek,
	.release	= single_release,
};


// ★★★★ioの定義を当てはめる

	ONE("io",	S_IRUSR, proc_tgid_io_accounting),

#define ONE(NAME, MODE, show) {				\
	.name = ("io"),					\
	.len  = sizeof("io") - 1,			\
	.mode = (S_IFREG|(S_IRUSR)),			\
	.iop  = NULL,					\
	.fop  = &proc_single_file_operations,		\
	.op   = { .proc_show = proc_tgid_io_accounting },	\
}

ファイルopen時にproc_single_open()、read時にseq_read()が呼ばれるように、あとはop.proc_showにproc_tgid_io_accounting()を指定しています。

open時の動作

どうやってproc_single_open()が呼ばれるか?は一旦無視して、open時に呼ばれるものとして調べます。

open周りの実装

// linux/fs/proc/base.c

static int proc_single_open(struct inode *inode, struct file *filp)
{
	return single_open(filp, proc_single_show, inode);
}


// linux/fs/seq_file.c

int single_open(struct file *file, int (*show)(struct seq_file *, void *),
		void *data)
{
	struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL_ACCOUNT);
	int res = -ENOMEM;

	if (op) {
		op->start = single_start;
		op->next = single_next;
		op->stop = single_stop;
		op->show = show;          //★★show = proc_single_show()
		res = seq_open(file, op);
		if (!res)
			((struct seq_file *)file->private_data)->private = data;
		else
			kfree(op);
	}
	return res;
}
EXPORT_SYMBOL(single_open);

int seq_open(struct file *file, const struct seq_operations *op)
{
	struct seq_file *p;

	WARN_ON(file->private_data);

	p = kmem_cache_zalloc(seq_file_cache, GFP_KERNEL);
	if (!p)
		return -ENOMEM;

	file->private_data = p;

	mutex_init(&p->lock);
	p->op = op;           //★★op = struct seq_operations, op->show = proc_single_show()

	// No refcounting: the lifetime of 'p' is constrained
	// to the lifetime of the file.
	p->file = file;

	/*
	 * seq_files support lseek() and pread().  They do not implement
	 * write() at all, but we clear FMODE_PWRITE here for historical
	 * reasons.
	 *
	 * If a client of seq_files a) implements file.write() and b) wishes to
	 * support pwrite() then that client will need to implement its own
	 * file.open() which calls seq_open() and then sets FMODE_PWRITE.
	 */
	file->f_mode &= ~FMODE_PWRITE;
	return 0;
}
EXPORT_SYMBOL(seq_open);

ここで出てくるseq_で始まる関数群とsingle_で始まる関数群は、seq_fileと呼ばれる仕組みThe seq_file interface - The Linux Kernel Documentation)です。変な名前だなあ?と思うのはごもっともですけど正式名称なのです……。

仮想ファイルシステム上にファイルを定義するときは、openやreadを実装しなければなりません。特にread()の実装は曲者で読み出し位置の管理が面倒です。しかしseq_fileを使うと内部にバッファを勝手に確保し、読み出し位置を管理してくれます。もっとも簡単な実装だと、seq_printf()で内部バッファに書き出すだけで済みます。/procのような何か情報を返すファイルシステムを実装するときに便利です。

read時の動作

次にread時の動作を見ます。open同様にどうやってseq_read()が呼ばれるか?は一旦無視して、read時に呼ばれるものとして調べます。

read周りの実装

// linux/fs/seq_file.c

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	struct iovec iov = { .iov_base = buf, .iov_len = size};
	struct kiocb kiocb;
	struct iov_iter iter;
	ssize_t ret;

	init_sync_kiocb(&kiocb, file);
	iov_iter_init(&iter, READ, &iov, 1, size);

	kiocb.ki_pos = *ppos;
	ret = seq_read_iter(&kiocb, &iter);    //★★★★
	*ppos = kiocb.ki_pos;
	return ret;
}
EXPORT_SYMBOL(seq_read);

ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
	struct seq_file *m = iocb->ki_filp->private_data;
	size_t copied = 0;
	size_t n;
	void *p;
	int err = 0;

//...略...

	// get a non-empty record in the buffer
	m->from = 0;
	p = m->op->start(m, &m->index);
	while (1) {
		err = PTR_ERR(p);
		if (!p || IS_ERR(p))	// EOF or an error
			break;
		err = m->op->show(m, p);    //★★ここでshow = proc_single_show()を呼ぶ
		if (err < 0)		// hard error
			break;
		if (unlikely(err))	// ->show() says "skip it"
			m->count = 0;
		if (unlikely(!m->count)) { // empty record
			p = m->op->next(m, p, &m->index);
			continue;
		}
		if (!seq_has_overflowed(m)) // got it
			goto Fill;
		// need a bigger buffer
		m->op->stop(m, p);
		kvfree(m->buf);
		m->count = 0;
		m->buf = seq_buf_alloc(m->size <<= 1);
		if (!m->buf)
			goto Enomem;
		p = m->op->start(m, &m->index);
	}
	// EOF or an error
	m->op->stop(m, p);
	m->count = 0;
	goto Done;

//...略...


// linux/fs/proc/base.c

static int proc_single_show(struct seq_file *m, void *v)
{
	struct inode *inode = m->private;
	struct pid_namespace *ns = proc_pid_ns(inode->i_sb);
	struct pid *pid = proc_pid(inode);
	struct task_struct *task;
	int ret;

	task = get_pid_task(pid, PIDTYPE_PID);
	if (!task)
		return -ESRCH;

	ret = PROC_I(inode)->op.proc_show(m, ns, pid, task);  //★★ここでproc_show = proc_tgid_io_accounting()を呼ぶ

	put_task_struct(task);
	return ret;
}

やっとONE()に渡したproc_tgid_io_accountingが呼ばれる場所がわかりました。長かった……。

編集者:すずき(2025/10/06 02:52)

コメント一覧

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



link もっと前
2025年10月17日 >>> 2025年10月4日
link もっと後

管理用メニュー

link 記事を新規作成

<2025>
<<<10>>>
---1234
567891011
12131415161718
19202122232425
262728293031-

最近のコメント5件

  • link 25年10月6日
    すずきさん (10/10 13:14)
    「ですね。ccはもはやコンパイラというより...」
  • link 25年10月6日
    hdkさん (10/10 08:27)
    「ただのHello, worldでも試して...」
  • link 25年9月29日
    すずきさん (10/03 00:29)
    「なんと、メタパッケージ入れてなかったです...」
  • link 25年9月29日
    hdkさん (10/02 06:51)
    「あれ、dkmsは自動ビルドされるのが便利...」
  • link 20年8月24日
    すずきさん (08/30 22:06)
    「ですね、自分も今はPulseAudioを...」

最近の記事3件

  • link 25年10月15日
    すずき (10/19 16:54)
    「[PipeWireの音切れ問題 - サーバー側の設定確認と反映] 目次: ALSAPipeWireに変えてから音切れがなくなり...」
  • link 25年10月18日
    すずき (10/19 16:52)
    「[PipeWireの音切れ問題 - サーバー側PipeWireの設定] 目次: ALSAPipeWireに変えてから音切れがな...」
  • link 22年5月5日
    すずき (10/19 16:49)
    「[ALSA - まとめリンク] 目次: ALSAALSAの話。ALSAその1 - 使ってみようALSAその2 - カードとデバ...」
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 2025年
open/close 過去日記について

その他の情報

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

合計:  counter total
本日:  counter today

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

最終更新: 10/19 16:54