8

私は Web 上の Linux char ドライバーの例を調べてみましたが、説明できない動作に出くわしました。

static ssize_t my_read(struct file *f, char __user *user_buf, size_t cnt, loff_t* off)
{
   printk( KERN_INFO "Read called for %zd bytes\n", cnt );
   return cnt;
}

cnt=4096メッセージは、ユーザー空間で指定されたバイト数に関係なく、そのバイトを常に示します。

[11043.021789] Read called for 4096 bytes

ただし、ユーザー空間の読み取り呼び出し

retval = fread(_rx_buffer, sizeof(char), 5, file_ptr);
printf( "fread returned %d bytes\n", retval );

ユーザー空間からの出力は

fread returned 5 bytes.

size in の値my_readは常に 4096 ですが、 from の値freadは 5 を示しているのはなぜですか? 欠けているものがあることは知っていますが、何がわからないのですか...

4

1 に答える 1

12

read(2)(で)試してみるunistd.hと、5文字が出力されるはずです。libc ( fread(3)fwrite(3)など) を使用する場合、通常はページのサイズ (ほとんどの場合 4 kiB) である内部 libc バッファーを使用しています。

初めて 5 バイトを呼び出すと、libc は4096 バイトfread()の内部処理を実行し、次は、使用する構造に関連付けられたバッファーに libc が既に持っているバイトを返すだけだと思います。4096 に到達するまで。4097 番目のバイトは別の 4096 バイトなどを発行します。read()fread()FILEread

printf()これは、最初の引数としてを使用する場合など、記述時にも発生しますfprintf()stdout()libc はwrite(2)直接呼び出しませんが、代わりに内部バッファー (これも 4096 バイト) にデータを入れます。呼び出すとフラッシュされます

fflush(stdout);

自分自身、または送信されたバイト内にバイト 0x0a (ASCII の改行) が見つかったときはいつでも。

試してみてください。

#include <stdio.h> /* for printf() */
#include <unistd.h> /* for sleep() */

int main(void) {
    printf("the following message won't show up\n");
    printf("hello, world!");
    sleep(3);
    printf("\nuntil now...\n");

    return 0;
}

ただし、これは機能します (libc のバッファリングを使用しません):

#include <stdio.h> /* for printf() */
#include <unistd.h> /* for sleep(), write(), and STDOUT_FILENO */

int main(void) {
    printf("the following message WILL show up\n");
    write(STDOUT_FILENO, "hello!", 6);
    sleep(3);
    printf("see?\n");

    return 0;
}

STDOUT_FILENO標準出力 (1) のデフォルトのファイル記述子です。

改行があるたびにフラッシュすることは、端末のユーザーがメッセージを即座に見るために不可欠であり、Unix 環境で多く行われる行ごとの処理にも役立ちます。

そのため、libc がread()およびwrite()syscall を直接使用してバッファを埋めたりフラッシュしたりしても (ちなみに、Microsoft の C 標準ライブラリの実装では Windows のものを使用している必要があります (おそらくReadFileおよびWriteFile))、これらの syscall は libc をまったく認識していません。これにより、両方を使用すると興味深い動作が発生します。

#include <stdio.h> /* for printf() */
#include <unistd.h> /* for write() and STDOUT_FILENO */

int main(void) {
    printf("1. first message (flushed now)\n");
    printf("2. second message (without flushing)");
    write(STDOUT_FILENO, "3. third message (flushed now)", 30);
    printf("\n");

    return 0;
}

出力:

1. first message (flushed now)
3. third message (flushed now)2. second message (without flushing)

(2 番目の前に 3 番目!)。

また、libc のバッファリングを無効にできることに注意してくださいsetvbuf(3)。例:

#include <stdio.h> /* for setvbuf() and printf() */
#include <unistd.h> /* for sleep() */

int main(void) {
    setvbuf(stdout, NULL, _IONBF, 0);
    printf("the following message WILL show up\n");
    printf("hello!");
    sleep(3);
    printf("see?\n");

    return 0;
}

私は試したことはありませんが、キャラクターデバイスを ing したFILE*ときに得られるものと同じことができfopen()、これの I/O バッファリングを無効にできると思います。

FILE* fh = fopen("/dev/my-char-device", "rb");
setvbuf(fh, NULL, _IONBF, 0);
于 2013-11-08T20:02:25.680 に答える