Sysfs_notify唤醒调用epoll的用户面进程

(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操作系统中一种很有用的、内核与用户面进程之间的异步通信机制。

你可能感兴趣的:(Sysfs_notify唤醒调用epoll的用户面进程)