Linux 드라이버를 작성하는 과정에서 장치 차단/비차단 읽기 및 쓰기는 매우 중요한 기술입니다. 효율적인 데이터 전송 및 이벤트 처리를 달성하여 시스템 성능과 응답 속도를 향상시킬 수 있습니다. 이번 글에서는 리눅스 드라이버 기술(5)_장치 차단/비차단 읽기 및 쓰기 구현 원리와 관련 기술에 대해 알아보겠습니다.
대기 대기열은 프로세스 예약을 위한 커널의 매우 중요한 데이터 구조입니다. 연결 목록의 각 노드는 PCB(프로세스 제어 블록)입니다. 대기 대기열의 모든 프로세스는 특정 깨우기 조건이 발생할 때까지 잠자기 상태로 예약됩니다. Linux I/O 다중화 기사에서 애플리케이션 계층에서 차단 IO 및 비차단 IO 사용에 대해 이미 논의했습니다. 이 기사에서는 드라이버에서 장치 IO의 차단 및 비차단 읽기 및 쓰기를 구현하는 방법을 주로 설명합니다. 분명히 이 차단 관련 메커니즘을 구현하려면 대기 대기열 메커니즘이 필요합니다. 이 글의 커널 소스 코드는 버전 3.14.0을 사용합니다.
인터페이스는 읽기 및 쓰기 장치 프로세스의 프로세스(커널) 공간에도 나타납니다. 조건이 충족되지 않으면 인터페이스 기능으로 인해 프로세스가 절전 상태로 전환됩니다. 장치를 읽고 쓰는 사용자 프로세스가 절전 모드에 들어가더라도 흔히 차단된다고 말하는 것입니다. 한마디로, 장치 파일 읽기 및 쓰기 차단의 본질은 드라이버가 장치 파일 차단을 드라이버에서 구현한다는 것입니다. 읽기 및 쓰기 프로세스는 다음과 같이 요약할 수 있습니다.
소스 코드를 추적하고 위 줄의 기능을 확인할 수 있습니다.
으아악
그런 다음 초기화 매크로를 살펴보겠습니다.“
wait_queue_head_t –36–>이 대기열에서 사용하는 스핀 잠금
–27–>전체 대기열을 함께 "연결"하는 링크
”
으아악
“
DECLARE_WAIT_QUEUE_HEAD() –60–>수신 문자열 이름을 기반으로 name이라는 대기 대기열 헤드를 생성합니다
–57–>위 task_list 필드를 초기화하기 위해 커널 표준 초기화 매크로가 사용되지 않습니다. . .
”
버전은 절전 모드가 중단될 수 있음을 의미하고, _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,我们只需要在驱动中注册的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); }
刚才我们讨论了如何使用等待队列实现阻塞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 중국어 웹사이트의 기타 관련 기사를 참조하세요!