日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁(yè) > 科技  > 軟件

聊聊 Libuv 最近引入的 io_uring

來源: 責(zé)編: 時(shí)間:2023-10-20 10:02:31 288觀看
導(dǎo)讀io_uring 是 Linux 下高性能的異步 IO 框架,網(wǎng)上很多相關(guān)資料,我之前也初步分析了一下它的實(shí)現(xiàn),有興趣的可以查看 https://zhuanlan.zhihu.com/p/387620810。Libuv 中最近加入了對(duì) io_uring 的支持,那么為什么要把它引入

OiU28資訊網(wǎng)——每日最新資訊28at.com

io_uring 是 Linux 下高性能的異步 IO 框架,網(wǎng)上很多相關(guān)資料,我之前也初步分析了一下它的實(shí)現(xiàn),有興趣的可以查看 https://zhuanlan.zhihu.com/p/387620810。OiU28資訊網(wǎng)——每日最新資訊28at.com

Libuv 中最近加入了對(duì) io_uring 的支持,那么為什么要把它引入 Libuv 呢?因?yàn)?epoll 不支持普通文件的 Poll 能力,所以在 Libuv 中,異步文件 IO 操作需要通過線程池來實(shí)現(xiàn),具體來說就是當(dāng)用戶發(fā)起一個(gè)異步文件 IO 操作時(shí),Libuv 會(huì)把這個(gè)操作放到線程池中,當(dāng)子線程處理這個(gè)任務(wù)時(shí),會(huì)執(zhí)行一個(gè)阻塞式的系統(tǒng)調(diào)用,這個(gè)系統(tǒng)調(diào)用會(huì)引起線程阻塞,從而導(dǎo)致這個(gè)線程被消耗掉了,當(dāng) IO 操作完成后,子線程就會(huì)被喚醒,子線程再通過主線程去執(zhí)行用戶的回調(diào)。在 Libuv 早期的實(shí)現(xiàn)中,如果執(zhí)行比較慢的任務(wù)過多就會(huì)把線程池中的線程消耗完,從而導(dǎo)致執(zhí)行比較快的 IO 操作需要等待很長(zhǎng)時(shí)間,一個(gè)例子就是 DNS 解析會(huì)阻塞文件 IO 任務(wù)。而 io_uring 可以支持普通文件 IO(當(dāng)然能力不僅于此),不再需要借助線程池的能力,目前 Libuv 中部分異步文件 IO 操作已經(jīng)替換成 io_uring(需要通過環(huán)境變量開啟),下面來看看它的實(shí)現(xiàn)。OiU28資訊網(wǎng)——每日最新資訊28at.com

原生 io_uring 的使用比較復(fù)雜,通常需要借助 liburing 庫(kù),但是 Libuv 中可能為了減少對(duì)第三方庫(kù)的依賴,實(shí)現(xiàn)上使用原生的方式。OiU28資訊網(wǎng)——每日最新資訊28at.com

io_uring 初始化OiU28資訊網(wǎng)——每日最新資訊28at.com

在 Libuv 初始化時(shí)會(huì)進(jìn)行 io_uring 的初始化。OiU28資訊網(wǎng)——每日最新資訊28at.com

uv__iou_init(loop->backend_fd, &lfields->iou, 64, UV__IORING_SETUP_SQPOLL);

lfields->iou 為 io_uring 核心結(jié)構(gòu)體,UVIORING_SETUP_SQPOLL 設(shè)置內(nèi)核創(chuàng)建線程輪詢是否有任務(wù)需要處理(用戶層設(shè)置),接著看看 uviou_init。OiU28資訊網(wǎng)——每日最新資訊28at.com

static void uv__iou_init(int epollfd,struct uv__iou* iou,uint32_t entries,uint32_t flags) {struct uv__io_uring_params params;struct epoll_event e;size_t cqlen;size_t sqlen;size_t maxlen;size_t sqelen;uint32_t i;char* sq;char* sqe;int ringfd;memset(?ms, 0, sizeof(params));params.flags = flags;// UV__IORING_SETUP_SQPOLL 模式下,設(shè)置多久沒有任務(wù)提交則內(nèi)核線程進(jìn)入 sleep 狀態(tài)if (flags & UV__IORING_SETUP_SQPOLL)params.sq_thread_idle = 10;  /* milliseconds /// 調(diào)用系統(tǒng)調(diào)用初始化 io_uringringfd = uv__io_uring_setup(entries, ?ms);// 映射到內(nèi)核發(fā)送 / 完成隊(duì)列的內(nèi)存,用戶層和內(nèi)核可以共同操作這個(gè)隊(duì)列sq = mmap(0,maxlen,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE,ringfd,0);  /sqe = mmap(0,sqelen,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE,ringfd,0x10000000ull);  /* IORING_OFF_SQES */memset(&e, 0, sizeof(e));e.events = POLLIN;e.data.fd = ringfd;// 注冊(cè)等待可讀事件,io_uring 中有任務(wù)完成后就會(huì)通過 epollepoll_ctl(epollfd, EPOLL_CTL_ADD, ringfd, &e);// 初始化 io_uring 結(jié)構(gòu)體iou->sqhead = (uint32_t*) (sq + params.sq_off.head);iou->sqtail = (uint32_t*) (sq + params.sq_off.tail);iou->sqmask = (uint32_t) (sq + params.sq_off.ring_mask);iou->sqarray = (uint32_t*) (sq + params.sq_off.array);iou->sqflags = (uint32_t*) (sq + params.sq_off.flags);iou->cqhead = (uint32_t*) (sq + params.cq_off.head);iou->cqtail = (uint32_t*) (sq + params.cq_off.tail);iou->cqmask = (uint32_t) (sq + params.cq_off.ring_mask);iou->sq = sq;iou->cqe = sq + params.cq_off.cqes;iou->sqe = sqe;iou->sqlen = sqlen;iou->cqlen = cqlen;iou->maxlen = maxlen;iou->sqelen = sqelen;iou->ringfd = ringfd;iou->in_flight = 0;iou->flags = 0;}

uv__iou_init 完成了 io_uring 的初始化,并且把 io_uring 對(duì)應(yīng)的 fd 注冊(cè)到 epoll,當(dāng) io_uring 有任務(wù)完成時(shí),就可以通過 epoll 感知到。接著就可以使用 io_uring 了。OiU28資訊網(wǎng)——每日最新資訊28at.com

提交異步任務(wù)

下面看一個(gè)異步文件 IO 的操作。OiU28資訊網(wǎng)——每日最新資訊28at.com

int uv_fs_open(uv_loop_t* loop,uv_fs_t* req,const char* path,int flags,int mode,uv_fs_cb cb) {INIT(OPEN);PATH;req->flags = flags;req->mode = mode;if (cb != NULL)if (uv__iou_fs_open(loop, req))return 0;POST;}

uv_fs_open 可以以異步的方式打開一個(gè)文件,之前時(shí)通過線程池實(shí)現(xiàn)的,加入 io_uring 后,就會(huì)多了一層攔截,來看看 uv__iou_fs_open。OiU28資訊網(wǎng)——每日最新資訊28at.com

OiU28資訊網(wǎng)——每日最新資訊28at.com

int uv__iou_fs_open(uv_loop_t* loop, uv_fs_t* req) {struct uv__io_uring_sqe* sqe;struct uv__iou* iou;// 獲取 io_uring 結(jié)構(gòu)體iou = &uv__get_internal_fields(loop)->iou;// 獲取一個(gè)任務(wù)節(jié)點(diǎn),任務(wù)節(jié)點(diǎn)會(huì)和 req 互相關(guān)聯(lián),回調(diào)時(shí)會(huì)用到sqe = uv__iou_get_sqe(iou, loop, req);// 設(shè)置操作上下文sqe->addr = (uintptr_t) req->path;sqe->fd = AT_FDCWD;sqe->len = req->mode;// 設(shè)置操作類型sqe->opcode = UV__IORING_OP_OPENAT;sqe->open_flags = req->flags | O_CLOEXEC;// 提交任務(wù)uv__iou_submit(iou);return 1;}

uviou_fs_open 中有兩個(gè)核心邏輯 uviou_get_sqe 和 uviou_submit,首先來看 uviou_get_sqe。OiU28資訊網(wǎng)——每日最新資訊28at.com

OiU28資訊網(wǎng)——每日最新資訊28at.com

static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,uv_loop_t* loop,uv_fs_t* req) {struct uv__io_uring_sqe* sqe;uint32_t head;uint32_t tail;uint32_t mask;uint32_t slot;if (iou->ringfd == -1)return NULL;head = atomic_load_explicit((_Atomic uint32_t*) iou->sqhead,memory_order_acquire);tail = *iou->sqtail;mask = iou->sqmask;slot = tail & mask;sqe = iou->sqe;// 從請(qǐng)求隊(duì)列中獲取一個(gè)節(jié)點(diǎn)sqe = &sqe[slot];memset(sqe, 0, sizeof(*sqe));// 任務(wù)節(jié)點(diǎn)關(guān)聯(lián)到 req,回調(diào)時(shí)需要使用sqe->user_data = (uintptr_t) req;req->work_req.loop = loop;req->work_req.work = NULL;req->work_req.done = NULL;uv__queue_init(&req->work_req.wq);uv__req_register(loop, req);iou->in_flight++;return sqe;}

uviou_get_sqe 主要是從任務(wù)隊(duì)列中獲取一個(gè)空閑節(jié)點(diǎn)并關(guān)聯(lián)上請(qǐng)求上下文結(jié)構(gòu)體,uviou_get_sqe 的調(diào)用方需要設(shè)置操作上下文,比如操作類型,操作的 fd 等。通過 uviou_get_sqe 獲取任務(wù)節(jié)點(diǎn)并設(shè)置了操作上下文后,這個(gè)任務(wù)就會(huì)自動(dòng)被操作系統(tǒng)感知。因?yàn)?Libuv 是使用了 UVIORING_SETUP_SQPOLL 模式,所以還需要判斷這時(shí)候內(nèi)核輪訓(xùn)線程是否處于睡眠狀態(tài),這就是 uv__iou_submit 的邏輯。OiU28資訊網(wǎng)——每日最新資訊28at.com

static void uv__iou_submit(struct uv__iou* iou) {uint32_t flags;atomic_store_explicit((_Atomic uint32_t*) iou->sqtail,*iou->sqtail + 1,memory_order_release);flags = atomic_load_explicit((_Atomic uint32_t*) iou->sqflags,memory_order_acquire);// 判斷內(nèi)核線程是否處于睡眠狀態(tài)if (flags & UV__IORING_SQ_NEED_WAKEUP)// 喚醒內(nèi)核線程,說明有任務(wù)需要處理if (uv__io_uring_enter(iou->ringfd, 0, 0, UV__IORING_ENTER_SQ_WAKEUP))if (errno != EOWNERDEAD)  /* Kernel bug. Harmless, ignore. /perror("libuv: io_uring_enter(wakeup)");  /

這樣就完成了任務(wù)的提交。OiU28資訊網(wǎng)——每日最新資訊28at.com

任務(wù)完成

任務(wù)完成后,io_uring 對(duì)應(yīng)的 fd 就會(huì)變成可讀,從而 epoll 就會(huì)感知到,來看看 epoll 的處理。下面是 epoll 處理就緒 fd 時(shí)的一段邏輯。OiU28資訊網(wǎng)——每日最新資訊28at.com

OiU28資訊網(wǎng)——每日最新資訊28at.com

if(fd == iou->ringfd) {uv__poll_io_uring(loop, iou);have_iou_events = 1;continue;}

如果是 io_uring 的 fd 可讀,則執(zhí)行 uv__poll_io_uring。OiU28資訊網(wǎng)——每日最新資訊28at.com

OiU28資訊網(wǎng)——每日最新資訊28at.com

static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {struct uv__io_uring_cqe* cqe;struct uv__io_uring_cqe* e;uv_fs_t* req;uint32_t head;uint32_t tail;uint32_t mask;uint32_t i;uint32_t flags;int nevents;int rc;// 完成隊(duì)列頭/尾節(jié)點(diǎn)head = iou->cqhead;tail = atomic_load_explicit((_Atomic uint32_t) iou->cqtail,memory_order_acquire);mask = iou->cqmask;cqe = iou->cqe;nevents = 0;// 遍歷完成隊(duì)列for (i = head; i != tail; i++) {e = &cqe[i & mask];// 拿到操作關(guān)聯(lián)的請(qǐng)求結(jié)構(gòu)體req = (uv_fs_t*) (uintptr_t) e->user_data;uv__req_unregister(loop, req);iou->in_flight--;// 操作返回值,表示操作是否成功req->result = e->res;// 執(zhí)行回調(diào)req->cb(req);}

uv__poll_io_uring 的邏輯很簡(jiǎn)單,就是遍歷完成隊(duì)列,然后拿到對(duì)應(yīng)的請(qǐng)求上下文結(jié)構(gòu)體,最后執(zhí)行它的回調(diào)。OiU28資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)代軟件中大多數(shù)使用的 IO 模型是 epoll,隨著 io_uring 的發(fā)展和成熟,io_uring 將會(huì)出現(xiàn)在更多的軟件中,之前我也體驗(yàn)了一下 io_uring,有興趣的可以體驗(yàn)下 https://github.com/theanarkh/nodejs_io_uring。OiU28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-14326-0.html聊聊 Libuv 最近引入的 io_uring

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: Kubernetes中的優(yōu)雅關(guān)閉和零停機(jī)時(shí)間部署

下一篇: 業(yè)務(wù)痛點(diǎn)如此不同,為什么解決辦法卻那么一致?

標(biāo)簽:
  • 熱門焦點(diǎn)
  • K6:面向開發(fā)人員的現(xiàn)代負(fù)載測(cè)試工具

    K6 是一個(gè)開源負(fù)載測(cè)試工具,可以輕松編寫、運(yùn)行和分析性能測(cè)試。它建立在 Go 和 JavaScript 之上,它被設(shè)計(jì)為功能強(qiáng)大、可擴(kuò)展且易于使用。k6 可用于測(cè)試各種應(yīng)用程序,包括 Web
  • 谷歌KDD'23工作:如何提升推薦系統(tǒng)Ranking模型訓(xùn)練穩(wěn)定性

    谷歌在KDD 2023發(fā)表了一篇工作,探索了推薦系統(tǒng)ranking模型的訓(xùn)練穩(wěn)定性問題,分析了造成訓(xùn)練穩(wěn)定性存在問題的潛在原因,以及現(xiàn)有的一些提升模型穩(wěn)定性方法的不足,并提出了一種新
  • 微信語音大揭秘:為什么禁止轉(zhuǎn)發(fā)?

    大家好,我是你們的小米。今天,我要和大家聊一個(gè)有趣的話題:為什么微信語音不可以轉(zhuǎn)發(fā)?這是一個(gè)我們經(jīng)常在日常使用中遇到的問題,也是一個(gè)讓很多人好奇的問題。讓我們一起來揭開這
  • 使用LLM插件從命令行訪問Llama 2

    最近的一個(gè)大新聞是Meta AI推出了新的開源授權(quán)的大型語言模型Llama 2。這是一項(xiàng)非常重要的進(jìn)展:Llama 2可免費(fèi)用于研究和商業(yè)用途。(幾小時(shí)前,swyy發(fā)現(xiàn)它已從LLaMA 2更名為L(zhǎng)la
  • 共享單車的故事講到哪了?

    來源丨海克財(cái)經(jīng)與共享充電寶相差不多,共享單車已很久沒有被國(guó)內(nèi)熱點(diǎn)新聞關(guān)照到了。除了一再漲價(jià)和用戶直呼用不起了。近日多家媒體再發(fā)報(bào)道稱,成都、天津、鄭州等地多個(gè)共享單
  • 認(rèn)真聊聊東方甄選:如何告別低垂的果實(shí)

    來源:山核桃作者:財(cái)經(jīng)無忌爆火一年后,俞敏洪和他的東方甄選依舊是頗受外界關(guān)心的“網(wǎng)紅”。7月5日至9日,為期5天的東方甄選“甘肅行”首次在自有App內(nèi)直播,
  • 朋友圈可以修改可見范圍了 蘋果用戶可率先體驗(yàn)

    近日,iOS用戶迎來微信8.0.27正式版更新,除了可更換二維碼背景外,還新增了多項(xiàng)實(shí)用功能。在新版微信中,朋友圈終于可以修改可見范圍,簡(jiǎn)單來說就是已發(fā)布的朋友圈
  • SN570 NVMe SSD固態(tài)硬盤 價(jià)格與性能兼具

    SN570 NVMe SSD固態(tài)硬盤是西部數(shù)據(jù)發(fā)布的最新一代WD Blue系列的固態(tài)硬盤,不僅閃存技術(shù)更為精進(jìn),性能也得到了進(jìn)一步的躍升。WD Blue SN570 NVMe SSD的包裝外
  • 三翼鳥智能家居亮相電博會(huì),讓用戶體驗(yàn)更真實(shí)

    2021電博會(huì)在青島國(guó)際會(huì)展中心開幕中,三翼鳥直接把“家”搬到了現(xiàn)場(chǎng),成為了展會(huì)的一大看點(diǎn)。這也是三翼鳥繼9月9日發(fā)布了行業(yè)首個(gè)一站式定制智慧家平臺(tái)后的
Top 主站蜘蛛池模板: 济宁市| 卢湾区| 溧水县| 滨海县| 宝丰县| 佛山市| 黑河市| 阿克苏市| 襄城县| 准格尔旗| 临湘市| 武平县| 康保县| 马尔康县| 德惠市| 长白| 青田县| 扎囊县| 东城区| 井陉县| 开化县| 中宁县| 冀州市| 大田县| 阜新市| 威信县| 宜兰市| 酒泉市| 丰顺县| 鄱阳县| 韩城市| 通城县| 临朐县| 平湖市| 浦江县| 渭源县| 武冈市| 石楼县| 博罗县| 永胜县| 荔波县|