2012-03-15 21 views
14

文字バッファーを読み書きするデバイスドライバを設計しています。しかし、私の質問は、file_operations構造体readwriteの2つの機能に関するものです。私は本当にloff_t *offpが本当に何であるかを理解していません。私は、読み取りと書き込みの両方の操作について、*offpがファイルの現在の読み取り/書き込み位置を意味するファイルオフセットであることを知っていますが、デバイスファイルとの間での書き込みまたは読み取りの意味についてはわかりません。file_operationsのloff_t * 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がここに言及しているのでしょうか?そのような読み取り/書き込みなど、いくつかのfile_operationが最初に設定offp *と呼ばれ、私が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 = some_arbitrary_address(私がそう言ったからです)が、この操作が再度呼び出されたときにoffpが設定されるのはどうでしょうか?

他のfile_opens操作が実行されている場合は、最後のfile_operationをそのまま残すか、file_open操作のタブを保持して* offpをfile_openのものに置き換えますか?

文字デバイスのコンセプトは、デバイス自体がファイルのように情報を格納するのではなく、情報を保存するドライバであると思われるときには抽象的すぎます。私は私の曇りを説明してくれることを願っています。

答えて

19

「loff_t」は、「off_t」、「off64_t」などのクレイジーダイバーシティを統一するシーク位置です。ドライバはloff_tを使用して心配する必要はありません。

ドライバに入るときのポインタ自体は、ユーザが提供するオフセットを指しています(ドライバをアクセスするユーザコードが技術的には、カーネルが独自に提供することができると仮定しますが、ユーザケースは考えてみるとlseekまたはllseekまたはlseek64などを介して読み込み、その後通常の読み書き操作を行います。通常のオンディスクファイルの場合を考えてみましょう。最初にファイルをopenにすると、ファイル内の現在の位置を追跡するデータ構造をカーネルに提供します(readまたはwriteいくつかのバイト、次のreadまたはwriteは、中止したところからピックアップします。

あなたdupファイルディスクリプタ、または一連のコマンドを実行しているという点で(例えば)forkexecで同等の操作を行う場合はさらに、そのシーク位置は、すべての継承のプロセスによって共有されています。したがって、シェルプロンプトで、コマンド:prog2が書き込む出力はすぐにprog1からの出力後のファイルになりますように

(prog1; prog2; prog3) > outputfile 

は、dup秒、その後、三つのプログラムへの記述子を出力ファイルを作成し、出力がprog3から他の2つすべてに続くのは、3つの別々のプロセスがすべて同じ内部カーネルのデータ構造を共有するためです(loff_t)。

デバイスドライバファイルについても同様です。読み書き機能が呼び出されると、ユーザーが提供する「現在のオフセット」を受け取り、必要に応じて更新することができます(必要があります)。あなたが読み書きするときにオフセットを移動するという事実を含む、通常のファイルの出現)。デバイスにシークオフセットの論理アプリケーションがある場合は、ここで使用できます。

もちろん、デバイスドライバにはさらに多くのものがあります。そのため、このような書籍(q.v.)の全章があります。 :-)

+1

オフセットをoffset = = bytes_read/writeに変更すると、ユーザーポインタは変更されますが、自動的には行われませんか?私はこれが私のためにいくつかのものをクリアすると思う。私はlinuxのデバイスドライバの第3版の本を読んでいました。誰もが紹介していて、オフセットポインタがファイル位置と呼ばれる奇妙なカーネル抽象化(より良い言葉の欠如)を参照していたこの図がありました。助けてくれてありがとう、この種のものをクリアする:) –

+2

はい。 (おそらくあなたは '* offp + = nbytes'を意味します。)変更している変数は実際にはカーネルスペースのものですが、*はユーザーのシークオフセットを表します*。 (あるいは、 'pread'や' pwrite'呼び出しに与えられたオフセット、あるいは何か他のものですが、たいていの場合、ユーザの 'lseek'オフセットです。)あなたが呼び出すように、"奇妙なカーネル抽象化 " '(prog1; prog2)> output'を動作させるものです。ちなみに、* BSDカーネルには、あなたのための "ユーザI/Oオフセット"を自動的に更新する 'uiomove'という関数があります。 Linuxはまったく逆の方向に進みました。 – torek

0

Torek's answerが優れています。以前の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); 
}