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機制”文章中已經講過,可以參考那裏的分析。