关于io:什么是IO阻塞

一、什么是IO?咱们都晓得unix世界里、所有皆文件、而文件是什么呢?文件就是一串二进制流而已、不论socket、还是FIFO、管道、终端、对咱们来说、一切都是文件、一切都是流、在信息替换的过程中、咱们都是对这些流进行数据的收发操作、简称为I/O操作(input and output)、往流中读出数据、零碎调用read、写入数据、零碎调用write、不过话说回来了、计算机里有这么多的流、我怎么晓得要操作哪个流呢?做到这个的就是文件描述符、即通常所说的fd、一个fd就是一个整数、所以对这个整数的操作、就是对这个文件(流)的操作、咱们创立一个socket、通过零碎调用会返回一个文件描述符、那么剩下对socket的操作就会转化为对这个描述符的操作、不能不说这又是一种分层和形象的思维。 二、IO交互通常用户过程中的一个残缺IO分为两个阶: 三、IO模式1.阻塞IO(blocking I/O)A拿着一支鱼竿在河边钓鱼,并且始终在鱼竿前等,在等的时候不做其余的事件,非常分心。只有鱼上钩的时,才完结掉等的动作,把鱼钓上来。在内核将数据筹备好之前,零碎调用会始终期待所有的套接字,默认的是阻塞形式。 2.非阻塞IO(noblocking I/O)B也在河边钓鱼,然而B不想将本人的所有工夫都破费在钓鱼上,在等鱼上钩这个时间段中,B也在做其余的事件(一会看看书,一会读读报纸,一会又去看其他人的钓鱼等),但B在做这些事件的时候,每隔一个固定的工夫查看鱼是否上钩。一旦查看到有鱼上钩,就停下手中的事件,把鱼钓上来。 3.信号驱动IO(signal blocking I/O)C也在河边钓鱼,但与A、B不同的是,C比拟聪慧,他给鱼竿上挂一个铃铛,当有鱼上钩的时候,这个铃铛就会被碰响,C就会将鱼钓上来。 4.IO多路复用(I/O multiplexing)D同样也在河边钓鱼,然而D生存程度比拟好,D拿了很多的鱼竿,一次性有很多鱼竿在等,D一直的查看每个鱼竿是否有鱼上钩。减少了效率,缩小了期待的工夫。 5.异步IO(asynchronous I/O)E也想钓鱼,但E有事件,于是他雇来了F,让F帮他期待鱼上钩,一旦有鱼上钩,F就打电话给E,E就会将鱼钓上去。 阻塞水平:阻塞IO>非阻塞IO>多路转接IO>信号驱动IO>异步IO,效率是由低到高的。

September 18, 2023 · 1 min · jiezi

关于io:度加剪辑App的MMKV应用优化实践

作者 | 我爱吃海米 导读  挪动端开发中,IO密集问题在很多时候没有失去短缺的器重和解决,贸然的把IO导致的卡顿放到异步线程,可能会导致真正的问题被覆盖,前人挖坑前人踩。其实首先要想的是,数据存储形式是否正当,数据的应用形式是否正当。本文介绍度加剪辑对MMKV的应用和优化。 全文14813字,预计浏览工夫38分钟。 01 所有皆文件-挪动端IO介绍挪动端的App程序很多状况是IO密集型,比如说聊天信息的读取和发送、短视频的下载和缓存、信息流利用的图文缓存等。 绝对于计算密集,IO密集场景更加多样,比方零碎SharedPreferences和NSUserDefault自带的一些问题、Android中忙碌的binder通信、文件磁盘读取和写入、文件句柄泄露、主线程操作Sqlite导致的卡顿等,解决起来相当烫手。 IO不忙碌的状况下,主线程低频次的调用IO函数是没什么问题的。然而在IO忙碌时,IO性能急剧进化,任何IO操作都可能是压死骆驼的最初一根稻草。在平时开发测试中很难遇到IO卡顿,到了线上后才会裸露进去,iOS/Android双端根本都是如此:罕用的open零碎调用,线下测试只须要4ms,线上大把用户执行工夫超过10秒;就连获取文件长度、查看文件是否存在这种惯例操作,居然也能卡顿。 以Android线上抓到的卡顿为例(>5秒): at libcore.io.Linux.access(Native Method)at libcore.io.ForwardingOs.access(ForwardingOs.java:128)at libcore.io.BlockGuardOs.access(BlockGuardOs.java:76)at libcore.io.ForwardingOs.access(ForwardingOs.java:128)at android.app.ActivityThread$AndroidOs.access(ActivityThread.java:8121)at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:281)at java.io.File.exists(File.java:813)at com.a.b.getDownloaded(SourceFile:2)at libcore.io.Linux.stat(Native Method)at libcore.io.ForwardingOs.stat(ForwardingOs.java:853)at libcore.io.BlockGuardOs.stat(BlockGuardOs.java:420)at libcore.io.ForwardingOs.stat(ForwardingOs.java:853)at android.app.ActivityThread$AndroidOs.stat(ActivityThread.java:8897)at java.io.UnixFileSystem.getLength(UnixFileSystem.java:298)at java.io.File.length(File.java:968)具体源码能够参考 : https://android.googlesource.com/platform/libcore/+/master/lu...\_io\_Linux.cpp 最终是在C++中发动了零碎调用access()和stat()。 IO问题在很多时候被鄙视,贸然的把IO导致的卡顿放到异步线程,可能会导致真正的问题被覆盖,前人挖坑前人踩。其实首先要想的是,数据存储形式是否正当,数据的应用形式是否正当。 作为一款视频剪辑工具,度加剪辑在内存、磁盘、卡顿方面有大量的技术挑战,同时也积攒了大量的技术债。我从隔壁做图片丑化工具的团队那失去了双端的IO卡顿数据,能够说是难兄难弟,不分伯仲:有卧龙的中央,十步以内必有凤雏。 上面简略介绍度加剪辑App中对文件磁盘IO这部分的应用和优化,本文是无关MMKV。 (广告工夫:度加剪辑是一款音视频剪辑软件,针对口播用户开发了很多贴心性能,比如说疾速剪辑,各类素材也比拟丰盛,比方贴纸、文字模板等,欢送下载应用。) 02 高性能kv神器-MMKVMMKV是基于mmap的高性能通用key-value组件,性能极佳,让咱们在主线程应用kv成为了可能,堪称挪动端的Redis,实际上这两者在设计上也能找到类似的影子。 mmap是应用极其宽泛的内存映射技术,对内存的读写约等于对磁盘的读写,内存中的字节与文件中的字节相映成趣,一一对应。像Kafka和RocketMQ等消息中间件都应用了mmap,防止了数据在用户态跟内核态大量的拷贝切换, 所谓零拷贝。 为了进步性能,度加逐步从SharedPreferences向MMKV迁徙,对于Sp的卡顿逐步隐没,性能晋升成果非常哇塞。 然而,MMKV仍然有不少IO操作产生在主线程,这些函数在用户缓冲区都没有buffer(比照fread和fwrite等f打头的带有缓冲的函数),且磁盘绝对是低速设施,同步时效率较低,有时难免会呈现性能问题。 度加剪辑作为MMKV的重度甚至变态用户,随着应用越来越频繁,陆续发现了线上很多和MMKV相干的乏味问题,上面抛砖引玉简略介绍。 03 setX/encodeX卡顿-占度加剪辑总卡顿的1.2%at com.tencent.mmkv.MMKV.encodeString(Native Method)at com.tencent.mmkv.MMKV.encode(Proguard:8)通过剖析,卡顿根本都产生IO忙碌时刻。度加App在应用中充斥了大量的磁盘IO,在编辑页面会读取大量的视频文件、贴纸、字体等各种文件,像降噪、语音转文字等大量场景都须要本地写入;导出页面会在短时间内写入上G的视频到磁盘中:为了保障输入视频的清晰度,度加App设置了极高的视频和音频码率。 不可避免,当磁盘处于大规模写入状态,在视频合成导出、视频文件读取和下载、各类素材的下载过程中很容易发现MMKV卡顿的身影;通过减少研发打点数据以及其余辅助伎俩后,我大体演绎了两种卡顿产生的典型场景。 1、存储较长的字符串,例如云控json这个卡顿大部分是MMKV的重写和扩容机制引起,首先简略介绍MMKV的数据存储布局。_(https://github.com/Tencent/MMKV/wiki/design)_ MMKV在创立一个ID时,例如默认的mmkv.default,会为这个ID独自创立两个4K大小(操作系统pagesize值)的文件,寄存内容的文件和CRC校验文件。 每次插入新的key-value,以append模式在内容文件的尾部追加,取值以最初插入的数据为准,即便是已有雷同的key-value,也间接append在文件开端;key与value交替存储,与Redis的AOF非常相似。 便于了解不便,省去了key长度和value长度等其余字段: 此时MMKV的dict中有两对无效的key=>value数据: {"key1":"val3", "key2", "val2"} 重写:Append模式有个问题,当一个雷同的key一直被写入时,整个文件有局部区域是被节约掉的,因为后面的value会被前面的代替掉,只有最初插入的那组kv对才无效。所以当文件不足以寄存新增的kv数据时,MMKV会先尝试对key去重,重写文件以重整布局升高大小,相似Redis的bgrewriteaof。(重写后实际上是key2在前key1在后。) 扩容:在重写文件后,如果空间还是不够,会一直的以2倍大小扩容文件直到满足需要:JAVA中ArrayList的扩容系数是1.5,GCC中std::vector扩容系数是2,MMKV的扩容系数也是2。 size_t oldSize = fileSize;do { fileSize *= 2;} while (lenNeeded + futureUsage >= fileSize);重写和扩容都会波及到IO相干的零碎调用,重写会调用msync函数强制同步数据到磁盘;而扩容时逻辑更为简单,零碎调用次数更多: ...

