web-dev-qa-db-ja.com

file_operationsのloff_t * offpを理解する

私は、文字バッファーに対して単純に読み書きするデバイスドライバーを設計しています。しかし、私の質問は、_file_operations_構造体readおよびwriteの2つの関数に関するものです。 _loff_t *offp_が本当に何であるか私は本当に理解していません。読み取りと書き込みの両方の操作で、_*offp_はファイルオフセットであり、ファイルの現在の読み取り/書き込み位置を意味しますが、デバイスへの書き込みまたはデバイスからの読み取りが何を意味するのかさえわかりませんファイル。

私が収集したものから、これが私のデバイスから読み書きしている方法ですが、以下に示す_my_char_struct_と呼ぶデバイスを表す構造を作成します。

_struct my_char_structure{
    struct cdev my_cdev;
    struct semaphore sem;
    char *data;
    ssize_t data_size;
    unsigned int access_key;
    unsigned long size;
};
_

これは、私のドライバーがinsmodであるときに初期化され、ポイントされる静的構造です。

_static dev_t dev_num;
static struct my_char_structure Dev;

int start_mod(void){
    //Because we are dealing with a fictitious device, I want
    //the driver to create my two devices with arbitrarily 
    //assigned major numbers.
    struct my_char_structure *my_dev = &Dev;
    int err;

    alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME);

    sema_init(&(my_dev->sem),1);

    cdev_init(&(my_dev->my_cdev), &fops);
    my_dev->my_cdev.owner = THIS_MODULE;
    my_dev->my_cdev.ops = &fops;// fops is my file operations struct

    err = cdev_add(&my_dev->my_cdev, dev_num, COUNT);
    if(err<0)
        printk(KERN_ALERT "There was an error %d.",err);
    printk(KERN_ALERT " insmod to major number %d",MAJOR(dev_num));

    return 0;   
}

module_init(start_mod);
_

デバイスが開いているときは、ファイルのポインターを開いて、module_init(start_mod)の実行中に設定した静的構造を指すようにします...

_int dev_open(struct inode *in_node, struct file *filp){
    static struct my_char_structure *my_dev;
    my_dev = container_of(in_node->i_cdev, struct my_char_structure, my_cdev);
    printk(KERN_ALERT "The device number is %d",iminor(in_node));
    if(!my_dev)
        printk(KERN_ALERT "something didn't work. my_dev not initialized.");
    filp->private_data = my_dev;
    return 0;
}
_

私の読み取りおよび書き込みメソッドが行うことは、開いているファイルで指摘した初期構造Devを変更することです。私の構造の_copy_to_user_は、ユーザーがデバイスに書き込まれたと見なすものであり、ユーザーが_copy_from_user_の書き込みをユーザーが考えているものです。しかし、最初の構造Devを変更するだけでなく、ファイルの位置やオフセットの考え方は、カーネル内のバッファされたメモリへのポインタが何らかの任意の構造や型で参照されない限り意味がありません。それは私がファイルのオフセットについて持っている唯一の解釈です...これは正しいですか?ここの_loff_t *offp_は何を指しているのですか?

_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
_

(私の理解が正しければ)読み取り/書き込みなどのいくつかのfile_operationが呼び出され、_*offp_を個人的に設定しなかった場合、loff_t * offpは最初に何に設定されますか?

最後のfile_operation offp = some_arbitrary_address(私がそう言ったため)の場合、この操作が再度呼び出されたときにoffpが設定されるのは何ですか?

他のfile_opens操作を実行している場合はどうなりますか?最後のfile_operationが残したものに設定されますか?それとも、使用したfile_open操作のタブを保持し、* offpをfile_openが持っていたものに置き換えますか?

Charデバイスの概念は、デバイス自体がファイルのように情報を保存するのではなく、情報を保存するドライバーであるように思える場合、私には抽象的すぎます。私がぼやけていることを説明できればいいのですが、曖昧に思われるものはすべて解決します。

21
Dr.Knowitall

「loff_t」は「長いオフセット」、つまり、off_toff64_tなどのクレイジーな多様性を統合するシークポジションであり、ドライバーはloff_tを使用するだけで心配する必要はありません。

ドライバーに入るときのポインター自体は、ユーザーによって提供されたオフセットを指します(ドライバーアクセスを実行するユーザーコードであると仮定します。技術的にはカーネルが独自に提供することができますが、ユーザーのケースは考慮すべきケースです)。 lseekまたはllseekまたはlseek64などを介して、通常の読み取りおよび書き込み操作によって。通常のディスク上のファイルの場合を考えてみましょう。最初にファイルをopenすると、(ユーザーとして)ファイルの現在の位置を追跡するデータ構造をカーネルに提供します。 readまたはwrite数バイトの場合、次のreadまたはwriteは中断したところから再開します。

さらに、ファイル記述子をdupするか、コマンドのシーケンスの実行に関してforkおよびexecによって同等のことを行うと、そのシーク位置が共有されますすべての継承プロセスによって。したがって、シェルプロンプトで次のコマンドを実行します。

(prog1; prog2; prog3) > outputfile

出力ファイルを作成し、次に記述子を3つのプログラムにdupsします。これにより、prog2が書き込む出力は、prog1からの出力の直後にファイルに入り、prog3からの出力は他の2つに従います。プロセスは、同じ内部loff_tを使用して、同じ基本的なカーネルデータ構造を共有します。

同じことがデバイスドライバファイルにも当てはまります。読み取り関数と書き込み関数が呼び出されると、ユーザーから提供された「現在のオフセット」を受け取り、必要に応じてそれを更新できます(そして更新する必要があります)...必要がある場合(たとえば、ユーザーに提供したい場合)通常のファイルの外観(読み取りおよび書き込み時にシークオフセットが移動するなど)。デバイスにシークオフセットの論理アプリケーションがある場合は、ここでそれを使用できます。

もちろん、デバイスドライバーには他にも多くの機能があります。そのため、本に関する章全体(q.v.)があります。 :-)

20
torek

トレックの答え はすばらしい。少し余分な詳細/コンテキストを追加するだけ...以前のLinuxカーネル(2.6.28)から、システムコールで使用されているオフセットの例を次に示します...取得する前に、ユーザースペースから一時変数にオフセットをコピーします。カーネルドライバー呼び出しメカニズムに挿入し、それをユーザーファイルにコピーします。これは、ドライバーが見るオフセットがそのユーザービューから切り離される方法であり、システムコールでオフセットがNULLである状況を容易にするため、SEGVIOは発生しません。

SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count)
{
    loff_t pos;
    ssize_t ret;

    if (offset) {
        if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t))))
            return -EFAULT;
        ret = do_sendfile(out_fd, in_fd, &pos, count, 0);
        if (unlikely(put_user(pos, offset)))
            return -EFAULT;
        return ret;
    }

    return do_sendfile(out_fd, in_fd, NULL, count, 0);
}
1
clearlight