栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux系统IO

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux系统IO

章节
    • Linux IO 原理
      • 文件系统
        • 文件目录
        • 文件类型
        • 文件描述符
      • PageCache
      • 网络IO
      • 磁盘IO
    • 网络IO模型变化
      • BIO
      • NIO

Linux IO 原理

文件系统 文件目录

Linux系统的文件系统目录是稳定的,只不过启动后可以重新挂载到不同设备。

文件目录(稳定)

chroot可以更改用户的根目录位置

系统挂载点

文件类型



查看文件内存innode位置

文件拷贝填充命令 dd if=/dev/zero of=my.txt bs=104857 count=100

文件描述符

查看线程的文件描述符

lsof命令

查看 /proc/pid/fd

PageCache

pagecache内核维护(内存容量,淘汰策略,数据持久化(延时))

文件丢失:由于内存中脏页还有持久化,系统异常断电,会造成数据丢失。

查看系统参数 sysctl -a|grep dirty


应用调用过程:

网络IO

网络常用指令:
文件描述符:lsof -p
端口分析: netstat -natp
包分析:tcpdump -nn -i eth0 port xxx

网络参数:

  • backlog:连接排队等待的数量
  • timeout: 连接超时时间
  • tcpNoDelay:TCP发送会做缓存优化,会有一定的发生延迟
  • keepalive:tcp探活

tcp内核数据丢失
tcp数据接受缓存区的大小是固定,但超过这个大小的数据会丢失。
所以tcp协议有窗口大小控制和拥塞控制


磁盘IO

fileoutputstream VS bufferoutputstream

文件的随机读写
系统内核维护文件的pagecache,不同进程持有同一文件的不同文件描述符,文件描述符可以seek定位到文件的随机位置。

MMAP内存映射文件


应用

网络IO模型变化

IO模:同步 异步 阻塞 非阻塞

追踪系统调用 strace -ff -o out java xxx.class
注意:进程可以打开最大的文件描述符 ulimit -n = 1024 (root不受限制)

BIO
public class BioServer {
    public static void main(String[] args){
     
        try{
            //创建serverSocket
            //netstat -nalp  和 losf -p pid 可以查看到listern状态
           ServerSocket  serverSocket = new ServerSocket(9001);
            //监听客户端的请求 堵塞
           Socket accept = serverSocket.accept();
            //获取字节流
           InputStream  inputStream = accept.getInputStream();
            //把字节流 转换成 字符流
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            //把字符流转换成 缓冲区字符流
           BufferedReader  br = new BufferedReader(inputStreamReader);
            //开始读取内容
            String msg ;
            while ((msg = br.readLine()) != null){
                System.out.println("读取内容为:" + msg);
            }
            System.out.println("读取完毕");
        }catch (Exception ex){
            log.error("读取异常",ex);
        }
    }
}

追踪到系统调用

socket=fd3 -> bind(fd3,8090) -> listen(fd3) -> BLOCK accept(fd3)=fd5 -> BLOCK recv(fd5)

NIO
public class NIOServer {

    // 服务端通道
    private ServerSocketChannel serverChannel;

    // 选择器
    private Selector selector;

    // 默认服务绑定端口
    private static int DEFAULT_BIND_PORT = 9000;

    public NIOServer(int port) {
        initServer(port);
    }

    private void initServer(int port) {
        try {
            // 开启一个服务通道
            this.serverChannel = ServerSocketChannel.open();
            // 将通道绑定到指定端口
            this.serverChannel.bind( (port < 1 || port > 65535) ?
                    new InetSocketAddress(DEFAULT_BIND_PORT) :
                    new InetSocketAddress(port));
            // 将通道设置为非阻塞模式  os->nonblocking
            this.serverChannel.configureBlocking(false);  
            // 打开一个 IO 监视器:Selector
            this.selector = Selector.open();
            // 将服务通道注册到 Selector 上,并在服务端通道注册 OP_ACCEPT 事件
            this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
        } catch (IOException ioException) {
            ioException.printStackTrace();
            System.out.println("init exception: " + ioException);
        }
    }

    public void startServer() throws InterruptedException {
        while (true){
            System.out.println("Selector 巡查 IO 事件---------------开始");
            try {
                int ioEventCount = this.selector.select();  // 此处以收集到所有 IO 事件
                System.out.println("Selector 检测到:" + ioEventCount);
            } catch (IOException ioException) {
                ioException.printStackTrace();
                break;
            }
            // 对各个 IO 事件做出对应的响应
            Set selectionKeys = selector.selectedKeys();
            Iterator iterator = selectionKeys.iterator();

            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();  //通过调用迭代器的 remove() 方法将这个键 key 从已选择键的集合中删除

                try {
                    // 可接收连接 能注册SelectionKey.OP_ACCEPT事件的只有 ServerSocketChannel通道
                    if (key.isAcceptable()) {
                        System.out.println("监控到 OP_ACCEPT 连接事件");
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();

                        // 接受客户端连接
                        SocketChannel client = server.accept();
                        System.out.println("Accept connection from " + client);
                        client.configureBlocking(false); // 设置客户端通道非阻塞
                        // 为客户端通道注册 OP_WRITE 和 OP_READ 事件
                        SelectionKey clientKey = client.register(selector,
                                SelectionKey.OP_WRITE |
                                        SelectionKey.OP_READ);
                        // 为客户端通道添加一个数据缓存区
                        ByteBuffer buffer = ByteBuffer.allocate(100);
                        clientKey.attach(buffer);
                    }
                    // 可读数据
                    if (key.isReadable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        int read = client.read(output);
                        System.out.println("Read data from client: " + client);
                        System.out.println("------------MSG : " + output.toString());
                    }
                    // 可写数据
                    if (key.isWritable()) {
                        SocketChannel client = (SocketChannel) key.channel();
                        ByteBuffer output = (ByteBuffer) key.attachment();
                        output.flip();
                        client.write(output);
                        output.compact();
                        System.out.println("Write data to " + client);
                    }
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }

            }

            Thread.sleep(2000);// 为了观察控制台打印数据
            System.out.println("Selector 巡查 IO 事件---------------完成");
        }
    }
}

追踪到系统调用
socket=fd3 -> bind(fd3,8090) -> listen(fd3) -> nonblocking(fd3) -> 不阻塞循环 accept(fd3)=fd5 或则 -1 ->不阻塞循环 recv(fd5)返回-1 或 数据

验证到accept非阻塞

多路复用器:
c10k问题,一个或多个线程既要处理客户端连接,数据接受的工作。每次循环一次 O(n)复杂度,很多调用是无意义,还要涉及到系统调用。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/880760.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号