目录
阻塞、非阻塞与同步、异步
阻塞、非阻塞:说的是进程/线程是否被阻塞,进程/线程被阻塞后,不能执行其他任务,会让
出资源给其他进程/线程使用;
同步、异步:说的是调用程序一个方法时,是等这个方法返回之后,再进行下一步;还是直
接往下走;
阻塞IO,非阻塞IO与同步IO、异步IO
阻塞IO,非阻塞IO:说的是应用程序调用IO子系统时,如果没有获得响应,应用程序的线程
是否会被阻塞,不能执行其他任务,让出资源给其他线程使用;
同步IO、异步IO:说的是应用程序调用操作系统中的IO子系统时,IO子系统是等处理完IO相
关操作之后再返回结果给应用程序,还是不等操作处理完,直接返回一个标识给应用程序,告诉应
用程序:已经收到IO请求,你可以先做其他的事情,如果IO操作处理完了,我会发通知给你(发信
号方式),或者在调用时把后续要做的事情告诉IO子系统,IO子系统帮忙调用处理(回调方式)。
五种IO模型
1,阻塞,同步IO
阻塞:应用程序调用IO系统之后,在IO系统处理好数据之前,应用程序的线程是阻塞状态,
让渡资源,不能进行其他任务;
同步:IO系统被调用后,没有立刻返回,而是等数据相关操作处理完成之后,才返回给应用
线程;
2,同步,非阻塞模型IO
非阻塞:应用程序调用IO系统之后,在IO系统处理好数据之前,应用程序的线程是非阻塞状
态,线程可以继续运行,不停的循环访问内核,看数据是否准备完成;
同步:IO系统被调用后,没有立刻返回,而是等数据相关操作处理完成之后,才返回给应用
线程;
3,IO多路复用技术
多路复用技术按分类是阻塞IO,应用线程会阻塞在selector上;
selector会去轮询数据读取状态,所以多路复用技术在java里面叫NIO(非阻塞IO);
selector会替代应用程序,轮询一批注册到selector中的文件描述符(套接字,标准输入输
出),只要有部分文件描述符的数据准备就绪,就通知用户程序。
linux系统的selector由select,poll,epoll方式实现;
select 的缺点:
1,每次调用 select,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大;
2,每次调用 select 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大;
3,select 支持的文件描述符数量,默认是1024;
epoll(Linux 2.6内核正式引入,可被用于代替 select 和 poll 系统调用):
1,内核与用户空间共享一块内存;
2,通过回调解决遍历问题;
3,fd 没有限制,可以支撑10万连接;
多路复用IO在web编程中的应用
示例代码:
...
// 监听socketServer的文件描述符
listen_fd = tcp_server_listen(SERV_PORT);
...
// 把上述文件描述符,添加到文件描述符集合中
struct pollfd event_set[INIT_SIZE];
event_set[0].fd = listen_fd;
event_set[0].events = POLLRDNORM;
...
for (;;) {
//调用poll函数,轮询文件描述符集合,进行事件检测
if ((ready_number = poll(event_set, INIT_SIZE, -1)) < 0) {
error(1, errno, "poll failed ");
}
if (event_set[0].revents & POLLRDNORM) {
socklen_t client_len = sizeof(client_addr);
//socketServer监听,如果有链接事件,创建socket链接描述符
connected_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &client_len);
//把socket链接描述符,添加到描述符集合中;
for (i = 1; i < INIT_SIZE; i++) {
if (event_set[i].fd < 0) {
event_set[i].fd = connected_fd;
event_set[i].events = POLLRDNORM;
break;
}
}
}
// 链接描述符,读写数据
for (i = 1; i < INIT_SIZE; i++) {
if (event_set[i].revents & (POLLRDNORM | POLLERR)) {
if ((n = read(socket_fd, buf, MAXLINE)) > 0) {
if (write(socket_fd, buf, n) < 0) {
error(1, errno, "write error");
}
} else if (n == 0 || errno == ECONNRESET) {
close(socket_fd); event_set[i].fd = -1;
} else {
error(1, errno, "read error");
}
}
}
}
4,信号驱动IO
5,异步,非阻塞IO
应用程序,发出IO调用后,非阻塞,执行其他任务;
内核,处理完数据之后,把数据从内核空间,拷贝到用户空间,再发信号给应用程序;