女人18毛片a级毛片一区二区-男男瑟瑟视频-午夜亚洲一区二区,国产福利91精品,日韩精品在线免费视频,国产欧美在线一区

沃趣科技產(chǎn)品與技術(shù)資源中心
產(chǎn)品白皮書丨最佳實(shí)踐合集丨行業(yè)報(bào)告丨技術(shù)文章
資源 > 數(shù)據(jù)庫技術(shù)a|技術(shù)干貨 | 漫游Linux塊IO

數(shù)據(jù)庫技術(shù)a|技術(shù)干貨 | 漫游Linux塊IO

2023年06月13日

前言

在計(jì)算機(jī)的世界里,我們可以將業(yè)務(wù)進(jìn)行抽象簡化為兩種場景——計(jì)算密集型IO密集型。這兩種場景下的表現(xiàn),決定這一個(gè)計(jì)算機(jī)系統(tǒng)的能力。數(shù)據(jù)庫作為一個(gè)典型的基礎(chǔ)軟件,它的所有業(yè)務(wù)邏輯同樣可以抽象為這兩種場景的混合。因此,一個(gè)數(shù)據(jù)庫系統(tǒng)性能的強(qiáng)悍與否,往往跟操作系統(tǒng)和硬件提供的計(jì)算能力、IO能力緊密相關(guān)。



除了硬件本身的物理極限,操作系統(tǒng)在軟件層面的處理以及提供的相關(guān)機(jī)制也尤為重要。因此,想要數(shù)據(jù)庫發(fā)揮更加極限的性能,對操作系統(tǒng)內(nèi)部相關(guān)機(jī)制和流程的理解就很重要。


本篇文章,我們就一起看下Linux中一個(gè)IO請求的生命周期。Linux發(fā)展到今天,其內(nèi)部的IO子系統(tǒng)已經(jīng)相當(dāng)復(fù)雜。每個(gè)點(diǎn)展開都能自成一篇,所以本次僅是對塊設(shè)備的寫IO做一個(gè)快速的漫游,后續(xù)再對相關(guān)專題進(jìn)行詳細(xì)分解。

image-20220921214446637.png




從用戶態(tài)程序出發(fā)

首先需要明確的是,什么是塊設(shè)備?我們知道IO設(shè)備可以分為字符設(shè)備和塊設(shè)備,字符設(shè)備以字節(jié)流的方式訪問數(shù)據(jù),比如我們的鍵盤鼠標(biāo)。而塊設(shè)備則是以塊為單位訪問數(shù)據(jù),并且支持隨機(jī)訪問,典型的塊設(shè)備就是我們常見的機(jī)械硬盤和固態(tài)硬盤。


一個(gè)應(yīng)用程序想將數(shù)據(jù)寫入磁盤,需要通過系統(tǒng)調(diào)用來完成:open打開文件 ---> write寫入文件 ---> close關(guān)閉文件。


下面是write系統(tǒng)調(diào)用的定義,我們可以看到,應(yīng)用程序只需要指定三個(gè)參數(shù):

1. 想要寫入的文件

2. 寫入數(shù)據(jù)所在的內(nèi)存地址

3. 寫入數(shù)據(jù)的長度

SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
    size_t, count)
{
  struct fd f = fdget_pos(fd);
  ssize_t ret = -EBADF;
 
  if (f.file) {
    loff_t pos = file_pos_read(f.file);
    ret = vfs_write(f.file, buf, count, &pos);
    if (ret >= 0)
      file_pos_write(f.file, pos);
    fdput_pos(f);
  }
 
  return ret;
}


而剩下的工作就進(jìn)入到內(nèi)核中的虛擬文件系統(tǒng)(VFS)中進(jìn)行處理。



虛擬文件系統(tǒng)(VFS)

