淺談Linux內(nèi)核IO體系之磁盤IO

轉(zhuǎn)載于淺談Linux內(nèi)核IO體系之磁盤IO

前言

Linux I/O體系是Linux內(nèi)核的重要組成部分,主要包含網(wǎng)絡(luò)IO、磁盤IO等。基本所有的技術(shù)棧都需要與IO打交道,分布式存儲系統(tǒng)更是如此。本文主要簡單分析一下磁盤IO,看看一個IO請求從發(fā)起到完成到底經(jīng)歷了哪些流程。

目錄

  • 名詞解釋
  • IO體系
  • VFS層
  • PageCache層
  • 映射層
  • 通用塊層
  • IO調(diào)度層
  • 設(shè)備驅(qū)動層
  • 物理設(shè)備層
  • FAQ

名詞解釋

  • Buffered I/O:緩存IO又叫標(biāo)準(zhǔn)IO,是大多數(shù)文件系統(tǒng)的默認(rèn)IO操作,經(jīng)過PageCache。
  • Direct I/O:直接IO,By Pass PageCache。offset、length需對齊到block_size。
  • Sync I/O:同步IO,即發(fā)起IO請求后會阻塞直到完成。緩存IO和直接IO都屬于同步IO。
  • Async I/O:異步IO,即發(fā)起IO請求后不阻塞,內(nèi)核完成后回調(diào)。通常用內(nèi)核提供的Libaio。
  • Write Back:Buffered IO時,僅僅寫入PageCache便返回,不等數(shù)據(jù)落盤。
  • Write Through:Buffered IO時,不僅僅寫入PageCache,而且同步等待數(shù)據(jù)落盤。

IO體系

我們先看一張總的Linux內(nèi)核存儲棧圖片:

image

Linux IO存儲棧主要有以下7層:

image

VFS層

我們通常使用open、read、write等函數(shù)來編寫Linux下的IO程序。接下來我們看看這些函數(shù)的IO棧是怎樣的。在此之前我們先簡單分析一下VFS層的4個對象,有助于我們深刻的理解IO棧。

VFS層的作用是屏蔽了底層不同的文件系統(tǒng)的差異性,為用戶程序提供一個統(tǒng)一的、抽象的、虛擬的文件系統(tǒng),提供統(tǒng)一的對外API,使用戶程序調(diào)用時無需感知底層的文件系統(tǒng),只有在真正執(zhí)行讀寫操作的時候才調(diào)用之前注冊的文件系統(tǒng)的相應(yīng)函數(shù)。

VFS支持的文件系統(tǒng)主要有三種類型:

  • 基于磁盤的文件系統(tǒng):Ext系列、XFS等。
  • 網(wǎng)絡(luò)文件系統(tǒng):NFS、CIFS等。
  • 特殊文件系統(tǒng):/proc、裸設(shè)備等。

VFS主要有四個對象類型(不同的文件系統(tǒng)都要實(shí)現(xiàn)):

  • superblock:整個文件系統(tǒng)的元信息。對應(yīng)的操作結(jié)構(gòu)體:struct super_operations。
  • inode:單個文件的元信息。對應(yīng)的操作結(jié)構(gòu)體:struct inode_operations。
  • dentry:目錄項,一個文件目錄對應(yīng)一個dentry。對應(yīng)的操作結(jié)構(gòu)體:struct dentry_operations
  • file:進(jìn)程打開的一個文件。對應(yīng)的操作結(jié)構(gòu)體:struct file_operations

關(guān)于VFS相關(guān)結(jié)構(gòu)體的定義都在include/linux/fs.h里面。

superblock

superblock結(jié)構(gòu)體定義了整個文件系統(tǒng)的元信息,以及相應(yīng)的操作。

https://github.com/torvalds/linux/blob/v4.16/fs/xfs/xfs_super.c#L1789

static const struct super_operations xfs_super_operations = {
    ......
};

static struct file_system_type xfs_fs_type = {
    .name           = "xfs",
    ......
};

inode

inode結(jié)構(gòu)體定義了文件的元數(shù)據(jù),比如大小、最后修改時間、權(quán)限等,除此之外還有一系列的函數(shù)指針,指向具體文件系統(tǒng)對文件操作的函數(shù),包括常見的open、read、write等,由i_fop函數(shù)指針提供。

