【Notes5】IO


1.IO多路复用select/poll/epoll:bF用o

硬盘和网卡(IO)。如下A,B。。都是客户端,方框是服务端。首先想到应对并发,写一个多线程程序,每个传上来的请求都是一个线程,现在很多rpc框架用了这种方式,多线程存在弊端:cpu上下文切换,因而多线程不是最好的解决方案,转回单线程。如下while(1)…for…就是单线程。
在这里插入图片描述
如下虚线上面是准备fds和max(max用来卡bitmap多少位)。bitmap是1024位,涵盖了fds中所有信息。上面if(fdx是否有数据)用程序判断在select中是内核来判断,内核判断效率比用户态判断高,因为用户态判断也是询问内核,有个用户态和内核态切换,我们判断每一个每一次都要用户态和内核态切换。

有数据来,内核将FD有数据的插个旗子,FD置位中FD指的是rset中对应的那一位,而不是真正的fds中的元素。select返回后(如下最后5行)再遍历fd集合中5个fd并判断哪一个fd被set置位了,被set的那个有数据将它数据读出来并puts(处理),和上面用程序判断fd有无数据再处理思路一致。rset被内核set置位了需要每次while回来FD_ZERO赋空值,再FD_SET将fd赋到rset中
在这里插入图片描述
system是一个C/C++的库函数,select是系统调用函数,以下是select的4个缺点:
1.单个进程可监视fd数量有限制(能监听端口的大小有限制)
3.需要维护一个用来存放大量fd的数据结构,这样使得用户空间和内核空间在传递该结构时复制开销大。
4.对socket进行扫描时是线性扫描,即采用轮询方法,效率低。
在这里插入图片描述
如下pollfds数组里有5个pollfd结构体struct,poll一切改进围绕pollfd结构体展开,采用pollfds数组替代bitmap(数组远远不止1024大小)。
在这里插入图片描述
在这里插入图片描述
epoll_wait函数执行中epfd(白板)是共享内存,不需要用户态切换到内核态。epoll中置位没有revevts标志位了,将有数据的fd(即触发了POLLIN事件)放到最前面。epoll_wait和前面select和poll不一样,有返回值。最后只遍历nfds,不需要轮询,时间复杂度为O(1)。epoll解决select的1,2,3,4。redis,nginx,javaNIO(linux)都用的是epoll。多路io复用借助了硬件上优势DMA。
在这里插入图片描述

2.硬盘,网口,套接字:inode

在这里插入图片描述
在这里插入图片描述
上面是硬盘(块,扇区,文件管理中inode记录的内容),下面是网卡。
在这里插入图片描述
在这里插入图片描述
除了tcp的套接字(客户端随机一端口来连服务端ip和指定端口,一般服务端端口专门指定如访问百度网页https默认443端口),还有很多unix底层套接字,如mysql本地连接localhost的3306,其实并不走tcp的套接字,而是走底层mysql目录下mysql.sock文件,直接走文件通过unix的底层套接字进行连接,所以数据库是本机的数据库访问速度更快。

3.IO相关的系统调用:strace

编程语言没有魔法,全部依赖操系os的支持,其中最强大的支持莫过于系统调用。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如上看出java比C语言系统调用多的多,因为java要启动jvm虚拟机,jvm要读jdk的lib库等很多操作。如上并没有发现open…xml操作,因为java程序主要启动jvm进程,jvm进程可能又起了很多线程去真正运行main函数,所以加-f。
在这里插入图片描述

4.Java中BIO:阻塞,多线程

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

package com.itheima.com;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
// 服务端两个socket,一个用于listen,一个用于connect
// 客户端一个socket用于connect  // bind,accept,read
public class qqserver {
    
    
    static byte[] bytes = new byte[1024];
    public static void main(String[] args) {
    
    
        try {
    
    
            ServerSocket serverSocket = new ServerSocket();
            serverSocket.bind(new InetSocketAddress(8080));
            while(true) {
    
      // 一直接受客户端
                System.out.println("wait conn");

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111
                Socket socket = serverSocket.accept(); //阻塞           
                // 不考虑多线程,BIO无法处理并发
                // 下面全部代码放thread1线程中,不影响上面主线程accept
                // 1000万线程里只有200万活跃,800万不活跃,线程上下文切换耗资源
                // 所以在服务端不活跃多,考虑单线程
                System.out.println("connect success");
                System.out.println("wait data");

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111                                   
                socket.getInputStream().read(bytes); //阻塞  read读了多少字节,如果第一个客户端不发消息,则一直停留在这
                System.out.println("data success");                
                String content = new String(bytes); //将bytes字节转为字符串
                System.out.println(content);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}
package com.itheima.com;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

public class client {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            Socket socket=new Socket("127.0.0.1",8080);
            Scanner scanner = new Scanner(System.in);
            String txt = scanner.next();
            socket.getOutputStream().write(txt.getBytes());
            // socket.getOutputStream().write("111".getBytes());
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

5.Java中NIO:buffer就是一数组

在这里插入图片描述
很少自己去写NIO,但是一直在用NIO,因为netty,tomcat等基础网络框架都在用NIO,1.7之前是NIO,1.7之后是AIO或NIO2。在java中封了8种数据类型的buffer,像int buffer等。channel就是一入口,如我们把一个文件生成一个channel意思就是我要读这个文件,把这个文件内容读到buffer里,后面从buffer里拿出数据来就把文件内容读出来了。
在这里插入图片描述
下面是文件nio,没用到selector:
在这里插入图片描述
下面是网络nio,用到selector:每个channel比作tcp客户端连接,3个channel在整个socketserver创建时会注册到selector中,selector不断扫描下面的channel,一旦channel有数据就会处理,像io多路复用编程方式。
在这里插入图片描述
如上是NioTest1.java,下面看下系统调用,发现j2.out里有epoll_create,epoll_wait。
在这里插入图片描述
B站/知乎/微信公众号:码农编程录
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43435675/article/details/109986346
今日推荐