August 24, 2023 · 5 min · jiezi

关于io:新一代异步IO框架-iouring-|-得物技术

1.Linux IO 模型分类 相比于kernel bypass 模式须要联合具体的硬件撑持来讲,native IO是日常工作中接触到比拟多的一种,其中同步IO在较长一段时间内被宽泛应用,通常咱们接触到的IO操作次要分为网络IO和存储IO。在大流量高并发的明天,提到网络IO,很容易想到赫赫有名的epoll  以及reactor架构。然而epoll并不属于异步IO的领域。实质上是一个同步非阻塞的架构。对于同步异步,阻塞与非阻塞的概念区别这里做简要概述: 什么是同步指过程调用接口时须要期待接口解决完数据并相应过程能力继续执行。这里重点是数据处理活逻辑执行实现并返回,如果是异步则不用期待数据实现,亦能够继续执行。同步强调的是逻辑上的秩序性; 什么是阻塞当过程调用一个阻塞的零碎函数时,该过程被 置于睡眠(Sleep)状态,这时内核调度其它过程运行,直到该过程期待的事件产生了(比 如网络上接管到数据包,或者调用sleep指定的睡眠工夫到了)它才有可能持续运行。与睡眠状态绝对的是运行(Running)状态,在Linux内核中,处于运行状态的过程分为两种状况,一种是过程正在被CPU调度,另一种是处于就绪状态随时可能被调度的过程;阻塞强调的是函数调用下过程的状态。 2.Linux常见文件操作形式2.1   open/close/read/write基本操作API 如下: #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> // 返回值:胜利返回新调配的文件描述符,出错返回-1并设置errno int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);// 返回值:胜利返回0,出错返回-1并设置errno int close(int fd); // 返回值:胜利返回读取的字节数,出错返回-1并设置errno,如果在调read之前已达到文件开端,则这次read返回0 ssize_t read(int fd, void *buf, size_t count); // 返回值:胜利返回写入的字节数,出错返回-1并设置errno ssize_t write(int fd, const void *buf, size_t count);在关上文件时能够指定为,只读,只写,读写等权限,以及阻塞或者非阻塞操作等;具体通过open函数的flags 参数指定 。这里以关上一个读写文件为例,同时定义了写文件的形式为追加写,以及应用间接IO模式操作文件,具体什么是间接IO下文会细述。open("/path/to/file", O\_RDWR|O\_APPEND|O_DIRECT);flags 可选参数如下: Flag 参数含意O_CREATE创立文件时,如果文件存在则出错返回O_EXCL如果同时指定了O_CREAT,并且文件已存在,则出错返回。O_TRUC把文件截断成0O_RDONLY只读O_WRONLY只写O_RDWR读写O_APPEND追加O_NONBLOCK非阻塞标记O_SYNC每次读写都期待物理IO操作实现O_DIRECT提供最间接IO反对通常读写操作的数据首先从用户缓冲区进入内核缓冲区,而后由内核缓冲区实现与IO设施的同步: 2.2   Mmap// 胜利执行时,mmap()返回被映射区的指针。失败时,mmap()返回MAP_FAILED[其值为(void *)-1],// error被设为以下的某个值:// 1 EACCES:拜访出错// 2 EAGAIN:文件已被锁定,或者太多的内存已被锁定// 3 EBADF:fd不是无效的文件形容词// 4 EINVAL:一个或者多个参数有效// 5 ENFILE:已达到系统对关上文件的限度// 6 ENODEV:指定文件所在的文件系统不反对内存映射// 7 ENOMEM:内存不足,或者过程已超出最大内存映射数量// 8 EPERM:权能有余,操作不容许// 9 ETXTBSY:已写的形式关上文件,同时指定MAP_DENYWRITE标记//10 SIGSEGV:试着向只读区写入//11 SIGBUS:试着拜访不属于过程的内存区void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);// 胜利执行时,munmap()返回0。失败时,munmap返回-1,error返回标记和mmap统一;// 该调用在过程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小;int munmap( void * addr, size_t len )// 过程在映射空间的对共享内容的扭转并不间接写回到磁盘文件中,往往在调用munmap()后才执行该操作。// 如果冀望内存的数据变动可能立即反馈到磁盘上,能够通过调用msync()实现。int msync( void *addr, size_t len, int flags )Mmap 是一种内存映射办法,通过将文件映射到内存的某个地址空间上,在对该地址空间的读写操作时,会触发相应的缺页异样以及脏页回写操作,从而实现文件数据的读写操作; ...

April 14, 2023 · 6 min · jiezi

关于io:IO阻塞和非阻塞同步和异步