文件系統(tǒng)最核心的功能全部由inode的函數(shù)指針提供。主要是inode的i_op、i_fop字段。

struct inode {
    ......
    // inode 文件元數(shù)據(jù)的函數(shù)操作
    const struct inode_operations   *i_op;

    // 文件數(shù)據(jù)的函數(shù)操作,open、write、read等
    const struct file_operations    *i_fop;
    ......
}

在設(shè)置inode的i_fop時候,會根據(jù)不同的inode類型設(shè)置不同的i_fop。我們以xfs為例:

https://github.com/torvalds/linux/blob/v4.16/fs/xfs/xfs_iops.c#L1266

https://github.com/torvalds/linux/blob/v4.16/fs/inode.c#L1980

如果inode類型為普通文件的話,那么設(shè)置XFS提供的xfs_file_operations。

如果inode類型為塊設(shè)備文件的話,那么設(shè)置塊設(shè)備默認(rèn)提供的def_blk_fops

void xfs_setup_iops(struct xfs_inode *ip)
{
    struct inode *inode = &ip->i_vnode;

    switch (inode->i_mode & S_IFMT) {
    case S_IFREG:
        inode->i_op = &xfs_inode_operations;
        // 在IO棧章節(jié)會分析一下xfs_file_operations
        inode->i_fop = &xfs_file_operations;
        inode->i_mapping->a_ops = &xfs_address_space_operations;
        break;
    ......
    default:
        inode->i_op = &xfs_inode_operations;
        init_special_inode(inode, inode->i_mode, inode->i_rdev);
        break;
    }
}

void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
    inode->i_mode = mode;
    ......
    if (S_ISBLK(mode)) {
        // 塊設(shè)備相應(yīng)的系列函數(shù)
        inode->i_fop = &def_blk_fops;
        inode->i_rdev = rdev;
    }
    ......
}

dentry

dentry是目錄項,由于每一個文件必定存在于某個目錄內(nèi),我們通過路徑查找一個文件時,最終肯定找到某個目錄項。在Linux中,目錄和普通文件一樣,都是存放在磁盤的數(shù)據(jù)塊中,在查找目錄的時候就讀出該目錄所在的數(shù)據(jù)塊,然后去尋找其中的某個目錄項。

struct dentry {
    ......
    const struct dentry_operations *d_op;
    ......
};

在我們使用Linux的過程中,根據(jù)目錄查找文件的例子無處不在,而目錄項的數(shù)據(jù)又都是存儲在磁盤上的,如果每一級路徑都要讀取磁盤,那么性能會十分低下。所以需要目錄項緩存,把dentry放在緩存中加速。

VFS把所有的dentry放在dentry_hashtable哈希表里面,使用LRU淘汰算法。

file

用戶程序能接觸的VFS對象只有file,由進(jìn)程管理。我們常用的打開一個文件就是創(chuàng)建一個file對象,并返回一個文件描述符。出于隔離性的考慮,內(nèi)核不會把file的地址返回,而是返回一個整形的fd。

struct file {
    // 操作文件的函數(shù)指針,和inode里面的i_fop一樣,在open的時候賦值為i_fop。
    const struct file_operations *f_op;

    // 指向?qū)?yīng)inode對象
    struct inode *f_inode;

    // 每個文件都有自己的一個偏移量
    loff_t f_pos;
    ......
}

file對象是由內(nèi)核進(jìn)程直接管理的。每個進(jìn)程都有當(dāng)前打開的文件列表,放在files_struct結(jié)構(gòu)體中。

struct files_struct {
    ......
    struct file __rcu * fd_array[NR_OPEN_DEFAULT];
    ......
};

fd_array數(shù)組存儲了所有打開的file對象,用戶程序拿到的文件描述符(fd)實(shí)際上是這個數(shù)組的索引。

IO棧

https://github.com/torvalds/linux/blob/v4.16/fs/read_write.c#L566

SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
    ......
    ret = vfs_read(f.file, buf, count, &pos);
    ......
    return ret;
}
SYSCALL_DEFINE3(write, unsigned int, fd, char __user *, buf, size_t, count)
{
    ......
    ret = vfs_write(f.file, buf, count, &pos);
    ......
    return ret;
}

由此可見,我們經(jīng)常使用的read、write系統(tǒng)調(diào)用實(shí)際上是對vfs_read、vfs_write的一個封裝。

