โ† ๋ชฉ๋ก์œผ๋กœ
AI-assisted content
Tokio File Write Internals

ํŒŒ์ผ I/O๋Š” ๋น„๋™๊ธฐ๊ฐ€ ์•„๋‹ˆ๋‹ค

Tokio๋กœ ํŒŒ์ผ์„ ์“ธ ๋•Œ ๊ฐ€์žฅ ๋จผ์ € ์•Œ์•„์•ผ ํ•  ์‚ฌ์‹ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.
์šด์˜์ฒด์ œ ์ˆ˜์ค€์—์„œ ์ผ๋ฐ˜ ํŒŒ์ผ์˜ ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ๋Š” ๋ณธ์งˆ์ ์œผ๋กœ ๋ธ”๋กœํ‚น ์ž‘์—…์ž…๋‹ˆ๋‹ค.

๋„คํŠธ์›Œํฌ ์†Œ์ผ“์€ epoll, kqueue, IOCP ๊ฐ™์€ ์ง„์งœ ๋น„๋™๊ธฐ ํ†ต์ง€ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ OS๊ฐ€ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ผ๋ฐ˜ ํŒŒ์ผ์—๋Š” ๊ทธ๋Ÿฐ ๊ฒƒ์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Tokio๋Š” ํŒŒ์ผ I/O๋ฅผ ์ง„์งœ ๋น„๋™๊ธฐ๋กœ ๋งŒ๋“ค์ง€ ์•Š๊ณ , ๋ธ”๋กœํ‚น ์ฝ”๋“œ๋ฅผ ๋ณ„๋„ ์Šค๋ ˆ๋“œ๋กœ ๊ฒฉ๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

use tokio::fs::File;
use tokio::io::AsyncWriteExt;

async fn example() -> std::io::Result<()> {
    let mut file = File::create("my_file.txt").await?;
    file.write_all(b"First line.\n").await?;
    file.flush().await?;
    Ok(())
}

์ด ์ฝ”๋“œ์˜ .await๊ฐ€ ๋‚ด๋ถ€์—์„œ ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ๋‹จ๊ณ„๋ณ„๋กœ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.

spawn_blocking์œผ๋กœ ์œ„์ž„ํ•œ๋‹ค

tokio::fs์˜ ํ•จ์ˆ˜๋“ค์€ ๋‚ด๋ถ€์ ์œผ๋กœ std::fs์˜ ๋ธ”๋กœํ‚น ์ฝ”๋“œ๋ฅผ spawn_blocking์œผ๋กœ ๋ณ„๋„ ์Šค๋ ˆ๋“œ ํ’€์— ๋˜์ง‘๋‹ˆ๋‹ค.

use std::fs::File;
use std::io::Write;
use tokio::task::spawn_blocking;

spawn_blocking(move || {
    let mut file = File::create("my_file.txt")?;
    file.write_all(b"First line.\n")?;
    Ok::<(), std::io::Error>(())
}).await??;

tokio::fs::write๋Š” ์ด ํŒจํ„ด์„ ๊ทธ๋Œ€๋กœ ๊ฐ์‹ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋™๋“ฑํ•œ ๋ธ”๋กœํ‚น ์—ฐ์‚ฐ์„ spawn_blocking์œผ๋กœ ๋ณ„๋„ ์Šค๋ ˆ๋“œ ํ’€์—์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๋™์ž‘ ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. ๋น„๋™๊ธฐ task๊ฐ€ ํŒŒ์ผ ์“ฐ๊ธฐ๋ฅผ ์š”์ฒญํ•˜๋ฉด Tokio๋Š” ๊ทธ ๋ธ”๋กœํ‚น ์ž‘์—…์„ ๋ธ”๋กœํ‚น ์ „์šฉ ์Šค๋ ˆ๋“œ ํ’€์— ๋„˜๊ธด๋‹ค
  2. ๋น„๋™๊ธฐ task๋Š” ๊ทธ ์ž‘์—…์˜ ๊ฒฐ๊ณผ์ธ JoinHandle์„ .await ํ•œ๋‹ค
  3. ๋ธ”๋กœํ‚น ์Šค๋ ˆ๋“œ๊ฐ€ ์ผํ•˜๋Š” ๋™์•ˆ task๋Š” Poll::Pending์œผ๋กœ ์ž ๋“ค๊ณ , executor๋Š” ๊ฐ™์€ ์Šค๋ ˆ๋“œ์—์„œ ๋‹ค๋ฅธ task๋ฅผ ์‹คํ–‰ํ•œ๋‹ค
  4. ๋ธ”๋กœํ‚น ์Šค๋ ˆ๋“œ๊ฐ€ ์‹ค์ œ I/O๋ฅผ ๋๋‚ด๋ฉด Waker๋ฅผ ํ˜ธ์ถœํ•ด ์ž ๋“  task๋ฅผ ๊นจ์šด๋‹ค
  5. executor๊ฐ€ task๋ฅผ ๋‹ค์‹œ poll ํ•˜๊ณ  JoinHandle์ด Poll::Ready๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด .await๊ฐ€ ํ•ด์†Œ๋œ๋‹ค

