阻塞、非阻塞、异步、同步

IO | bRPC

一般有三种操作IO的方式:

  • blocking IO: 发起IO操作后阻塞当前线程直到IO结束,标准的同步IO,如默认行为的posix readwrite
  • non-blocking IO: 发起IO操作后不阻塞,用户可阻塞等待多个IO操作同时结束。non-blocking也是一种同步IO:"批量的同步"。如linux下的poll,select, epoll,BSD下的kqueue
  • asynchronous IO: 发起IO操作后不阻塞,用户得递一个回调待IO结束后被调用。如windows下的OVERLAPPED + IOCP。linux的native AIO只对文件有效。

linux一般使用non-blocking IO提高IO并发度。当IO并发度很低时,non-blocking IO不一定比blocking IO更高效,因为后者完全由内核负责,而read/write这类系统调用已高度优化,效率显然高于一般得多个线程协作的non-blocking IO。但当IO并发度愈发提高时,blocking IO阻塞一个线程的弊端便显露出来:内核得不停地在线程间切换才能完成有效的工作,一个cpu core上可能只做了一点点事情,就马上又换成了另一个线程,cpu cache没得到充分利用,另外大量的线程会使得依赖thread-local加速的代码性能明显下降,如tcmalloc,一旦malloc变慢,程序整体性能往往也会随之下降。而non-blocking IO一般由少量event dispatching线程和一些运行用户逻辑的worker线程组成,这些线程往往会被复用(换句话说调度工作转移到了用户态),event dispatching和worker可以同时在不同的核运行(流水线化),内核不用频繁的切换就能完成有效的工作。线程总量也不用很多,所以对thread-local的使用也比较充分。这时候non-blocking IO就往往比blocking IO快了。不过non-blocking IO也有自己的问题,它需要调用更多系统调用,比如epoll_ctl,由于epoll实现为一棵红黑树,epoll_ctl并不是一个很快的操作,特别在多核环境下,依赖epoll_ctl的实现往往会面临棘手的扩展性问题。non-blocking需要更大的缓冲,否则就会触发更多的事件而影响效率。non-blocking还得解决不少多线程问题,代码比blocking复杂很多。

阻塞:当我们调用read、send、write等等系统调用API时,会把内核的缓存区里的数据拷贝到用户态的缓存区里,如果这个时候内核缓存区里没有数据,则会等待内核把数据准备好,此时,应用进程处于一种挂起状态。

非阻塞:当我们的应用程序调用write、send、read(所有的IO接口都是的),如果仅仅是从内核缓存区里把数据copy走,或者没有数据copy,此时也会直接返回,明显我们的用户进程并没有任何的等待,或者说处于挂起的状态,这种就是非阻塞。

默认创建的socket是阻塞的,但是可以调用下面这个函数设置为非阻塞:

非阻塞意味着在调用IO接口时,都不会发送等待,会立即返回。

同步

int function(xxxxx)

{

char buf[1024] = {0};

int size = read(fd, buf, bsize);

}

int read(xxxxx)

{

xxxxxjj

kk

。。。

memcpy(buf, kener_buffer, size);

}

内核把数据准备好了之后,它也是通过read返回的,read一返回,其实也就意味着数据是否准备好。

int callback(char* buffer, size)

{

printf(buffer);

}

int gun(oooo)

{

char buffer[1024] = {0};

s_read(fd, callback, buffer, size);

}

内核:

int s_read(fd, callback, buffer, size)

{

update_fd_set(fd, callback)  更新了fd的结构,也就是设置了一个回调函数

send_signal(sig);

}

int process_signal(sig)

{

copy_data_from_disk_2_kener_buffer()

copy_data_from_kener_buffer_2_user_buffer();

callback(user_buffer, size);

}

异步,我调用read时,只是告诉你一个回调函数,并且read已经返回,但是此时并不代表数据准备好了,只有当内核调用了我们的callback才意味着数据已经准备好了。

信号,通知:

延时,IO的操作,同步和异步:

ssize_t read(int fd, void *buf, size_t count);

int aio_read(struct aiocb *aiocbp);

struct aiocb {

/* The order of these fields is implementation-dependent */

int aio_fildes; /* File descriptor */

off_t aio_offset; /* File offset */

volatile void *aio_buf; /* Location of buffer */

size_t aio_nbytes; /* Length of transfer */

int aio_reqprio; /* Request priority */

struct sigevent aio_sigevent; /* Notification method */

int aio_lio_opcode; /* Operation to be performed;

lio_listio() only */

/* Various implementation-internal fields not shown */

};

struct sigevent {

int sigev_notify; /* Notification method */

int sigev_signo; /* Notification signal */

union sigval sigev_value; /* Data passed with notification */

void (*sigev_notify_function) (union sigval); /* Function used for thread

notification (SIGEV_THREAD) */

void *sigev_notify_attributes; /* Attributes for notification thread

(SIGEV_THREAD) */

pid_t sigev_notify_thread_id; /* ID of thread to signal (SIGEV_THREAD_ID) */

};

相关推荐
论迹15 分钟前
【JavaEE】-- Cookie &&Session
java·java-ee
j_xxx404_32 分钟前
C++ STL:list|了解list|相关接口|相关操作
开发语言·c++
czhc114007566335 分钟前
Java114 LeeCode 翻转二叉树
java
一 乐37 分钟前
个人理财系统|基于java+小程序+APP的个人理财系统设计与实现(源码+数据库+文档)
java·前端·数据库·vue.js·后端·小程序
m0_7482480242 分钟前
Redis的数据淘汰策略解读
数据库·redis·缓存
盖世英雄酱581361 小时前
java深度调试技术【第四五章:多线程和幽灵代码】
java·后端
脏脏a1 小时前
类与对象(上):面向过程到面向对象的跨越,类的定义、封装与 this 指针等核心概念深度剖析
开发语言·c++
稚辉君.MCA_P8_Java1 小时前
深入理解 TCP;场景复现,掌握鲜为人知的细节
java·linux·网络·tcp/ip·kubernetes
熊猫比分站1 小时前
[特殊字符] Java/Vue 实现体育比分直播系统,支持多端实时更新
java·开发语言·vue.js
Freed&2 小时前
《Nginx进阶实战:反向代理、负载均衡、缓存优化与Keepalived高可用》
nginx·缓存·负载均衡