size_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
    ......
    if (file->f_op->read)    
        ret = file->f_op->read(file, buf, count, pos);
    else
        ret = do_sync_read(file, buf, count, pos);
    ......
}
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
    ......
    if (file->f_op->write)
        ret = file->f_op->write(file, buf, count, pos);    
    else
        ret = do_sync_write(file, buf, count, pos);
    ......
}

我們發(fā)現(xiàn),VFS會調(diào)用具體的文件系統(tǒng)的實(shí)現(xiàn):file->f_op->readfile->f_op->write。

對于通用的文件系統(tǒng),Linux封裝了很多基本的函數(shù),很多文件系統(tǒng)的核心功能都是以這些基本的函數(shù)為基礎(chǔ),再封裝一層。接下來我們以XFS為例,簡單分析一下XFS的read、write都做了什么操作。

https://github.com/torvalds/linux/blob/v4.16/fs/xfs/xfs_file.c#L1137

const struct file_operations xfs_file_operations = {
    ......
    .llseek     = xfs_file_llseek,
    .read       = do_sync_read,
    .write      = do_sync_write,
    // 異步IO,在之后的版本中名字為read_iter、write_iter。
    .aio_read   = xfs_file_aio_read,
    .aio_write  = xfs_file_aio_write,
    .mmap       = xfs_file_mmap,
    .open       = xfs_file_open,
    .fsync      = xfs_file_fsync,
    ......
};

這是XFS的f_op函數(shù)指針表,我們可以看到read、write函數(shù)直接使用了內(nèi)核提供的do_sync_readdo_sync_write函數(shù)。

ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
    ......
    ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);
    ......
}
ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
    ......
    ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
    ......
}

這兩個函數(shù)最終也是調(diào)用了具體文件系統(tǒng)的aio_readaio_write函數(shù),對應(yīng)XFS的函數(shù)為xfs_file_aio_readxfs_file_aio_write。

xfs_file_aio_readxfs_file_aio_write雖然有很多xfs自己的實(shí)現(xiàn)細(xì)節(jié),但其核心功能都是建立在內(nèi)核提供的通用函數(shù)上的:xfs_file_aio_read最終會調(diào)用generic_file_aio_read函數(shù),xfs_file_aio_write最終會調(diào)用generic_perform_write函數(shù),這些通用函數(shù)是基本上所有文件系統(tǒng)的核心邏輯。

接下來便要進(jìn)入PageCache層的相關(guān)邏輯了,我們先簡單概括一下讀寫多了哪些事情。

generic_file_aio_read

  1. 根據(jù)文件偏移量計算出要讀取數(shù)據(jù)在PageCache中的位置。
  2. 如果命中PageCache則直接返回,否則觸發(fā)磁盤讀取任務(wù),會有預(yù)讀的操作,減少IO次數(shù)。
  3. 數(shù)據(jù)讀取到PageCache后,拷貝到用戶態(tài)Buffer中。

generic_perform_write

  1. 根據(jù)文件偏移量計算要寫入的數(shù)據(jù)再PageCache中的位置。
  2. 將用戶態(tài)的Buffer拷貝到PageCache中。
  3. 檢查PageCache是否占用太多,如果是則將部分PageCache的數(shù)據(jù)刷回磁盤。

使用Buffered IO時,VFS層的讀寫很大程度上是依賴于PageCache的,只有當(dāng)Cache-Miss,Cache過滿等才會涉及到磁盤的操作。

塊設(shè)備文件

我們在使用Direct IO時,通常搭配Libaio使用,避免同步IO阻塞程序。而往往Direct IO + Libaio應(yīng)用于裸設(shè)備的場景,盡量不要應(yīng)用于文件系統(tǒng)中的文件,這時仍然會有文件系統(tǒng)的種種開銷。

通常Direct IO + Libaio使用的場景有幾種:

  • write back journal,journal也是裸設(shè)備。
  • 不怎么依賴文件系統(tǒng)的絕大部分功能,僅僅是讀寫即可,可直接操作裸設(shè)備。

上面基本都是普通文件的讀寫,我們通常的使用場景中還有一種特殊的文件即塊設(shè)備文件(/dev/sdx),這些塊設(shè)備文件仍然由VFS層管理,相當(dāng)于一個特殊的文件系統(tǒng)。當(dāng)進(jìn)程訪問塊設(shè)備文件時,直接調(diào)用設(shè)備驅(qū)動程序提供的相應(yīng)函數(shù),默認(rèn)的塊設(shè)備函數(shù)列表如下:

