Rumah  >  Artikel  >  Tutorial sistem  >  Penjelasan terperinci tentang teknologi pemacu Linux (5)_Membaca dan menulis tidak menyekat/tidak menyekat peranti

Penjelasan terperinci tentang teknologi pemacu Linux (5)_Membaca dan menulis tidak menyekat/tidak menyekat peranti

WBOY
WBOYke hadapan
2024-02-15 16:00:23422semak imbas

Dalam proses menulis pemacu Linux, peranti menyekat/tidak menyekat membaca dan menulis adalah teknologi yang sangat penting. Ia boleh mencapai penghantaran data dan pemprosesan acara yang cekap, meningkatkan prestasi sistem dan kelajuan tindak balas. Dalam artikel ini, kami akan menyelidiki teknologi pemacu Linux (5)_Prinsip pelaksanaan dan teknologi berkaitan membaca dan menulis menyekat/tidak menyekat peranti.

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

Waiting queue ialah struktur data yang sangat penting dalam kernel untuk penjadualan proses barisan menunggu Semua proses masuk dijadualkan tidur sehingga keadaan bangun tertentu berlaku. Saya telah membincangkan penggunaan menyekat IO dan tidak menyekat IO pada lapisan aplikasi dalam artikel Linux I/O Multiplexing Artikel ini terutamanya membincangkan cara melaksanakan menyekat dan tidak menyekat membaca dan menulis peranti IO dalam pemacu. Jelas sekali, mekanisme giliran menunggu diperlukan untuk melaksanakan mekanisme berkaitan penyekatan ini. Kod sumber kernel artikel ini menggunakan versi 3.14.0

Pelaksanaan peranti menyekat IO

Apabila kita membaca dan menulis IO fail peranti, antara muka yang sepadan dalam pemacu akhirnya akan dipanggil semula, dan

antara muka ini juga akan muncul dalam ruang proses (kernel) proses peranti membaca dan menulis Jika syarat tidak dipenuhi, fungsi antara muka akan menyebabkan proses Memasuki keadaan tidur, walaupun proses pengguna membaca dan menulis peranti memasuki tidur, ia adalah apa yang sering kita katakan disekat. Secara ringkasnya, intipati penyekatan fail peranti membaca dan menulis ialah pemacu melaksanakan penyekatan fail peranti dalam pemacu Proses membaca dan menulis boleh diringkaskan seperti berikut:

1. Definisi - mulakan kepala barisan menunggu
//定义等待队列头
wait_queue_head_t waitq_h;
//初始化,等待队列头
init_waitqueue_head(wait_queue_head_t *q);
 //或
//定义并初始化等待队列头
DECLARE_WAIT_QUEUE_HEAD(waitq_name);

Antara pilihan di atas, yang terakhir akan mentakrifkan dan memulakan pengepala menunggu secara langsung Namun, jika anda menggunakan pembolehubah global untuk menghantar parameter dalam modul, ia adalah menyusahkan yang mana satu untuk digunakan bergantung pada keperluan anda.

Kita boleh mengesan kod sumber dan melihat apa yang dilakukan oleh baris di atas:

//include/linux/wait.h 
 35 struct __wait_queue_head { 
 36         spinlock_t              lock;
 37         struct list_head        task_list;
 38 };
 39 typedef struct __wait_queue_head wait_queue_head_t;

tunggu_queue_head_t –36–>Kunci putaran yang digunakan oleh baris gilir ini
–27–>Pautan yang "mengikat" keseluruhan baris gilir bersama

Kemudian mari kita lihat makro pemula:

 55 #define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                           \
 56         .lock           = __SPIN_LOCK_UNLOCKED(name.lock),              \
 57         .task_list      = { &(name).task_list, &(name).task_list } }
 58 
 59 #define DECLARE_WAIT_QUEUE_HEAD(name) \
 60         wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

ISYTIHKAN_TUNGGU_KEPALA_BARIAN() –60–>Buat nama kepala baris gilir menunggu berdasarkan nama rentetan masuk
–57–>Untuk memulakan medan senarai_tugas di atas, makro pemula standard kernel tidak digunakan. . .

2. Tambahkan proses ini pada barisan menunggu

Tambah acara pada baris gilir menunggu, iaitu, proses memasuki keadaan tidur dan tidak kembali sehingga keadaan benar. Versi **_interruptible

bermakna tidur boleh terganggu, dan versi _timeout** bermaksud versi tamat masa, yang akan dikembalikan selepas tamat masa ini boleh dilihat di mana-mana dalam API kernel.

void wait_event(wait_queue_head_t *waitq_h,int condition);
void wait_event_interruptible(wait_queue_head_t *waitq_h,int condition);
void wait_event_timeout(wait_queue_head_t *waitq_h,int condition);
void wait_event_interruptible_timeout(wait_queue_head_t *waitq_h,int condition);

Ini adalah teras barisan menunggu, mari kita lihat

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驱动技术(五) _设备阻塞/非阻塞读写的实现原理和相关技术。

Atas ialah kandungan terperinci Penjelasan terperinci tentang teknologi pemacu Linux (5)_Membaca dan menulis tidak menyekat/tidak menyekat peranti. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:lxlinux.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam