globalfifo linux

17870 ワード

私はglobalfifo駆動を見ているときによく見えませんでした.一部の場所は理解していません.今日はフォーラムの投稿を見て、私が理解していないことを理解しました.抜粋して参考にしてください.
=================================================
LZ:
  /*globalfifo */
static ssize_t globalfifo_read( struct file * filp, char __user * buf, size_t count ,
  loff_t * ppos)
{
  int ret;
  struct globalfifo_dev * dev = filp- > private_data; //

  DECLARE_WAITQUEUE( wait, current) ; //


  down( & dev- > sem) ; // ?

  add_wait_queue( & dev- > r_wait, & wait) ; // ?


  /* FIFO */
  while ( dev- > current_len = = 0)
  {
    if ( filp- > f_flags & O_NONBLOCK) //?

    {
      ret = - EAGAIN; //?

      goto out;
    }
    __set_current_state( TASK_INTERRUPTIBLE) ; //

    up( & dev- > sem) ;

    schedule( ) ; //

    if ( signal_pending( current) ) //?

    //

    {
      ret = - ERESTARTSYS;
      goto out2;
    }

    down( & dev- > sem) ;
  }

  /* */
  if ( count > dev- > current_len)
    count = dev- > current_len;

  if ( copy_to_user( buf, dev- > mem, count ) )
  {
    ret = - EFAULT;
    goto out;
  }
  else
  {
    memcpy ( dev- > mem, dev- > mem + count , dev- > current_len - count ) ; //fifo

    dev- > current_len - = count ; //

    printk( KERN_INFO "read %d bytes(s),current_len:%d/n" , count , dev- > current_len) ;
     
    wake_up_interruptible( & dev- > w_wait) ; //

    
    ret = count ;
  }
  out: up( & dev- > sem) ; //

  out2: remove_wait_queue( & dev- > w_wait, & wait) ; //

  set_current_state( TASK_RUNNING) ;
  return ret;
}

