美文网首页
unix 下5种io模型

unix 下5种io模型

作者: juteman | 来源:发表于2018-03-02 15:35 被阅读18次

    文章从我的个人博客备份而来
    unix下有5种可用的io模型,它们分别是:

    • 阻塞io
    • 非阻塞io
    • io复用
    • 信号式驱动io
    • 以及异步io

    阻塞式io模型

    一般来说,我们平时编程使用的最多的,就是阻塞式io模型。
    举个例子,比如说我们有一个函数

    struct hostent *gethostbyname(const char *name);
    

    这个时候我们调用这个函数

    gethostbyname("www.google.cn")
    

    进程会一直阻塞,直到调用完成,或者调用失败。


    阻塞式io.jpg

    非阻塞式io

    在类unix操作系统下,我们有:

    fcntl(fd, F_SETFL, O_NONBLOCK);
    

    这里的fd可以是文件描述符,也可以是套接字描述符。
    假设我们调用了

    recv(fd, buf, sizeof(buf), 0)
    

    如果是服务端接收客户端的信息,假如客户端的数据没有准备好,服务端并不会被阻塞,而是会发起轮询。

    轮询.jpg

    但是假如我们有多个客户端,都设为非阻塞式,那么我们会对多个客户端都发起轮询。这样非常的消耗资源。在某些特定的情况,我们会使用非阻塞式的模型

    io复用模型

    这里使用常见的select()来介绍:

    复用io.jpg

    进程阻塞于select()调用,当条件准备好的时候,我们调用recv().
    和阻塞式io比起来,这好像没什么区别,还多了一次select()调用。对于单个套接字来说,确实是这样。但是对于多个套接字来说效率会大大的提高。
    假设我们有fd[0], fd[1], fd[2], .... fd[n] (n < 1024)
    如果我们采用阻塞式io的话,这时我们遍历 fd数组,而在这个数组中fd[3]是准备好的,而fd[0], fd[1], fd[2]都没有准备好。我们会一直阻塞到直到fd[0],fd[1],fd[2]都准备好才会使用recv接收fd[3]的数据。
    假如我们使用select(),遍历过fd数组以后确认fd[3]是准备好的则会先执行fd[3]。

    下面是一段复用的示例程序

    fd_set readset;
    int i, n;
    char buf[1024];
    
    while (i_still_want_to_read()) {
        int maxfd = -1;
        FD_ZERO(&readset);
    
        for (i=0; i < n_sockets; ++i) {
             if (fd[i]>maxfd) maxfd = fd[i];
             FD_SET(fd[i], &readset);
        }
    
        select(maxfd+1, &readset, NULL, NULL, NULL);
    
        for (i=0; i < n_sockets; ++i) {
            if (FD_ISSET(fd[i], &readset)) {
                n = recv(fd[i], buf, sizeof(buf), 0);
                if (n == 0) {
                    handle_close(fd[i]);
                } else if (n < 0) {
                    if (errno == EAGAIN)
                    else
                         handle_error(fd[i], errno);
                 } else {
                    handle_input(fd[i], buf, n);
                 }
            }
        }
    }
    

    但是select有许多缺点,比如说它每次扫描都是线性扫描,当描述符变得很多以后,他的效率会非常的低。而且单个进程打开的fd数量是有限制的,默认是1024.而另一个unix提供的poll()函数同样也会随着套接字的增加效率降低。
    在Linux2.6内核以后提供了epoll(),而freebsd也提供了kqueue。相比起poll(),select(),他们更加的优秀。

    信号式驱动

    假设我们还是服务器接收客户端的数据,信号式驱动是客户端先发送一个信号表示数据已经准备好,可以发送。这种方法的优势在于等待期间我们的进程不会被阻塞。

    异步io模型

    还是我们调用一个函数,异步io在执行时进程并不会被阻塞,而在执行完成之时,会传递一个信号来告知执行完成。

    最后用一张图来看看5中io的区别

    5种io模型.jpg

    相关文章

      网友评论

          本文标题:unix 下5种io模型

          本文链接:https://www.haomeiwen.com/subject/lppaxftx.html