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

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

};

相关推荐
豆本-豆豆奶16 分钟前
23个Python在自然语言处理中的应用实例
开发语言·python·自然语言处理·编程语音
曳渔22 分钟前
Java-数据结构-二叉树-习题(三)  ̄へ ̄
java·开发语言·数据结构·算法·链表
shark-chili32 分钟前
数据结构与算法-Trie树添加与搜索
java·数据结构·算法·leetcode
白乐天_n35 分钟前
FRIDA-JSAPI:Java使用
java·jsapi·frida
你可以自己看38 分钟前
python中函数式编程与高阶函数,装饰器与生成器,异常处理与日志记录以及项目实战
服务器·开发语言·python
无奇不有 不置可否1 小时前
JVM基础篇学习笔记
java·jvm
gopher95111 小时前
go语言 数组和切片
开发语言·golang
ymchuangke1 小时前
线性规划------ + 案例 + Python源码求解(见文中)
开发语言·python
gopher95111 小时前
go语言Map详解
开发语言·golang
Python私教1 小时前
Go语言现代web开发15 Mutex 互斥锁
开发语言·前端·golang