 手冊解析:通過本地域套接字帶外傳遞文件描述符)
SerenityOS recvfd(2) 手冊解析通過本地域套接字帶外傳遞文件描述符【免費下載鏈接】serenityThe Serenity Operating System 項目地址: https://gitcode.com/GitHub_Trending/se/serenity本篇圍繞 Serenity OS 系統(tǒng)調(diào)用手冊頁 recvfd.md 展開完整覆蓋其函數(shù)原型、options標(biāo)志、返回值與錯誤碼語義并結(jié)合內(nèi)核源碼 sys$recvfd 實現(xiàn) 與 LocalSocket::recvfd 的實現(xiàn)細節(jié)說明 fd 如何以帶外out-of-band方式在本地套接字兩端傳遞。讀完后你將掌握如何在兩個 SerenityOS 進程間通過AF_LOCAL套接字傳遞打開的文件描述符、recvfd()的非阻塞語義與全部錯誤碼的內(nèi)核出處、以及它與配對調(diào)用sendfd()的協(xié)作方式。1. 功能概述跨進程傳遞打開的 fdrecvfd()用于從經(jīng)sockfd連接的本地套接字對端進程接收一個已打開的文件描述符file descriptor。它與sendfd()構(gòu)成一對對稱系統(tǒng)調(diào)用調(diào)用原型語義返回sendfdint sendfd(int sockfd, int fd);發(fā)送一個打開的 fd 給本地套接字對端成功返回 0失敗返回 -1recvfdint recvfd(int sockfd, int options);接收對端發(fā)來的 fd成功返回新分配的非負 fd失敗返回 -1兩者都要求sockfd指向本地域local domain套接字且 fd 的傳遞是帶外的走獨立的 fd 隊列不影響套接字常規(guī)數(shù)據(jù)流read/write緩沖區(qū)的內(nèi)容與順序。這一機制源自 Plan 9 from User SpaceSerenityOS 將其納入自身系統(tǒng)調(diào)用集。1.1 與常規(guī)數(shù)據(jù)流的分離這一點在內(nèi)核結(jié)構(gòu)上可以直接印證。LocalSocket 為每對連接維護了兩個DoubleBuffer數(shù)據(jù)緩沖m_for_client/m_for_server見 LocalSocket 構(gòu)造函數(shù)用于常規(guī)讀寫而sendfd/recvfd使用的m_fds_for_client/m_fds_for_server兩個VectorNonnullRefPtrOpenFileDescription隊列是完全獨立的。從源碼結(jié)構(gòu)看兩個通道的阻塞判定互不影響因此傳 fd 不會干擾同一連接上的普通字節(jié)流通信。1.2 系統(tǒng)調(diào)用層與 LibC 封裝recvfd在系統(tǒng)調(diào)用表中的注冊條目見 Syscall.hS(recvfd, NeedsBigProcessLock::No)NeedsBigProcessLock::No表明該調(diào)用不要求持有進程大鎖執(zhí)行路徑較短。用戶態(tài)入口位于 LibC 的 sys/socket.cppint recvfd(int sockfd, int options) { int rc syscall(SC_recvfd, sockfd, options); ... }原型聲明見 sys/socket.h。與手冊頁一致需要#include sys/socket.h后即可調(diào)用。2. Synopsis 與 options 參數(shù)手冊頁給出的函數(shù)原型為#include sys/socket.h int recvfd(int sockfd, int options);options是一個位掩碼目前手冊頁僅定義了一個標(biāo)志O_CLOEXEC接收到的新 fd 應(yīng)設(shè)置 close-on-exec 標(biāo)志即調(diào)用進程后續(xù)exec時該 fd 自動關(guān)閉exec(2) 手冊頁交叉引用的執(zhí)行語義。在內(nèi)核實現(xiàn)中該標(biāo)志的解析只有一步sys$recvfd 中若options O_CLOEXEC則向新 fd 寫入FD_CLOEXEC標(biāo)志位隨后連同收到的OpenFileDescription一起掛入調(diào)用進程的 fd 表。換言之recvfd()成功返回的 fd 是調(diào)用進程 fd 表中新分配的最小空閑號它引用的是對端發(fā)來的同一份打開文件描述對象共享打開狀態(tài)如文件偏移、異步信號狀態(tài)。3. 非阻塞語義與接收流程手冊頁明確指出recvfd()是非阻塞調(diào)用若套接字隊列中沒有等待接收的 fd 則會失敗返回EAGAIN。內(nèi)核側(cè)的完整調(diào)用鏈為前置檢查sys$recvfdrequire_promise(Pledge::recvfd)——進程必須已pledge了recvfd能力否則拒絕EDEFAULTopen_file_description(sockfd)失敗則返回對應(yīng)錯誤fd 未打開即EBADF不是套接字 →ENOTSOCK不是本地套接字 →EAFNOSUPPORT。預(yù)分配 fd 槽位先在調(diào)用進程 fd 表中allocate()一個 fd 號為即將收到的描述符預(yù)留位置。出隊LocalSocket::recvfd 在套接字互斥鎖下按當(dāng)前 fd 的角色Role::Connected或Role::Accepted選出對應(yīng)的recvfd隊列隊列為空則返回EAGAIN否則queue.take_first()取出隊首的OpenFileDescription并轉(zhuǎn)移所有權(quán)。落入 fd 表回到 sys$recvfd將描述符連同O_CLOEXEC轉(zhuǎn)換后的標(biāo)志寫入已分配的 fd 槽位返回 fd 號。角色role與隊列的對應(yīng)關(guān)系由 recvfd_queue_for / sendfd_queue_for 決定客戶端角色Connected從m_fds_for_client收、往m_fds_for_server發(fā)被接受端角色Accepted反之。兩個方向各自獨立排隊互不干擾。4. 返回值與錯誤碼附內(nèi)核出處成功時recvfd()返回收到的 fd非負整數(shù)失敗返回 -1 并設(shè)置errno。手冊頁列出的五個錯誤在內(nèi)核中的產(chǎn)生位置如下errno手冊頁含義內(nèi)核產(chǎn)生位置EBADFsockfd不是打開的 fdopen_file_description(sockfd)解析失敗見 sys$recvfdENOTSOCKsockfd不指向套接字!socket_description-is_socket()見 sys$recvfdEAFNOSUPPORTsockfd不是本地域套接字!socket.is_local()見 sys$recvfdEINVALsockfd不是已連接/已接受的套接字LocalSocket::recvfd 中 role 既非Connected也非AcceptedEAGAIN該套接字上沒有排隊等待的 fdLocalSocket::recvfd 隊列為空時與之配對的sendfd()手冊頁見 sendfd.md還額外定義了兩個錯誤ENOTCONNsys$sendfd 中!socket.is_connected()與EBUSYLocalSocket::sendfd 中對端待收隊列超過 128 個 fd 時拒絕源碼中該上限標(biāo)注為 FIXME、尚待確定正式的限制策略。這解釋了為何recvfd()是非阻塞的隊列無界增長不受鼓勵發(fā)送端滿 128 即快速失敗接收端則以EAGAIN表示當(dāng)前沒有可取的 fd。5. 典型使用模式基于前文語義一次 fd 傳遞的完整流程示意如下以AF_LOCAL流式套接字為例#include sys/socket.h #include fcntl.h /* 接收方先建立本地連接bind/listen/accept 或 connect 完成后再取 fd */ int client /* accept() 返回的已接受套接字或 connect() 后的對端套接字 */; int fd recvfd(client, O_CLOEXEC); /* 要求 pledge 過 recvfd */ if (fd 0) { /* errno: EBADF/ENOTSOCK/EAFNOSUPPORT/EINVAL/EAGAIN */ } /* 發(fā)送方在與 client 同一連接的另一個 fd 上執(zhí)行 sendfd(peer_fd, fd_to_pass); 要求 pledge 過 sendfd返回 0 表示入隊成功。 */要點歸納兩端必須先完成本地套接字的連接/接受使角色達到Connected或Accepted否則會收到EINVALrecvfd()返回EAGAIN時通常意味著對端尚未sendfd或已取空隊列可稍后重試該調(diào)用本身不會掛起傳過來的是打開的文件描述符接收方得到的是一個全新 fd 號但共享同一打開文件狀態(tài)可直接讀寫、dup或再傳給第三方。6. 歷史與延伸閱讀recvfd()最早由 Plan 9 from User Space 引入SerenityOS 保留了該命名與語義并擴展了options位掩碼當(dāng)前僅O_CLOEXEC。相關(guān)手冊頁與源碼入口配對調(diào)用手冊頁sendfd(2)系統(tǒng)調(diào)用實現(xiàn)Kernel/Syscalls/sendfd.cppsys$sendfd與sys$recvfd同文件實現(xiàn)內(nèi)核套接字層隊列Kernel/Net/LocalSocket.cpp系統(tǒng)調(diào)用注冊表Kernel/API/Syscall.h用戶態(tài)封裝Userland/Libraries/LibC/sys/socket.cpp適用前提以上行為描述基于當(dāng)前倉庫內(nèi)核與 LibC 的實際實現(xiàn)sendfd端 128 個 fd 的隊列上限是源碼中明確標(biāo)注待完善的臨時值FIXME后續(xù)版本可能調(diào)整以倉庫當(dāng)時的 LocalSocket::sendfd 為準(zhǔn)?!久赓M下載鏈接】serenityThe Serenity Operating System 項目地址: https://gitcode.com/GitHub_Trending/se/serenity創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考