ホームページ  >  記事  >  システムチュートリアル  >  Linuxドライバ技術の詳細解説(5)_デバイスブロッキング/ノンブロッキング読み書き

Linuxドライバ技術の詳細解説(5)_デバイスブロッキング/ノンブロッキング読み書き

WBOY
WBOY転載
2024-02-15 16:00:23387ブラウズ

Linux ドライバーを作成するプロセスでは、デバイスのブロッキング/ノンブロッキング読み取りおよび書き込みは非常に重要なテクノロジです。効率的なデータ転送とイベント処理を実現し、システムのパフォーマンスと応答速度を向上させます。今回は、Linuxドライバ技術(5)_デバイスブロッキング/ノンブロッキング読み書きの実装原理と関連技術について掘り下げていきます。

详解Linux驱动技术(五) _设备阻塞/非阻塞读写

待機キューは、プロセス スケジューリングのためのカーネル内の非常に重要なデータ構造です。そのタスクはリンク リストを維持することです。リンク リストの各ノードは PCB (プロセス コントロール ブロック) です。 カーネルは、PCB の待機キュー内のすべてのプロセスを、特定のウェイクアップ条件が発生するまでスリープするようにスケジュールします。 Linux I/O 多重化の記事でアプリケーション層でのブロッキング IO とノンブロッキング IO の使用についてはすでに説明しましたが、この記事では主にドライバーでデバイス IO のブロッキングおよびノンブロッキングの読み取りと書き込みを実装する方法について説明します。明らかに、このブロッキング関連メカニズムを実装するには、待機キュー メカニズムが必要です。この記事のカーネル ソース コードはバージョン 3.14.0 を使用しています。

デバイスブロッキングIOの実装

デバイス ファイルの IO の読み取りおよび書き込みを行うと、ドライバー内の対応するインターフェイスが最終的にコールバックされ、これらの

インターフェイスは読み取りおよび書き込みデバイス プロセスのプロセス (カーネル) 空間にも表示されます。 、条件が満たされていない場合、デバイスの読み取りおよび書き込みのユーザー プロセスがスリープ状態に入った場合でも、インターフェース関数はプロセスをスリープ状態にします。これはよくブロッキングと呼ばれるものです。一言で言えば、デバイス ファイルの読み取りおよび書き込みのブロックの本質は、ドライバーがドライバー内でデバイス ファイルのブロックを実装することです。読み取りおよび書き込みのプロセスは次のように要約できます。

1. 定義 - 待機キューのヘッドを初期化します

リーリー 上記選択肢のうち、最後の選択肢は待機中のヘッドを直接定義して初期化するものですが、モジュール内でパラメータを渡すのにグローバル変数を使うと不便なので、どちらを使うかは要件によって異なります。

ソース コードをトレースして、上記の行が何を行うかを確認できます。 リーリー


#「

wait_queue_head_t

–36–>このキューで使用されるスピン ロック

–27–>キュー全体を「繋ぐ」リンク

#次に、初期化マクロを見てみましょう: リーリー

#「

DECLARE_WAIT_QUEUE_HEAD()

–60–>受信文字列名に基づいて name という名前の待機キュー ヘッドを作成します –57–>上記の task_list フィールドの初期化には、カーネル標準の初期化マクロは使用されません。 。 。



#2. このプロセスを待機キューに追加します

イベントを待機キューに追加します。つまり、プロセスはスリープ状態に入り、条件が true になるまで戻りません。 **_interruptible

のバージョンはスリープを中断できることを示し、
_timeout** のバージョンはタイムアウト後に返されるタイムアウト バージョンを示します。この命名規則はカーネル API のあらゆる場所で見られます。

リーリー これが待機列の核心です。見てみましょう

wait_event
└── wait_event
└──
_wait_event
├── abort_exclusive_wait
├── finish_wait
├── prepare_to_wait_event
└── ___wait_is_interruptible

