>시스템 튜토리얼 >리눅스 >Linux 드라이버 기술 상세 설명(5)_장치 차단/비차단 읽기 및 쓰기

Linux 드라이버 기술 상세 설명(5)_장치 차단/비차단 읽기 및 쓰기

WBOY
WBOY앞으로
2024-02-15 16:00:23430검색

Linux 드라이버를 작성하는 과정에서 장치 차단/비차단 읽기 및 쓰기는 매우 중요한 기술입니다. 효율적인 데이터 전송 및 이벤트 처리를 달성하여 시스템 성능과 응답 속도를 향상시킬 수 있습니다. 이번 글에서는 리눅스 드라이버 기술(5)_장치 차단/비차단 읽기 및 쓰기 구현 원리와 관련 기술에 대해 알아보겠습니다.

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

대기 대기열은 프로세스 예약을 위한 커널의 매우 중요한 데이터 구조입니다. 연결 목록의 각 노드는 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 lxlinux.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제