十一.Linux系统中的IO模型
1.分类
(1)同步阻塞式IO
应用层调用read/write时,内核同步响应,如果内核 读/写 条件不满足,应用程序将被阻塞直到 读/写 条件满足,条件满足时结束阻塞,阻塞过程中睡眠,让出CPU。
(2)同步非阻塞时IO
应用层调用read/write时,内核同步响应,不管读写条件是否满足,都立即返回。
(3)IO多路复用
阻塞的不是读/写操作本身,而是阻塞描述符的监控,当描述符的状态变为可读/写之后,解除描述符监控的阻塞状态,应用层再进行读写操作。
(4)异步通知
对描述符添加异步通知的标志,当内核读/写条件满足时,内核通过信号/回调函数通知应用程序。
2.同步IO的实现
(1)在应用程序中如何选择阻塞式/非阻塞式
默认使用阻塞式IO,如果要选择非阻塞式,必须在open时加上非阻塞标志 ------ O_NONBLOCK
int fd = open(文件路径,O_RDWR);//阻塞式
int fd = open(文件路径,O_RDWR|O_NONBLOCK);//非阻塞式
(2)在内核中如何实现阻塞式/非阻塞式
内核中通过判断标志(struct file结构),内核中的非竞态阻塞使用等待队列来实现。
3.IO多路复用
内核IO多路复用的实现对应用户空间的 select poll epoll,其对应的内核接口时poll函数,也就是实现file_operations中的poll函数。
(1)内核中poll函数的实现
需要包含的头文件:
#include <linux/poll.h>
poll函数:
unsigned int cdd_poll(struct file *filp, struct poll_table_struct *wait)
{
unsigned int mask;
//声明造成阻塞的等待队列
poll_wait(filp,等待队列,wait);
//poll_wait(filp, &wqh, wait);
//poll_wait(filp, &rqh, wait);
down(&sema);
//根据当前的情形决定哪些操作可用
if(len>0){//可读
mask |= POLLIN|POLLRDNORM;
}
if(len<128){//可写
mask |= POLLOUT|POLLWRNORM;
}
//...........
up(&sema);
return mask;
}
(2)poll和epoll的用法
poll:
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
//前两个参数是传入struct pollfd数组,第三个参数是超时时间(传入负数表示不超时)
struct pollfd {
int fd; /*要监控的描述符*/
short events; /*要监控的事件*/
short revents; /*返回事件*/
};
epoll:
#include <sys/epoll.h>
epoll_create =======> epoll_ctl =======> epoll_wait =======>IO操作
创建专用描述符 添加要监控的事件 阻塞等待事件发生
struct epoll_event {
uint32_t events; /*要监控的事件*/
epoll_data_t data; /*额外数据*/
};
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
select,poll,epoll机制的区别