阻塞和非阻塞阻塞的时候线程会被挂起阻塞: 当数据还没筹备好时,调用了阻塞的办法,则线程会被挂起,会让出CPU工夫片,此时是无奈解决过去的申请,须要期待其余线程来进行唤醒,该线程能力进行后续操作或者解决其余申请。 非阻塞: 意味着,当数据还没筹备好的时候,即使我调用了阻塞办法,该线程也不会被挂起,后续的申请也可能被解决。 同步同步和异步跟串行和并行十分形似。假如在一个场景下:实现一个大工作须要4个小工作。 同步的做法:须要顺次4个步骤,留神这里是顺次,也就是说实现这个步骤,须要先实现前置步骤,也就是说下一个步骤是要看上一个步骤的执行后果。 异步的做法:能够同时进行4个步骤,无需期待其余步骤的执行后果。 阻塞和同步的最实质差异在于: 即使是同步,在期待的过程中,线程是不会被挂起,也不须要让出CPU工夫片的,在IO中的体现网络编程的根本模型是:Client/Server模型两个过程之间要互相通信,其中服务端须要提供地位信息,让客户端找到本人。服务端提供IP地址和监听的端口。 客户端拿着这些信息去向服务端发动建设连贯申请,通过三次握手胜利建设连贯后,客户端就能够通过socket向服务器发送和承受音讯。 BIOBIO通信模型采纳的是典型的:一申请一应答通信模型采纳BIO通信模型的服务端,通常会由一个独立的Acceptor线程负责监听客户端的连贯。 他不负责解决申请,他只是起到一个委派工作的作用,当他接管到申请之后,会为每个客户端创立一个新的线程进行链路解决。 解决完之后,通过输入流,返回应答给客户端,而后线程被销毁,资源被回收。 该模型的最大问题就是不足弹性伸缩能力,服务端的线程个数和客户端的并发拜访数是1:1的关系。 因为线程是Java虚拟机十分贵重的资源,当线程书收缩之后,零碎的性能会随着并发量减少呈反比的趋势降落。 而且会有OOM的危险,当没有内存空间创立线程时,就无奈解决客户端申请,最终导致过程宕机或卡死,无奈对外提供服务。 最大的问题就是:每当有一个客户端申请接入时,就会创立一个线程来解决申请。 为了改良这个一线程一连贯模型,前面又演进出通过: 线程池音讯队列来实现1个或者多个线程解决N个客户端的模型。 在这里,无论是线程池和音讯队列,都是解决内存空间,线程的问题,并没有实质性地扭转同步阻塞通信实质问题所以这种优化版本的BIO也被称为是伪异步。 伪异步IO采纳线程池和工作队列能够实现一种:伪异步的IO通信 将客户端的申请封装成一个Task(该工作实现java.lang.Runnable接口),投递到音讯队列中。 如果通过线程池保护一堆解决线程,去生产队列中的音讯。 处理完毕之后,再去通过客户端就能够了,他的资源是可控的,无论客户端的申请量是多少,也不会发生变化,同样这也是他的毛病之一。 建设连贯的accpet办法、读取数据的read办法都是阻塞。 这就意味着,如果有一方解决申请或者发出请求的比较慢,或者是网络传输比较慢,那么都会影响对方。 当调用OutputStream的write办法写输入流的时候,它将会被阻塞,直到所有要发送的字节全副写入结束,或者产生异样。 在TCP/IP中,当音讯的接管方解决迟缓的时候,因为音讯滑动窗口的存在,那么它的接管窗口就会变小,就是那个TCP window size。 如果这里采纳同步阻塞IO,并且write操作被阻塞很久,直到TCP window size 大于0或者产生IO异样了。 那么通信对方返回应答工夫过长会引起的级联故障: 线程问题:如果所有的可用线程都被故障服务器阻塞,那么后续所有的IO音讯都将被队列中排队。队列问题:如果队列采纳的是有界队列,队列满了之后那么就会无奈后续解决申请;如果采纳的是无界队列,那么会有OOM危险。NIONIO,官网叫法是new IO,因为它绝对于之前出的java.io包是新增的 然而之前老的IO库都是阻塞的,New IO类库指标就是为了让Java反对非阻塞IO,所有更多的人称为Non-Block IO 缓冲区BufferBuffer是一个对象,通常是ByteBuffer类型 任何时候操作NIO中的数据,都须要通过缓冲区。 在NIO库里,所有数据操作是用缓冲区解决的。 读取数据时,是间接读到缓冲区中(这里并没有间接读到某个中央,而是都放到缓冲区中)写入数据时,写入到缓冲区缓冲区本质上是一个数组,通常是一个字节数组ByteBuffer,本身还须要保护读写地位,能够用指针或者偏移量来实现。 除了ByteBuffer还有其余根本类型缓冲区: CharBuffer:字符缓冲区ShortBuffer:短整型缓冲区IntBuffer:整形缓冲区LongBuffer:长整型缓冲区DoubleBuffer:双精度缓冲区通常是用ByteBuffer通道Channel网络数据通过Channel读取和写入Channel通道和Stream流最大的区别在于: Channel的数据流向是双向的Stream的数据流向是单向的 这就意味着:应用Channel,能够同时进行读和写,他是全双工模型。(能够联想到HTTP1.1 HTTP2.0 HTTP3.0 `websocket`) 多路复用器SelectorSelector是NIO编程的根底Selector会一直轮询注册在其上的Channel。 如果某个Channel产生读写事件,就代表这个Channel是就绪状态,会被Selector轮询进去。 而后依据SelectionKey能够获取就绪Channel的汇合,进行后续IO操作。 一个Selector能够轮询多个Channel,JDK是基于epoll代替传统的select,所以不受句柄fd的限度。 意味着,一个线程负责Selector的轮询千万个客户端, AIONIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现通过java.util.concurrent.Future类来示意异步操作的后果。在执行异步操作的时候传入一个java.nio.channelsCompletionHandler接口的实现类作为操作实现的回调 NIO2.0的异步socket通道是真正的异步非阻塞IO。 同步socket channel:SocketServerChannel异步socket channel:AsynchronousServerSocketChannel它不须要通过多路复用器(selector)对注册到外面的通过进行轮询操作,就能够实现异步读写。 AIO和NIO最大的区别在于:异步Socket Channel是被动执行对象NIO须要咱们把channel注册到selector上进行程序扫描、轮询AIO则是通过Future类,实现回调办法:completed、failed4种IO比照IO模型次要是探讨2个维度: ...

February 26, 2023 · 1 min · jiezi

关于io:io的基本原理nio

