精华内容
下载资源
问答
  • IO和网络通讯基础概念 首先要从冯诺依曼说起,由他提出计算机体系结构: 计算器控制(CPU…) ——> 主存(内存…) ——> 输入输出(硬盘、网卡、显示器、键盘…) IO说白了就是输入输出,宏观角度讲可以分为IO...
    IO和网络通讯基本概念

    首先要从冯诺依曼说起,由他提出的计算机体系结构:

    计算器控制(CPU…) ——> 主存(内存…) ——> 输入输出(硬盘、网卡、显示器、键盘…)

    IO说白了就是输入输出,宏观角度讲可以分为IO设备和IO接口两个部分,IO设备就是输入输出设备,IO设备的工作方式可以由程序进行控制的。IO接口可以理解为 “计算机和其他计算机”,或者 ,“程序与计算机的IO设备”之间的传输接口。

    IO它对于任何计算机系统都非常关键,因为所有 I/O 的主体实际上是内置在操作系统中的。程序一般是调用系统为它们完成大部分的工作。

    网络通讯,就相当于一台计算机给另外一台计算机传输数据,中间的过程就叫做通信,也就是通过IO接口输入输出到另一台计算机,这个就叫做网络IO,可以把网络通讯理解为IO的一种,很多人会把网络IO和文件IO的概念区分开,其实他俩是一样的,只不过是通过不同的方式把数据输入输出到了不同的地方。

    操作IO的模式也有很多种,有BIO、NIO之类的,这些可以对应到Java中的类来加深我们的概念。

    java.io包基于流模型实现,提供File抽象、输入输出流等IO的功能。交互方式是同步、阻塞的方式,在读取输入流或者写入输出流时,在读、写动作完成之前,线程会一直阻塞。java.io包的好处是代码比较简单、直观,缺点则是IO效率和扩展性存在局限性,容易成为应用性能的瓶颈

    java.net包下提供的部分网络API,比如Socket、ServerSocket、HttpURLConnection
    也可以被归类到同步阻塞IO类库,因为网络通信同样是IO行为

    在Java 1.4中引入了NIO框架(java.nio 包),提供了Channel、Selector、Buffer等新的抽象,可以构建多路复用IO程序,同时提供更接近操作系统底层的高性能数据操作方式

    在Java7中,NIO有了进一步的改进,也就是NIO2,引入了异步非阻塞IO方式,也被称为AIO(Asynchronous IO),异步IO操作基于事件和回调机制

    以上提到的NIO、BIO、AIO、多路复用等概念会在后文详细介绍

    详细说IO和网络通讯前,还需要先有一个文件描述符的概念,这是一个实际可以看到的东西,我们先用Linux和java来说下计算机如何运行程序的。后面再来看这个文件描述符是什么鬼

    文件描述符

    我们编写的代码是放在某一个输出输入设备当中,当程序被执行的时候,代码会被加载到主存中,最终代码变成计算机指令,再由计算机控制完成计算等等其他操作。即程序的运行是由内存和计算机控制完成运行的。最终程序执行是对系统内核的调用,系统内核再对硬件进行调用。

    以java代码来讲,java文件编译后是class字节码文件,字节码文件也是普通的文本文件,只不过里面是字节,存储序列化的东西,这个字节码会运行在C语言写的JVM虚拟机或者其他java进程里面,JVM或java进程会把字节码解析成系统指令再去调用系统的API完成java程序要做的事。(正是因为不同的JVM虚拟机可以把java代码解析成对应的系统指令,所以java才可以跨平台)

    说完这些之后我们来看看如何调用Linux系统内核的API,在Linux系统中,有一个思想就是“一切皆文件”,包括输入输出设备都被看做文件,比如把打印机抽象为文件,那么我们向打印机文件写入的话,就会把我们写入的打印出来。那么如何去控制这个打印机这个文件帮我们输出打印呢?其实就是怎么操作系统提供的API的事。

    在Java中可以通过调用对象去做事情。在Linux当中没有对象的概念,而是都抽象为文件了,那么我们对想操作的设备,或是想操作的连接都变成了一个占位符,其实就是个数字,它的专业名词就叫做“文件描述符”,就像java中对象的引用变量一样。内核正是利用文件描述符来访问文件,打开或者新建文件时,内核会返回一个文件描述符,读写文件也需要使用文件描述符来指定待读写的文件。

    百度百科:文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。文件描述符在形式上是一个非负整数。实际上它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统而windows为句柄的概念

    如上介绍可能还是有些抽象,后面会实际看到这个东西,并操作它,然后我们再铺垫个东西,学一下怎么查看linux系统内核提供的API文档,方便后面学习

    Linux API文档

    在Linux系统当中可以通过安装 帮助程序 和 帮助页 插件查看一些系统内核API或命令

    yun install man man-pages
    

    比如我们查看怎么读取一个文件,读就是read,2就代表第二类,即我们查询的是操作系统内核对程序的系统调用

    man 2 read
    

    read
    因为Linux是C语言写的,这里可以看到调用和java类似,有参数,fd这个数值类型的参数就是file descriptor,也就是文件操作符,这个操作符怎么得到呢?前面也提到过打开或者新建文件时,内核会返回一个文件描述符

    比如说read之前需要先打开一个文件

    man 2 open
    

    在这里插入图片描述
    在这里插入图片描述
    我们可以看到传入一个文件名称,返回的就是一个file descriptor 文件操作符

    除了open以外还有socket也能得到文件操作符

    man 2 socket
    

    在这里插入图片描述
    在这里插入图片描述

    结尾

    到现在就有一个IO的基本概念了,可以把文档插件装上,试一下,后面还会一直用到,下章使用文件描述符模拟浏览器获取网页,带大家感受通信,可以关注我的公众号,里面有完整系列的文章,回复关键字“资源”,可以免费领取架构师、大数据、AI等课程
    在这里插入图片描述

    原文链接:https://mp.weixin.qq.com/s?__biz=MzI5MDk1Mzc3MQ==&mid=100000012&idx=1&sn=cf49e575b6305d37ff541018d181b674&chksm=6c194e485b6ec75e6834b6a71d34d23896b8a6bbffb4d70a1519b656cf3d7cb34b06899a94fb&scene=25#wechat_redirect

    展开全文
  • IO和NIO的区别

    2020-03-18 10:08:41
    题外话 看到NIO一堆繁杂的API瞬间就不想学这个NIO了,但是我们可以先从模糊的认识上先理解下NIO和IO的区别。...IO可以分为文件IO和网络IO。文件IO是中的read和write都是阻塞的,网络IO中的read、...

    题外话

    看到NIO一堆繁杂的API瞬间就不想学这个NIO了,但是我们可以先从模糊的认识上先理解下NIO和IO的区别。

     

    初识IO

    特点:采用面向流的操作。IO流是没有缓存的概念,所以就需要每次从流中一个一个字节的去读或者每次从流中一个一个字节的去写。而且我们知道流本身是单工的数据通信。IO可以分为文件IO和网络IO。文件IO是中的read和write都是阻塞的,网络IO中的read、write、accept等都是阻塞的。由于IO流是阻塞的又叫做BIO。
    IO文件流阻塞例子:

    public static void writeByBIO(String path, String context) {
            FileOutputStream fos = null;
            
            try {
                fos = new FileOutputStream(path, true);
                
                for (int i = 0 ; i < 3 ; i++) {
                    fos.write((context + "\n").getBytes());
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }
    
    public static void readByBIO(String path) {
            FileInputStream fis = null;
            
            try {
                fis = new FileInputStream(path);
                int length;
                
                while ((length = fis.read()) != -1) {
                    System.out.print((char) length);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }
    

    测试:

    public static void main(String[] args) {
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    writeByBIO("F:\\Hello.log", "1");
                }
            }).start();
            
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    writeByBIO("F:\\Hello.log", "2");
                }
            }).start();
            
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    readByBIO("F:\\Hello.log");
                }
            }).start();
    }
    

    BIO.png

     

    说明:可以发现当第一个线程写入的时候,第二个线程和第三个线程虽然已经启动,但是处于线程阻塞挂起的状态。虽然多线程调度具有随机性,CPU按照时间分片,但是由于其本身的阻塞的特点,不会出现脏数据;虽然简单的顺序执行得到保证,但是扩展性不高、效率不高,很容易成为系统瓶颈。

    初识NIO

         采用面向缓冲区的操作。NIO采用缓冲区的概念,先将数据都缓存在缓冲区中,缓冲区采用开始读写的位置position、读写结束位置limit、缓冲区最大容量capacity等标记来设计。NIO还采用了通道的概念,可以进行全双工数据通信。同样NIO分为文件NIO和网络NIO。
         文件NIO中的通道是一种阻塞式。
    文件NIO阻塞例子:

    public static void writeByNIO(String path, String context) {
            FileOutputStream fos = null;
            FileChannel fc = null;
            
            try {
                fos = new FileOutputStream(path, true);
                fc = fos.getChannel();
                ByteBuffer bb = ByteBuffer.allocate(1024 * 1024 * 5);
                
                for (int i = 0 ; i < 3 ; i++) {
                    bb.put((context + "\n").getBytes());
                }
                
                bb.flip();
                fc.write(bb);
                bb.clear();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fc != null) {
                        fc.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }
        
    public static void readByNIO(String path) {
            FileInputStream fis = null;
            FileChannel fc = null;
            
            try {
                fis = new FileInputStream(path);
                fc = fis.getChannel();
                ByteBuffer bb = ByteBuffer.allocate(1024 * 1024 * 5);
                int length;
                
                while ((length = fc.read(bb)) != -1) {
                    bb.flip();
                    System.out.println(new String(bb.array(), 0 , length));
                    bb.clear();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fc != null) {
                        fc.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }
    

    测试:

    public static void main(String[] args) {
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    writeByNIO("F:\\Hello.log", "1");
                }
            }).start();
            
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    writeByNIO("F:\\Hello.log", "2");
                }
            }).start();
            
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    readByNIO("F:\\Hello.log");
                }
            }).start();
    }
    

    NIO.png


    网络NIO

         网络NIO的ServerSocketChannel可以通过configureBlocking方法来设置非阻塞。NIO读写分为准备读写和真正读写,而在准备读写的阶段:将通道中SelectionKey中接收请求事件、读事件、写事件、连接事件这些注册到Selector选择器上,此时每个键对应的通道已经做好了I/O操作的准备(注册这个操作本身是非阻塞的)。
    NIO一个重要的特点是:socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。 (这段话来源于https://zhuanlan.zhihu.com/p/23488863


         NIO是采用了Selector选择器的概念,对于前面准备好I/O操作的通道的键。选择器使用select方法进行死循环轮询使用的键SelectionKey找对应的事件和通道。因为select方法是阻塞的并且进行死循环,所以只能是单线程的操作。然后通过SelectionKey中的方法测试通道是否可读可写等操作再进行对缓冲区读写。


         流和缓冲区对比:流就是每次去购物,一个东西一个东西去拿到收银台付款。而缓冲区就是将每个东西都放到购物车,然后一次拿到收银台付款。


    区别

    IO流的特点就是建立连接,然后去读写就行了。对于是否可读可写通过阻塞等方式来处理。NIO的特点就是将读写拆分为准备读写和真正读写两个方面,在准备读写阶段采用非阻塞的方式来处理,而到真正读写的时候实际上还是阻塞的方式。

     

    展开全文
  • 在Linux中,对文件的读写其实就是IO。 与IO有关名词:同步,异步,阻塞,非阻塞,甚至是同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。别急,下面有举例IO分为两大种,同步异步 同步IO:阻塞IO非阻塞IOIO多路...

    在Linux中,对文件的读写其实就是IO。

    与IO有关的名词:同步,异步,阻塞,非阻塞,甚至是同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。别急,下面有举例
    IO分为两大种,同步和异步

    同步IO:
    阻塞IO
    非阻塞IO
    IO多路复用(包括select,poll,epoll三种)
    信号驱动IO
    异步IO
    那么如何理解区别这几个概念呢?尤其是同步和阻塞,异步和非阻塞,看起来就是一样的.
    举个例子结合自己的理解来说明一下:

    • ①:你去甜在心馒头店买太极馒头,阿梅说:"暂时没,正在蒸呢,你自己看着点儿!".于是你就站在旁边只等馒头.此时的你,是阻塞的,是同步的.阻塞表现在你除了等馒头,别的什么都不做了.同步表现在等馒头的过程中,阿梅不提供通知服务,你不得不自己要等到"馒头出炉"的消息.
    • ②:你去甜在心馒头店买太极馒头,阿梅说:"暂时没,正在蒸呢,你自己看着点儿!".于是你就站在旁边发微信,然后问一句:"好了没?",然后发QQ,然后再问一句:"好了没?".此时的你,是非阻塞的,是同步的.非阻塞表现在你除了等馒头,自己还干干别的时不时会主动问问馒头好没好.同步表现在等馒头的过程中,阿梅不提供通知服务,你不得不自己要等到"馒头出炉"的消息.
    • ③:你去甜在心馒头店买太极馒头,阿梅说:"暂时没,正在蒸呢,蒸好了我打电话告诉你!".但你依然站在旁边只等馒头,此时的你,是阻塞的,是异步的.阻塞表现在你除了等馒头,别的什么都不做了.异步表现在等馒头的过程中,阿梅提供电话通知"馒头出炉"的消息,你只需要等阿梅的电话.
    • ④:你去甜在心馒头店买太极馒头,阿梅说:"暂时没,正在蒸呢,蒸好了我打电话告诉你!".于是你就走了,去买了双新球鞋,看了看武馆,总之,从此不再过问馒头的事情,一心只等阿梅电话.此时的你,是非阻塞的,是异步的.非阻塞表现在你除了等馒头,自己还干干别的时不时会主动问问馒头好没好.异步表现在等馒头的过程中,阿梅提供电话通知"馒头出炉"的消息,你只需要等阿梅的电话


    如果你仔细品过上面案例中的每一个字,你就能慢慢体会到之所以异步和非阻塞,同步和阻塞容易混淆,仅仅是因为二者的表现形式稍微有点儿相似而已.
    阻塞和非阻塞关注的是:在等馒头的过程中,你在干啥.
    同步和异步关注的是:等馒头这件事,你是一直等到"馒头出炉"的结果,还是立即跑路等阿梅告诉你的"馒头出炉".重点的是你是如何得知"馒头出炉"的.
    所以现实世界中,最傻的人才会采用异步阻塞的IO方式去写程序.其余三种方式,更多的人都会选择同步阻塞或者异步非阻塞.同步非阻塞最大的问题在于,你需要不断在各个任务中忙碌着,导致你的大脑混乱,非常累.

     

    原文地址:https://blog.ti-node.com/blog/6389362802519179264

    转载于:https://www.cnblogs.com/wt645631686/p/8386100.html

    展开全文
  • 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同人在不同上下文下给出答案是不同。所以先限定一下本文上下文。本文讨论背景是Linux环境下network IO。一 概念说明在进行解释之前,...

    同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的。所以先限定一下本文的上下文。

    本文讨论的背景是Linux环境下的network IO。

    一 概念说明

    在进行解释之前,首先要说明几个概念:

    - 用户空间和内核空间

    - 进程切换

    - 进程的阻塞

    - 文件描述符

    - 缓存 I/O

    用户空间与内核空间

    现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。

    进程切换

    为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。

    从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:

    1. 保存处理机上下文,包括程序计数器和其他寄存器。

    2. 更新PCB信息。

    3. 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。

    4. 选择另一个进程执行,并更新其PCB。

    5. 更新内存管理的数据结构。

    6. 恢复处理机上下文。

    总而言之就是很耗资源,具体的可以参考这篇文章:进程切换

    注:进程控制块(Processing Control Block),是操作系统核心中一种数据结构,主要表示进程状态。其作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位或与其它进程并发执行的进程。或者说,OS是根据PCB来对并发执行的进程进行控制和管理的。 PCB通常是系统内存占用区中的一个连续存区,它存放着操作系统用于描述进程情况及控制进程运行所需的全部信息

    进程的阻塞

    正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的。

    文件描述符fd

    文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。

    文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

    缓存 I/O

    缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

    缓存 I/O 的缺点:

    数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

    二 IO模式

    刚才说了,对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:

    1. 等待数据准备 (Waiting for the data to be ready)

    2. 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

    正式因为这两个阶段,linux系统产生了下面五种网络模式的方案。

    - 阻塞 I/O(blocking IO)

    - 非阻塞 I/O(nonblocking IO)

    - I/O 多路复用( IO multiplexing)

    - 信号驱动 I/O( signal driven IO)

    - 异步 I/O(asynchronous IO)

    注:由于signal driven IO在实际中并不常用,所以我这只提及剩下的四种IO Model。

    阻塞 I/O(blocking IO)

    在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:

    当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

    所以,blocking IO的特点就是在IO执行的两个阶段都被block了。

    非阻塞 I/O(nonblocking IO)

    linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:

    当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。

    所以,nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。

    I/O 多路复用( IO multiplexing)

    IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。

    当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

    所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。

    这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。

    所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)

    在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。

    异步 I/O(asynchronous IO)

    inux下的asynchronous IO其实用得很少。先看一下它的流程:

    用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

    总结

    blocking和non-blocking的区别

    调用blocking IO会一直block住对应的进程直到操作完成,而non-blocking IO在kernel还准备数据的情况下会立刻返回。

    synchronous IO和asynchronous IO的区别

    在说明synchronous IO和asynchronous IO的区别之前,需要先给出两者的定义。POSIX的定义是这样子的:

    - A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;

    - An asynchronous I/O operation does not cause the requesting process to be blocked;

    两者的区别就在于synchronous IO做”IO operation”的时候会将process阻塞。按照这个定义,之前所述的blocking IO,non-blocking IO,IO multiplexing都属于synchronous IO。

    有人会说,non-blocking IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO operation”是指真实的IO操作,就是例子中的recvfrom这个system call。non-blocking IO在执行recvfrom这个system call的时候,如果kernel的数据没有准备好,这时候不会block进程。但是,当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内,进程是被block的。

    而asynchronous IO则不一样,当进程发起IO 操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。

    各个IO Model的比较如图所示:

    通过上面的图片,可以发现non-blocking IO和asynchronous IO的区别还是很明显的。在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

    三 I/O 多路复用之select、poll、epoll详解

    select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。(这里啰嗦下)

    select

    select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。

    select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但 是这样也会造成效率的降低。

    poll

    不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。

    struct pollfd {

    int fd; /* file descriptor */

    short events; /* requested events to watch */

    short revents; /* returned events witnessed */

    };

    pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。

    从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

    epoll

    epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

    一 epoll操作过程

    epoll操作过程需要三个接口,分别如下:

    1. int epoll_create(int size);

    创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大,这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值,参数size并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。

    当创建好epoll句柄后,它就会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

    2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

    函数是对指定描述符fd执行op操作。

    - epfd:是epoll_create()的返回值。

    - op:表示op操作,用三个宏来表示:添加EPOLL_CTL_ADD,删除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。分别添加、删除和修改对fd的监听事件。

    - fd:是需要监听的fd(文件描述符)

    - epoll_event:是告诉内核需要监听什么事

    3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

    等待epfd上的io事件,最多返回maxevents个事件。

    参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

    原文http://www.cnblogs.com/alex3714/articles/5876749.html

    展开全文
  • 文件IONIO

    2020-03-04 19:02:05
    /** * * NIO: * 是JDk1.4 开始提供了一系列新输入/输出新特性 被称... * 相同点:网络IO和文件IO * 区别: 实现方式不同,BIO以流方式处理数据 * NIO以块数据进行处理是数据 ,块I/O 比流 I/O效率高 * ...
  • 1,NIO vs BIO2,NIO工作模式NIO有三大核心:1. Channel(通道)2....Channel提供了从文件网络读取数据通道,但是读或写数据,都必须经过Buffer,如下图所示:Selector用于监听多个通道事件,从而...
  • linux文件IO操作

    2016-12-13 15:51:29
    说起linux编程来,甚至包括其他系统的编程,很大一部分都是处理IO操作了。特别是在linux系统上,由于一起都是文件(fd)的...当然如果我们考虑最多的两类IO,即文件IO和网络IO,它们的区别也很大。  文件IO虽然也是
  • 1、InputStreamReader的区别? InputStream是表示字节输入流的所有类的超类 Reader是用于读取字符流的抽象类 InputStream提供的是字节流的读取,而非文本读取,这是Reader类的根本区别。 即用Reader读取出来的是...
  • 看完此文,题目不言自明。... 在Linux 开发中,有几个关系到性能的东西,技术人员非常关注:进程,CPU,MEM,网络...剖析文件IO的细节。从多个角度探索如何提高IO性能。本文尽量用通俗易懂的视角去阐述。不copy内核代码。
  • MMAPDIRECT IO区别

    2016-09-23 21:11:00
    看完此文,题目不言自明。... 在Linux开发中,有几个关系到性能的东西,技术人员非常关注:进程,CPU,MEM...剖析文件IO的细节。从多个角度探索如何提高IO性能。本文尽量用通俗易懂的视角去阐述。不copy内核代码。 ...
  • 也有别情况,比如你和别程序做交互,和你交互程序也属于外部,但一般来说,就是文件和网络这么两种。从文件里或者从网络上读数据到内存里,就叫输入;从内存里写到文件里或者发送到网络上,就叫输出Java I/O ...
  • IO有内存IO、网络IO和磁盘IO三种,通常我们说IO指是后两者。 阻塞和非阻塞,是函数/方法实现方式,即在数据就绪之前是立刻返回还是等待。 以文件IO为例,一个IO读过程是文件数据从磁盘→内核缓冲区→用户内存...
  • Java中三者IO的区别

    2020-09-28 14:00:03
    在java.base包中有一个java.io的包,里面有这样一句话Provides for system input and output through data streams, serialization and the file system.这句话的大概意思就是通过数据流,序列化和文件系统提供系统...
  • 转自:... 看完此文,题目不言自明。... 在Linux开发中,有几个关系到性能的东西,技术人员非常关注:进程,CPU,MEM,网络IO,磁盘IO。本篇文件打算详细全面,深入浅出。剖析文件IO的细节。从...
  • 前面学习了select、pollepoll三组IO复用系统调用,现在从向内核传递文件描述符数、内核实现、检索就绪描述符方式、工作模式时间复杂度等五个方面比较其中的区别,以明确在实际应用中应该选择使用哪个。...
  • 阻塞:用户进程访问数据时,如果未完成IO,等待IO操作完成或者进行系统调用来判断IO是否完成非阻塞:用户进程访问数据时,会...网络IO模型和文件IO模型是一样的,上图是IO的5种模型,包括阻塞IO、非阻塞IO、多路复用...
  • 文章目录BIO、NIO、Epoll发展历程以及原理回顾BIO原理与缺陷NIO的原理与缺陷同步非阻塞NIO 到 多路复用NIO多路复用NIO 到 epollselect pollepoll的区别select的几大缺点:poll实现epoll总结: BIO、NIO、Epoll发展...
  • 先说一下几个单词。 阻塞:用户进程访问数据时,如果未完成IO,等待IO操作完成或者进行系统调用来判断IO是否完成 非阻塞:用户进程访问数据时,会马上返回一个状态值...网络IO模型和文件IO模型是一样的,上图是IO的
  • 1,带缓冲的文件复制 ...(mkdirsmkdir的区别,当路径中存在原先不存在的路径,mkdirs可以创建它,而mkdir不行,使用mkdir时路径必须原来就存在!) 3,网络下载文件 4,删除文件夹
  • IO主要分成两类:硬盘IO和网络IO,本内容主要针对网络IO。复用含义可以理解为重复使用某个事物,而在本文,这个事物是指一个线程。因此,IO多路复用,是指并发socket连接复用一个IO线程(只需要一个线程,即可为多...

空空如也

空空如也

1 2 3 4 5 ... 18
收藏数 354
精华内容 141
关键字:

文件io和网络io的区别