本人はブロック操作についてまだこの実例と結びつけて理解できない.睡眠に入ってからwait_event()眠りが覚めるまでwake_up()でもここにはwait_event()関連関数、なぜですか?また、ここの睡眠、待ち行列は、信号量と何の関係がありますか??本を読んで気絶した!!
=============================================================
回答1:
まず全体的にプログラムの思想を把握しなければならない.信号量はここで2つの関数に関連している.ここであなたが与えた読解関数と、あなたが与えなかった書写関数(globalfifo_write)がある.globalfifo_dev構造は、下位層が駆動されるハードウェア(ここではソフトウェアシミュレーションのリングFIFOを表す場合がある)を表し、この構造は明らかに2つのプロセススリープキューを定義している:r_wait, w_wait、すなわち、データを読むプロセススリープキューと、データを書くプロセスのスリープキューです.読み書きプロセスは同じ領域を操作しているので、この領域は一般的に「臨界領域」と呼ばれています.読み書きプロセスはこの領域に同時にアクセスできません.そうしないと、システム状態の不一致を引き起こす可能性があります.この点は理解しやすいと思います.dev->sem信号量の目的は、この「臨界領域」を作成することです.読み書きプロセスが共有領域に入って操作する場合(読み書き)にかかわらず、down(&dev->sem);//睡眠に入る動作は含まれていますか?質問したように、このdown関数は現在のプロセスをスリープ状態にすることができ、この共有領域を操作している書き込みプロセスがある場合、読み取りプロセス(down呼び出しの下で)が保留されます(つまり、スリープに入ります).
このプロセスの起動は、globalfifoが与えたように、書き込みプロセスが担当します.read関数の後の文:wake_up_interruptible(&dev->w_wait); 対応するglobalfifoを表示するとwrite関数ではglobalfifo_write関数には、wake_という文があります.up_interruptible(&dev->r_wait); (個人的にはup(&dev->sem)がプロセスを呼び覚ますべきだ、wake_upはsleepを起こすはずだonによるプロセスの睡眠、レンガの撮影を歓迎します)つまり、1つの書き込みプロセスがデータの書き込みを完了した後、読み取りを待つ可能性があるプロセスを起動します.globalfifo_read関数は、次の文から実行されます.add_wait_queue(&dev->r_wait, &wait);//なぜ信号量を取得した後にリード待機キューヘッダに入るのですか?質問したように、なぜ現在のプロセスをr_に早期に一時停止するのか.waitキューは、後でプロセスをスリープ(データ読み取り可能でない場合)に配置することを明確に符号化する必要があるため、down関数を呼び出すときにカーネルによって現在のプロセスをスリープ状態に置くのとほぼ一致し、downを呼び出すときにカーネルによってリソースが使用できないときにプロセスをスリープ状態に置くのとは異なり、ここでは、データの読み取りがない場合、ユーザがNON_を設定していないため、データの有無に応じてプロセスをスリープ状態にする.BLOCKフラグビットは、CPUを占有し続けることはできません.CPUを譲る必要があります.それによって、書き込みプロセスが共有領域にデータを書く可能性があります.そのため、アクティブにプロセスを睡眠させるときに、up関数を呼び出して信号量を解放しなければなりません.それによって、書き込みプロセスが「臨界領域」に入ってデータを書くことができます.一方、次のコードを実行して、プロセスをスリープに明確にします.set_current_state(TASK_INTERRUPTIBLE);//プロセス状態を中断可能な睡眠up(&dev->sem)に変更します.      schedule();//他のプロセスをスケジューリングschedule関数他のプロセスの実行をスケジューリングします.現在のプロセスがTASK_に設定されていることに注意してください.INTERUPTIBLE状態では、睡眠を中断することができ、この状態のプロセスは、起動(すなわちTASK_RUNNING状態に入る)までプロセススケジューリング資格から除外され、先に提案されたように、この起動はglobalfifo_によって実行されるwrite関数(または割り込み関数)のプロセスが起動します.globalfifoを除いてwrite関数の起動に加えて、もう1つの起動可能性があります.すなわち、プロセスは外部割り込みを受信します.以下のコードは、ユーザーが待ちきれない場合、Ctrl+Cを使用して割り込みリードプロセス操作を行う場合、Ctrl+Cのキャプチャはここで完了します.if(signal_pending(current)//他の割り込みによって起動されます.例えば、Ctrl+C、{ret=-ERESTARTSYS;goto out 2;»================================================================================
ブロガー補足資料:
signal_pending(current)——』は、現在のプロセスに信号処理があるかどうかをチェックし、0でないことを返して信号があることを示します.-ERESTARTSYSは、信号関数の処理が完了した後、信号関数を再実行する前のシステム呼び出しを示します.すなわち、信号関数の前にシステム呼び出しが発生すると、ユーザの信号関数をスケジューリングする前に、カーネルはシステム呼び出しの戻り値をチェックし、この信号によってシステム呼び出しが中断するかどうかを確認する.戻り値−ERESTARTSYSであり、現在スケジューリングされている信号が−ERESTARTSYS属性を備えている場合、ユーザ信号関数が戻った後にシステム呼び出しが実行される.
===================================================================================================wait_queue(&dev->w_wait, &wait);//原文に誤りがあるwait_queue(&dev->r_wait, &wait);//これこそ正しい書き方関数が終了する前に、現在のプロセスをr_からwaitキューから削除されます.これは、前の文のように対称になります.add_wait_queue(&dev->r_wait, &wait); その他の難解な点は、上記のコードには2つのプロセス睡眠点があります.1>「臨界領域」に入って信号量を取得する場合、睡眠(すなわちdown関数を呼び出す場合)に入る可能性があります.ここでの睡眠動作はカーネルが担当します(すなわちdown関数の最下位で実現されます).2>読み取り可能なデータがない場合、自分でエンコードして読み取りプロセスの睡眠を実現する.次のコードは、多くのドライバで典型的です.DECLARE_WAITQUEUE( wait, current) ;
  down( & dev- > sem) ;
  add_wait_queue( & dev- > r_wait, & wait) ;


  while ( condition not meet)
  {
   
      __set_current_state( TASK_INTERRUPTIBLE) ; //

    up( & dev- > sem) ;

      schedule( ) ; //

    if ( signal_pending( current) ) {
      ret = - ERESTARTSYS;
      goto out2;
     }

     down( & dev- > sem) ;
  }