在Linux中一切皆文件,它提供了虛擬文件系統(tǒng)VFS的機(jī)制,用來抽象各種資源,使應(yīng)用程序無需關(guān)心底層細(xì)節(jié),只需通過open、read/write、close這幾個(gè)通用接口便可以管理各種不同的資源。不同的文件系統(tǒng)通過實(shí)現(xiàn)各自的通用接口來滿足不同的功能。


devtmpfs

掛載在/dev目錄,devtmpfs中的文件代表各種設(shè)備。因此,對devtmpfs文件的讀寫操作,就是直接對相應(yīng)設(shè)備的操作。


如果應(yīng)用程序打開的是一個(gè)塊設(shè)備文件,則說明它直接對一個(gè)塊設(shè)備進(jìn)行讀寫,調(diào)用塊設(shè)備的write函數(shù):

const struct file_operations def_blk_fops = {
  .open    = blkdev_open,
    ... ...
  .read    = do_sync_read,
  .write    = do_sync_write,
    ... ...
};



磁盤文件系統(tǒng)(ext4等)

這是我們最為熟悉的文件系統(tǒng)類型,它的文件就是我們一般理解的文件,對應(yīng)實(shí)際磁盤中按照特定格式組織并管理的區(qū)域。對這類文件的讀寫操作,都會(huì)按照固定規(guī)則轉(zhuǎn)化為對應(yīng)磁盤的讀寫操作。


應(yīng)用程序如果打開的是一個(gè)ext4文件系統(tǒng)的文件,則會(huì)調(diào)用ext4的write函數(shù):

const struct file_operations_extend  ext4_file_operations = {
  .kabi_fops = {
    ... ...
    .read    = do_sync_read,
    .write    = do_sync_write,
    ... ...
    .open    = ext4_file_open,
    ... ...
};


buffer/cache


Linux提供了緩存來提高IO的性能,無論打開的是設(shè)備文件還是磁盤文件,一般情況IO會(huì)先寫入到系統(tǒng)緩存中并直接返回,IO生命周期結(jié)束。后續(xù)系統(tǒng)刷新緩存或者主動(dòng)調(diào)用sync,數(shù)據(jù)才會(huì)被真正寫入到塊設(shè)備中。有意思的是,針對塊設(shè)備的稱為buffer,針對磁盤文件的稱為cache。

ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
         unsigned long nr_segs, loff_t *ppos)
    ... ...
  if (io_is_direct(file)) {
    ... ...
    written = generic_file_direct_write(iocb, iov, &nr_segs, pos,
              ppos, count, ocount);
    ... ...
  } else {
    written = generic_file_buffered_write(iocb, iov, nr_segs,
        pos, ppos, count, written);
  }
    ... ...



Direct IO

當(dāng)打開文件時(shí)候指定了O_DIRECT標(biāo)志,則指定文件的IO為direct IO,它會(huì)繞過系統(tǒng)緩存直接發(fā)送給塊設(shè)備。在發(fā)送給塊設(shè)備之前,虛擬文件系統(tǒng)會(huì)將write函數(shù)參數(shù)表示的IO轉(zhuǎn)化為dio,在其中封裝了一個(gè)個(gè)bio結(jié)構(gòu),接著調(diào)用submit_bio將這些bio提交到通用塊層進(jìn)行處理。

 do_blockdev_direct_IO 
    -> dio_bio_submit 
      -> submit_bio



通用塊層

核心結(jié)構(gòu)

1.bio/request

  • bio是Linux通用塊層和底層驅(qū)動(dòng)的IO基本單位,可以看到它的最重要的幾個(gè)屬性,一個(gè)bio就可以表示一個(gè)完整的IO操作:

struct bio {
  sector_t    bi_sector; //io的起始扇區(qū)
... ...
  struct block_device  *bi_bdev;  //對應(yīng)的塊設(shè)備
... ...
  bio_end_io_t    *bi_end_io;  //io結(jié)束的回調(diào)函數(shù)
... ...
  struct bio_vec    *bi_io_vec;  //內(nèi)存page列表
... ...
};


  • request代表一個(gè)獨(dú)立的IO請求,是通用塊層和驅(qū)動(dòng)層進(jìn)行IO傳遞的結(jié)構(gòu),它容納了一組連續(xù)的bio。通用塊層提供了很多IO調(diào)度策略,將多個(gè)bio合并生成一個(gè)request,以提高IO的效率。


