(Sysfs_notify唤醒用户面进程)
这是一个很有用的内核与用户面交互的机制。
简单而言就是:sysfs_notify()可以唤醒在读写属性文件上时因调用select()、poll()、epoll()而阻塞的用户面进程。(sysfs_notify_dirent()也可以唤醒,但我没有实际测试过)。内核借助于sysfs创建了属性文件,用户面程序可以读写这些属性文件实现与内核的交互。
在本项目中,处理器是xilinx的ZYNQ MPSOC,它是一款高性能、AMP架构的处理器,内部集成了FPGA、4个A53核、2个R5核。在应用中,A53使用嵌入式linux操作系统,R5使用实时多任务操作系统FreeOS,我们使用IPI邮箱机制来实现A53与R5核之间的通信:当A53收到R5的邮箱消息时,就通知用户面的进程去作相应处理。在用户面的进程中,我们选择epoll()来监测文件描述符,之所以选择epoll而没有选择selec、poll,是因为在绝大多数情况下,epoll有更好的性能。
(关于深入理解并熟练使用epoll,如下的这篇文章总结得很透彻:blog.chinaunix.net/uid-28541347-id-4273856.html )。
A53的linux内核使用sysfs创建了两个属性文件,这两个属性文件是:
remote_kick:文件属性是-rw-r–r--,当A53接收到R5的中断时,在该文件中写入“1”;
remote_pending_message:文件属性是-r–r--r–,当A53接收到R5的中断时,将邮箱消息写入该文件,等待用户面进程读取;
A53上用户面与内核的交互机制如下:
1、用户面进程通过fd_remote_kick = open(“remote_kick”,O_RDNOLY)打开“remote_kick”文件的描述符;
2、用户面进程通过epoll()监测fd_remote_kick,当监测条件未满足时,epoll()阻塞、进入睡眠;
3、当R5向A53发送中断时,内核读取邮箱消息并写入到remote_pending_message文件,同时内核调用sysfs_notify()。由于内核调用了sysfs_notify(),唤醒了阻塞在其上的epoll();
4、epoll()唤醒后,用户面进程读取remote_pending_message文件的内容,并进行相应处理。
用户面代码:
#include
struct epoll_event PollEvent, *pEvent = NULL;
int fd_epoll = -1, fd_remote_kick=-1;
fd_epoll=epoll_create(MAX_EPOLL_HANDLE);
pEvent = (struct epoll_event*)malloc(sizeof(struct epoll_event) * MAX_EPOLL_HANDLE );
if(pEvent == NULL || fd_epoll < 0) {
return FAIL;
}
fd_remote_kick=open(“sys/devices/platform/remoteproc0/remte_kick”,O_RDONLY);
if( fd_remote_kick < 0 ){
return FAIL;
}
read( fd_remote_kick, &buf, sizeof(int)); //dummy reading
PollEvent.events=EPOLLWAKEUP | EPOLLERR;
if(0!= epoll_ctl(fd_epoll,EPOLL_CTL_ADD,fd_remote_kick,&PollEvent)) {
printf(“epoll add fail, errno=%d\n”,errno); //注册监控事件
}
……
while(1)
{
n=epoll_wait(fd_epoll, pEvent, MAX_EPOLL_HANDLE, -1); //阻塞
if( n < 0 ){
exit(0);
}
else if( n == 0 ){ //timeout
//doing nothing
}
else {
// epoll() wakeup, do your processing
}
}
内核代码:
内核代码中,当接收到R5中断后,做如下调用:
sysfs_notify( &(rproc->dev.kobj), NULL, “remote_kick”));
第一个参数是如下调用后得到的struct kobject对象:
struct device *dev = &rproc->dev;
sysfs_create_file( &dev->kobj, &dev_attr_kick.attr);
调试中发现并解决了如下问题:
1、创建fd_remote_kikc文件描述符后,需要对此文件进行一次dummy read操作,否则,该描述符加入到epoll()的event后,总是会读到EPOLLERR;
2、epoll()的events监测标志要设置为EPOLLWAKEUP | EPOLLERR,当内核调用sysfs_notify()后,epoll()能成功唤醒;如设置成其他标志,如:EPOLLIN|EPOLLERR,测试发现:即使内核没有调用sysfs_notify(),epoll()也能被唤醒;这与EPOLLIN标志的含义有关;
3、用户面在调用epoll_wait()时,超时时间需要仔细考虑,我一开始设置成100ms,却发现即使超时了,还没有收到内核发来的notify,原因是超时时间设置得太小。将超时参数设置为-1(永远等待),或者更长时间的超时,就解决了这个问题;
用户进程通过select、poll、epoll监测属性文件,当被监测的属性文件未发生改变时,进程被阻塞;内核通过调用sysfs_notify、sysfs_notify_dirent来唤醒被阻塞的用户面进程。这是linux操作系统中一种很有用的、内核与用户面进程之间的异步通信机制。