Windows和Linux IO模型简单介绍

Windows和Linux IO模型简单介绍

  • Socket IO模型
    • Windows下的Socket IO
    • Linux下的Socket IO
    • 两个操作系统下IO模型的区别差异

Socket IO模型

IO模型相信大家都很熟悉,主要就分下面几种:

  • 同步
  • 异步
  • 阻塞
  • 非阻塞
    同步和异步就不多解释了,下面主要分析一下阻塞。
    很多人会将异步和非阻塞搞混,异步一定是非阻塞的吗?

阻塞:指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务,函数只有在得到结果之后才会返回。

非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

异步可以通过回调完成,让当前线程并不会阻塞,但是回调线程中的IO是否是非阻塞的呢?

其实并不一定。所以不要将异步和非阻塞搞混。当然,一般异步和非阻塞是同时出现的。

举个例子:我创建一个SOCKET句柄调用ACCPET,这就是同步+阻塞的情况。

但是我当前线程并不想在这等着,怎么办呢?我用__beginthread创建一个线程让它去等待,而我当前线程可以继续往下执行,这就是一个异步操作,但对创建出的线程来说,ACCPET还是阻塞的。

Windows下的Socket IO

  • WSAEventSelect (NIO)
    需要创建WSAEVENT数组,并且使用WSAEventSelect 对fd和event进行绑定
    然后使用WSAWaitForMultipleEvents等待事件触发,之后再进行遍历。
  • WSASyncSelect (NIO)
    基于窗口消息的方式,使用WSAAsyncSelect将fd和hWnd进行绑定,自定义消息类型。
    在窗口消息的回调函数中处理FD,比上面那种更方便,不需要遍历数组
  • Select (NIO)
    通过一个fd_set集合管理fd,通过select遍历fd_set来判断fd当前是否有事件触发,如果触发需要自己再操作fd。
  • Overlapped
    注意:这是一个异步模型
    为了以异步方式访问设备,需要先调用CreateFile ,并在dwFlagsAndAttributes传入FILE_FLAG_OVERLAPPED 标志打开设备
    然后使用ReadFile/WriteFile 进行操作。
    当我们调用者两个函数中的任何一个时,函数会检查hFile 参数标识是否是使用FILE_FLAG_OVERLAPPED 标志打开的
    如果打开设备时制定了这个标志,那么函数会执行异步设备I/O,当调用这2个函数进行异步I/O的时候,我们可以传入NULL给pdwNumBytes,毕竟我们希望这2个函数在I/O请求完成之前就返回
    因此这时就检查已经传输的字节数是没有意义的。
    在执行异步I/O的时候,我们应该意识到一些问题,首先,设备驱动程序不必以先入先出的方式来处理队列中的I/O请求。
    意思就是说 就算你是先Read后Write,但是系统处理顺序并不一定是你代码的顺序。
    还有最主要的一点 ol结构不要放在和read write同栈中,除非你放在堆中或者确保它不会被移除。因为传过去的只是地址,内存拷贝代价太大.
  • IOCP(IoCompletionPort)
    使用IOCP是和OL结构一起使用,比较复杂,但是是windows中最好的IO选择。

最大的区别:在ol结构之前介绍的几种模型,系统只通知你是否可以对fd进行操作,而ol和iocp是完全由系统处理,你只需要控制逻辑,读或写,因为在read或write时已经要求你传一个buffer进去,当系统通知你完成时,你的buffer已经被处理完了,不需要你再处理

Linux下的Socket IO

  • poll (NIO)
    poll函数与select函数差不多,要说最大的区别在于FD_SET是个数组,有大小限制,而poll是没有大小限制的,因为它是个链表
  • epoll (NIO)
    epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))
    需要注意的是epoll默认是Level Trigger,所以使用时需要注意切换成Edge Trigger(events = EPOLLIN | EPOLLET)

两个操作系统下IO模型的区别差异

Windows 最好的方法就是IOCP,并且连创建SOCKET都可以提前创建好,而不需要等待accept接收到后再创建,缺点是这种方式在不确定用户数量的情况下会浪费空间。当然,也可以在IOCP中使用accept不使用ex方式。

IOCP的和Linux中poll epoll最大的区别就在于WINDOWS下的IOCP模型是系统会在内核中帮你处理好buffer,而基于事件或消息方式的情况下,你只能自己再去读取或写入buffer

本文只是大概介绍几种IO模型和比较直观区别,并不是来教如何使用这些API,如果想了解可以自己查资料具体了解如何使用。上面列的模型事实上不应该归到SocketIO上,传统文件IO同样可以处理,其实都一样,在操作系统底层都是一个fd。写成SocketIO的原因是这些API在传统文件IO上基本很少被人使用,除非文件过大,文件过多等等。

你可能感兴趣的:(C/C++,epoll)