linux系统的文件接口是十分重要的抽象, 也就是“一切都是文件”。借助文件IO,用户程序可以读写磁盘设备、网络设备、甚至管道、内存等。
C语言 首先介绍linux 系统调用提供的文件io
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int fd = open ("file.txt" , O_RDONLY);size_t bytes = read (fd, buf, sizeof (buf)) size_t bytes = write (fd, buf, sizeof (buf)) close (fd);off_t offset = lseek (fd, 0 , SEEK_END); #include <sys/mman.h> int fd = open ("data.bin" , O_RDWR);void * addr = mmap (NULL , file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );munmap (addr, file_size);
C语言的文件IO位于stdio.h库,stdio默认启用用户态缓冲区(可通过 setvbuf 调整),可以减少系统调用次数,提升小文件效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 typedef struct _IO_FILE FILE;struct _IO_FILE { int _flags; #define _IO_file_flags _flags char * _IO_read_ptr; char * _IO_read_end; char * _IO_read_base; char * _IO_write_base; char * _IO_write_ptr; char * _IO_write_end; char * _IO_buf_base; char * _IO_buf_end; char *_IO_save_base; char *_IO_backup_base; char *_IO_save_end; }; FILE *fopen (const char *filename, const char *mode) ; int fclose (FILE *stream) ; int fgetc (FILE *stream) int fgets (char *str, int num, FILE *stream) size_t fread (void *ptr, size_t size, size_t count, FILE *stream) ;int numbers[5 ];size_t n = fread (numbers, sizeof (int ), 5 , file);int fputc (int c, FILE *stream) int fputs (const char *str, FILE *stream) size_t fwrite (const void *ptr, size_t size, size_t count, FILE *stream) ;int numbers[5 ] = {1 , 2 , 3 , 4 , 5 };size_t n = fwrite (numbers, sizeof (int ), 5 , file);fseek (FILE *stream, long offset, int whence):ftell (FILE *stream) int feof (FILE *stream) ; int printf (const char *format, ...) ;int scanf (const char *format, ...) ;scanf ("%d" , &a); getchar () putchar () int fscanf (FILE *stream, const char *format, ...) ; int fprintf (FILE *stream, const char *format, ...) ; int sprintf (char *str, const char *format, ...) ;sprintf (buffer, "Number: %d, Pi: %.2f" , num, pi);int sscanf (const char *str, const char *format, ...) ;
linu网络socket系统调用
服务端,绑定(bind)到指定端口。监听(listen)端口,等待客户端连接。接受(accept)客户端的连接请求。进行数据通信(send,recv, read, write),关闭 Socket(close)。
客户端,创建 Socket。连接(connect)到服务器端。发送和接收数据。关闭 Socket。
三次握手之前, 服务端和客户端分别阻塞在accept和connect,三次握手建立连接后, 向下执行。三次握手过程
客户端发送SYN报文(SYN=1,序列号seq=x), 客户端进入 SYN_SENT 状态
服务器回复SYN-ACK报文(SYN=1,ACK=1,确认号ack=x+1,序列号seq=y), 服务器进入 SYN_RCVD 状态。客户端请求加入到SYN队列, 由net.ipv4.tcp_max_syn_backlog控制
客户端发送ACK报文(ACK=1,确认号ack=y+1,序列号seq=x+1)。客户端进入 ESTABLISHED 状态。服务器收到后也进入 ESTABLISHED 状态。请求进入ESTABLISHED队列,大小由net.core.somaxconn控制。 使用三次握手, 服务端可以控制连接的流控,例如服务端能力不足时,可以通过减缓发送SYN-ACK的频率,减少连接创建。同时双方都可以确定对方接受和发送连接正常。
close()函数, 释放套接字文件描述符(File Descriptor),减少其引用计数。当引用计数归零时,触发 TCP 连接的关闭流程(发送 FIN 包,进入四次挥手阶段),主动关闭双向连接。TCP四次回收关闭
主动方发送FIN报文(FIN=1,序列号seq=u), 主动方从 ESTABLISHED 进入 FIN_WAIT_1 状态。此时不再发送数据,但可接收数据
被动方回复ACK报文(ACK=1,确认号ack=u+1,序列号seq=v), 被动方进入 CLOSE_WAIT 状态,主动方收到后进入 FIN_WAIT_2 状态。此时被动方还可以发送数据
被动方数据发送完毕后,发送FIN报文(FIN=1,ACK=1,确认号ack=u+1,序列号seq=w),被动方进入 LAST_ACK 状态。
主动方发送ACK报文(ACK=1,确认号ack=w+1,序列号seq=u+1), 主动方进入 TIME_WAIT 状态,等待 2MSL(最长报文段寿命,MSL在 /proc/sys/net/ipv4/tcp_fin_timeout配置, 默认60s)后关闭。被动方收到后进入 CLOSED 状态。
shutdown(),主动关闭套接字的 单向或双向 通信通道,立即触发 TCP 协议栈的关闭流程, 不影响文件描述符。shutdown支持允许半关闭,即只关闭一端网络
shutdown(SHUT_WR), 关闭发送端, 触发两次挥手。主动方发送 FIN → 进入 FIN_WAIT_1, 被动方回复 ACK → 主动方进入 FIN_WAIT_2,被动方进入 CLOSE_WAIT。被动方需调用 close() 或 shutdown() 关闭连接,才会发送 FIN,触发完整四次挥手。若被动方不关闭,主动方会停留在 FIN_WAIT_2,直到内核超时(默认约 60 秒)
shutdown(SHUT_RD),关闭接收端, 丢弃接收缓冲区的数据,不再读取新数据。不发送任何报文,因此不会触发挥手流程。对方可能继续发送数据,但会被本地内核丢弃(无 RST 或 ACK 回应)。
shutdown(SHUT_RDWR), 触发完整四次挥手。
服务端大量TIME_WAIT链接,在高并发短连接的TCP服务器上,服务器处理完请求后往往选择立刻主动正常关闭连接。或者当 connection 头部取值被设置为 close 时,基本都由「服务端」发起主动关闭连接。
处理办法,在HTTP 请求的头部,connection 设置为 keep-alive,保持存活一段时间:现在的浏览器,一般都这么设置
缩短timewait时间为1MSL
如果是客户端主动关闭导致的TIMEWAIT(例如爬虫程序访问完请求立刻关闭连接),那可能导致客户端端口耗尽,可以开启net.ipv4.tcp_tw_reuse允许客户端新建连接复用timewait的链接。通过net.ipv4.ip_local_port_range扩大客户端使用端口的范围。
1 2 3 4 5 6 7 #include <sys/socket.h> int shutdown (int sockfd, int how) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 int socket (int domain, int type, int protocol) ;int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen) ;struct sockaddr_in server_addr;server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons (PORT); if (bind (sockfd, (struct sockaddr *)&server_addr, sizeof (server_addr)) < 0 ) { perror ("Bind failed" ); close (sockfd); exit (EXIT_FAILURE); } int listen (int sockfd, int backlog) ;int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen) ;int connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen) ;ssize_t recv (int sockfd, void *buf, size_t len, int flags) ;ssize_t read (int fd, void *buf, size_t count) ;ssize_t send (int sockfd, const void *buf, size_t len, int flags) ;ssize_t write (int fd, const void *buf, size_t count) ;int close (int fd) ;int shutdown (int sockfd, int how) ;
C++ C++ 文件IO库, 主要是
ifstream:用于从文件中读取数据。
ofstream:用于向文件中写入数据。
fstream:用于同时从文件中读取和写入数据。
iostream:用于输入输出流。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 std::ofstream outfile ("example.txt" , std::ios::out | std::ios::trunc) ;is_open () eof () seekg (offset, direction) seekp (offset, direction) tellg () tellp () #include <iostream> #include <fstream> #include <string> int main () { std::ifstream infile ("example.txt" ) ; if (!infile) { std::cerr << "Failed to open file.\n" ; return 1 ; } std::string line; while (std::getline (infile, line)) { std::cout << line << std::endl; } infile.close (); return 0 ; } std::ofstream fout ("data.txt" ) ;fout << 123 << " " << 3.14 ;
C++ 的 I/O 流(如 << 和 >> 运算符)需要处理数据类型转换、格式化和错误检查,导致多次虚函数调用和条件判断。每次 I/O 操作会检查流的状态(如 fail()、eof()),增加分支判断。一般要避免使用(包括cout和cin)
优化C++流的方案
使用二进制模式, 避免文本解析开销。std::ofstream fout(“data.bin”, std::ios::binary);
关闭与 C 标准库的同步,提升流操作速度。std::ios::sync_with_stdio(false); // 关闭与 stdio 的同步
不建议使用C++的流,除了C语言的stdio,C++常用的流还有protobuf。protobuf 支持ZeroCopy流
1 2 3 4 5 6 7 8 9 10 #include <google/protobuf/io/zero_copy_stream_impl.h> int fd = open ("data.bin" , O_WRONLY);google::protobuf::io::FileOutputStream file_stream (fd) ; MyMessage msg; msg.set_bytes_data ("large_data" ); msg.SerializeToZeroCopyStream (&file_stream);
自定义高效流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class SharedMemoryInputStream : public google::protobuf::io::ZeroCopyInputStream {public : SharedMemoryInputStream (void * shared_mem, int size) : ptr_ (static_cast <const uint8_t *>(shared_mem)), size_ (size), position_ (0 ) {} bool Next (const void ** data, int * size) override { if (position_ >= size_) return false ; *data = ptr_ + position_; *size = size_ - position_; position_ = size_; return true ; } void BackUp (int count) override { position_ -= count; } bool Skip (int count) override { position_ += count; return position_ <= size_; } int64 ByteCount () const override { return position_; } private : const uint8_t * ptr_; int size_; int position_; }; void * shared_mem = ...; int mem_size = ...;SharedMemoryInputStream stream (shared_mem, mem_size) ;MyMessage msg; msg.ParseFromZeroCopyStream (&stream);
JAVA 文件IO JAVA文件流分为两种主要类型:字节流和字符流。
字节流用于处理所有二进制文件
字符流则专门用于处理字符/文本文件
字节流FileInputStream
1 2 3 4 5 6 7 8 9 try (InputStream in = new FileInputStream ("input.jpg" ); OutputStream out = new FileOutputStream ("output.jpg" )) { byte [] buffer = new byte [4096 ]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1 ) { out.write(buffer, 0 , bytesRead); } }
字符流FileReader,会自动将字节按编码转换为字符
1 2 3 4 5 6 7 8 try (Reader reader = new FileReader ("input.txt" , StandardCharsets.UTF_8); BufferedReader br = new BufferedReader (reader)) { String line; while ((line = br.readLine()) != null ) { System.out.println(line); } }
缓冲流, 提供了一个缓冲区,可以减少对硬盘的读取次数,从而提高性能。
RandomAccessFile,支持随机访问文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.io.RandomAccessFile;import java.io.IOException;public class RandomAccessFileExample { public static void main (String[] args) { try (RandomAccessFile file = new RandomAccessFile ("example.txt" , "rw" )) { file.writeUTF("Hello, World!" ); file.seek(0 ); String data = file.readUTF(); System.out.println("Read from file: " + data); } catch (IOException e) { e.printStackTrace(); } } }
标准输入输出 System.out 是 Java 中标准输出流,是 PrintStream 类型的对象
System.in 是 Java 中标准输入流, 是 InputStream 类型的对象,读取的是byte数据。
对于字符数据,通常会使用 Scanner 或 BufferedReader 类接收输入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.util.Scanner;public class ScannerInput { public static void main (String[] args) { Scanner scanner = new Scanner (System.in); System.out.println("Enter your name:" ); String name = scanner.nextLine(); System.out.println("Enter your age:" ); int age = scanner.nextInt(); System.out.println("Name: " + name + ", Age: " + age); scanner.close(); } } System.out.printf(String format, Object... args);
网络库 JAVA NIO(New Input/Output)是Java提供的高性能、非阻塞I/O框架,位于java.nio 包, 适用于高并发网络编程。
Buffer(缓冲区)临时存储数据,所有读写操作均通过缓冲区完成。
Channel(通道)连接数据源与缓冲区,支持双向读写。包括FileChannel:文件I/O, ServerSocketChannel:TCP服务端
Selector(选择器)单线程监听多个通道事件(如连接、读、写)。
JAVA Netty 是一个高性能、异步事件驱动的网络应用框架, 基于 Java NIO(Non-blocking I/O)设计
基于 Reactor 模型,通过多路复用(Selector)实现非阻塞 I/O。
内置 HTTP/1.x、HTTP/2、WebSocket、TCP/UDP、gRPC 等协议支持。
零拷贝和内存优化
核心抽象
Channel 网络连接的抽象,代表一个开放的 Socket 连接。
EventLoop:事件循环,处理 I/O 操作和事件(如连接、读写)。EventLoopGroup:管理一组 EventLoop,通常分为 BossGroup(接收连接)和 WorkerGroup(处理 I/O)。
Pipeline:处理链,由多个 ChannelHandler 组成,负责处理入站(Inbound)和出站(Outbound)事件。
Handler:业务逻辑单元,如编解码、日志、业务处理。
ByteBuf, Netty 的字节容器,支持堆内/堆外内存、内存池化。
Go 文件IO golang中,io包定义了 I/O 操作的抽象接口(Reader、Writer、io.Closer)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type Reader interface { Read(p []byte ) (n int , err error ) } type Writer interface { Write(p []byte ) (n int , err error ) } type Closer interface { Close() error } type Seeker interface { Seek(offset int64 , whence int ) (int64 , error ) } type ReadWriter interface { Reader Writer }
文件 I/O 操作主要通过 os、io 和 bufio 包实现
os包,直接与操作系统交互,提供最底层的文件描述符(*os.File)操作。方法Open、Create、Close、Stat、Chmod、Truncate等。每次读写都是系统调用。
bufio包,是io包接口的一种实现,提供了缓冲区,可以减少系统调用次数,提高性能。缓冲读取(bufio.Reader、Scanner)。缓冲写入(bufio.Writer)。
实现io包的抽象接口
os.File, 直接与操作系统交互,提供最底层的文件描述符(*os.File)操作。方法Open、Create、Close、Stat、Chmod、Truncate等。每次读写都是系统调用。
net.Conn, 提供了网络IO的Read(b []byte) (n int, err error)等接口, net.TCPConn、net.UDPConn 等具体实现
bufio,提供了缓冲io,一般需要os.File或net.Conn的构造
tcp实现的Read, Write接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func (c *conn) Read(b []byte ) (int , error ) { n, err := c.fd.Read(b) return n, err } func (fd *FD) Read(p []byte ) (int , error ) { for { n, err := syscall.Read(fd.Sysfd, p) if err != nil { } return n, err } }
os.File和bufio
1 2 3 4 5 6 7 8 file, _ := os.Open("data.txt" ) reader := bufio.NewReader(file) reader = bufio.NewReaderSize(file, 8192 ) file, _ := os.Create("output.txt" ) writer := bufio.NewWriter(file)
打开文件, os.Open, os.OpenFile
1 2 3 4 5 6 7 8 9 10 file, err := os.Open("data.txt" ) if err != nil { log.Fatal(err) } defer file.Close() file, err := os.Create("output.txt" ) defer file.Close()file, err := os.OpenFile("log.txt" , os.O_APPEND|os.O_WRONLY, 0644 )
读取文件, os.ReadFile, bufio.NewScanner(file), file.Read(buffer)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 data, err := os.ReadFile("data.txt" ) if err != nil { log.Fatal(err) } fmt.Println(string (data)) file, _ := os.Open("data.txt" ) defer file.Close()scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() fmt.Println(line) } if err := scanner.Err(); err != nil { log.Fatal(err) } file, _ := os.Open("data.txt" ) buffer := make ([]byte , 4096 ) for { n, err := file.Read(buffer) if err == io.EOF { break } process(buffer[:n]) }
写入文件os.WriteFile, file.WriteString, writer.WriteString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 data := []byte ("Hello, Go!\n" ) err := os.WriteFile("output.txt" , data, 0644 ) file, _ := os.OpenFile("log.txt" , os.O_APPEND|os.O_WRONLY, 0644 ) defer file.Close()_, err := file.WriteString("New log entry\n" ) writer := bufio.NewWriter(file) _, err := writer.WriteString("Buffered data\n" ) writer.Flush()
序列化数据到文件
1 2 3 4 5 6 7 8 type User struct { Name string `json:"name"` Age int `json:"age"` } user := User{"Alice" , 30 } data, _ := json.Marshal(user) os.WriteFile("user.json" , data, 0644 )
golang fmt格式化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 %v 值的默认格式表示 %+v 带字段名的结构体表示 %c 对应 Unicode 码点的字符 %d 十进制表示 %x 十六进制(小写字母) %X 十六进制(大写字母) %s 字符串 %.nf: 指定小数点后 n 位 %m.nf 设置宽度和小数位数 fmt.Printf("My name is %s and I am %d years old.\n" , name, age) fmt.Fprint(os.Stdout, "Hello, world!" ) fmt.Fprintf(file, "Hello, file!" ) fmt.Scanf("%s %d" , &name, &age) fmt.Scanln(&name) result := fmt.Sprintf("Name: %s, Age: %d" , name, age) fmt.Sscanf(input, "%s %d" , &name, &age)
网络库 Go 语言的 net 包是标准库中用于网络编程的核心模块,提供了简洁高效的 API 支持 TCP/IP、UDP、HTTP 等协议。net/http和net/rpc分别支持http和rpc协议。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 import "net" func main () { conn, err := net.Dial("tcp" , "localhost:8080" ) if err != nil { fmt.Println("Error dialing:" , err) os.Exit(1 ) } defer conn.Close() _, err = conn.Write([]byte ("Hello, Server!" )) if err != nil { fmt.Println("Error sending message:" , err) return } buffer := make ([]byte , 1024 ) n, err := conn.Read(buffer) if err != nil { fmt.Println("Error reading response:" , err) return } fmt.Printf("Received: %s\n" , string (buffer[:n])) } func main () { listen, err := net.Listen("tcp" , ":8080" ) if err != nil { fmt.Println("Error starting server:" , err) os.Exit(1 ) } defer listen.Close() fmt.Println("Server started, waiting for connections..." ) for { conn, err := listen.Accept() if err != nil { fmt.Println("Error accepting connection:" , err) continue } defer conn.Close() buffer := make ([]byte , 1024 ) n, err := conn.Read(buffer) if err != nil { fmt.Println("Error reading from connection:" , err) } fmt.Printf("Received: %s\n" , string (buffer[:n])) _, err = conn.Write([]byte ("Hello, Client!" )) if err != nil { fmt.Println("Error sending response:" , err) } } }
flag 命令行解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func main () { var name string var age int var isAdmin bool flag.StringVar(&name, "name" , "Guest" , "Your name" ) flag.IntVar(&age, "age" , 18 , "Your age" ) flag.BoolVar(&isAdmin, "admin" , false , "Is admin?" ) flag.Parse() fmt.Printf("Name: %s\n" , name) fmt.Printf("Age: %d\n" , age) fmt.Printf("Is Admin: %v\n" , isAdmin) }
Python 文件IO python 提供open, file, read, write, close 等原生操作文件的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 file = open ('filename' , mode='r' , buffering=-1 , encoding=None , errors=None , newline=None , closefd=True , opener=None ) with open ('example.txt' , 'r' ) as file: content = file.read() print (content) file.seek(0 ) first_10_chars = file.read(10 ) print (first_10_chars) print (file.tell()) file.write("Hello, World!\n" ) lines = ['Line 1\n' , 'Line 2\n' , 'Line 3\n' ] file.writelines(lines) file.flush()
python的os 模块提供文件元数据操做的方法,如remove, stat, rename, remove, mkdir
1 2 3 4 5 os.remove('example.txt' ) stat_info = os.stat('example.txt' ) os.mkdir('new_directory' )
print() 函数用于标准输出, 支持格式化输出
1 2 3 4 name = "Alice" age = 25 print (f"My name is {name} , and I am {age} years old." )print ("My name is {}, and I am {} years old." .format (name, age))
input()用于标准输入, input读到的类型都是字符串, 需要转换为对应类型
1 2 3 4 name = input ("Enter your name: " ) print (f"Hello, {name} !" )age = int (input ("Enter your age: " ))
argparse 命令行参数解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import argparseparser = argparse.ArgumentParser(description="A simple command-line tool" ) parser.add_argument('name' , type =str , help ="Your name" ) parser.add_argument('age' , type =int , help ="Your age" ) parser.add_argument('--admin' , action='store_true' , help ="Are you an admin?" ) args = parser.parse_args() print (f"Name: {args.name} " )print (f"Age: {args.age} " )print (f"Admin: {args.admin} " )
json 序列化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import jsondata = { "name" : "Alice" , "age" : 25 , "city" : "New York" } with open ("data.json" , "w" ) as json_file: json.dump(data, json_file, indent=4 ) with open ("data.json" , "r" ) as json_file: data = json.load(json_file) json_string = json.dumps(data, indent=4 ) data = json.loads(json_string)