const struct file_operations def_blk_fops = {
    ......
    .open       = blkdev_open,
    .llseek     = block_llseek,
    .read       = do_sync_read,
    .write      = do_sync_write,
    .aio_read   = blkdev_aio_read,
    .aio_write  = blkdev_aio_write,
    .mmap       = generic_file_mmap,
    .fsync      = blkdev_fsync,
    ......
};

使用Direct IO + Libaio + 裸設(shè)備時,VFS層的函數(shù)指針會指向裸設(shè)備的def_blk_fops。因為我們通常使用DIO+Libaio+裸設(shè)備,所以我們簡單分析一下Libaio的IO流程。

Libaio提供了5個基本的方法,只能以DIO的方式打開,否則可能會進(jìn)行Buffered IO。

io_setup, io_cancal, io_destroy, io_getevents, io_submit

Linux內(nèi)核AIO的實(shí)現(xiàn)在https://github.com/torvalds/linux/blob/v4.16/fs/aio.c,我們簡單分析一下io_submit的操作。

SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
        struct iocb __user * __user *, iocbpp)
{
    return do_io_submit(ctx_id, nr, iocbpp, 0);
}
long do_io_submit(aio_context_t ctx_id, long nr,struct iocb __user *__user *iocbpp, bool compat){
    ...
    for (i=0; i<nr; i++) {
        ret = io_submit_one(ctx, user_iocb, &tmp, compat);
    }
    ...
}
static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,struct iocb *iocb, bool compat){
    ...
    ret = aio_run_iocb(req, compat);
    ...
}
static ssize_t aio_run_iocb(struct kiocb *req, bool compat){
    ...
    case IOCB_CMD_PREADV:
        rw_op = file->f_op->aio_read;
    case IOCB_CMD_PWRITEV:
        rw_op = file->f_op->aio_write;
    ...
}

可以發(fā)現(xiàn),最終也是調(diào)用f_opaio_read函數(shù),對應(yīng)于文件系統(tǒng)的文件就是xfs_file_aio_read函數(shù),對應(yīng)于塊設(shè)備文件就是blkdev_aio_read函數(shù),然后進(jìn)入通用塊層,放入IO隊列,進(jìn)行IO調(diào)度。由此可見Libaio的隊列也就是通用塊層之下的IO調(diào)度層中的隊列。

PageCache層

在HDD時代,由于內(nèi)核和磁盤速度的巨大差異,Linux內(nèi)核引入了頁高速緩存(PageCache),把磁盤抽象成一個個固定大小的連續(xù)Page,通常為4K。對于VFS來說,只需要與PageCache交互,無需關(guān)注磁盤的空間分配以及是如何讀寫的。

當(dāng)我們使用Buffered IO的時候便會用到PageCache層,與Direct IO相比,用戶程序無需offset、length對齊。是因為通用塊層處理IO都必須是塊大小對齊的。

Buffered IO中PageCache幫我們做了對齊的工作:如果我們修改文件的offset、length不是頁大小對齊的,那么PageCache會執(zhí)行RMW的操作,先把該頁對應(yīng)的磁盤的數(shù)據(jù)全部讀上來,再和內(nèi)存中的數(shù)據(jù)做Modify,最后再把修改后的數(shù)據(jù)寫回磁盤。雖然是寫操作,但是非對齊的寫仍然會有讀操作。

Direct IO由于跳過了PageCache,直達(dá)通用塊層,所以需要用戶程序處理對齊的問題。

臟頁刷盤

如果發(fā)生機(jī)器宕機(jī),位于PageCache中的數(shù)據(jù)就會丟失;所以僅僅寫入PageCache是不可靠的,需要有一定的策略將數(shù)據(jù)刷入磁盤。通常有幾種策略:

  • 手動調(diào)用fsync、fdatasync刷盤,可參考淺談分布式存儲之sync詳解。
  • 臟頁占用比例超過了閾值,觸發(fā)刷盤。
  • 臟頁駐留時間過長,觸發(fā)刷盤。

Linux內(nèi)核目前的做法是為每個磁盤都建立一個線程,負(fù)責(zé)每個磁盤的刷盤。

預(yù)讀策略

