青春往事
- 高中时期,当时是夏天,天气非常炎热。我们还是住宿舍,一个大院子,园子的南面是2m高的墙,东西两面是宿舍楼,北面是宿舍大门,把我们围得很安全,但宿舍很简陋,连锁都没有,所以到晚上时候,都是敞开门,穿着内裤睡觉,裤子也放在床尾。 由于是刚开学,几千块钱的学费都在自己手里攥着,有的同学就也放在了床尾。
- 凌晨两点,正睡得熟,迷迷糊糊听见抓小偷。我当时听见这个顿时很兴奋,就跑了出来,一看一个人影正在翻南面的2m的高墙,身手很好,三下五除二就来到了墙头,跳了下去。我一个同学的学费被这个小偷顺了出去,也跟着翻,当然没有人家专业,翻不过去。当时心中正义感还是满满的,我们喊宿舍的阿姨开门,一群人穿着内裤,拿着断的桌子腿,椅子腿冲出宿舍,在南墙的道路上找小偷,人家偷东西是专业的,逃跑也是专业的,我们一群人找了半天也没有找到。回来之后,大家就在也睡不着了,在宿舍门口蹲着等小偷,再来光顾。可能是我们都穿着内裤,被小偷看见了,不敢回来了…
(现在想想那时候真好!不像现在,为了生活忙碌,这几天在debug 高通芯片dsi-lcd屏在bp侧xbl花屏问题,有点难搞,明天加班看看能不能有进展,解决之后,也会出文章介绍)
简介
前面的非阻塞或者阻塞访问的方式都是应用程序主动读取的,对于非阻塞方式还需要应用程序通过poll函数不断轮询读取。最好的方式是驱动程序能主动向应用程序发出通知,报告自己可以可以访问,然后应用程序能从驱动中读取或者写入数据。在linux中,提供了这个异步通知的机制,本章梳理了一下异步通知以及如何在驱动中添加异步通知的相关代码。
异步通知
- 回顾一下中断们我们配置好中断以后就让处理器处理其他事情了,当中断发生之后触发我们预制的中断服务函数函数,在中断服务中做具体的处理事情。其实中断就是处理器提供的一种异步机制。
- ”信号“就为异步中断应用而生,类似硬件上使用的中断,只不过信号是软件层次的中断。算是软件上一次对中断的一种模拟,驱动可以通过主动把向应用程序发送信号报告自己可以访问了,应用程序获取到信号之后就可以从驱动设备中read
or write data。 - 整个过程:app 收到driver主动发送过来的一个interrupt,然后app去response这个interrupt,在这个process中,app并没有去查询driver devices是否可以访问,一切都是driver devices自己告诉app的。
- 阻塞,非阻塞,异步通知是针对不同的场合提出来的解决方法,没有优劣之分,根据场景灵活运用。
- 异步通知的core就是signal,在arch/xtensa/include/uapi/asm/signal.h中定义了linux所有信号,如下:
#define SIGHUP 1 终端挂起 or 控制进程终止
#define SIGINT 2 终端中断
#define SIGQUIT 3 终端退出
#define SIGILL 4 非法指令
#define SIGTRAP 5
#define SIGABRT 6
#define SIGIOT 6
#define SIGBUS 7
#define SIGFPE 8
#define SIGKILL 9 杀死、终止进程
#define SIGUSR1 10
#define SIGSEGV 11
#define SIGUSR2 12
#define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGSTKFLT 16
#define SIGCHLD 17
#define SIGCONT 18
#define SIGSTOP 19 停止进程的执行,只是暂停
#define SIGTSTP 20
#define SIGTTIN 21
#define SIGTTOU 22
#define SIGURG 23
#define SIGXCPU 24
#define SIGXFSZ 25
#define SIGVTALRM 26
#define SIGPROF 27
#define SIGWINCH 28
#define SIGIO 29
#define SIGPOLL SIGIO
/* #define SIGLOST 29 */
#define SIGPWR 30
#define SIGSYS 31
#define SIGUNUSED 31
/* These should not be considered constants from userland. */
#define SIGRTMIN 32
#define SIGRTMAX (_NSIG-1)
/*
1.除了SIGKEILL(9)和SIGSTOP(19)这两个信号不能被忽略,其它信号都可被忽略。
2.这些信号就相当于终端号,不同中断号代表不同中断。
3.驱动程序可以向app发送不同的signal来实现不同的func。
*/
函数简介
1.signal
在使用中断的时候要设置中断处理函数,同样,在app中使用信号,必须设置中断信号所使用的中断处理函数,在app中使用signal来设置指定信号的处理函数,原型如下:
sighandler_t signal(int signum,sighandler_t handler)
//signum 要设置处理函数的信号
//hander 处理函数
//return 信号的处理函数,失败返回SIG_ERR
信号函数处理原型:
typedef void(*sighandler_t)(int)
例子:
kill -9 pid
向指定进程发送SIGKILL信号。
ctrl+c
组合键回向当前占用中断的应用程序发送SIGINT信号,默认是关闭当前app。
/*编译一下应用程序,ctrl+c按下,发送SIGINT信号,
sigint_handler就会执行,打印出SIGINT signal!!后退出,关闭app。
*/
void sigint_handler(int num){
printf("SIGINT signal !!\r\n");
exit(0);
}
int main(){
signal(SIGINT,signet_handler);
for(;;){};
return 0;
}
驱动中信号处理
1. fasync_struct 结构体
首先我们在driver app中define一个fasync_struct结构体指针变量,fasync_struct 结构体如下:
struct fasync_struct{
spinlock_t fa_lock;
int magic;
int fa_fa;
struct fasync_struct *fa_next;
struct file *fa_file;
struct rcu_head fa_rcu;
};
一般将fasync_struct定义到设备结构体重,比如:
struct xxx_dev{
......
struct fasync_struct *async_queue;
//异步相关结构体
};
2. fasync函数
如果要使用异步通知,需要在设备驱动中实现file_operations操作集中的fasync函数,函数如下:
int (*fasync)(int fd,struct file *filp,int on);
fasync 函数一般通过调用fasync_helper函数来初始化前面定义的fasync_struct结构体指针,fasync_helper函数如下:
int fasync_helper(int fd, struct file *filp,int on,struct fasync_struct **fapp)
//前三个参数就是fasync函数的参数,第四个参数就是要初始化的fasync_struct结构体指定变量。当应用程序通过<fcntl(fd,F_SETFL,falgs|FASYNC)>改变fasync标记的时候,驱动程序file_operations操作集中的fasync就会执行。
**3.驱动fasync参考如下:
//fasync start.......
struct xxx_dev{
......
struct fasync_struct *async_queue ;
//异步相关结构体
};
static int xxx_faync(int fd, struct file *filp,int on){
struct xxx_dev *dev = (xxx_dev)filp->private_data;
if(fasync_helper(fd,flip,on,&dev->aync_struct)<0){
return -EIO;
}
return 0;
}
static struct file_operations xxx_ops = {
......
.async = xxx_fasync,
};
//fasync end ......
/*
在关闭驱动文件的时候,同样在file_operations操作集中的release函数中释放fasync_struct,
fasync_struct的释放函数同样为fasync_helper,
release函数参考如下:
*/
//release start ......
static int xxx_release(struct inode *inode ,struct file *flip){
return xxx_fasync(-1,flip,0);
//删除异步通知
}
static struct file_operations xxx_ops = {
......
.release = xxx_release,
};
//release end ......
4. kill_fasync函数
当设备可以访问的时候,驱动程序需要向应用程序发出信号,相当于产生中断。kill_fasync 函数负责发送指定信号,kill_fasync函数原型如下:
void kill_faysnc(struct fasync_struct **fp,int sig,iny band)
// fp 要操作的faysnc_struct
//sig 要发送的信号
// band 可读时为POLL_IN,可写时为POLL_OUT.
应用程序中对异步通知处理
包含三步:
- 注册信号处理函数 (例:signal(SIGINT,signet_handler);)
- 将本应用的进程号告诉内核(使用fcutl(fd,F_SETWN,getpid)告诉内核)
- 开启异步通知
flags = fcntl(fd,F_GETFL);//获取当前进程状态
fcntl(fd,F_SETFL,flags | FASYNC);//开启当前进程的异步通知功能。