1.同步
笔者在本文只分析poll同步机制,首先看poll函数的用法:
#include
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
struct pollfd {
int fd; /*文件描述符*/
short events; /* 需要测试的事件 */
short revents; /* 实际发生的事件,也就是返回结果 */
};
功能:
查询数组fds中的fd是否发生了events事件,若发生了events事件,poll立即返回,不阻塞;若没有fd发生events事件,则poll阻塞,经过timeout时间后被唤醒。
参数:
nfds指示fds数组的项数,timeout指示poll函数的超时时间(超过时间,则调用poll函数的进程被唤醒),timeout=0,进程不阻塞;timeout<0,进程阻塞到事件发生为止。例子如下:
ret = poll(fds, 1, 5000);
if (ret == 0) //fds中没有可读写的设备文件
{
do something;
}
else //fds中有可读写的设备文件
{
do otherthing;
}
返回值:
>0,发生了指定events的fd的数量;=0,没有fd发生了events;-1,调用poll失败
原理:
poll函数最终会调用每个fd对应的xxx_poll函数(属于驱动程序),xxx_poll调用poll_wait()将进程挂到等待队列(不会阻塞),xxx_poll()函数返回一个mask给poll(),该mask里面包含了特定的events,xxx_poll()决定哪种情况下使得mask里面含有POLLIN、POLLRDNORM等events 。poll()函数调用schedule_timeout()进行休眠。例子如下:
static unsigned xxx_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); // 不休眠
if (condition)
mask |= POLLIN | POLLRDNORM;//条件成立,则设备文件可读,返回表示可读的mask
return mask;
}
2.异步
原理:
由文件(驱动程序)向应用程序发送信号,应用程序再调用信号函数。
应用层:
首先需要应用程序绑定一个信号函数,当收到某个信号后,执行该函数;然后需要将该进程和某个设备文件绑定,这样设备文件可读写时,才知道给那个进程发信号。例子如下:
signal(SIGIO, my_signal);.//绑定信号函数
fcntl(fd, F_SETOWN, getpid());//绑定进程和设备文件
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC);//给设备文件添加异步通知标识(调用驱动程序里的xxx_fasync()函数)
三个步骤执行后,设备文件就可以向进程发送信号了。
驱动层:
在设备驱动程序里,实现向进程发送信号的功能。
xxx_fasync(),调用fasync_helper()函数,初始化一个结构体
kill_fasync(),向进程发送信号。
static int xxx_fasync (int fd, struct file *filp, int on)
{
return fasync_helper (fd, filp, on, &button_async);//初始化fasync_struct结构体,该结构体在向进程发送信号时用到
}
kill_fasync (&button_async, SIGIO, POLL_IN); //设备文件可读写时,调用该函数,向进程发送信号
3.总结
我们在读写设备文件时,可以采用同步或异步机制,同步机制下,需要我们的应用程序自己不断去查询设备文件是否可读写,属于主动出击;而采用异步机制时,应用程序是被动接受信号,当设备文件可读写时,由设备驱动程序给应用程序发信号,应用程序然后才去读写设备文件,属于被动接受。
同步和异步实现,都是靠对应的设备驱动程序。