從VFS層我們知道寫是異步的,寫完P(guān)ageCache便直接返回了;但是讀是同步的,如果PageCache沒有命中,需要從磁盤讀取,很影響性能。如果是順序讀的話PageCache便可以進(jìn)行預(yù)讀策略,異步讀取該P(yáng)age之后的Page,等到用戶程序再次發(fā)起讀請求,數(shù)據(jù)已經(jīng)在PageCache里,大幅度減少IO的次數(shù),不用阻塞讀系統(tǒng)調(diào)用,提升讀的性能。

映射層

映射層是在PageCache之下的一層,由多個文件系統(tǒng)(Ext系列、XFS等,打開文件系統(tǒng)的文件)以及塊設(shè)備文件(直接打開裸設(shè)備文件)組成,主要完成兩個工作:

  • 內(nèi)核確定該文件所在文件系統(tǒng)或者塊設(shè)備的塊大小,并根據(jù)文件大小計算所請求數(shù)據(jù)的長度以及所在的邏輯塊號。
  • 根據(jù)邏輯塊號確定所請求數(shù)據(jù)的物理塊號,也即在在磁盤上的真正位置。

由于通用塊層以及之后的的IO都必須是塊大小對齊的,我們通過DIO打開文件時,略過了PageCache,所以必須要自己將IO數(shù)據(jù)的offset、length對齊到塊大小。

我們使用的DIO+Libaio直接打開裸設(shè)備時,跳過了文件系統(tǒng),少了文件系統(tǒng)的種種開銷,然后進(jìn)入通用塊層,繼續(xù)之后的處理。

通用塊層

通用塊層存在的意義也和VFS一樣,屏蔽底層不同設(shè)備驅(qū)動的差異性,提供統(tǒng)一的、抽象的通用塊層API。

通用塊層最核心的數(shù)據(jù)結(jié)構(gòu)便是bio,描述了從上層提交的一次IO請求。

https://github.com/torvalds/linux/blob/v4.16/include/linux/blk_types.h#L96

struct bio {
    ......
    // 要提交到磁盤的多段數(shù)據(jù)
    struct bio_vec *bi_io_vec;

    // 有多少段數(shù)據(jù)
    unsigned short bi_vcnt;
    ......
}
struct bio_vec {
    struct page *bv_page;
    unsigned int    bv_len;
    unsigned int    bv_offset;
};

所有到通用塊層的IO,都要把數(shù)據(jù)封裝成bio_vec的形式,放到bio結(jié)構(gòu)體內(nèi)。

在VFS層的讀請求,是以Page為單位讀取的,如果改Page不在PageCache內(nèi),那么便要調(diào)用文件系統(tǒng)定義的read_page函數(shù)從磁盤上讀取數(shù)據(jù)。

const struct address_space_operations xfs_address_space_operations = {
    ......
    .readpage       = xfs_vm_readpage,
    .readpages      = xfs_vm_readpages,
    .writepage      = xfs_vm_writepage,
    .writepages     = xfs_vm_writepages,
    ......
};

IO調(diào)度層

Linux調(diào)度層是Linux IO體系中的一個重要組件,介于通用塊層和塊設(shè)備驅(qū)動層之間。IO調(diào)度層主要是為了減少磁盤IO的次數(shù),增大磁盤整體的吞吐量,會隊列中的多個bio進(jìn)行排序和合并,并且提供了多種IO調(diào)度算法,適應(yīng)不同的場景。

Linux內(nèi)核為每一個塊設(shè)備維護(hù)了一個IO隊列,item是struct request結(jié)構(gòu)體,用來排隊上層提交的IO請求。一個request包含了多個bio,一個IO隊列queue了多個request。

struct request {
    ......
    // total data len
    unsigned int __data_len;

    // sector cursor
    sector_t __sector;

    // first bio
    struct bio *bio;

    // last bio
    struct bio *biotail;
    ......
}

上層提交的bio有可能分配一個新的request結(jié)構(gòu)體去存放,也有可能合并到現(xiàn)有的request中。

Linux內(nèi)核目前提供了以下幾種調(diào)度策略:

  • Deadline:默認(rèn)的調(diào)度策略,加入了超時的隊列。適用于HDD。
  • CFQ:完全公平調(diào)度器。
  • Noop:No Operation,最簡單的FIFIO隊列,不排序會合并。適用于SSD、NVME。

