目录
1.虚拟设备文件概述 1
2.虚拟设备文件eventfd 1
2.1 虚拟设备文件eventfd概述 1
2.2 打开虚拟设备文件eventfd 1
2.3 读取事件文件 3
2.4 写事件文件 3
3.参考文献 4
-
虚拟设备文件概述
Linux内核自2.6.22版本开始逐步增加了三个虚拟设备文件:eventfd、timerfd、signalfd。这三个文件让应用程序可以通过标准I/O操作的方式代替传统调用API的方式来使用事件(信号量)、定时器和信号资源,这带来的最大好处是应用程序可以通过使用select(或poll、epoll)同时监听多个此类文件(或此类文件与其他文件),将对多个事件的异步并行处理方便地转化为同步串行处理,这在许多应用中非常有用。SylixOS完全兼容这三个虚拟设备文件,并且增加了一个hstimerfd,用于高精度定时器。
-
虚拟设备文件eventfd
-
虚拟设备文件eventfd概述
SylixOS中的虚拟设备文件eventfd主要用于线程间的事件通信,其本质上是内核为应用程序提供的信号量。它与POSIX信号量相比的优势是,eventfd在内核中是以文件形式存在的,可用于select/epoll/poll循环中,因此可以实现异步的信号量,避免了消费者在资源不可用时的阻塞。
event表示它可用来作事件通知(异步),fd表示它是一个"文件"。
-
打开虚拟设备文件eventfd
如果用户想要使用eventfd的相关API函数,需要满足如下条件:
1.当前允许使用系统的设备管理功能(LW_CFG_DEVICE_EN的参数值大于零);
2.已在系统中加入eventfd功能模块(LW_CFG_EVENTFD_EN的参数值大于零);
3.调用函数API_EventfdDrvInstall安装eventfd设备驱动程序,具体实现如程序清单 21所示;
程序清单 21安装设备驱动
LW_APIINTAPI_EventfdDrvInstall (VOID)
{
if (_G_iEvtfdDrvNum <= 0) {
_G_iEvtfdDrvNum =iosDrvInstall(LW_NULL,
LW_NULL,
_evtfdOpen,
_evtfdClose,
_evtfdRead,
_evtfdWrite,
_evtfdIoctl);
DRIVER_LICENSE(_G_iEvtfdDrvNum,"GPL->Ver 2.0");
DRIVER_AUTHOR(_G_iEvtfdDrvNum,"Han.hui");
DRIVER_DESCRIPTION(_G_iEvtfdDrvNum,"eventfd driver.");
}
if (_G_hEvtfdSelMutex ==LW_OBJECT_HANDLE_INVALID) {
_G_hEvtfdSelMutex =API_SemaphoreMCreate("evtfdsel_lock",LW_PRIO_DEF_CEILING,
LW_OPTION_WAIT_PRIORITY |LW_OPTION_DELETE_SAFE |
LW_OPTION_INHERIT_PRIORITY |LW_OPTION_OBJECT_GLOBAL,
LW_NULL);
}
return ((_G_iEvtfdDrvNum == (PX_ERROR)) ? (PX_ERROR) : (ERROR_NONE));
}
4.调用函数API_EventfdDevCreate安装eventfd设备,具体实现如程序清单 22所示。
程序清单 22安装设备
LW_APIINTAPI_EventfdDevCreate (VOID)
{
if (_G_iEvtfdDrvNum <= 0) {
_DebugHandle(__ERRORMESSAGE_LEVEL,"no driver.\r\n");
_ErrorHandle(ERROR_IO_NO_DRIVER);
return (PX_ERROR);
}
if (iosDevAddEx(&_G_evtfddev.ED_devhdrHdr,LW_EVTFD_DEV_PATH,
_G_iEvtfdDrvNum,DT_CHR) != ERROR_NONE) {
return (PX_ERROR);
}
return (ERROR_NONE);
}
打开虚拟设备文件eventfd,在该函数中initval参数表示事件的初始状态,为0则表示当前没有任何事件产生,参数flags为该事件文件的操作选项。
flags标志位有如下三种:
1.EFD_SEMAPHORE的含义为将事件按照计数信号量来操作;
2.EFD_NONBLOCK 类似于使用O_NONBLOCK标志来设置文件描述符;
3.EFD_CLOEXEC 类似open以O_CLOEXEC标志打开,O_CLOEXEC应该表示执行exec()时,之前通过open()打开的文件描述符会自动关闭测试,在open()之后,调用exec(),在新的进程中检测描述符是否已经关闭,具体实现如程序清单 23所示。
程序清单 23打开eventfd文件
LW_APIinteventfd (unsignedintinitval,intflags)
{
INTiFd;
PLW_EVTFD_FILEpevtfdfil;
flags &= (EFD_SEMAPHORE |EFD_CLOEXEC | EFD_NONBLOCK);
iFd =open(LW_EVTFD_DEV_PATH,O_RDWR | flags);
if (iFd >= 0) {
pevtfdfil = (PLW_EVTFD_FILE)API_IosFdValue(iFd);
pevtfdfil->EF_u64Counter = (UINT64)initval;
if (pevtfdfil->EF_u64Counter) {
API_SemaphoreBPost(pevtfdfil->EF_ulReadLock);
}
}
return (iFd);
}
-
读取事件文件
用户在使用虚拟设备文件eventfd时,如果需要读取事件文件或者等待事件的发送则需要用到函数eventfd_read,当没有事件需要调用并且没有使用参数EFD_SEMAPHORE时,该函数将会阻塞。具体实现如程序清单 24所示。
程序清单 24读取事件文件
LW_APIinteventfd_read (intfd,eventfd_t *value)
{
return (read(fd,value, sizeof(eventfd_t)) !=sizeof(eventfd_t) ?PX_ERROR : ERROR_NONE);
}
需要用户注意的地方是如果使用的eventfd函数的参数flags包含了EFD_SEMAPHORE时,其含义为将事件按照计数信号量来操作。即如果当前已经产生了多个事件,每一次读取只会包含一个事件,该文件内部的事件计数器只减一,value里面的内容为1。当没有使用EFD_SEMAPHORE时,一次读取所有的事件,value里面的内容即为读取的事件数量,内部的事件计数器归零。两种方式可满足应用程序在不同场景的需求。
-
写事件文件
当用户在使用虚拟设备文件eventfd时,如果需要写事件文件或者发送事件则需要用到函数eventfd_write,当内部事件计数器达到最大值时,将不能继续发送事件,此时将返回错误。具体实现如程序清单 25所示。
程序清单 25写事件文件
LW_APIinteventfd_write (intfd,eventfd_tvalue)
{
return (write(fd, &value,sizeof(eventfd_t)) !=sizeof(eventfd_t) ?PX_ERROR : ERROR_NONE);
}
需要用户注意的是,eventfd_write函数总是会等待上一次写入的事件被读取完后才能完成本次事件的写入,所以事件文件的读和写是一个相互同步的过程,一次事件的数值可理解为事件发生后所能处理的资源数量,EFD_SEMAPHORE则允许事件接受方决定是一次处理多个资源还是单个资源。
-
参考文献
1.《SylixOS应用开发手册》
2. SylixOS源码