一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

AIO編程的相關(guān)知識(shí)

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-10 16:17 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

在Direct IO模式下,異步是非常有必要的(因?yàn)槔@過了pagecache,直接和磁盤交互)。linux Native AIO正是基于這種場(chǎng)景設(shè)計(jì)的,具體的介紹見:KernelAsynchronousI/O (AIO)SupportforLinux。下面我們就來分析一下AIO編程的相關(guān)知識(shí)。

阻塞模式下的IO過程如下:

int fd = open(const char *pathname, int flags, mode_t mode);
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
int close(int fd);

因?yàn)檎麄€(gè)過程會(huì)等待read/write的返回,所以不需要任何額外的數(shù)據(jù)結(jié)構(gòu)。但異步IO的思想是:應(yīng)用程序不能阻塞在昂貴的系統(tǒng)調(diào)用上讓CPU睡大覺,而是將IO操作抽象成一個(gè)個(gè)的任務(wù)單元提交給內(nèi)核,內(nèi)核完成IO任務(wù)后將結(jié)果放在應(yīng)用程序可以取到的地方。這樣在底層做I/O的這段時(shí)間內(nèi),CPU可以去干其他的計(jì)算任務(wù)。但異步的IO任務(wù)批量的提交和完成,必須有自身可描述的結(jié)構(gòu),最重要的兩個(gè)就是iocb和io_event。

libaio中的structs

struct iocb {
        void     *data;  /* Return in the io completion event */
        unsigned key;   /*r use in identifying io requests */
        short           aio_lio_opcode;
        short           aio_reqprio;
        int             aio_fildes;
        union {
                struct io_iocb_common           c;
                struct io_iocb_vector           v;
                struct io_iocb_poll             poll;
                struct io_iocb_sockaddr saddr;
        } u;
};
struct io_iocb_common {
        void            *buf;
        unsigned long   nbytes;
        long long       offset;
        unsigned        flags;
        unsigned        resfd;
};

iocb是提交IO任務(wù)時(shí)用到的,可以完整地描述一個(gè)IO請(qǐng)求:

data是留給用來自定義的指針:可以設(shè)置為IO完成后的callback函數(shù);

aio_lio_opcode表示操作的類型:IO_CMD_PWRITE | IO_CMD_PREAD;

aio_fildes是要操作的文件:fd;

io_iocb_common中的buf, nbytes, offset分別記錄的IO請(qǐng)求的mem buffer,大小和偏移。

struct io_event {
        void *data;
        struct iocb *obj;
        unsigned long res;
        unsigned long res2;
};

io_event是用來描述返回結(jié)果的:

obj就是之前提交IO任務(wù)時(shí)的iocb;

res和res2來表示IO任務(wù)完成的狀態(tài)。

libaio提供的API和完成IO的過程

libaio提供的API有:io_setup, io_submit, io_getevents, io_destroy。

  1. 建立IO任務(wù)
int io_setup (int maxevents, io_context_t *ctxp);

io_context_t對(duì)應(yīng)內(nèi)核中一個(gè)結(jié)構(gòu),為異步IO請(qǐng)求提供上下文環(huán)境。注意在setup前必須將io_context_t初始化為0。

當(dāng)然,這里也需要open需要操作的文件,注意設(shè)置O_DIRECT標(biāo)志。

2.提交IO任務(wù)

long io_submit (aio_context_t ctx_id, long nr, struct iocb **iocbpp);

提交任務(wù)之前必須先填充iocb結(jié)構(gòu)體,libaio提供的包裝函數(shù)說明了需要完成的工作:

void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
{
        memset(iocb, 0, sizeof(*iocb));
        iocb- >aio_fildes = fd;
        iocb- >aio_lio_opcode = IO_CMD_PREAD;
        iocb- >aio_reqprio = 0;
        iocb- >u.c.buf = buf;
        iocb- >u.c.nbytes = count;
        iocb- >u.c.offset = offset;
}
void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
{
        memset(iocb, 0, sizeof(*iocb));
        iocb- >aio_fildes = fd;
        iocb- >aio_lio_opcode = IO_CMD_PWRITE;
        iocb- >aio_reqprio = 0;
        iocb- >u.c.buf = buf;
        iocb- >u.c.nbytes = count;
        iocb- >u.c.offset = offset;
}

這里注意讀寫的buf都必須是按扇區(qū)對(duì)齊的,可以用posix_memalign來分配。

3.獲取完成的IO

long io_getevents (aio_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);

