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

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) */

};

相关推荐
湫ccc4 分钟前
《Python基础》之字符串格式化输出
开发语言·python
弗拉唐4 分钟前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi7736 分钟前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
mqiqe42 分钟前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin44 分钟前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python
少说多做3431 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀1 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20201 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea