后端与系统架构3833 字

从操作系统底层彻底理解Java IO演进之路

·全库 576 / 611

《从操作系统底层彻底理解Java IO演进之路》共 0.4 万字,通篇围绕 并发、场景、安全 展开,归在「后端与系统架构」这一层。

覆盖并发场景安全

适合给 AI 系统搭底座、要管得住的人

目录骨架(8 节)
  1. **前言**
  2. **一 BIO**
  3. **二 NIO(non block)**
  4. **三 IO多路复用(select、poll、epoll)**
  5. **四 AIO**
  6. 五 总结
  7. 1 发生的系统调用
  8. 2 优缺点

示例代码

📎 附件:io-demo.zip(zip, 20KB)

原文链接:https://www.yuque.com/attachments/yuque/0/2024/zip/2424104/1713851378092-ef03fca4-ff53-4703-89db-2a92ce542551.zip

分享内容

  1. BIO优缺点和适用场景
  2. NIO优缺点和适用场景
  3. IO多路复用优缺点
  4. AIO优缺点和适用场景

前言

以操作系统实际调用角度(以 CentOS Linux release 7.5 操作系统为示例),看 IO 的每一步操作到底发生了什么。

关于如何查看系统调用,Linux 可以使用 strace 来查看任何软件的系统调动(这是个很好的分析学习方法):strace -ff -o ./out java Test.Java

一 BIO

BIO(Blocking I/O):同步阻塞型IO

BIO 是 Java 中最早的一种 I/O 模式,它的特点是 I/O 操作会阻塞线程,直到 I/O 操作完成。BIO 的缺点是并发性能较差,因为每个线程都会阻塞等待 I/O 完成。BIO 适用于连接数较少的网络应用,比如 Web 应用中的 Servlet。

分析BIO代码实现

package io; 
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class BIOSocket {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8090);
        System.out.println("step1: new ServerSocket ");
        while (true) {
            Socket client = serverSocket.accept();
            System.out.println("step2: client\t" + client.getPort());
            new Thread(() -> {
                try {
                    InputStream in = client.getInputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    while (true) {
                        System.out.println(reader.readLine());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

1 发生的系统调用

启动时

执行strace -ff -o ./out java BIOSocket.Java

socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 5
bind(5, {sa_family=AF_INET, sin_port=htons(8090), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(5, 50)                           = 0
poll([{fd=5, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=5, revents=POLLIN}])

poll 函数会阻塞直到其中任何一个 FD 发生事件。

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

有客户端连接后

accept(5, {sa_family=AF_INET, sin_port=htons(10253), sin_addr=inet_addr("127.0.0.1")}, [16]) = 6
clone(child_stack=0x7f013e5c4fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f013e5c59d0,         tls=0x7f013e5c5700, child_tidptr=0x7f013e5c59d0) = 13168
poll([{fd=5, events=POLLIN|POLLERR}], 1, -1

资料里没有你的场景?

把你手上的数据和想解决的那个环节发上来,我们按具体情况回,不复述方法论。

发布需求