Future, poll, Waker ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ๊ทธ๋Œ€๋กœ ์“ฐ์ž…๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๊นจ์›€์„ ์ผ์œผํ‚ค๋Š” ์ด๋ฒคํŠธ๊ฐ€ OS ์ด๋ฒคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ณ„๋„ ๋ธ”๋กœํ‚น ์Šค๋ ˆ๋“œ์˜ ์ž‘์—… ์™„๋ฃŒ๋ผ๋Š” ์ ๋งŒ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

write์˜ await๋Š” ์“ฐ๊ธฐ ์™„๋ฃŒ๊ฐ€ ์•„๋‹ˆ๋‹ค

์—ฌ๊ธฐ๊ฐ€ ์ง๊ด€๊ณผ ์–ด๊ธ‹๋‚˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.
file.write_all(...).await๊ฐ€ ๋๋‚˜๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์‹ค์ œ๋กœ ํŒŒ์ผ์— ์“ฐ์ธ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

write ํ˜ธ์ถœ์€ ์“ฐ๊ธฐ๊ฐ€ ๋๋‚˜๊ธฐ ์ „์— ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. write์˜ .await๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ถ€ ๋ฒ„ํผ์— ๋ณต์‚ฌํ•˜๊ณ  ๋ธ”๋กœํ‚น ์ž‘์—…์„ ์ œ์ถœํ•œ ์‹œ์ ์— ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ๋””์Šคํฌ ์“ฐ๊ธฐ๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ธ”๋กœํ‚น ์Šค๋ ˆ๋“œ๊ฐ€ ๋‚˜์ค‘์— ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

let mut file = File::create("my_file.txt").await?;
file.write_all(b"First line.\n").await?; // ๋ฒ„ํผ์— ๋ณต์‚ฌ, ์ž‘์—… ์ œ์ถœ๋งŒ ๋๋‚จ
file.flush().await?;                     // ์‹ค์ œ ์“ฐ๊ธฐ ์™„๋ฃŒ๋ฅผ ์—ฌ๊ธฐ์„œ ๊ธฐ๋‹ค๋ฆผ

flush ์—†์ด ํ•จ์ˆ˜๊ฐ€ ๋๋‚˜๊ฑฐ๋‚˜ ํŒŒ์ผ์ด drop๋˜๋ฉด ์“ฐ๊ธฐ ์ž์ฒด๋Š” ๊ฒฐ๊ตญ ์ผ์–ด๋‚˜์ง€๋งŒ, ์™„๋ฃŒ ์‹œ์ ์ด ๋ณด์žฅ๋˜์ง€ ์•Š๊ณ  ์—๋Ÿฌ๋„ ํ™•์ธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Tokio File์— ์“ธ ๋•Œ๋Š” flush๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ std::fs::File๊ณผ ๋‹ค๋ฅธ ์ ์ด๋ฉฐ, File์ด ๋‚ด๋ถ€์ ์œผ๋กœ spawn_blocking์„ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์—์„œ ๋น„๋กฏ๋ฉ๋‹ˆ๋‹ค.

์ž‘์€ ์“ฐ๊ธฐ๋Š” BufWriter๋กœ ๋ฌถ๋Š”๋‹ค

spawn_blocking ํ˜ธ์ถœ์—๋Š” ๋น„์šฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
๋ธ”๋กœํ‚น ์Šค๋ ˆ๋“œ๋กœ์˜ ์™•๋ณต, ์ฆ‰ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ๊ณผ ๋ฒ„ํผ ์ฒ˜๋ฆฌ ๋น„์šฉ์ด ๋งค ํ˜ธ์ถœ๋งˆ๋‹ค ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ž‘์€ ์“ฐ๊ธฐ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ํ•˜๋ฉด spawn_blocking ํ˜ธ์ถœ์ด ๊ทธ๋งŒํผ ๋Š˜์–ด๋‚ฉ๋‹ˆ๋‹ค. ์ด๋•Œ BufWriter๋กœ ๊ฐ์‹ธ๋ฉด ์ž‘์€ ์“ฐ๊ธฐ๋“ค์ด ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„ํผ์— ๋ชจ์ด๊ณ , ์‹ค์ œ write์™€ spawn_blocking ํ˜ธ์ถœ์€ flush ์‹œ์ ์— ํ•œ ๋ฒˆ๋งŒ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.