這里最重要的就是提供一個(gè)io_event數(shù)組給內(nèi)核來copy完成的IO請(qǐng)求到這里,數(shù)組的大小是io_setup時(shí)指定的maxevents。

timeout是指等待IO完成的超時(shí)時(shí)間,設(shè)置為NULL表示一直等待所有到IO的完成。

4.銷毀IO任務(wù)

int io_destroy (io_context_t ctx);

libaio和epoll的結(jié)合

在異步編程中,任何一個(gè)環(huán)節(jié)的阻塞都會(huì)導(dǎo)致整個(gè)程序的阻塞,所以一定要避免在io_getevents調(diào)用時(shí)阻塞式的等待。還記得io_iocb_common中的flags和resfd嗎?看看libaio是如何提供io_getevents和事件循環(huán)的結(jié)合:

void io_set_eventfd(struct iocb *iocb, int eventfd)
{
        iocb- >u.c.flags |= (1 < < 0) /* IOCB_FLAG_RESFD */;
        iocb- >u.c.resfd = eventfd;
}

這里的resfd是通過系統(tǒng)調(diào)用eventfd生成的。

int eventfd(unsigned int initval, int flags);

eventfd是linux 2.6.22內(nèi)核之后加進(jìn)來的syscall,作用是內(nèi)核用來通知應(yīng)用程序發(fā)生的事件的數(shù)量,從而使應(yīng)用程序不用頻繁地去輪詢內(nèi)核是否有時(shí)間發(fā)生,而是由內(nèi)核將發(fā)生事件的數(shù)量寫入到該fd,應(yīng)用程序發(fā)現(xiàn)fd可讀后,從fd讀取該數(shù)值,并馬上去內(nèi)核讀取。

有了eventfd,就可以很好地將libaio和epoll事件循環(huán)結(jié)合起來:

  1. 創(chuàng)建一個(gè)eventfd
efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
  1. 將eventfd設(shè)置到iocb中
io_set_eventfd(iocb, efd);
  1. 交接AIO請(qǐng)求
io_submit(ctx, NUM_EVENTS, iocb);
  1. 創(chuàng)建一個(gè)epollfd,并將eventfd加到epoll中
epfd = epoll_create(1);
epoll_ctl(epfd, EPOLL_CTL_ADD, efd, &epevent);
epoll_wait(epfd, &epevent, 1, -1);
  1. 當(dāng)eventfd可讀時(shí),從eventfd讀出完成IO請(qǐng)求的數(shù)量,并調(diào)用io_getevents獲取這些IO
