IO积累01

家住魔仙堡 提交于 2020-07-29 00:46:47

mysql更新数据需要redo log,然后把数据保存到内存,有系统调用flus保存到磁盘中,是因为系统IO的原因,系统会把数据放到pagecacache中,如果对数据修改会标记为脏,这个标记是对所有的pagecache的,不是单指一个,然后有系统统一的flus到磁盘中;这就是为什么数据库修改数据保存到redo log中有延迟不是直接写入到磁盘中的原因。

 

说的不准确,知道大概过程

Linux有一个虚拟文件目录,都会挂载到相应的地方,比如dev3这块磁盘挂载到book虚拟目录上,进入book目录,其实就是进入到dev3中,所以就可以扩容,比如要存10T文件存入某个目录,可以把一个磁盘挂载到这个目录上,就可以保存了。

Linux虚拟文件系统,可以创建一个xxx.img文件,然后挂载到虚拟目录上,然后把可执行程序保存到虚拟目录中,实际操作的就是被挂载的xxx.img这块控件,可执行程序的根目录就是xxx.img,然后卸载xxx.img,把xxx.img挂载到其他虚拟目录也一样可以执行里面的程序,相当于一个容器,docket使用类似的原理;

磁盘IO

pagecache作用:内存中运行着各种应用,包括操作系统(kernel),但是应用不是一次性加载到内存中的,内存是分页存储的,每页大小4K,应用从磁盘加载到内存都是一页一页加载的,每个应用都有一个页表,里面记录页的线性地址,线性地址是虚拟的内存地址需要进行转换,通过内存管理单元(mmu)转换成物理地址,如果多个应用读取相同的一个文件,如a.txt,系统调用(IO)会把资源加载到内存中缓存起来,这个就是pagecache,多个应用读取形同的pagecache,应用都有个一个文件描述符(fd),fd中有偏移量(seek),多个应用使用seek来读取相同pagechache互不干扰,做到文件共享,pagecache是一种内存优化,想要减少系统调用,读取文件设计到IO操作,IO操作是系统调用,会产生80中断,也就是软中断运行状态会从用户态切换到内核态,内核态切换到用户态,所以使用pagecache来进行优化,访问相同的pagecache不会进行系统调用,BufferedOutputStream效率高就是这个原因,buffer有一个8k的缓冲区,不会每次都把数据写入到磁盘中,相当于批处理,批处理就会减少80中断,减少系统调用,所以效率高;FileOutputStream的IO操作比较频繁,所以效率不如批处理的BufferedOutputStream;

pagecache的缺点:使用输入流把数据写入磁盘,不是立刻的写入,而是写入内存中的pagecache中,操作系统会维护一个dirty的变量,表示脏,当内存中的数据到阈值时,操作系统会统一的把脏页刷入磁盘中,这要是为什么redis不设置次操作都持久化的时候,会丢数据的原因,当没有把数据刷入到磁盘中时,突然断电,保存在pagecache中的数据会丢失;当内存到大阈值的时候,会先把数据刷新到磁盘中,然后使用LRU算法进行淘汰

NIO:RandomAccessFile raf = new RandomAccessFile(path, "rw")

NIO使用channerl进行数据写入,主要多了一个内存映射,没有有经过系统调用,就可以把数据映射到pagecache中,kernel进行系统调用把pagecache写入到磁盘,所以nio效率更高;
byteBuffer数据保存到磁盘,先把数据从堆内存复制到堆外内存,然后在保存到pagecache中,最后保存到磁盘;

网络IO

TCP:面相连接的,可靠的传输协议,socket是基于tcp协议的,socket是一个四元组(客户端IP_客户端PORT + 服务端IP_服务端PORT)三次握手以后,操作系统回分配资源空间,四元组会在程序中对应一个fd,程序通过fd映射socket找到对应的内存空间,来进行数据交互(服务端启动以后,会监听端口,当有客户端连接进来以后,三次握手以后分配内存空间)

拥塞:简历socket连接时,回发送win窗口大小,用来优化数据交互,服务端端窗口满了,通过win的值,客户端就能知道,满了就是拥塞,客户端会暂停传送数据包,MTU数据包的大小,MSS是数据包中数据的大小;

网络IO变化

BIO模型
ServiceSocket(端口号,back_log)
1.操作系统创建socket会的到一个文件描述符
2.文件描述符bind端口
3.然后监听该端口,等待客户端的连接,accept(fd3,-》client的fd)此时为阻塞状态

BIO在获取客户端连接的时候会阻塞,得到连接以后读取数据也会阻塞,所以因为读取阻塞才要抛出线程,否者下一个连接获取不到,因为线程在读数据主色中,如何一直没有数据,就一直阻塞,所以抛出线程避免下一个客户端获取不到;

public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(9090,20);

        System.out.println("step1: new ServerSocket(9090) ");

        while (true) {
            Socket client = server.accept();  //阻塞1
            System.out.println("step2:client\t" + client.getPort());

            new Thread(() -> {
                InputStream in = null;
                try {
                    in = client.getInputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    while(true){
                        String dataline = reader.readLine(); //阻塞2

                        if(null != dataline){
                        System.out.println(dataline);
                        }else{
                            client.close();
                            break;
                        }
                    }
                    System.out.println("客户端断开");

                } catch (IOException e) {
                    e.printStackTrace();
                }


            }).start();

        }
    }

NIO

nio有两个维度,一个是操作系统,nio是非阻塞io,另一个是jvm中,是new io 新的io,底层调用就是操作系统的nio;在获取连接的时候BIO底层是阻塞的(调用accept(ervice的fd,等待client的fd)),一直在阻塞,现在accept(service的fd)如果没有client的fd就会返回-1,jvm把-1转换成null,返回给程序,这就是NIO的原理,BIO效率低就是因为阻塞,获取客户端阻塞,读取数据阻塞,所以才抛出线程;NIO使用channel,设置 ss.configureBlocking(false); 就不会阻塞,也设置client.configureBlocking(false);客户端也不会阻塞,所以效率高;

public static void main(String[] args) throws Exception {

        LinkedList<SocketChannel> clients = new LinkedList<>();

        ServerSocketChannel ss = ServerSocketChannel.open();
        ss.bind(new InetSocketAddress(9090));
        ss.configureBlocking(false); //重点  OS  NONBLOCKING!!!

        ss.setOption(StandardSocketOptions.TCP_NODELAY, false);
//        StandardSocketOptions.TCP_NODELAY
//        StandardSocketOptions.SO_KEEPALIVE
//        StandardSocketOptions.SO_LINGER
//        StandardSocketOptions.SO_RCVBUF
//        StandardSocketOptions.SO_SNDBUF
//        StandardSocketOptions.SO_REUSEADDR


        while (true) {
            Thread.sleep(1000);
            SocketChannel client = ss.accept(); //不会阻塞?  -1NULL

            if (client == null) {
                System.out.println("null.....");
            } else {
                client.configureBlocking(false);
                int port = client.socket().getPort();
                System.out.println("client...port: " + port);
                clients.add(client);
            }

            ByteBuffer buffer = ByteBuffer.allocateDirect(4096);  //可以在堆里   堆外

            for (SocketChannel c : clients) {   //串行化!!!!  多线程!!
                int num = c.read(buffer);  // >0  -1  0   //不会阻塞
                if (num > 0) {
                    buffer.flip();
                    byte[] aaa = new byte[buffer.limit()];
                    buffer.get(aaa);

                    String b = new String(aaa);
                    System.out.println(c.socket().getPort() + " : " + b);
                    buffer.clear();
                }
            }
        }
    }

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!