use tokio::fs::File;
use tokio::io::{AsyncWriteExt, BufWriter};

let mut file = BufWriter::new(File::create("my_file.txt").await?);
file.write_all(b"First line.\n").await?;
file.write_all(b"Second line.\n").await?;
file.write_all(b"Third line.\n").await?;
file.flush().await?; // ์„ธ ๋ฒˆ์˜ ์“ฐ๊ธฐ๊ฐ€ ํ•œ ๋ฒˆ์˜ spawn_blocking์œผ๋กœ ์ฒ˜๋ฆฌ๋จ

์ฐธ๊ณ ๋กœ BufWriter ์—†์ด๋„ Tokio์˜ File์€ ์ž์ฒด ๋‚ด๋ถ€ ๋ฒ„ํผ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค. File::set_max_buf_size๊ฐ€ ๋‹จ์ผ spawn_blocking ํ˜ธ์ถœ์—์„œ ์ฝ๊ฑฐ๋‚˜ ์“ฐ๋Š” ์ตœ๋Œ€ ๋ฐ”์ดํŠธ ์ˆ˜๋ฅผ ์ œ์–ดํ•˜๋ฉฐ ๊ธฐ๋ณธ๊ฐ’์€ 2๋ฉ”๊ฐ€๋ฐ”์ดํŠธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ์ž‘์€ ์“ฐ๊ธฐ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋ฌถ์œผ๋ ค๋ฉด BufWriter๋ฅผ ์“ฐ๋Š” ํŽธ์ด ํ™•์‹คํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ชจ์„ ์ˆ˜ ์žˆ๋‹ค๋ฉด Vec<u8>๋‚˜ String์— ๋ชจ์•„์„œ tokio::fs::write๋ฅผ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ๋‹จ์ˆœํ•ฉ๋‹ˆ๋‹ค.

let mut contents = String::new();
contents.push_str("First line.\n");
contents.push_str("Second line.\n");
contents.push_str("Third line.\n");
tokio::fs::write("my_file.txt", contents.as_bytes()).await?;

Tokio์˜ ํŒŒ์ผ I/O ์„ฑ๋Šฅ์„ ์ข‹๊ฒŒ ํ•˜๋ ค๋ฉด ์—ฐ์‚ฐ์„ ๊ฐ€๋Šฅํ•œ ํ•œ ์ ์€ ์ˆ˜์˜ spawn_blocking ํ˜ธ์ถœ๋กœ ๋ฌถ๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

์ •๋ฆฌ

Tokio์˜ ํŒŒ์ผ ์“ฐ๊ธฐ๋Š” ๋ธ”๋กœํ‚น ์ฝ”๋“œ๋ฅผ ๋ณ„๋„ ์Šค๋ ˆ๋“œ ํ’€์— ์œ„์ž„ํ•˜๊ณ , ๊ทธ๋™์•ˆ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ๋‹ค๋ฅธ ์ผ์„ ํ•˜๋‹ค๊ฐ€, ์ž‘์—…์ด ๋๋‚˜๋ฉด Waker๋กœ ๊นจ์›Œ์ง€๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

write์˜ .await๋Š” ์“ฐ๊ธฐ ์™„๋ฃŒ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฒ„ํผ ๋ณต์‚ฌ์™€ ์ž‘์—… ์ œ์ถœ๊นŒ์ง€๋งŒ ๋ณด์žฅํ•˜๋ฏ€๋กœ flush๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  spawn_blocking ํ˜ธ์ถœ ํšŸ์ˆ˜๋ฅผ ์ค„์ด๋Š” ๊ฒƒ์ด ์„ฑ๋Šฅ์˜ ๊ด€๊ฑด์ด๋ฏ€๋กœ, ์ž‘์€ ์“ฐ๊ธฐ๋Š” BufWriter๋กœ ๋ฌถ๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ์•„ ํ•œ ๋ฒˆ์— ์“ฐ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.