I/O(Input/Output)是计算机科学中指计算机和外部设备进行数据交换的过程。I/O模型是指用于治理过程和设施之间数据传输的技术。 io读写的基本原理操作系统将内存(虚拟内存)划分为两局部:一部分是内核空间(Kernel-Space),另一部分是用户空间(User-Space)应用程序不容许间接在内核空间区域进行读写,也不容许间接调用内核代码定义的函数。每个应用程序过程都有一个独自的用户空间,对应的过程处于用户态,用户态过程不能拜访内核空间中的数据,也不能间接调用内核函数,因而须要将过程切换到内核态能力进行零碎调用。 上面一个read或者一次write指令的大抵流程:通过零碎调用,抉择不同的内核函数进行状态切换,而后把内核缓冲区的数据复制到用户缓冲区中,(内核缓冲区是惟一的) io的模型1.同步阻塞IO首先,解释一下阻塞与非阻塞。阻塞IO指的是须要内核IO操作彻底实现后才返回到用户空间执行用户程序的操作指令。“阻塞”指的是用户程序(发动IO申请的过程或者线程)的执行状态。能够说传统的IO模型都是阻塞IO模型,并且在Java中默认创立的socket都属于阻塞IO模型。其次,解释一下同步与异步。简略来说,能够将同步与异步看成发动IO申请的两种形式。同步IO是指用户空间(过程或者线程)是被动发动IO申请的一方,零碎内核是被动接管方。异步IO则反过来,零碎内核是被动发动IO申请的一方,用户空间是被动接管方。同步阻塞IO(Blocking IO)指的是用户空间(或者线程)被动发动,须要期待内核IO操作彻底实现后才返回到用户空间的IO操作。在IO操作过程中,发动IO申请的用户过程(或者线程)处于阻塞状态。 2. 同步非阻塞IO非阻塞IO(Non-Blocking IO,NIO)指的是用户空间的程序不须要期待内核IO操作彻底实现,能够立刻返回用户空间去执行后续的指令,即发动IO申请的用户过程(或者线程)处于非阻塞状态,与此同时,内核会立刻返回给用户一个IO状态值。阻塞和非阻塞的区别是什么呢?阻塞是指用户过程(或者线程)始终在期待,而不能做别的事件;非阻塞是指用户过程(或者线程)取得内核返回的状态值就返回本人的空间,能够去做别的事件。在Java中,非阻塞IO的socket被设置为NONBLOCK模式。阐明同步非阻塞IO也能够简称为NIO,然而它不是Java编程中的NIO。Java编程中的NIO(New IO)类库组件所归属的不是根底IO模型中的NIO模型,而是IO多路复用模型。同步非阻塞IO指的是用户过程被动发动,不须要期待内核IO操作彻底实现就能立刻返回用户空间的IO操作。在IO操作过程中,发动IO申请的用户过程(或者线程)处于非阻塞状态。 3. IO多路复用为了进步性能,操作系统引入了一种新的零碎调用,专门用于查问IO文件描述符(含socket连贯)的就绪状态。在Linux零碎中,新的零碎调用为select/epoll零碎调用。通过该零碎调用,一个用户过程(或者线程)能够监督多个文件描述符,一旦某个描述符就绪(个别是内核缓冲区可读/可写),内核就可能将文件描述符的就绪状态返回给用户过程(或者线程),用户空间能够依据文件描述符的就绪状态进行相应的IO零碎调用。IO多路复用(IO Multiplexing)属于一种经典的Reactor模式实现,有时也称为异步阻塞IO,Java中的Selector属于这种模型。 4. 异步IO异步IO(Asynchronous IO,AIO)指的是用户空间的线程变成被动接收者,而内核空间成为被动调用者。在异步IO模型中,当用户线程收到告诉时,数据曾经被内核读取结束并放在了用户缓冲区内,内核在IO实现后告诉用户线程间接应用即可。异步IO相似于Java中典型的回调模式,用户过程(或者线程)向内核空间注册了各种IO事件的回调函数,由内核去被动调用。Java AIO 也被称为 NIO2.0,提供了异步I/O的形式,用法和规范的I/O有十分大的差别。 5.半同步半阻塞半异步IO目前在Thrift中有所体现(ThreadedSelectorServer)半同步半阻塞半异步I/O是一种计算机网络通信模型,是一种折衷的解决方案,旨在均衡同步I/O、阻塞I/O和异步I/O的优缺点。在半同步半阻塞半异步I/O模型中,服务器端通过阻塞形式期待客户端的连贯申请,一旦建设连贯,服务器端将该连贯转换为异步形式解决申请。这样既能够解决同步I/O的性能问题,又能够保障客户端的申请不会被服务器端长时间阻塞。半同步半阻塞半异步I/O模型在性能和可靠性方面较好的均衡了同步I/O和异步I/O的优缺点,因而在许多网络系统中失去了宽泛的利用。 nio是什么?NIO是“New I/O”的缩写,是一组Java API,提供非阻塞、可扩大和高性能的I/O操作。NIO在Java 1.4中被引入,作为传统的阻塞I/O(BIO)模型的替代品,用于解决高性能和高并发利用。NIO提供了若干要害个性,如通道、缓冲区、选择器和非阻塞I/O操作,使得Java应用程序可能更无效、高效和可扩大地解决I/O操作。NIO宽泛用于各种类型的应用程序,包含服务器、代理和其余网络应用程序。 NIO 的外围原理:**缓冲区:NIO 应用缓冲区(Buffer)作为数据容器,数据读写时都是通过缓冲区进行的。通道:NIO 应用通道(Channel)作为数据的读写入口,所有的数据读写操作都是通过通道实现的。选择器:NIO 应用选择器(Selector)来治理多个通道,当通道筹备好读写操作时,选择器可能疾速地发现并告诉相应的线程。** 在 NIO 中,线程不再须要阻塞期待 I/O 操作实现,而是在读写操作实现时由选择器告诉线程,这大大提高了零碎的效率。 java版代码import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Iterator;public class NioServer { public static void main(String[] args) throws IOException { Selector selector = Selector.open(); ServerSocketChannel serverSocket = ServerSocketChannel.open(); serverSocket.bind(new InetSocketAddress("localhost", 8080)); serverSocket.configureBlocking(false); serverSocket.register(selector, SelectionKey.OP_ACCEPT); // 示意示通道感兴趣的事件类型 SelectionKey.OP_ACCEPT,从而将通道注册到选择器中。 while (true) { selector.select(); Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); while (keys.hasNext()) { SelectionKey key = keys.next(); keys.remove(); if (!key.isValid()) { continue; } if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); // 将一个通道注册到选择器中 } else if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(256); int read = client.read(buffer); if (read == -1) { client.close(); key.cancel(); continue; } buffer.flip(); client.write(buffer); } } } }}cpp版本#include <iostream>#include <string>#include <chrono>#include <thread>#include <vector>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <unistd.h>#include <fcntl.h>using namespace std;int set_nonblock(int fd) { int flags;#if defined(O_NONBLOCK) if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { // 获取文件描述符 fd 的标记 flags = 0; } return fcntl(fd, F_SETFL, flags | O_NONBLOCK); // flags 和 O_NONBLOCK 标记进行按位或运算,从而将 O_NONBLOCK 标记增加到原有的标记中。设置文件描述符 fd 为非阻塞模式#else flags = 1; return ioctl(fd, FIOBIO, &flags); //文件描述符设置为非阻塞模式。ioctl 操作通常用于特定的设施驱动程序#endif}int main(int argc, char **argv) { int MasterSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); vector<int> SlaveSockets; sockaddr_in SockAddr; SockAddr.sin_family = AF_INET; SockAddr.sin_port = htons(12345); SockAddr.sin_addr.s_addr = htonl(INADDR_ANY); bind(MasterSocket, (sockaddr *)&SockAddr, sizeof(SockAddr)); set_nonblock(MasterSocket); // 将MasterSocket设置成非阻塞状态 listen(MasterSocket, SOMAXCONN); while (true) { fd_set Set; FD_ZERO(&Set); /*将set清零使汇合中不含任何fd*/ FD_SET(MasterSocket, &Set); /*将MasterSocket退出set汇合*/ for (int Slave : SlaveSockets) { FD_SET(Slave, &Set); /*将Slave注册到选择器中*/ } int Max = max(MasterSocket, *max_element(SlaveSockets.begin(), SlaveSockets.end())); select(Max + 1, &Set, NULL, NULL, NULL); // 监督的最大文件描述符加1 if (FD_ISSET(MasterSocket, &Set)) { //*MasterSocket是否在set汇合中*/ int Slave = accept(MasterSocket, 0, 0); // 期待连贯 set_nonblock(Slave); //设置非阻塞 SlaveSockets.push_back(Slave); } for (int Slave : SlaveSockets) { if (FD_ISSET(Slave, &Set)) { static char Buffer[1024]; int RecvSize = recv(Slave, Buffer, 1024, MSG_NOSIGNAL); // 从而从套接字中接收数据并将其存储到 Buffer 缓冲区中。 if ((RecvSize == 0) && (errno != EAGAIN)) { shutdown(Slave, SHUT_RDWR); close(Slave); SlaveSockets.erase(remove(SlaveSockets.begin(), SlaveSockets.end(), Slave)); } else if (RecvSize > 0) { send(Slave, Buffer, RecvSize, MSG_NOSIGNAL); // 将 } } } } return 0;}摘要:java高并发编程 ...

February 12, 2023 · 2 min · jiezi

关于io:IO优化1

我的项目须要解决上游180g的gz文件,读取文件内容过滤去重后仍以gz格局传给上游,上面是几种解决思路. 1.间接应用linux less命令绕过压缩/解压缩,实测效率极差,放弃. 2.Java api GZIPOutputStream,待测试,预计体现不会太好 3.单线程bash gzip/gunzipgunzip耗时16分钟gzip耗时18分钟 2022-08-01 13:21 started2022-08-01 13:55 finished4.多线程 bash gzip/gunzipio操作的瓶颈应该在磁盘,感觉多线程效率不会高,决定做个测试.(脱敏伪代码) public class CopyFileMain { static Integer callShell(String command) { try { Process p = Runtime.getRuntime().exec(command); return p.waitFor(); } catch (Exception e) { // } return -1; } public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); int fileSize = 3; String command = "bash gunzip xx.gz"; List<Future<?>> list = new ArrayList<>(); for (int i = 0; i < fileSize; i++) { Future<?> result = executorService.submit(() -> callShell(command)); list.add(result); } while (true) { boolean finished = false; for (Future<?> future : list) { if(!future.isDone()){ finished=false; break; } finished=true; } if(finished){ System.out.println("finished"); break; } } } shutdown();}gunzip耗时:13分钟临时得出后果: 瓶颈在磁盘io ...

August 2, 2022 · 1 min · jiezi

关于io:高级IO模型之kqueue和epoll

简介任何一个程序都离不开IO,有些是很显著的IO,比方文件的读写,也有一些是不显著的IO,比方网络数据的传输等。那么这些IO都有那些模式呢?咱们在应用中应该如何抉择呢?高级的IO模型kqueue和epoll是怎么工作的呢?一起来看看吧。 block IO和nonblocking IO大家先来理解一下IO模型中最简略的两个模型:阻塞IO和非阻塞IO。 比方咱们有多个线程要从一个Socket server中读取数据,那么这个读取过程其实能够分成两个局部,第一局部是期待socket的数据筹备结束,第二局部是读取对应的数据进行业务解决。对于阻塞IO来说,它的工作流程是这样的: 一个线程期待socket通道数据筹备结束。当数据筹备结束之后,线程进行程序处理。其余线程期待第一个线程完结之后,持续上述流程。为什么叫做阻塞IO呢?这是因为当一个线程正在执行的过程中,其余线程只能期待,也就是说这个IO被阻塞了。 什么叫做非阻塞IO呢? 还是下面的例子,如果在非阻塞IO中它的工作流程是这样的: 一个线程尝试读取socket的数据。如果socket中数据没有筹备好,那么立刻返回。线程持续尝试读取socket的数据。如果socket中的数据筹备好了,那么这个线程继续执行后续的程序处理步骤。为什么叫做非阻塞IO呢?这是因为线程如果查问到socket没有数据,就会立即返回。并不会将这个socket的IO操作阻塞。 从下面的剖析能够看到,尽管非阻塞IO不会阻塞Socket,然而因为它会始终轮询Socket,所以并不会开释Socket。 IO多路复用和selectIO多路复用有很多种模型,select是最为常见的一种。实时不论是netty还是JAVA的NIO应用的都是select模型。 select模型是怎么工作的呢? 事实上select模型和非阻塞IO有点类似,不同的是select模型中有一个独自的线程专门用来查看socket中的数据是否就绪。如果发现数据曾经就绪,select能够通过之前注册的事件处理器,抉择告诉具体的某一个数据处理线程。 这样的益处是尽管select这个线程自身是阻塞的,然而其余用来真正解决数据的线程却是非阻塞的。并且一个select线程其实能够用来监控多个socket连贯,从而进步了IO的解决效率,因而select模型被利用在多个场合中。 为了更加具体的理解select的原理,咱们来看一下unix下的select办法: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);先来解释一下这几个参数的含意,咱们晓得unix零碎中,所有的对象都是文件,所以这里的fd示意的就是file descriptor ,也就是文件描述符。 fds示意的是 file descriptor sets,也就是文件描述符汇合。 nfds是一个整数值,示意的是文件描述符汇合中最大值+1. readfds是要查看的文件读取的描述符汇合。 writefds是要查看的文件写入的描述符汇合。 errorfds是要查看的文件异样描述符汇合。 timeout是超时工夫,示意的是期待抉择实现的最大距离。 其工作原理是轮询所有的file descriptors,而后找到要监控的那些文件描述符, pollpoll和select类很相似,只是形容fd汇合的形式不同. poll次要是用在POSIX零碎中。 epoll实时上,select和poll尽管都是多路复用IO,然而他们都有些毛病。而epoll和kqueue就是对他们的优化。 epoll是linux零碎中的系统命令,能够将其看做是event poll。首次是在linux外围的2.5.44版本引入的。 次要用来监控多个file descriptors其中的IO是否ready。 对于传统的select和poll来说,因为须要一直的遍历所有的file descriptors,所以每一次的select的执行效率是O(n) ,然而对于epoll来说,这个工夫能够晋升到O(1)。 这是因为epoll会在具体的监控事件产生的时候触发告诉,所以不须要应用像select这样的轮询,其效率会更高。 epoll 应用红黑树 (RB-tree) 数据结构来跟踪以后正在监督的所有文件描述符。 epoll有三个api函数: int epoll_create1(int flags);用来创立一个epoll对象,并且返回它的file descriptor。传入的flags能够用来管制epoll的体现。 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);这个办法用来对epoll进行管制,能够用来监控具体哪些file descriptor和哪些事件。 ...

March 21, 2022 · 1 min · jiezi

关于io:性能分析之IO分析从IO高到具体文件

IO的性能剖析始终是性能剖析的重点之一,剖析的思路是:在代码的逻辑清晰的状况下,是齐全能够晓得哪些文件是频繁读写的。然而对性能剖析人员来说,通常是面对一个不是本人编写的零碎,有时还是多个团队单干产生的零碎。这时就会呈现很多的推诿和争执。如果能够迅速地把问题到一个段具体的代码,到一个具体的文件,那就能够进步沟通的效率。 通常状况在linux 环境下,通过 vmstat 或者 iostat 命令能够发现磁盘IO的异样,能够看到零碎级的磁盘读写量及CPU占用率,但无奈明确定位到是什么过程在作怪,装置iotop 后,能够定位到过程,但并不知道改过程在操作什么文件。本文是思考从零碎级的工具来实现这个操作,比拟具备通用性。在这之前须要先了解一下文件的一个重要的属性:inode。什么是inode呢?先来看一个示意图:磁盘上最小的存储单元是扇区sector,每8个扇区组成一个块block(4096字节)。如下所示:[root@7DGroup2 ~]# tune2fs -l /dev/vda1|grep BlockBlock count: 10485504Block size: 4096Blocks per group: 32768[root@7DGroup2 ~]#文件的存储就是由这些 块组成的,当块多了之后就成了如下这样(其实磁盘上的块比这个图中多得多,这里只是示意图):其中红色的这部分是存储的文件,咱们通常在文件系统中间接ls或者用其余命令操作文件的时候是依据门路来操作的,那些是下层的命令。当咱们执行了一个命令之后,操作系统会来找到这些文件做相应的操作,怎么找到这些文件呢,那就须要inode了。Inode用来存储这些文件的元信息,也就是索引节点,它包含的信息有:· 字节数· User ID· Group ID· 读、写、执行权限· 工夫戳,共有三个:ctime指inode上一次变动的工夫,mtime指文件内容上一次变动的工夫,atime指文件上一次关上的工夫· 链接数,有多少文件名指向这个inode· 文件数据block的地位通过这些信息,咱们能力实现对文件的操作。这个inode其实也是存储在磁盘上的,也须要占用一些空间,如上图中的绿色局部所示。当咱们在零碎级看到IO过高的时候,比方下图所示:从上图能够看到,这零碎简直所有的CPU都在等IO。这时怎么办?就用咱们后面提到的剖析的思路,查看过程级和线程级的IO,进而找到具体的文件。上面咱们来具体实现。这里咱们用的是systemtap,这个工具7Dgroup之前的文章中提到的,但没有开展说。前面如果有可能咱们再多写些相似的工具原理和应用办法。Systemtap的逻辑图如下:从逻辑图上看,它工作在内核层面,不是shell的层面。SystemTap为咱们开启了一扇通往零碎内核的大门,SystemTap 自带的examples中提供一些磁盘IO相干的监控例子。以 iotop.stp 为例,源码如下: #!/usr/bin/stapglobal reads, writes, total_ioprobe vfs.read.return {reads[execname()] += bytes_read}probe vfs.write.return {writes[execname()] += bytes_written}# print top 10 IO processes every 5 secondsprobe timer.s(5) {foreach (name in writes) total_io[name] += writes[name]foreach (name in reads) total_io[name] += reads[name]printf ("%16s\t%10s\t%10s\n", "Process", "KB Read", "KB Written")foreach (name in total_io- limit 10) printf("%16s\t%10d\t%10d\n", name, reads[name]/1024, writes[name]/1024)delete readsdelete writesdelete total_ioprint("\n")}执行的后果是:每隔5秒打印读写总量排前10位的过程。该脚本有两个问题:依照过程名字统计,存在统计误差,过程名统一,但PID不一样的过程,都统计到一起;咱们仍然不能晓得过程操作了什么文件。通过对probe点的剖析(sudo stap -L'vfs.{write,read}'),咱们能够晓得,vfs.read,vfs.write有局部变量 ino 能够利用,ino 是文件的inode,这样咱们就能够明确的探测到读写量最多的过程及文件。$ sudo stap -L 'vfs.{write,read}'vfs.read file:long pos:long buf:long bytes_to_read:long dev:long devname:string ino:long name:string argstr:string $file:struct file* $buf:char $count:size_t $pos:loff_tvfs.write file:long pos:long buf:long bytes_to_write:long dev:long devname:string ino:long name:string argstr:string $file:struct file* $buf:char const $count:size_t $pos:loff_t扩大过的脚本如下: ...

June 18, 2021 · 2 min · jiezi

关于io:6-io

一个过程的地址空间分为用户空间user space和内核空间kernel space。 利用程序运行在用户空间。内核空间进行零碎态级别的资源无关的操作,比方文件治理,过程通信,内存治理。 用户空间的程序不能间接拜访内核空间,想执行io操作的时候,只能发动零碎调用(io调用)申请操作系统的内核来执行io操作。 应用程序发动io调用后,1.内核期待io设施筹备好数据 2.内核将数据从内核空间拷贝到用户空间。 5种常见io模型 同步阻塞io 同步非阻塞io io多路复用 信号驱动io 异步io同步非阻塞:内核筹备数据、数据就绪时重复调用read,不阻塞。拷贝数据时阻塞。轮询非常耗费cpu资源。 java中常见的三种iobio blocking io属于同步阻塞io,应用程序发动read调用后,会始终阻塞,直到内核把数据拷贝到用户空间。nio non-blocking io属于io多路复用,线程先发动select调用,询问内核数据是否准备就绪,等内核把数据筹备好了,用户线程再发动read调用。read调用的过程还是阻塞的。目前反对 IO 多路复用的零碎调用,有 select,epoll 等等。IO 多路复用模型,通过缩小有效的零碎调用,缩小了对 CPU 资源的耗费。Java 中的 NIO ,有一个十分重要的选择器 ( Selector ) 的概念,也能够被称为 多路复用器。通过它,只须要一个线程便能够治理多个客户端连贯。当客户端数据到了之后,才会为其服务。 AIO (Asynchronous I/O)就是 NIO 2,是异步 IO 模型,基于事件和回调机制实现,利用操作之后会间接返回,不会梗塞在那里,当后盾解决实现,操作系统会告诉相应的线程进行后续的操作。

June 3, 2021 · 1 min · jiezi

关于i-o:Linux-IO-原理和-Zerocopy-技术全面揭秘

博客原文https://strikefreedom.top/lin... 导言现在的网络应用早已从 CPU 密集型转向了 I/O 密集型,网络服务器大多是基于 C-S 模型,也即 客户端 - 服务端 模型,客户端须要和服务端进行大量的网络通信,这也决定了古代网络应用的性能瓶颈:I/O。 传统的 Linux 操作系统的规范 I/O 接口是基于数据拷贝操作的,即 I/O 操作会导致数据在操作系统内核地址空间的缓冲区和用户过程地址空间定义的缓冲区之间进行传输。设置缓冲区最大的益处是能够缩小磁盘 I/O 的操作,如果所申请的数据曾经寄存在操作系统的高速缓冲存储器中,那么就不须要再进行理论的物理磁盘 I/O 操作;然而传统的 Linux I/O 在数据传输过程中的数据拷贝操作深度依赖 CPU,也就是说 I/O 过程须要 CPU 去执行数据拷贝的操作,因而导致了极大的零碎开销,限度了操作系统无效进行数据传输操作的能力。 I/O 是决定网络服务器性能瓶颈的要害,而传统的 Linux I/O 机制又会导致大量的数据拷贝操作,损耗性能,所以咱们亟需一种新的技术来解决数据大量拷贝的问题,这个答案就是零拷贝(Zero-copy)。 计算机存储器既然要剖析 Linux I/O,就不能不理解计算机的各类存储器。 存储器是计算机的核心部件之一,在齐全现实的状态下,存储器应该要同时具备以下三种个性: 速度足够快:存储器的存取速度该当快于 CPU 执行一条指令,这样 CPU 的效率才不会受限于存储器容量足够大:容量可能存储计算机所需的全副数据价格足够便宜:价格低廉,所有类型的计算机都能装备然而事实往往是残暴的,咱们目前的计算机技术无奈同时满足上述的三个条件,于是古代计算机的存储器设计采纳了一种分档次的构造: 从顶至底,古代计算机里的存储器类型别离有:寄存器、高速缓存、主存和磁盘,这些存储器的速度逐级递加而容量逐级递增。存取速度最快的是寄存器,因为寄存器的制作资料和 CPU 是雷同的,所以速度和 CPU 一样快,CPU 拜访寄存器是没有时延的,然而因为价格昂贵,因而容量也极小,个别 32 位的 CPU 装备的寄存器容量是 32✖️32 Bit,64 位的 CPU 则是 64✖️64 Bit,不论是 32 位还是 64 位,寄存器容量都小于 1 KB,且寄存器也必须通过软件自行治理。 ...

December 28, 2020 · 12 min · jiezi

关于io:彻底搞懂-IO-底层原理

武侠小说里有很多的“心法”和“招式”。计算机技术里的“心法”和“招式”呢,咱们能够简称为“道”和“术”; “道”  最根底的计算机实践,暗藏于表象之下,十分形象、艰涩难懂,须要用具象化的事物加以了解;“术” 具体的技能,它有可能是一门语言,比方:python 出手见效快; 咱们明天要给大家讲的底层的IO就属于“道”的领域,看上去简略,实则形象。并且在它之上衍生出了语言层面用于实战的技术,比方咱们相熟的java语言中的NIO或者像Netty这样的框架。 一、凌乱的 IO 概念IO是Input和Output的缩写,即输出和输入。狭义上的围绕计算机的输入输出有很多:鼠标、键盘、扫描仪等等。而咱们明天要探讨的是在计算机外面,次要是作用在内存、网卡、硬盘等硬件设施上的输入输出操作。 谈起IO的模型,大多数人脑子里必定是一坨凌乱的概念,“阻塞”、“非阻塞”,“同步”、“异步”有什么区别?很多同学傻傻分不清,有尝试去搜寻相干材料去探索假相,后果又被吞没在茫茫的概念之中。 这里尝试简略地去解释下为啥会呈现这种景象,其中一个很重要的起因就是大家看到的材料对概念的解释都站在了不同的角度,有的站在了底层内核的视角,有的间接在java层面或者Netty框架层面给大家介绍API,所以给大家造成了肯定水平的困扰。 所以在开篇之前,还是要说下本文所站的视角,咱们将会从底层内核的层面给大家解说下IO。因为万变不离其宗,只有理解了底层原理,不论语言层面如何花里胡哨,咱们都能以不变应万变。 二、用户空间和内核空间为了便于大家了解简单的IO以及零拷贝相干的技术,咱们还是得花点工夫在回顾下操作系统相干的常识。这一节咱们重点看下用户空间和内核空间,基于此前面咱们能力更好地聊聊多路复用和零拷贝。 硬 件 层(Hardware)  包含和咱们熟知的和IO相干的CPU、内存、磁盘和网卡几个硬件;内核空间(Kernel Space)  计算机开机后首先会运行内核程序,内核程序占用的一块公有的空间就是内核空间,并且可反对拜访CPU所有的指令集(ring0 - ring3)以及所有的内存空间、IO及硬件设施;用户空间(User Space) 每个一般的用户过程都有一个独自的用户空间,用户空间只能拜访受限的资源(CPU的“保护模式”)也就是说用户空间是无奈间接操作像内存、网卡和磁盘等硬件的;如上所述,那咱们可能会有疑难,用户空间的过程想要去拜访或操作磁盘和网卡该怎么办呢? 为此,操作系统在内核中开拓了一块惟一且非法的调用入口“System Call Interface”,也就是咱们常说的零碎调用,零碎调用为下层用户提供了一组可能操作底层硬件的API。这样一来,用户过程就能够通过零碎调用拜访到操作系统内核,进而就可能间接地实现对底层硬件的操作。这个拜访的过程也即用户态到内核态的切换。常见的零碎调用有很多,比方:内存映射mmap()、文件操作类的open()、IO读写read()、write()等等。 三、IO模型1、 BIO(Blocking IO)咱们先看一下大家都相熟的BIO模型的 Java 伪代码: ServerSocket serverSocket = new ServerSocket(8080); // step1: 创立一个ServerSocket,并监听8080端口while(true) { // step2: 主线程进入死循环 Socket socket = serverSocket.accept(); // step3: 线程阻塞,开启监听 BufferedReader reader = new BufferedReader(nwe InputStreamReader(socket.getInputStream())); System.out.println("read data: " + reader.readLine()); // step4: 数据读取 PrintWriter print = new PrintWriter(socket.getOutputStream(), true); print.println("write data"); // step5: socket数据写入}这段代码能够简略了解成一下几个步骤: ...

November 30, 2020 · 2 min · jiezi

关于io:Linux服务器开发监控之-IO

简介能够通过如下命令查看与 IO 相干的零碎信息。 tune2fs -l /dev/sda7 ← 读取superblock信息 # blockdev --getbsz /dev/sda7 ← 获取block大小tune2fs -l /dev/sda7 | grep "Block size" ← 同上 # dumpe2fs /dev/sda7 | grep "Block size" ← 同上 # stat /boot/ | grep "IO Block" ← 同上 # fdisk -l ← 硬盘的扇区大小(Sector Size) 在 WiKi 中的定义:A “block”, a contiguous number of bytes, is the minimum unit of memory that is read from and written to a disk by a disk driver。 ...

November 18, 2020 · 7 min · jiezi

关于io:IQKeyboardManager-源代码看看

IQKeyboardManager 三步走大家都用 IQKeyboardManager, IQKeyboardManager 引入,就治理好了 第 1 步,注册零碎告诉,取得键盘事件从键盘事件中,失去输出文本框对象, UITextField / UITextView 的实例 IQKeyboardManager 初始化的时候,就实现了这些 第 2 步,计算出以后文本框的地位, 并挪动有了文本框,要找到他以后的地位,frame 就要从文本框溯源,找到他的根视图控制器 而后计算出以后文本框在哪个地位适合, 挪动过来,就好了 2.1 , 计算出适合的地位先算出,该文本框在根视图的地位 再算出,该文本框在以后窗口, KeyWindow, 中的适合地位 2.2,键盘呈现,与键盘隐没开始编辑,键盘呈现,挪动地位 完结编辑,键盘隐没,还原地位 3,状况判断UIView 上搁置几个 UITextField / UITextView ,好解决 UIView 上搁置 UITableView, UITableView 上的一个 cell,下面摆放 UITextField / UITextView,就简单了一些 3.1 非凡类解决,对于 UIAlertController 的输入框,不必解决 比拟非凡的,还有 UITableViewController、UISearchBar、 _UIAlertControllerTextFieldViewController 0, 键盘治理,很简略对于一个输入框 UITextField , 搁置在 UIView 上, 键盘进去了,这个 UITextField 的地位,要适当, 通过两个告诉解决掉, 个别状况下,键盘进去,把 UITextField 地位放高一点, 键盘隐没,把 UITextField 地位放回原处 ...

November 8, 2020 · 5 min · jiezi

关于io:网络的5种IO模型以及零拷贝

一、什么是IOIO是输出input输入output的首字母缩写模式,直观意思是计算机输入输出,它形容的是计算机的数据流动的过程,因而IO第一大特色是有数据的流动;另外,对于一次IO,它到底是输出还是输入,是针对不同的主体而言的,不同的主体有不同的形容。然而对于一个Java程序员来说,咱们个别把程序当做IO的主体,也能够了解为内存中的过程。那么对于IO的整个过程大体上分为2个局部,第一个局部为IO的调用,第二个过程为IO的执行。IO的调用指的就是零碎调用,IO的执行指的是在内核中相干数据的处理过程,这个过程是由操作系统实现的,与程序员无关。 二、一些基本概念阻塞IO:申请过程始终期待IO准备就绪。非阻塞IO:申请过程不会期待IO准备就绪。同步IO操作:导致申请过程阻塞,晓得IO操作实现。异步IO操作:不导致申请过程阻塞。 举个小例子来了解阻塞,非阻塞,同步和异步的关系,咱们晓得编写一个程序能够有多个函数,每一个函数的执行都是互相独立的,然而 对于一个程序的执行过程,每一个函数都是必须的,那么如果咱们须要期待一个函数的执行完结而后返回一个后果(比方接口调用),那么咱们说该函数的调用是阻塞的,对于至多有一个函数调用阻塞的程序,在执行的过程中,必然存在阻塞的一个过程,那么咱们就说该程序的执行是同步的,对于异步天然就是所有的函数执行过程都是非阻塞的。这里的程序就是一次残缺的IO,一个函数为IO在执行过程中的一个独立的小片段。 咱们晓得在Linux操作系统中内存分为内核空间和用户空间,而所有的IO操作都得取得内核的反对,然而因为用户态的过程无奈间接进行内核的IO操作,所以内核空间提供了零碎调用,使得处于用户态的过程能够间接执行IO操作,IO调用的目标是将过程的外部数据迁徙到内部即输入,或将内部数据迁徙到过程外部即输出。而在这里探讨的数据通常是socket过程外部的数据。 三、5种IO模型1、首先咱们来看看一次网络申请中服务端做了哪些操作。在上图中,每一个客户端会与服务端建设一次socket连贯,而服务端获取连贯后,对于所有的数据的读取都得通过操作系统的内核,通过零碎调用内核将数据复制到用户过程的缓冲区,而后才实现客户端的过程与客户端的交互。那么依据零碎调用的形式的不同分为阻塞和非阻塞,依据零碎解决利用过程的形式不同分为同步和异步。 2、阻塞式IO每一次客户端产生的socket连贯实际上是一个文件描述符fd,而每一个用户过程读取的实际上也是一个个文件描述符fd,在该期间的零碎调用函数会期待网络申请的数据的达到和数据从内核空间复制到用户过程空间,也就是说,无论是第一阶段的IO调用还是第二阶段的IO执行都会阻塞,那么就像图中所画的一样,对于多个客户端连贯,只能开拓多个线程来解决。 3、非阻塞IO模型对于阻塞IO模型来说最大的问题就体现在阻塞2字上,那么为了解决这个问题,零碎的内核因而产生了扭转。在内核中socket反对了非阻塞状态。既然这个socket是不阻塞的了,那么就能够应用一个过程解决客户端的连贯,该过程外部写一个死循环,一直的询问每一个连贯的网络数据是否曾经达到。此时轮询产生在用户空间,然而该过程仍然须要本人解决所有的连贯,所以该期间为同步非阻塞IO期间,也即为NIO。 4、IO多路复用在非阻塞IO模型中,尽管解决了IO调用阻塞的问题,然而产生了新的问题,如果当初有1万个连贯,那么用户线程会调用1万次的零碎调用read来进行解决,在用户空间这种开销太大,那么当初须要解决这个问题,思路就是让用户过程缩小零碎调用,然而用户本人是实现不了的,所以这就导致了内核产生了进一步变动。在内核空间中帮忙用户过程遍历所有的文件描述符,将数据筹备好的文件描述符返回给用户过程。该形式是同步阻塞IO,因为在第一阶段的IO调用会阻塞过程。 4.1、select/poll为了让内核帮忙用户过程实现文件描述符的遍历,内核减少了零碎调用select/poll(select与poll实质上没有什么不同,就是poll缩小了文件描述符的个数限度),当初用户过程只须要调用select零碎调用函数,并且将文件描述符全副传递给select就能够让内核帮忙用户过程实现所有的查问,而后将数据筹备好的文件描述符再返回给用户过程,最初用户过程顺次调用其余零碎调用函数实现IO的执行过程。 4.2、epoll在select实现的多路复用中仍然存在一些问题。 1、用户过程须要传递所有的文件描述符,而后内核将数据筹备好的文件描述符再次传递回去,这种数据的拷贝升高了IO的速度。2、内核仍然会执行复杂度为O(n)的被动遍历操作。对于第一个问题,提出了一个共享空间的概念,这个空间为用户过程和内核过程所共享,并且提供了mmap零碎调用,实现用户空间和内核空间到共享空间的映射,这样用户过程就能够将1万个文件描述符写到共享空间中的红黑树上,而后内核将准备就绪的文件描述符写入共享空间的链表中,而用户过程发现链表中有数据了就间接读取而后调用read执行IO即可。 对于第二个问题,内核引入了事件驱动机制(相似于中断),不再被动遍历所有的文件描述符,而是通过事件驱动的形式被动告诉内核该文件描述符的数据筹备结束了,而后内核就将其写入链表中即可。 对于epoll来说在第一阶段的epoll_wait仍然是阻塞的,故也是同步阻塞式IO。 5、信号驱动式IO在IO执行的数据筹备阶段,不会阻塞用户过程。当用户过程须要期待数据的时候,会向内核发送一个信号,通知内核须要数据,而后用户过程就持续做别的事件去了,而当内核中的数据筹备好之后,内核立马发给用户过程一个信号,用户过程收到信号之后,立马调用recvfrom,去查收数据。该IO模型应用的较少。 6、异步IO(AIO)利用过程通过 aio_read 告知内核启动某个操作,并且在整个操作实现之后再告诉利用过程,包含把数据从内核空间拷贝到用户空间。信号驱动 IO 是内核告诉咱们何时能够启动一个 IO 操作,而异步 IO 模型是由内核告诉咱们 IO 操作何时实现。是真正意义上的无阻塞的IO操作,然而目前只有windows反对AIO,linux内核临时不反对。 四、总结前四种模型的次要区别于第一阶段,因为他们的第二阶段都是一样的:在数据从内核拷贝到利用过程的缓冲区期间,过程都会阻塞。相同,异步 IO 模型在这两个阶段都不会阻塞,从而不同于其余四种模型。 五、间接内存与零拷贝间接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机标准中农定义的内存区域。间接内存申请空间消耗更高的性能,间接内存IO读写的性能要优于一般的堆内存,对于java程序来说,零碎内核读取堆类的对象须要依据代码段计算其偏移量来获取对象地址,效率较慢,不太适宜网络IO的场景,对于间接内存来说更加适宜IO操作,内核读取寄存在间接内存中的对象较为不便,因为其地址就是袒露的过程虚拟地址,不须要jvm翻译。那么就能够应用mmap开拓一块间接内存mapbuffer和内核空间共享,并且该间接内存能够间接映射到磁盘上的文件,这样就能够通过调用本地的put而不必调用零碎调用write就能够将数据间接写入磁盘,RandomAccessFile类就是通过开拓mapbuffer实现的读写磁盘。 以音讯队列Kafka来说,有生产者和消费者,对于生产者,从网络发来一个音讯msg并且被拷贝到内核缓冲区,该音讯通过Kafka调用recvfrom将内核中的msg读到队列中,而后加上音讯头head,再将该音讯写入磁盘。如果没有mmap的话,就会调用一个write零碎调用将该音讯写入内核缓冲区,而后内核将该音讯再写入磁盘。在此过程中呈现一次80中断和2次拷贝。但实际上Kafka应用的是mmap开拓了间接内存到磁盘的映射,间接应用put将音讯写入磁盘。实际上也是通过内核拜访该共享区域将该音讯写入的磁盘。同时在Kafka中有一个概念叫segment,个别为1G大小。它会充分利用磁盘的程序性,只追加数据,不批改数据。而mmap会间接开拓1G的间接内存,并且间接与segment造成映射关系,在segment满了的时候再开拓一个新的segment,清空间接内存而后在与新的segment造成映射关系。 零拷贝形容的是CPU不执行拷贝数据从一个存储区域到另一个存储区域的工作,这通常用于通过网络传输一个文件时以缩小CPU周期和内存带宽。 在Kafka的消费者读取数据的时候,如果以后消费者想读取的数据是不是以后间接内存所映射的segment怎么办?如果没有零拷贝的话,过程会先去调用read读取,而后数据会从磁盘被拷贝到内核,而后内核再拷贝到Kafka队列,过程再调用write将数据拷贝到内核缓冲区,最初再发送给消费者。实际上能够发现,数据没有必要读到Kafka队列,间接读到内核的缓冲区的时候发送给消费者就行了。实际上,linux内核中有一个零碎调用就是实现了这种形式读取数据——sendfile,它有2个参数,一个是infd(读取数据的文件描述符),一个是outfd(客户端的socket文件描述符).消费者只需调用该函数,通知它须要读取那个文件就能够不通过Kafka间接将数据读到内核,而后由内核写到消费者过程的缓冲区中。

October 6, 2020 · 1 min · jiezi