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机制”文章中已经讲过,可以参考那里的分析。