IO模型

来源:互联网 发布:健身数据统计 编辑:程序博客网 时间:2024/05/16 14:08
IO模型
 阻塞
 非阻塞
 多路复用
 信号驱动

阻塞机制
1. 使用等待队列去实现阻塞机制
等待队列
1. 头文件: <linux/wait.h>
   或则 #include <linux/sched.h>

2. 等待队列头的定义
wait_queue_head_t q;
struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
3. 等待队列头的初始化init_waitqueue_head(wait_queue_head_t *q)
参数 q : 等待队列的结构体指针
extern void __init_waitqueue_head(wait_queue_head_t *q, struct lock_class_key *);

#define init_waitqueue_head(q)          \
    do {                                \
    static struct lock_class_key __key; \
                                        \
    __init_waitqueue_head((q), &__key); \
 } while (0)

4. 定义等待队列一个节点
DECLARE_WAITQUEUE(name, tsk)

5. 把节点添加等待队列头中
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
q : 要添加到哪一个等待队列头
wait : 要添加等待队列的节点

6. 把节点从等待队列中移除
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
q : 要从哪一个队列中移除节点
wait : 要移除等待队列的节点

7. 等待事件的发生
wait_event(wq,condition) 
#define wait_event_interruptible(wq, condition) \
wq : 是要去等的等待队列 
condition : a C expression for the event to wait for
如果condition 表达为假 ,wait_event 会进入睡眠 ,等待被唤醒(被wake_up唤醒)

8. 唤醒队列 wake_up(q)
q: 唤醒等待队列 ,等待队列结构体指针
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)


IO多路复用
1. 应用程序调用poll和select函数,驱动会调用poll函数
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 
其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合,
numfds的值是需要检查的号码最高的文件描述符加1。timeout参数是一个指向struct timeval类型的指针。

2. 驱动中实现poll函数
static unsigned int dev_poll(struct file *filp, poll_table *wait)
filp : 指向file的结构体指针
wait : 轮训的表,可以监控集合

3. 把要监控的对象加入到等待队列内 poll_wait
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
poll_wait(filp, &dev.rq, wait); /* 把rq添加到等待队列中,该函数不阻塞*/

4. 驱动中poll函数会查寻数组的状态,是否可读

可读: mask |= POLLIN  | POLLRDNORM; /*标示数据可获得*/
可写: mask |= POLLOUT | POLLWRNORM; /*标示数据可写入*/
返回当前的状态 return mask;驱动中poll函数返回,进而应用程序poll或则select返回


信号驱动IO
1. 应用程序要设置信号捕捉(处理)函数
signal(SIGIO, handler);
2. 应用程序要指定进程为文件的属主
 fcntl(fd, F_SETOWN, getpid());
 这句话的含义是: 把当前进程好的pid获取后赋值给驱动中的驱动中file ->f_owner = pid ,
 这样驱动发送信号是,知道向这个进程发送信号
3. 通过fcntl函数设置让文件可以操作在异步模式下,因为默认文件不能操作在异步模式下
 oflags = fcntl(fd, F_GETFL); //读取文件的属性
 fcntl(fd, F_SETFL, oflags | FASYNC); // 设置文件的属性
在设备文件中添加FASYNC标志,驱动中就会调用hello_fasync函数。

4.在驱动中实现异步机制函数(fasync)
static int hello_fasync(int fd, struct file *filp, int mode)
{
 struct hello_device *dev = filp->private_data;
    /* 设置设备文件支持fasync,即异步通知模式*/
    return fasync_helper(fd, filp, mode, &dev->async_queue);
    // 安装文件fd 和异步队列关联
}

5. 信号机制使用的是异步队列,因此需要创建异步队列
struct fasync_struct *async_queue;

6. 在设备可读时,发送一个可读信号
/* 产生异步读信号 向所属的进程发送一个SIGIO 信号 */
 if (dev->async_queue) // 指针不为空
       kill_fasync(&dev->async_queue, SIGIO, POLL_IN);

一个最重要的区别
1)异步通知是不会造成阻塞的。
2)调用阻塞IO时如果条件不满足,会在驱动函数中的test_read或test_write中阻塞。


0 0
原创粉丝点击