=================================================
LZ再問:
もともとここには2つの睡眠がありました.1つは、競合状態が発生したときに休眠することです.もう1つは、FIFOが空の場合はスリープします.すみません、最後にいくつか質問があります.どこから目が覚めますか.down関数の睡眠は、目が覚めると、プロセスはdown関数から続けられますか?  __set_current_state(TASK_INTERUPTIBLE)睡眠目が覚めた後、プロセスはどこから始まりますか?2 .while (dev->current_len == 0)   {     if (filp->f_flags &O_NONBLOCK)          {       ret =  - EAGAIN;                            goto out;     }     __set_current_state(TASK_INTERRUPTIBLE);     up(&dev->sem);     schedule();     if (signal_pending(current))                   {       ret =  - ERESTARTSYS;       goto out2;     }     down(&dev->sem);なぜここにdown関数があるのですか?その役割...  } 3. else{memcpy(dev->mem,dev->mem+count,dev->current_len-count);//fifoデータ前シフトdev->current_len-=count;//有効データ長減少printk(KERN_INFO"read%d bytes(s),current_len:%d/n",count,dev->current_len);wake_up_interruptible(&dev->w_wait);//書き込み待ちキューret=count;なぜup関数がないのですか?前にdown関数があるでしょう.
=================================================
返信:
1.プロセスが起動してスケジュールが実行されると、down文の次の文から実行されます.すなわち、コードの次の文から実行が開始されます.add_wait_queue(&dev->r_wait, &wait); また、次のコードの理解に誤りがあることに注意してください.set_current_state(TASK_INTERRUPTIBLE);//プロセスのステータスをスリープに変更この文は、プロセスのステータスを変更しただけで、プロセスをスリープにしていません.この場合、プロセスはCPUを使用して実行されます.shedule関数が呼び出されるまで、プロセスはスリープ待機状態に明確に設定されます.スリープの目的は、書き込みプロセスがデータを書き込むのを待つためです.schedule関数をスケジュールする前に信号量を解放する必要があります.「臨界領域」に対する「占領」を放棄することがup関数の役割である.    up(&dev->sem);       schedule();//スケジューリング他のプロセス実行schedule関数を呼び出してCPUを終了した後、次回起動後に実行に入ると、schedule文の次の文、すなわちif(signal_pending)文から開始します.2.dev->current_に気づくlenも共有変数であり、読み書きプロセスが同時に変数にアクセスする可能性があるため、変数値をチェックする前に「臨界領域」に入ります.多くのコードではdown文をここにwhileループの外に置き、99%の場合プログラムに異常は発生しませんが、不規則で正しくない符号化方式です.前の投稿では、次のようなコードの用途についてお話ししました.次のコードが成立しない場合は、このリードプロセスの起動は書き込みプロセスに対応する功労であるべきです.この場合、また読み取り可能なデータがある可能性が高いことを示しています.では、データを読み取るために「臨界領域」に入る必要があります.プログラムは、保険のためにdev->current_lenは、複数のリードプロセスが同時に待機し、同時に起動される極端な状況を回避するためにチェックされる.dev->current_lenはまた共有変数であるため,ここでdown文呼び出しがあり,ここでの呼び出しは非常に重要であり,極めて正確な符号化方式であると言える.if (signal_pending(current))                   {       ret =  - ERESTARTSYS;       goto out2;     }