塊設(shè)備驅(qū)動層

每一類設(shè)備都有其驅(qū)動程序,負(fù)責(zé)設(shè)備的讀寫。IO調(diào)度層的請求也會交給相應(yīng)的設(shè)備驅(qū)動程序去進(jìn)行讀寫。大部分的磁盤驅(qū)動程序都采用DMA的方式去進(jìn)行數(shù)據(jù)傳輸,DMA控制器自行在內(nèi)存和IO設(shè)備間進(jìn)行數(shù)據(jù)傳送,當(dāng)數(shù)據(jù)傳送完成再通過中斷通知CPU。

通常塊設(shè)備的驅(qū)動程序都已經(jīng)集成在了kernel里面,也即就算我們直接調(diào)用塊設(shè)備驅(qū)動驅(qū)動層的代碼還是要經(jīng)過內(nèi)核。

spdk實(shí)現(xiàn)了用戶態(tài)、異步、無鎖、輪詢方式NVME驅(qū)動程序。塊存儲是延遲非常敏感的服務(wù),使用NVME做后端存儲磁盤時,便可以使用spdk提供的NVME驅(qū)動,縮短IO流程,降低IO延遲,提升IO性能。

物理設(shè)備層

物理設(shè)備層便是我們經(jīng)常使用的HDD、SSD、NVME等磁盤設(shè)備了。

FAQ

1、write返回成功數(shù)據(jù)落盤了嗎?

Buffered IO:write返回數(shù)據(jù)僅僅是寫入了PageCache,還沒有落盤。

Direct IO:write返回數(shù)據(jù)僅僅是到了通用塊層放入IO隊列,依舊沒有落盤。

此時設(shè)備斷電、宕機(jī)仍然會發(fā)生數(shù)據(jù)丟失。需要調(diào)用fsync或者fdatasync把數(shù)據(jù)刷到磁盤上,調(diào)用命令時,磁盤本身緩存(DiskCache)的內(nèi)容也會持久化到磁盤上。

2、write系統(tǒng)調(diào)用是原子的嗎?

write系統(tǒng)調(diào)用不是原子的,如果有多線程同時調(diào)用,數(shù)據(jù)可能會發(fā)生錯亂??梢允褂?code>O_APPEND標(biāo)志打開文件,只能追加寫,這樣多線程寫入就不會發(fā)生數(shù)據(jù)錯亂。

3、mmap相比read、write快在了哪里?

mmap直接把PageCache映射到用戶態(tài),少了一次系統(tǒng)調(diào)用,也少了一次數(shù)據(jù)在用戶態(tài)和內(nèi)核態(tài)的拷貝。

mmap通常和read搭配使用:寫入使用write+sync,讀取使用mmap。

4、為什么Direct IO需要數(shù)據(jù)對齊?

DIO跳過了PageCache,直接到通用塊層,而通用塊層的IO都必須是塊大小對齊的,所以需要用戶程序自行對齊offset、length。

5、Libaio的IO棧?

write()--->sys_write()--->vfs_write()--->通用塊層--->IO調(diào)度層--->塊設(shè)備驅(qū)動層--->塊設(shè)備

6、為什么需要 by pass pagecache?

當(dāng)應(yīng)用程序不滿Linux內(nèi)核的Cache策略,有更適合自己的Cache策略時可以使用Direct IO跳過PageCache。例如Mysql。

7、為什么需要 by pass kernel?

當(dāng)應(yīng)用程序?qū)ρ舆t極度敏感時,由于Linux內(nèi)核IO棧有7層,IO路徑比較長,為了縮短IO路徑,降低IO延遲,可以by pass kernel,直接使用用戶態(tài)的塊設(shè)備驅(qū)動程序。例如spdk的nvme,阿里云的ESSD。

8、為什么需要直接操作裸設(shè)備?

當(dāng)應(yīng)用程序僅僅使用了基本的read、write,用不到文件系統(tǒng)的大而全的功能,此時文件系統(tǒng)的開銷對于應(yīng)用程序來說是一種累贅,此時需要跳過文件系統(tǒng),接管裸設(shè)備,自己實(shí)現(xiàn)磁盤分配、緩存等功能,通常使用DIO+Libaio+裸設(shè)備。例如Ceph FileStore的Journal、Ceph BlueStore。

參考資源

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容