read(efd, &finished_aio, sizeof(finished_aio);
r = io_getevents(ctx, 1, NUM_EVENTS, events, &tms);

圖片

異步非阻塞IO模型的流程圖

一個(gè)完整的編程實(shí)例

#define _GNU_SOURCE
#define __STDC_FORMAT_MACROS


#include < stdio.h >
#include < errno.h >
#include < libaio.h >
#include < sys/eventfd.h >
#include < sys/epoll.h >
#include < stdlib.h >
#include < sys/types.h >
#include < unistd.h >
#include < stdint.h >
#include < sys/stat.h >
#include < fcntl.h >
#include < inttypes.h >


#define TEST_FILE   "aio_test_file"
#define TEST_FILE_SIZE  (127 * 1024)
#define NUM_EVENTS  128
#define ALIGN_SIZE  512
#define RD_WR_SIZE  1024


struct custom_iocb
{
    struct iocb iocb;
    int nth_request;
};


void aio_callback(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
    struct custom_iocb *iocbp = (struct custom_iocb *)iocb;
    printf("nth_request: %d, request_type: %s, offset: %lld, length: %lu, res: %ld, res2: %ldn", 
            iocbp- >nth_request, (iocb- >aio_lio_opcode == IO_CMD_PREAD) ? "READ" : "WRITE",
            iocb- >u.c.offset, iocb- >u.c.nbytes, res, res2);
}


int main(int argc, char *argv[])
{
    int efd, fd, epfd;
    io_context_t ctx;
    struct timespec tms;
    struct io_event events[NUM_EVENTS];
    struct custom_iocb iocbs[NUM_EVENTS];
    struct iocb *iocbps[NUM_EVENTS];
    struct custom_iocb *iocbp;
    int i, j, r;
    void *buf;
    struct epoll_event epevent;


    efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if (efd == -1) {
        perror("eventfd");
        return 2;
    }


    fd = open(TEST_FILE, O_RDWR | O_CREAT | O_DIRECT, 0644);
    if (fd == -1) {
        perror("open");
        return 3;
    }
    ftruncate(fd, TEST_FILE_SIZE);
    
    ctx = 0;
    if (io_setup(8192, &ctx)) {
        perror("io_setup");
        return 4;
    }


    if (posix_memalign(&buf, ALIGN_SIZE, RD_WR_SIZE)) {
        perror("posix_memalign");
        return 5;
    }
    printf("buf: %pn", buf);


    for (i = 0, iocbp = iocbs; i < NUM_EVENTS; ++i, ++iocbp) {
        iocbps[i] = &iocbp- >iocb;
        io_prep_pread(&iocbp- >iocb, fd, buf, RD_WR_SIZE, i * RD_WR_SIZE);
        io_set_eventfd(&iocbp- >iocb, efd);
        io_set_callback(&iocbp- >iocb, aio_callback);
        iocbp- >nth_request = i + 1;
    }


    if (io_submit(ctx, NUM_EVENTS, iocbps) != NUM_EVENTS) {
        perror("io_submit");
        return 6;
    }


    epfd = epoll_create(1);
    if (epfd == -1) {
        perror("epoll_create");
        return 7;
    }


    epevent.events = EPOLLIN | EPOLLET;
    epevent.data.ptr = NULL;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, efd, &epevent)) {
        perror("epoll_ctl");
        return 8;
    }


    i = 0;
    while (i < NUM_EVENTS) {
        uint64_t finished_aio;


        if (epoll_wait(epfd, &epevent, 1, -1) != 1) {
            perror("epoll_wait");
            return 9;
        }


        if (read(efd, &finished_aio, sizeof(finished_aio)) != sizeof(finished_aio)) {
            perror("read");
            return 10;
        }


        printf("finished io number: %"PRIu64"n", finished_aio);
    
        while (finished_aio > 0) {
            tms.tv_sec = 0;
            tms.tv_nsec = 0;
            r = io_getevents(ctx, 1, NUM_EVENTS, events, &tms);
            if (r > 0) {
                for (j = 0; j < r; ++j) {
                    ((io_callback_t)(events[j].data))(ctx, events[j].obj, events[j].res, events[j].res2);
                }
                i += r;
                finished_aio -= r;
            }
        }
    }
    
    close(epfd);
    free(buf);
    io_destroy(ctx);
    close(fd);
    close(efd);
    remove(TEST_FILE);


    return 0;
}

說明:

  1. 在centos 6.2 (libaio-devel 0.3.107-10) 上運(yùn)行通過
  2. struct io_event中的res字段表示讀到的字節(jié)數(shù)或者一個(gè)負(fù)數(shù)錯(cuò)誤碼。在后一種情況下,-res表示對(duì)應(yīng)的

errno。res2字段為0表示成功,否則失敗

  1. iocb在aio請(qǐng)求執(zhí)行過程中必須是valid的
  2. 在上面的程序中,通過擴(kuò)展iocb結(jié)構(gòu)來保存額外的信息(nth_request),并使用iocb.data

