目次: Linux
知っている人には「なんだそんなことか」で終わりな話なんですが、お恥ずかしいことに私は今まで知らなかったのでメモしておきます。
Linuxには指定されたオフセットからデータを読み出すシステムコールpread(pread64)があります。私は今までpread = lseek + readだと思っていましたが、特定の環境だと両者の動作が違います。
まずは確認のためのテストプログラムを用意しました。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
int fd = -1;
char buf[256];
ssize_t nr;
off_t pos, offset1 = 0x1000, offset2 = 0x2000;
fd = open("/dev/sample_char/sample_char_dev", O_RDWR);
if (fd == -1) {
perror("open");
goto err_out;
}
// lseek + read
pos = lseek(fd, offset1, SEEK_SET);
if (pos == (off_t)-1) {
perror("lseek 1");
}
nr = read(fd, buf, sizeof(buf));
if (nr != (ssize_t)sizeof(buf)) {
perror("read 1-1");
}
nr = read(fd, buf, sizeof(buf));
if (nr != (ssize_t)sizeof(buf)) {
perror("read 1-2");
}
pos = lseek(fd, offset2, SEEK_SET);
if (pos == (off_t)-1) {
perror("lseek 2");
}
nr = read(fd, buf, sizeof(buf));
if (nr != (ssize_t)sizeof(buf)) {
perror("read 2-1");
}
nr = read(fd, buf, sizeof(buf));
if (nr != (ssize_t)sizeof(buf)) {
perror("read 2-2");
}
// pread
nr = pread(fd, buf, sizeof(buf), offset1);
if (nr != (ssize_t)sizeof(buf)) {
perror("pread 1");
}
nr = pread(fd, buf, sizeof(buf), offset2);
if (nr != (ssize_t)sizeof(buf)) {
perror("pread 2");
}
err_out:
if (fd != -1) {
close(fd);
fd = -1;
}
return 0;
}
やっていることは、
こんな操作です。
ファイルにどんな操作が行われたか知るために、キャラクタデバイスを作成してseekやreadのログを出します。方法は別にキャラクタデバイスでなくても何でも良いんですが、Linuxカーネルモジュールでキャラクタデバイスを実装するのが楽だと思います。ソースコードはGitHubに置きました。
sudo apt-get install linux-headers-amd64 make sudo insmod sample-char-dev.ko gcc test.c -o test ./test
カーネルモジュールのビルド方法とテストプログラムの実行方法は上記のとおりです。
特にカーネルモジュールの実装を変えなければ、キャラクタデバイスのファイル位置はlseekで変更可能かつreadにより進む実装になります。テストプログラムを実行すると下記のようなカーネルログが出るはずです。カーネルログはsudo dmesg -wなどで見てください。
sample_char: open. sample_char: seek to 1000. sample_char: read 256 bytes at 1000. ★lseek + read: 位置1000から読み出せる★ sample_char: read 256 bytes at 1256. sample_char: seek to 2000. sample_char: read 256 bytes at 2000. ★lseek + read: 位置2000から読み出せる★ sample_char: read 256 bytes at 2256. sample_char: read 256 bytes at 1000. ★pread: lseek(1000)+readと一緒★ sample_char: read 256 bytes at 2000. ★pread: lseek(2000)+readと一緒★ sample_char: release.
シングルスレッドかつlseek可能な通常のファイルではlseek + readでも、preadでも結果は同じです。位置1000もしくは位置2000からreadできます。
しかしlseekだけできない特殊なファイルだと結果が異なります。先ほどのキャラクタデバイスのlseekを無効にして試しましょう。
static const struct file_operations sample_char_fops = {
.owner = THIS_MODULE,
.open = sample_char_fops_open,
.release = sample_char_fops_release,
.read = sample_char_fops_read,
.write = sample_char_fops_write,
// .llseek = sample_char_fops_llseek, ★この行をコメントアウトする★
};
変更によりlseekだけ失敗し、readは成功するファイル(テストプログラムがIllegal seekのエラー表示を出す)になりました。この状態でテストプログラムを実行すると下記のようなカーネルログが出るはずです。カーネルログはsudo dmesg -wなどで見てください。
sample_char: open. sample_char: read 256 bytes at 0. ★lseek + read: 位置1000から読み出しているつもりが0からになる★ sample_char: read 256 bytes at 256. sample_char: read 256 bytes at 512. ★lseek + read: 位置2000から読み出しているつもりが512からになる★ sample_char: read 256 bytes at 768. sample_char: read 256 bytes at 1000. ★pread: 変わらず位置1000から読み出せる★ sample_char: read 256 bytes at 2000. ★pread: 変わらず位置2000から読み出せる★ sample_char: release.
最初はlseek + readのログですが、位置1000もしくは2000からreadしたいのに、lseekが失敗するため意図しない位置を読み出してしまう一方、preadはlseekと無関係に指定された位置1000もしくは2000から読み出せます。
個人的にはファイル位置に意味があるのにlseekできないファイルを作るのは良くない実装だと思いますが、Linuxカーネルにもこのような実装が存在しています。ちょうど最近出会ったvfioがlseek無効の実装になっていました。vfioはファイル位置によってデバイスのどのメモリ領域を読み出せるか変わります。例えばvfio-pciであればBAR 0はこの位置、BAR 1はこの位置、みたいに決まっています。
// linux/drivers/vfio/vfio_main.c
const struct file_operations vfio_device_fops = {
.owner = THIS_MODULE,
.open = vfio_device_fops_cdev_open,
.release = vfio_device_fops_release,
.read = vfio_device_fops_read,
.write = vfio_device_fops_write,
.unlocked_ioctl = vfio_device_fops_unl_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.mmap = vfio_device_fops_mmap,
};
しかし上記のようにvfioにはllseekの定義がありません。lseek+readではなくpreadで読み出さないと正常に動作しないことに気づくまでかなり困惑しました。こういう実装は良くないと思うんだ……。
この記事にコメントする
目次: 射的
アキバのターゲット1にて減圧レギュレータ(0.4MPa)とホース、コネクタを買いました。いわゆるエアガンを外部から供給するガスで動作させるための部品「外部ソースキット」と呼ばれるものの一部です。
ガスガンを作動させるには何らかの圧縮ガスが必要で、ざっくりいってこの3パターンがあります。
| 動作方式 | 動作ガス | 外部ソースキット | 空き缶 | デメリット |
|---|---|---|---|---|
| フロン | 液化フロンガス | 不要 | 発生する | 冷えに弱い、速く動かすと液化ガスが噴き出る |
| 外部ソース | CO2ボンベ | 必要 | 発生する | 必要な道具が多くやや高い |
| エアーコンプレッサー | 圧縮空気 | 必要 | 発生しない | ホースが必要、コンプレッサーの近くでしか使えない |
私はずっとフロンガスだったので、エアガンの動作用フロンガスの空き缶が家にどんどん溜まります。空き缶は500ccのPETボトルくらいあって割と邪魔で、なんとかならないかなーと思っていました。
CO2外部ソースは知っていたものの空き缶が発生することに変わりはなくて、フロンガスの空き缶がCO2ガスの空き缶に代わっても嬉しくないので購入を見送っていました。
最近になって、アキバのターゲット1にエアーコンプレッサーが設置されたため(しかも今のところは無料で使わせていただける)、これが決め手になって外部ソースキットの一部購入に踏み切りました。家にフロンガスの空き缶が増えるのが防げるのはかなりありがたいです。
この記事にコメントする
目次: Linux
Ubuntu 24.04 LTSで起動中にカーネルパニックを起こすとこんな感じの画面になります。画面にはエラーメッセージの最後と思われる1行のみ表示され、他のカーネル起動ログは隠されます。
エラーの原因がわかりにくくて開発の邪魔なので無効にするべく調べました。どうやらカーネルの設定らしくてCONFIG_DRM_PANICを有効/無効にするとこの画面が出る/出ないが切り替わります。
Device Drivers --->
Graphics support --->
<M> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) --->
[*] Display a user-friendly message when a kernel panic occurs
(0xffffff) Drm panic screen foreground color, in RGB
(0x5e2750) Drm panic screen background color, in RGB
Ubuntu 24.04 LTSと同じ配色にしたい場合は、上記のような設定にすると良いです。何がユーザーフレンドリーかわかんないし、私はこの機能は要らんです……。
この記事にコメントする
目次: Linux
LXDEにはLXPanelといってタスクバーやスタートメニューなどを表示しているアプリがいます。LXPanelのタスクバーにはドラッグするとボタンを入れ替えられる機能がありますが、LXPanelのボタン入れ替え機能はバグっているようで、たびたび動かなくなります。起動直後に発生率高い気がします。
ボタン入れ替え機能が死んでしまったときは、LXPanelの何もないところを右クリックして[パネルの設定]を選び、設定ウインドウを表示させましょう。
下記のように操作すると直ることが多いです。
私の環境(Debian Testing, forky/sid)だと、1回でダメでも何回かやったら直ることもあります。どうやっても直らないときもありますから、その時は諦めて再起動ですね……。
この記事にコメントする
| < | 2025 | > | ||||
| << | < | 12 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
| - | 1 | 2 | 3 | 4 | 5 | 6 |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 | - | - | - |
wiki
Linux JM
Java API
2002年
2003年
2004年
2005年
2006年
2007年
2008年
2009年
2010年
2011年
2012年
2013年
2014年
2015年
2016年
2017年
2018年
2019年
2020年
2021年
2022年
2023年
2024年
2025年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: