我的项目中,Linux驱动程序含有中断,而中断处理程序需要在GUI中处理(或者至少一部分是),我使用的是Qt,本来想能否让驱动程序像在Qt程序中一样emit 一个signal,但发现这太难实现了,因为我还不知道如何在Qt之外使用MetaObject或moc这类玩意。问了一下Qt群,Q胖说Qt是平台无关的,应该像没有Qt时一样处理。虽然听的我一头雾水,但仔细一想也大受启发,对呀,Linux下有那么多进程间通信的方法,为什么不用啊,可以在驱动程序的中断程序中用信号产生一个软件中断,唤醒Qt中的服务程序呀。虽然这可能不太及时,但满足GUI来讲应该绰绰有余。(先注明一下,本人菜鸟,说的不一定正确,试验可以,但请勿转载,若有错丢人丢不起)
于是我做了个小试验,让Qt感受到来自外进程的信号,并对其作出反应,让信号处理程序发出QEvent,利用QEvent()异步响应,不就等于将信号引入到Qt程序中了吗?(太繁了点)。以下是程序实现,(不好意思,用了ui)
#include <QWidget>
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
protected:
void changeEvent(QEvent *e);
void customEvent(QEvent *e);
private:
Ui::Widget *ui;
};
#include "widget.h"
#include "ui_widget.h"
#include <signal.h>
#include <iostream>
#define SIGMINE 56
QObject* ow;
const QEvent::Type CustomEvent_eint = (QEvent::Type)6666;//建议用5000以上唯一的标识
void eint(int sigNo)
{
if(sigNo==SIGMINE)
{
QCoreApplication::postEvent(ow, new QEvent(CustomEvent_eint)); //用new会不会内存泄漏
//std::cerr<<"signal received"<<std::endl;
}
}
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
ui->textEdit->setText(tr("I'm ready"));
ow=this;
if(signal(SIGMINE, &eint)==SIG_ERR)
{
std::cerr<<"signal install failed"<<std::endl;
}
}
Widget::~Widget()
{
delete ui;
}
void Widget::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void Widget::customEvent(QEvent *e)
{
switch (e->type()) {
case CustomEvent_eint:
{
ui->textEdit->append(tr("I receiveed signal"));
//delete e; 用不着delete,postevent自己就干了
break;
}
default:
break;
}
}
#include <QtGui/QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
编译完后在终端运行,例如
./try_signal &
[2229]
向进程发信号
kill -56 2229
在textedit中显示“I receiveed signal”,说明目的达到了
转念一想,Linux不会这么笨吧,难道没有一个现成的机制解决这类一般性问题?
当然有,我翻了一下Linux设备驱动程序的书,书中介绍了一个概念,专门解决这个问题——异步通知。(以下为转载和注释,这是查到的算比较清楚的了)
异步通知的意思是:一旦设备就绪,则主动通知应用程序。异步通知类似于“中断”的机制,而不像之前学的阻塞型I/O和poll。阻塞I/O意味着一直等待设备可访问后再访问,非阻塞I/O使用poll意味着查询设备是否可访问,而异步通知则意味着设备通知自身可访问,实现了异步I/O。
2、应用层中启用异步通知机制的三个步骤
1)调用signal函数,让指定的信号SIGIO与处理函数sig_handler对应。
signal(SIGIO, sig_handler);
2)指定一个进程作为文件的“属主(filp->owner)”,这样内核才知道信号要发给哪个进程。
fcntl(fd, F_SET_OWN, getpid());
3)在设备文件中添加FASYNC标志,即异步通知模式。
f_flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, f_flags | FASYNC);
疑惑:2、3两步目的是要调用后面的XXX_fasync(),但这是系统自动实现的还是要我们自己写点什么(例如ioctrl)才能实现?
注:查了一下,需要在驱动程序的fops结构中指定fcntl:XXX_fasync。
3、驱动中需要实现的异步通知
驱动中的3项工作和应用程序中的3项工作是一一对应的。
内核已经实现了两步,我们要实现一个简单的传参。由于FASYNC标志改变时,驱动程序中的fasync()函数得以执行,故在驱动中要实现fasync()函数。
1)定义结构体fasync_struct。
struct fasync_struct *async_queue;//异步结构体指针
2)实现XXX_fasync,把函数fasync_helper,fd,filp和定义的结构体传给内核。
int XXX_fasync (int fd, struct file *filp, int mode)
{
struct XXX_dev *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
3)当设备可写时,调用函数kill_fasync发送信号SIGIO给内核。
if (dev->async_queue){
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
4)当设备关闭时,需要将fasync_struct从异步队列中删除:
XXX_fasync(-1, filp, 0);
疑惑:1中的定义是否应该写在device的struct里?不然怎么会有dev->async_queue?
3中的kill_fasync是我改的,原文是XXX_fasync,但我实在是不能理解,到底那个对?
4中的-1和0是什么意思,-1表示当前fd?,0表示删除?
注:疑惑1已经证实。