Linux字符驱动-IO模型
1、阻塞
实现方法
**一、手动实现**
- 定义一个等待队列头,并初始化等待队列头
wait_queue_head_t wq;
init_waitqueue_head(&wq);
- 定义一个等待队列项,添加等待队列项到等待队列
DECLARE_WAITQUEUE(r_wait,current); //r_wait表示等待队列项的名称,current表示当前进程
add_wait_queue(&wq,&r_wait);
- 判断应用层是否以阻塞方式打开文件
if(file->f_flags & O_NONBLOCK){
//判断条件成立后表示用户是以非阻塞的方式打开的文件
return -EAGAIN;
}
//判断条件不成立表示用户是以非阻塞的方式打开的文件
- 若应用层以阻塞方式打开文件,切换进程的状态
set_current_state(TASK_INTERRUPTIBLE); //可中断的等待态
- 主动放弃CPU,等待进程被唤醒
schedule(); //主动放弃CPU
- 被唤醒
//可以被信号唤醒,也可以被中断唤醒(被中断唤醒表示数据已经住哪被好了)
//wake_up(x);
wake_up_interruptible(x);
- 唤醒后判断是否是被信号唤醒,如果不是信号被唤醒,表示数据已经准备好,可以进行数据的拷贝
if(signal_pending(current)){
//判断条件成立,表示是被信号唤醒的
}
//判断条件不成立,表示不是被信号唤醒的,可以进行数据的拷贝
- 数据拷贝前将进程状态切换为运行状态
set_current_state(TASK_RUNNING); //切换进程为运行态
- 拷贝数据
copy_to_user();
- 删除等待队列项
remove_wait_queue();
**一、自动实现**
- 定义等待队列头
wait_queue_head_t wq;
- 定义一个标志位,代表阻塞或者被唤醒
int condition = 0;
//condition等于0的时候表示阻塞,condition为真的时候表示被唤醒
- 实现阻塞
//wait_event(wq,condition);
wait_event_interrupt(wq,condition);
2、非阻塞
非阻塞的方式实现比较简单,只需要在应用层打开文件时使用O_NONBLOCK
open("/dev/char",O_RDWR|O_NONBLOCK);
3、IO多路复用
应用层用于监听多个文件描述符的函数有select、poll、epoll,这三个函数都调用驱动层的poll函数;
实现方法
- 定义等待队列头,并初始化
- 提交等待队列头
poll_wait();
- 当条件为真时,置位mask
POLLIN ------表示用户可读
POLLOUT------表示用户可写
4、异步通知
应用层代码实现:
1.添加信号处理函数
sighander_t signal(int signum,sighander_t hander);
2.设置信号发送的进程
fcntl(fd,F_SETOWN,getpid());
3.调用fasync函数
flags = fcntl(fd,F_GETFL);
fcntl(fd,F_SETFL,flags|FASYNC);
内核层代码实现:
1.异步通知结构体的初始化
fasync_helper(fd,filp,on,fapp);
2.发送信号
kill_fasync();