2.gendisk

每個(gè)塊設(shè)備都對應(yīng)一個(gè)gendisk結(jié)構(gòu),它定義了設(shè)備名、主次設(shè)備號(hào)、請求隊(duì)列,和設(shè)備的相關(guān)操作函數(shù)。通過add_disk,我們就真正在系統(tǒng)中定義一個(gè)塊設(shè)備。


3.request_queue

這個(gè)即是日常所說的IO請求隊(duì)列,通用塊層將IO轉(zhuǎn)化為request并插入到request_queue中,隨后底層驅(qū)動(dòng)從中取出完成后續(xù)IO處理。

struct request_queue {
 ... ...
 struct elevator_queue  *elevator;  //調(diào)度器

 request_fn_proc    *request_fn;  //請求處理函數(shù)
 make_request_fn    *make_request_fn;  //請求入隊(duì)函數(shù)
 ... ...
 softirq_done_fn    *softirq_done_fn;  //軟中斷處理

 struct device    *dev;
 unsigned long    nr_requests;
 ... ...
};



處理流程

在收到上層文件系統(tǒng)提交的bio后,通用塊層最主要的功能就是根據(jù)bio創(chuàng)建request,并插入到request_queue中。


在這個(gè)過程中會(huì)對bio進(jìn)行一系列處理:當(dāng)bio長度超過限制會(huì)被分割,當(dāng)bio訪問地址相鄰則會(huì)被合并。


request創(chuàng)建后,根據(jù)request_queue配置的不同elevator調(diào)度器,request插入到對應(yīng)調(diào)度器隊(duì)列中。在底層設(shè)備驅(qū)動(dòng)程序從request_queue取出request處理時(shí),不同elevator調(diào)度器返回request策略不同,從而實(shí)現(xiàn)對request的調(diào)度。

void blk_queue_bio(struct request_queue *q, struct bio *bio)
{
   ... ...
 el_ret = elv_merge(q, &req, bio);    //嘗試將bio合并到已有的request中
 ... ...
 req = get_request(q, rw_flags, bio, 0);  //無法合并,申請新的request
   ... ...
 init_request_from_bio(req, bio);
}

void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule)
{
   ... ...
     __elv_add_request(q, rq, ELEVATOR_INSERT_SORT_MERGE);  //將request插入request_queue的elevator調(diào)度器
   ... ...
}



請求隊(duì)列


Linux中提供了不同類型的request_queue,一個(gè)是本文主要涉及的single-queue,另外一個(gè)是multi-queue。single-queue是在早期的硬件設(shè)備(例如機(jī)械硬盤)只能串行處理IO的背景下創(chuàng)建的,而隨著更快速的SSD設(shè)備的普及,single-queue已經(jīng)無法發(fā)揮底層存儲(chǔ)的性能了,進(jìn)而誕生了multi-queue,它優(yōu)化了很多機(jī)制使IOPS達(dá)到了百萬級(jí)別以上。至于multi-queue和single-queue的詳細(xì)區(qū)別,本篇不做討論。


每個(gè)隊(duì)列都可以配置不同的調(diào)度器,常見的有noop、deadline、cfq等。不同的調(diào)度器會(huì)根據(jù)IO類型、進(jìn)程優(yōu)先級(jí)、deadline等因素,對request請求進(jìn)一步進(jìn)行合并和排序。我們可以通過sysfs進(jìn)行配置,來滿足業(yè)務(wù)場景的需求:

#/sys/block/sdx/queue
scheduler      #調(diào)度器配置
nr_requests      #隊(duì)列深度
max_sectors_kb    #最大IO大小


