Linux駆動プログラミングstep-by-step(8)


ブロック型文字デバイスドライバでは、read writeなどの操作をどのように実現するかについて説明しましたが、デバイスバッファがいっぱいで、ユーザーがこの時点でデバイスに書き込みたいと思っている場合、この要求はすぐに実行できません.どうすればいいのでしょうか.
1つ目は、ドライバが要求に失敗した情報をユーザーに返したい場合です.
第2のケースは、呼び出しプロセスをブロックさせてデバイスが動作できるようにすることである.
ユーザは要求が満たされないときにどのように操作するかを自分で選択したいので,ユーザ空間にO_がある.NONBLOCKフラグ
デバイスを開くときにユーザがこのフラグ(filp->f_flagsに保存される)を指定すると、ユーザが非ブロックでデバイスを開きたいことを示す、読み書き時にデバイスが要求を満たすことができなければエラーコード(-EAGAAIN)を返す.
ユーザがこのフラグビットを指定しない場合、デフォルトではブロック型が開き、デバイスが要求を満たすことができない場合、デバイスが動作できるまでブロックする必要があります.
スリープの紹介プロセスがブロックされているということは、CPUを解放することを意味し、別のプロセスがある状態を修正し、スケジューラが再び彼をスケジューリングするまで、プロセスがスリープした後、自分がスリープ中に何をしたのか分からないので、目が覚めた後に現在の状態をチェックし、待つ条件が本当かどうかを確認する必要があります.
linuxでの待機キューは、待機キューヘッダによって管理されます(wait_queue_head_t)
キューヘッダの初期化
init_waitqueue_head(wait_queue_head_t * queue);

キューヘッダを初期化する簡単な方法もあります(静的初期化)
DECLARE_WAIT_QUEUE_HEAD(name)
名前はstruct waitとして定義されます.queue_head_t構造、初期化
プロセスをスリープさせ、プロセスをスリープさせます.目が覚めると、プロセスは上記のように条件が真実かどうかを検出する必要があります.
#define wait_event(wq, condition) 
#define wait_event_timeout(wq, condition, timeout)
#define wait_event_interruptible(wq, condition)
#define wait_event_interruptible_timeout(wq, condition, timeout)
wq:対応する待機キューヘッダを指す
conditionは検出の条件を表し,conditionが真である場合には睡眠が終了する.
interruptibleを含む関数は、睡眠が信号で中断され得ることを示し、睡眠が信号で中断された場合、関数は負の値を返す(-E RESTARTSYS)
timeoutを含む関数timeoutパラメータは最も長い待ち時間を表し、
プロセスの起動
眠りがあれば目覚める
他のプロセスでデバイスが操作されて待機プロセスの待機条件(condition)を真にすると、このプロセスでスリープのプロセスを呼び覚ます必要があります.
#define wake_up(x)			__wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_all(x)			__wake_up(x, TASK_NORMAL, 0, NULL) 
#define wake_up_interruptible(x)	__wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
#define wake_up_interruptible_nr(x, nr)	__wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
#define wake_up_interruptible_all(x)	__wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
wake_upはまずキューの1つを起動します
待機中のプロセスを独占し(下に簡単な説明があります)、そうでなければ他のすべてのプロセスを呼び覚ます
wake_up_独占または非独占にかかわらず、待機キュー上のすべてのプロセスを起動します.
wake_up_interruptibleとwake_upは似ていますが、キュー内の中断可能な睡眠プロセスを呼び覚ますだけです.
wake_up_interruptible_allは待機キューのすべての中断可能な睡眠を呼び覚まします.
wake_up_interruptible_nrコールバック待ちキューのnr個の独占プロセス独占待ちプロセス:1つの睡眠キューでは、睡眠のプロセスが多くなる可能性があるため、コールバックするたびに1つのプロセスのみが実行を開始し、他のプロセスは待ちキューで睡眠を継続し、これはいくつか=重要なプロセスが遅々として呼び出されず、この状況を改善するために、独占プロセスの概念があり、睡眠中にパラメータWQを指定することができます.FLAG_EXCLUSEVEフラグはキューの末尾に追加され(このフラグを追加しないとデフォルトでキューヘッダに追加されます)、wake_が呼び出されます.up_xxに対応する関数は、まずこれらのプロセスを起動します.
while(dev->datawr == dev->datard)
{
      
      while(dev->datard == dev->datawr) //               
      {
	      up(&dev->semp)
	      if(filp->f_flags & O_NONBLOCK) //          
	      {
	            return -EAGAIN;
	      }
	      D("[%s] reading going to sleep!", current->comm);
	
	      if(wait_event_interruptible(dev->rdque, dev->datard != dev->datawr)) //             
	      {
	            return -ERESTARTSYS;
	      }
	      D("waked up %d
", __LINE__); ... if(down_interruptible(&dev->semp) < 0)// { printk(KERN_ERR "[%s]get the mutex lock error %d, %s", current->comm, __LINE__, __func__); return -ERESTARTSYS; } else { D("have get the mutex %d
", __LINE__); } } /*read data*/ wake_up_interruptible(dev->wrque)// }

poll関数実装
unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait);
システムプログラミングでは、pollまたはselect関数だけでファイル記述子が書き込み可能か読み取り可能かなどを監視することがよくあります.
これらのシステム呼び出しはpoll関数駆動によって実現される
この関数を呼び出すとカーネルにpollが割り当てられます.table_struct構造、私たちが多く必要とする動作は2つのステップがあります.
1、待機キューでpollを呼び出す_wait
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
、記述デバイスが読み書き可能なマスクを返す
#define POLLIN		0x0001 //    
#define POLLPRI		0x0002 //           
#define POLLOUT		0x0004 //     
#define POLLERR		0x0008 //      
#define POLLHUP		0x0010 //    
#define POLLRDNORM      0x0040 //normal data is ready now
#define POLLWRNORM	0x0100 //normal data can be writen to device
example:
static int simple_poll(struct file *filp, struct poll_table_struct *wait)
{
	struct simple_dev *dev = filp->private_data;
	unsigned int mask = 0;
	char *next_ptr;

	if(down_interruptible(&dev->semp))
	{
		printk(KERN_ERR "get the semphore err %d 
",__LINE__); return -ERESTARTSYS; } D("have get the semphore %d
", __LINE__); poll_wait(filp, &dev->inq, wait); poll_wait(filp, &dev->outq, wait); if(dev->datard != dev->datawr) { mask |= POLLIN | POLLRDNORM; //can be read } if(dev->datawr+1 == dev->dataend) next_ptr = dev->data; else next_ptr = dev->datawr+1; if(next_ptr != dev->datard) { mask |= POLLOUT | POLLWRNORM; //can be write } up(&dev->semp); return mask; }

次のセクションでは、メモリベースのアナログ文字デバイスドライバをドライバシミュレーションパイプで終了し、ドライバテストに関する説明を行います.