244 #define wait_event(wq, condition)                                       \
245 do {                                                                    \
246         if (condition)                                                  \
247                 break;                                                  \
248         __wait_event(wq, condition);                                    \ 
249 } while (0)

wait_event
–246–>如果condition为真,立即返回
–248–>否则调用__wait_event

194 #define ___wait_event(wq, condition, state, exclusive, ret, cmd)        \       
195 ({                                                                      \
206         for (;;) {                                                      \
207                 long __int = prepare_to_wait_event(&wq, &__wait, state);\
208                                                                         \  
209                 if (condition)                                          \       
210                         break;                                          \
212                 if (___wait_is_interruptible(state) && __int) {         \
213                         __ret = __int;                                  \
214                         if (exclusive) {                                \
215                                 abort_exclusive_wait(&wq, &__wait,      \
216                                                      state, NULL);      \
217                                 goto __out;                             \
218                         }                                               \
219                         break;                                          \
220                 }                                                       \
222                 cmd;                                                    \
223         }                                                               \
224         finish_wait(&wq, &__wait);                                      \
225 __out:  __ret;                                                          \
226 })

___wait_event
–206–>死循环的轮询
–209–>如果条件为真,跳出循环,执行finish_wait();进程被唤醒
–212–>如果进程睡眠的方式是interruptible的,那么当中断来的时候也会abort_exclusive_wait被唤醒
–222–>如果上面两条都不满足,就会回调传入的schedule(),即继续睡眠

模板

struct wait_queue_head_t xj_waitq_h;
static ssize_t demo_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    if(!condition)    //条件可以在中断处理函数中置位
        wait_event_interruptible(&xj_waitq_h,condition);
}
static file_operations fops = {
    .read = demo_read,
};
static __init demo_init(void)
{
    init_waitqueue_head(&xj_waitq_h);
}

IO多路复用的实现

对于普通的非阻塞IO,我们只需要在驱动中注册的read/write接口时不使用阻塞机制即可,这里我要讨论的是IO多路复用,即当驱动中的read/write并没有实现阻塞机制的时候,我们如何利用内核机制来在驱动中实现对IO多路复用的支持。下面这个就是我们要用的API

int poll(struct file *filep, poll_table *wait);
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)  

当应用层调用select/poll/epoll机制的时候,内核其实会遍历回调相关文件的驱动中的poll接口,通过每一个驱动的poll接口的返回值,来判断该文件IO是否有相应的事件发生,我们知道,这三种IO多路复用的机制的核心区别在于内核中管理监视文件的方式,分别是数组链表,但对于每一个驱动,回调的接口都是poll。

模板

struct wait_queue_head_t waitq_h;
static unsigned int demo_poll(struct file *filp, struct poll_table_struct *pts)
{
    unsigned int mask = 0;
    poll_wait(filp, &wwaitq_h, pts);
    if(counter){
        mask = (POLLIN | POLLRDNORM);
    }
    return mask;
}

static struct file_operations fops = {
    .owner  = THIS_MODULE,
    .poll   = demo_poll,
};
static __init demo_init(void)
{
    init_waitqueue_head(&xj_waitq_h);
}

其他API

刚才我们讨论了如何使用等待队列实现阻塞IO,非阻塞IO,其实关于等待队列,内核还提供了很多其他API用以完成相关的操作,这里我们来认识一下

//在等待队列上睡眠
sleep_on(wait_queue_head_t *wqueue_h);
sleep_on_interruptible(wait_queue_head_t *wqueue_h);

//唤醒等待的进程
void wake_up(wait_queue_t *wqueue);
void wake_up_interruptible(wait_queue_t *wqueue);

总之,设备阻塞/非阻塞读写是Linux驱动程序编写过程中不可或缺的一部分。它可以实现高效的数据传输和事件处理,提高系统的性能和响应速度。希望本文能够帮助读者更好地理解Linux驱动技术(五) _设备阻塞/非阻塞读写的实现原理和相关技术。

以上がLinuxドライバ技術の詳細解説(5)_デバイスブロッキング/ノンブロッキング読み書きの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlxlinux.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。