zircon fifo實現分析

fifo是一種進程間通信機制,是一種先進先出的queue。其設計目的是作爲共享內存傳輸的控制面,
其讀寫性能比socket或者channel都更加有效率,但是其在elements和buffers的大小上有嚴格的限制!
//TODO:限制的本質原因

系統調用banjo文件位置:zircon\system\public\zircon\syscalls.banjo

fifo的創建:zx_fifo_create
系統調用到內核的實現爲sys_fifo_create

sys_fifo_create
    auto up = ProcessDispatcher::GetCurrent() //調用process的dispatcher的靜態方法GetCurrent,獲取當前進程的dispatcher對象,主要做權限檢查,確認當前進程有FIFO的新建權限!
    FifoDispatcher::Create //限制(count * elemsize)不能大於PAGE_SIZE,即4096。應該可以改!
    out0->make //kernel句柄傳遞到用戶態
    out1->make
    
fifo的讀取:zx_fifo_read
系統調用到內核的實現爲sys_fifo_read

sys_fifo_read
    auto up = ProcessDispatcher::GetCurrent()
    up->GetDispatcherWithRights
    FifoDispatcher::ReadToUser
    actual_count.copy_to_user
    
fifo的讀取:zx_fifo_write
系統調用到內核的實現爲zx_fifo_write

zx_fifo_write
    auto up = ProcessDispatcher::GetCurrent()
    up->GetDispatcherWithRights
    FifoDispatcher::WriteFromUser
        FifoDispatcher::WriteSelfLocked
    actual_count.copy_to_user
        
其實fifo的實現非常簡單,提供的系統調用接口有3個:create、read、write。
fifo在內核中的實現呈現爲一個FifoDispatcher對象,該對象繼承自PeeredDispatcher。其包含如下的私有成員:
data_:uint8_t類型的指針,用於存儲FIFO數據
head_:指示FIFO中上次寫入的下一個位置
tail_:指示FIFO中上次讀取的下一個位置
elem_size_:FIFO中存取的元素結構體大小
elem_count_:FIFO中存取的元素結構體數量
mask_:elem_count_-1,用於對head_和tail_取模

不過fifo在zircon內核中還是非常有代表性;其代表了一種雙端(peer)對象。
內核中的所有peer對象都繼承PeeredDispatcher類。
它包含了一個PeerHolder類型的私有成員,而PeerHolder類型則主要提供一個基於mutex的lock對象及獲取這個lock對象指針的get_lock方法。
fifo的讀寫操作都需要在這個mutex對象的保護下進行,保證數據一致性!
PeeredDispatcher類還包含另一個重要數據成員——peer_,爲指向它自身類型的一個指針。

在FifoDispatcher::Create靜態方法中,首先會動態創建一個PeerHolder對象,然後將它同時傳遞給兩個fifo的構造函數。
對,這裏會創建兩個fifo對象(fifo0、fifo1),它們具有相同尺寸的存數據的buffer,但是注意,這兩個buffer是相互獨立的!
這兩個fifo的初始化時,傳入了同一個holder對象,這也標誌着兩個fifo在數據的讀寫上都會競爭同一把mutex鎖,串行執行!!
兩個fifo創建好後,會調用Init方法,將各自的peer_成員指向對方,如下:
    fifo0.dispatcher()->Init(fifo1.dispatcher());
    fifo1.dispatcher()->Init(fifo0.dispatcher());
這樣兩個fifo之間就被一把mutex鎖和各自的peer_對象聯繫了起來,下面還會詳細分析下fifo的讀寫,到時還會更深切的理解內核的peer對象!

FifoDispatcher::ReadToUser
    Guard<fbl::Mutex> guard{get_lock()}; //第一步先獲取holder的mutext鎖
    ptr.copy_array_to_user //將fifo的數據拷貝到用戶空間給定的指針
    peer_->UpdateStateLocked(0u, ZX_FIFO_WRITABLE) //這句的前提是之前fifo已經full,但經過這輪read後fifo有空閒了,所以通知對端(peer)可寫
    UpdateStateLocked(ZX_FIFO_READABLE, 0u) //如果本fifo被這輪讀空,則置爲本fifo dispatcher爲不可讀!

FifoDispatcher::WriteFromUser
    Guard<fbl::Mutex> guard{get_lock()}; //第一步仍然是先拿holder鎖
    peer_->WriteSelfLocked //FifoDispatcher::WriteSelfLocked,這裏是直接寫到對端(peer)的fifo中去
        ptr.copy_array_from_user //將數據拷貝到fifo中
        UpdateStateLocked(0u, ZX_FIFO_READABLE); //這句執行的前提是之前fifo爲空,這輪寫入後,需要通知本fifo dispatcher可讀
        peer_->UpdateStateLocked(ZX_FIFO_WRITABLE, 0u); //如果本fifo被這輪寫滿,則設置對端fifo dispatcher爲不可寫!

這裏邊涉及到一個UpdateStateLocked函數,沒有展開講,因爲在“zircon的event實現及async loop機制”文章中已經講過,可以參考那裏的分析。


 

發佈了79 篇原創文章 · 獲贊 28 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章