來保存回調(diào)函數(shù)的地址。如果回調(diào)函數(shù)是固定的,那么也可以使用iocb.data來保存額外信息。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    11080

    瀏覽量

    217114
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3689

    瀏覽量

    95261
  • 數(shù)據(jù)結(jié)構(gòu)

    關(guān)注

    3

    文章

    573

    瀏覽量

    40755
  • AIO
    AIO
    +關(guān)注

    關(guān)注

    1

    文章

    63

    瀏覽量

    10255
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    請(qǐng)問AIO與GPIO有什么本質(zhì)的區(qū)別?

    AIO與GPIO有什么本質(zhì)的區(qū)別?如果我只想輸出,或者輸入一個(gè)高電平(低電平)。GPIO 與AIO在電路上有什么區(qū)別?TI官網(wǎng)上哪份PDF里有介紹?
    發(fā)表于 09-29 15:09

    介紹UG編程的基本操作及相關(guān)加工工藝知識(shí)

    UG編程基本操作及工藝介紹分析本章主要介紹UG編程的基本操作及相關(guān)加工工藝知識(shí),讀者學(xué)習(xí)完本章后將會(huì)對(duì)UG編程
    發(fā)表于 09-01 06:36

    AIO-3399ProC NPU開發(fā)相關(guān)資料推薦

    1、AIO-3399ProCNPU開發(fā)簡(jiǎn)介AIO-3399ProC 開發(fā)者需要注意:NPU推理階段會(huì)與CPU進(jìn)行數(shù)據(jù)通信,單次傳輸數(shù)據(jù)量少但頻率高,但是與USB3.0相比PCIE不適合小文件
    發(fā)表于 07-01 17:38

    AIO-3399ProC無法開機(jī)請(qǐng)問如何重新燒錄?

    問題描述及復(fù)現(xiàn)步驟:誤把ROC-RK3399-PC-Pro提供的Android10固件燒錄到AIO-3399ProC,導(dǎo)致AIO-3399ProC無法開機(jī),開機(jī)畫面一直卡在如下圖所示,現(xiàn)在無法進(jìn)入loader模式,請(qǐng)問如何重新燒錄?
    發(fā)表于 03-13 14:29

    TLL系列電池的相關(guān)知識(shí)

    TLL系列電池的相關(guān)知識(shí)
    發(fā)表于 10-30 11:17 ?556次閱讀

    消毒柜相關(guān)知識(shí)

    消毒柜相關(guān)知識(shí) 為使大家進(jìn)一步了解消毒柜基本知識(shí),現(xiàn)簡(jiǎn)單介紹一些消毒柜相關(guān)知識(shí)。     基本術(shù)
    發(fā)表于 01-14 16:40 ?1452次閱讀

    故障電弧CPU相關(guān)設(shè)計(jì)知識(shí)

    故障電弧CPU相關(guān)設(shè)計(jì)知識(shí)
    發(fā)表于 04-05 14:11 ?21次下載

    什么是多線程編程?多線程編程基礎(chǔ)知識(shí)

    摘要:多線程編程是現(xiàn)代軟件技術(shù)中很重要的一個(gè)環(huán)節(jié)。要弄懂多線程,這就要牽涉到多進(jìn)程。本文主要以多線程編程以及多線程編程相關(guān)知識(shí)而做出的一些結(jié)
    發(fā)表于 12-08 16:30 ?1.3w次閱讀

    Linux內(nèi)核模塊編程必須了解哪些知識(shí)?

    模塊編程屬于內(nèi)核編程,因此,除了對(duì)內(nèi)核相關(guān)知識(shí)有所了解外,還需要了解與模塊相關(guān)知識(shí)。
    發(fā)表于 08-24 17:15 ?8次下載
    Linux內(nèi)核模塊<b class='flag-5'>編程</b>必須了解哪些<b class='flag-5'>知識(shí)</b>?

    數(shù)控車床編程入門知識(shí)

    數(shù)控車床的程序編制必須嚴(yán)格遵守相關(guān)的標(biāo)準(zhǔn),數(shù)控編程是一項(xiàng)很嚴(yán)格的工作,首先必須掌握一些基礎(chǔ)知識(shí),才能學(xué)好編程的方法并編出正確的程序。
    發(fā)表于 05-30 13:55 ?3w次閱讀

    043-ACMICPC相關(guān)知識(shí)

    043-ACMICPC相關(guān)知識(shí)(開關(guān)電源中高壓電容怎么選擇)-ACMICPC相關(guān)知識(shí);ACMICPC相關(guān)
    發(fā)表于 07-26 11:56 ?12次下載
    043-ACMICPC<b class='flag-5'>相關(guān)</b><b class='flag-5'>知識(shí)</b>

    總降調(diào)度相關(guān)知識(shí)

    總降調(diào)度相關(guān)知識(shí)(開關(guān)電源技術(shù)與設(shè)計(jì)潘pdf)-總降調(diào)度相關(guān)知識(shí)? ? ? ? ? ? ? ? ? ?
    發(fā)表于 09-23 16:33 ?8次下載
    總降調(diào)度<b class='flag-5'>相關(guān)</b><b class='flag-5'>知識(shí)</b>

    QAM調(diào)制的相關(guān)知識(shí)

    本文旨在總結(jié)最近復(fù)習(xí)的QAM調(diào)制的相關(guān)知識(shí)
    的頭像 發(fā)表于 05-23 11:47 ?3867次閱讀
    QAM調(diào)制的<b class='flag-5'>相關(guān)</b><b class='flag-5'>知識(shí)</b>

    母線保護(hù)相關(guān)知識(shí)分享

    母線保護(hù)相關(guān)知識(shí)分享
    的頭像 發(fā)表于 01-19 10:29 ?818次閱讀
    母線保護(hù)<b class='flag-5'>相關(guān)</b><b class='flag-5'>知識(shí)</b>分享

    plc編程語言編程相關(guān)技巧有哪些

    PLC(可編程邏輯控制器)編程語言及相關(guān)編程技巧是工業(yè)自動(dòng)化領(lǐng)域中不可或缺的知識(shí)。 一、PLC編程
    的頭像 發(fā)表于 10-21 16:56 ?1117次閱讀