設(shè)備驅(qū)動(dòng)

在IO經(jīng)過通用塊層的處理和調(diào)度后,就進(jìn)入到了設(shè)備驅(qū)動(dòng)層,就開始需要和存儲(chǔ)硬件進(jìn)行交互。


以scsi驅(qū)動(dòng)為例:在scsi的request處理函數(shù)scsi_request_fn中,循環(huán)從request_queue中取request,并創(chuàng)建scsi_cmd下發(fā)給注冊到scsi子系統(tǒng)的設(shè)備驅(qū)動(dòng)。需要注意的是,scsi_cmd中會(huì)注冊一個(gè)scsi_done的回調(diào)函數(shù)。


static void scsi_request_fn(struct request_queue *q)
{
 for (;;) {
   ... ...
   req = blk_peek_request(q);    //從request_queue中取出request
   ... ...
       cmd->scsi_done = scsi_done;    //指定cmd完成后回調(diào)
   rtn = scsi_dispatch_cmd(cmd);  //下發(fā)將request對應(yīng)的scsi_cmd
   ... ...
 }
}

int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
{
 ... ...
 rtn = host->hostt->queuecommand(host, cmd);
   ... ...
}



IO完成

軟中斷

每個(gè)request_queue都會(huì)注冊軟中斷號(hào),用來進(jìn)行IO完成后的下半部處理,scsi驅(qū)動(dòng)中注冊的為:scsi_softirq_done

struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
{
    ... ...
  q = __scsi_alloc_queue(sdev->host, scsi_request_fn);
    ... ...
  blk_queue_softirq_done(q, scsi_softirq_done);
  ... ...
}


硬中斷

當(dāng)存儲(chǔ)設(shè)備完成IO后,會(huì)通過硬件中斷通知設(shè)備驅(qū)動(dòng),此時(shí)設(shè)備驅(qū)動(dòng)程序會(huì)調(diào)用scsi_done回調(diào)函數(shù)完成scsi_cmd,并最終觸發(fā)BLOCK_SOFTIRQ軟中斷。

void __blk_complete_request(struct request *req)
{
      ... ...
      raise_softirq_irqoff(BLOCK_SOFTIRQ);
      ... ...
}


而BLOCK_SOFTIRQ軟中斷的處理函數(shù)就是之前注冊的scsi_softirq_done,通過自下而上層層回調(diào),到達(dá)bio_end_io,完成整個(gè)IO的生命周期。

  -> scsi_finish_command
      -> scsi_io_completion
        -> scsi_end_request
          -> blk_update_request
            -> req_bio_endio
              -> bio_endio



總結(jié)

以上,我們很粗略地漫游了Linux中一個(gè)塊設(shè)備IO的生命周期,這是一個(gè)很復(fù)雜的過程,其中很多機(jī)制和細(xì)節(jié)只是點(diǎn)到為止,但是我們有了對整個(gè)IO路徑的整體的認(rèn)識(shí)。當(dāng)我們再遇到IO相關(guān)問題的時(shí)候,可以更加快速地找到關(guān)鍵部分,并深入研究解決。



讓數(shù)據(jù)庫基礎(chǔ)設(shè)施更簡單
加速企業(yè)數(shù)字化轉(zhuǎn)型建設(shè)及落地
立即咨詢

沃趣科技

中立的企業(yè)級(jí)數(shù)據(jù)庫云
十年磨一劍十年來始終如一的專注數(shù)據(jù)庫生態(tài)領(lǐng)域
夯實(shí)技術(shù)底蘊(yùn)打造最適合時(shí)代的數(shù)據(jù)庫基礎(chǔ)設(shè)施
業(yè)績持續(xù)領(lǐng)先目前已累計(jì)服務(wù)超3000家企業(yè)客戶

留言咨詢

完善信息,我們第一時(shí)間跟您聯(lián)系
姓名
手機(jī)
公司
所在地區(qū)
咨詢問題