您的位置:首页 > 运维架构 > Linux

APUE,TLPI,UNP读书笔记——linux IO模型以及IO复用

2016-05-11 10:51 561 查看

异步,同步,阻塞,非阻塞的区别:

参考:知乎

阻塞指线程被挂起,关注程序调用结果返回的状态

同步和异步指的是调用后是否立即返回,是否得到结果,关注的是消息机制

linux上的五种IO模型:

1.阻塞式IO



程序一直阻塞在读写函数的调用上,一直等待内核数据复制完成。阻塞同步

2.非阻塞式IO



进程反复调用recvform轮询,直到返回成功指示。非阻塞同步

3.IO复用



进程阻塞在select调用,但IO可读写时,调用读写处理。

4.信号驱动IO



调用前准备信号处理,在IO事件发生时递交信号,交由信号处理。

5.异步IO



异步IO是由内核通知我们IO何时完成。

IO复用:

select:select

#include <sys/select.h>

int pselect(int nfds, fd_set *restrict readfds,
fd_set *restrict writefds, fd_set *restrict errorfds,
const struct timespec *restrict timeout,
const sigset_t *restrict sigmask);
int select(int nfds, fd_set *restrict readfds,
fd_set *restrict writefds, fd_set *restrict errorfds,
struct timeval *restrict timeout);

void FD_CLR(int fd, fd_set *fdset);
int FD_ISSET(int fd, fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);


poll:poll

#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);


#include <stropts.h>
#include <poll.h>
...
struct pollfd fds[2];
int timeout_msecs = 500;
int ret;
int i;

/* Open STREAMS device. */
fds[0].fd = open("/dev/dev0", ...);
fds[1].fd = open("/dev/dev1", ...);
fds[0].events = POLLOUT | POLLWRBAND;
fds[1].events = POLLOUT | POLLWRBAND;

ret = poll(fds, 2, timeout_msecs);

if (ret > 0) {
/* An event on one of the fds has occurred. */
for (i=0; i<2; i++) {
if (fds[i].revents & POLLWRBAND) {
/* Priority data may be written on device number i. */
...
}
if (fds[i].revents & POLLOUT) {
/* Data may be written on device number i. */
...
}
if (fds[i].revents & POLLHUP) {
/* A hangup has occurred on device number i. */
...
}
}
}

对于select,poll何时能够让文件描述符变为就绪。SUSv3规定:如果对IO函数调用不会阻塞,无论是否能够传输数据,就指定为就绪。

select,poll的比较:

1.select的fd_set有个1024的默认上限。

2.循环中重复调用时,select重复初始化fd_set;poll通过独立的evenets(针对输入)和revenets(针对输出)处理,避免重复调用。

3.select提供超时精度微秒,而poll为毫秒

4.当一个被检查的文件描述符关闭时,poll的revents会设置POLLNVAL标记,会准确告知那个文件关闭;select只会返回1,设置错误码为EBADF,只有自己通过在描述符上执行IO检查错误码才能判断。

select和poll存在的问题:

1.检查所有被指定的文件描述符

2.传递表示被检查的文件描述符的数据结构给内核,会随文件描述符增大而增大,占用拷贝时间等等

3.而这调用后必须检查每个元素查明文件描述符的就绪态。

epoll:epoll

epoll支持ET和LT模式,select,poll只支持LT,信号驱动IO支持ET;并且在大量描述符检查时性能比select和poll更优

#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;

/* Code to set up listening socket, 'listen_sock',
(socket(), bind(), listen()) omitted */

epollfd = epoll_create1(0);//epoll新创建方式
if (epollfd == -1) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}//防御式编程,每次调用系统函数用这种形式判断调用是否成功。

ev.events = EPOLLIN;//检测的事件
ev.data.fd = listen_sock;//检测的fd
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}

for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);//最大返回max_evenets个就绪的fd
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}

for (n = 0; n < nfds; ++n) {
if (events
.data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &addr, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events
.data.fd);
}
}
}

这里有一篇:epoll的LT和ET模式的参考ET和LT
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux