您的位置:首页 > 运维架构 > Linux

unix/linux下主要的I/O模型:

2013-08-17 15:33 211 查看
UNIX/LINUX下主要的4种i/o模型:

1,阻塞I/O:

     最常用,最简单,效率最低

2,非阻塞I/O:

      可防止进程阻塞在I/O操作上,需要轮询。

3,I/O多路复用:

       允许同时对多个I/O进行控制

4,信号驱动I/O:

     一种异步通信模型。

阻塞I/O模式:

     阻塞I/O是最普遍使用的I/O模式,大部分的程序使用的都是阻塞I/O。

    缺省的情况下,套按字建立以后所处于的模式就是阻塞I/O模式。

 例如:读操作read,recv,recvfrom

              写操作:write,send

           其他操作:accept,connect

读阻塞:进程调用read函数从套接字中读取数据,当套接字中缓冲区中没有要读的数据,函数会阻塞。

                它会一直阻塞下去,等待套接字的接收缓冲区中有数据可读

                经过一段的时间后,缓冲区中接收到数据,于是内核便去唤醒该进程,通过read访问这些数据。

               如果在进程阻塞过程中,对方发生故障,那这个进程将永远阻塞下去。

写阻塞:在写操作上发生阻塞的情况比较少,主要发生在要写入的缓冲区的大小小于要写入的的数据量的情况下。

            这时,写操作不进行任何的拷贝工作,将发生阻塞,一旦缓冲区有足够的空间,内核将唤醒进程,将数据从用户缓冲    拷   贝到相应的发送缓冲区中

 特例:UDPI不用等待用户的确认,没有实际的发送缓冲区,所以udp协议中不存在发送缓冲区满的情况,在UDP套接字上 执 行的写操作将永远不会阻塞。

非阻塞I/O:

                  于阻塞I/O相反,进程不会睡眠,立即返回一个错误的信息,这样需要不停的查询(polling),浪费CPU的资源操作。

 多路复用I/O:

                          先构造一张有关的描述符表,然后调用一个函数。当这些文件描述符表中的一个或多个已准备好进行I/O时函数才返回。 函数返回时告诉进程那个描述符准备就绪了,可以进行I/O操作了。

信号驱动I/O:是一种异步通信模式,相当于中断。

多路复用I/O来实现并发服务器:

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <sys/types.h>

#include <sys/select.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#define  N  64

typedef struct sockaddr  SA;

int main(int argc,  char *argv[])

{

    int i, listenfd, connfd, maxfd;

    char buf
;

    fd_set  rdfs,tempfs;

    struct sockaddr_in myaddr;

    if((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)

    {

        perror("fail to socket\n");

        exit(-1);

    }

    bzero(&myaddr, sizeof(myaddr));

    myaddr.sin_family = PF_INET;

    myaddr.sin_port = htons(8001);

    myaddr.sin_addr.s_addr = inet_addr("192.168.1.15");

    if (bind(listenfd, (SA *)&myaddr, sizeof(myaddr)) < 0)

    {

        perror("fail to bind");

        exit(-1);

    }

    listen(listenfd, 5);

    maxfd = listenfd;

    FD_ZERO(&rdfs);

    FD_SET(listenfd, &rdfs);

    //select的第一个参数为最大文件描述符加1,

    while ( 1 )

    {

 //构造一个临时的变量,这样初始化的时候,就不用太麻烦,

 //为什么每次都要初始化,因为这个函数是监控各个文件描述符的状态,

  //函数返回的时候状态就发生的变化,需要重新初始化,也就是没有准备好进行多路复用I/O操作。

        tempfs=rdfs;

        if (select(maxfd+1, &tempfs, NULL, NULL, NULL) < 0)

        {

            perror("fail to select");

            exit(-1);

        }

        for(i=0; i<=maxfd; i++)

        {

            if (FD_ISSET(i, &tempfs))

            {

                if (i == listenfd)

                {

                    connfd = accept(listenfd, NULL, NULL);

                    printf("New Connection %d is coming\n", connfd);

                    FD_SET(connfd,&rdfs);

                    maxfd = maxfd> connfd ?maxfd: connfd;

                }

                else

                {

                    if(recv(i,buf,N,0) ==0 )   //说明客户端已经已经关闭了socket即客户端的写端。

                    {

                        FD_CLR(i,&rdfs);

                        close(i);

                    }

                    else

                    {

                        send(i,buf,N,0);

                    }

                   

                }

            }

        }  //  end for

    } // end while

    return 0;

}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  IO模型