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


    IO | bRPC

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

    • blocking IO: 发起IO操作后阻塞当前线程直到IO结束,标准的同步IO,如默认行为的posix readwrite
    • non-blocking IO: 发起IO操作后不阻塞,用户可阻塞等待多个IO操作同时结束。non-blocking也是一种同步IO:“批量的同步”。如linux下的poll,selectepoll,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) */

           };

  • 相关阅读:
    10个python常用机器学习及深度学习库!
    哈希树讲解
    买卖股票的最佳时机系列问题
    svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer
    android 13.0 在自定义系统服务中通过代码设置默认laucher动态切换
    jmeter固定定时器,生效是在请求发送前还是发送后
    自然语言处理[信息抽取]:MDERank关键词提取方法及其预训练模型----基于嵌入的无监督 KPE 方法 MDERank
    【CCF CSP-20161203】权限查询
    服务器部署测试环境回顾与改进建议
    2024国际生物发酵展畅想未来-势拓伺服科技
  • 原文地址:https://blog.csdn.net/INGNIGHT/article/details/133655963