学完了休眠唤醒机制、poll机制、异步通知、定时器、tasklet、工作队列、mmap、input子系统后,该沉淀沉淀了
一、睡眠机制
案例:APP程序读取按键值 - 睡眠机制(阻塞或非阻塞)
1.等待队列头创建
static DECLARE_WAIT_QUEUE_HEAD(key_waitqueue);
2.等待队列
wait_event_interruptible(wq, condition);
等待wq队列,当condition条件为真时表示等到,则返回
3.唤醒队列
wake_up_interruptible(wq);
4.非阻塞机制
上面的方式是阻塞方式,想要非阻塞,打开文件时加上 O_NONBLOCK 标志
fd = open(argv[1], O_RDWR | O_NONBLOCK);
驱动程序也要改,读取时判断文件的 flag 是否有 O_NONBLOCK
if (!key_status && (filp->f_flags & O_NONBLOCK))
return -EAGAIN;
wait_event_interruptible(key_waitqueue, key_status);
“!key_status” 表示此时没有按键值可读,那么如果此时设置了 O_NONBLOCK 标志的话,则不需要等待队列了,直接返回 EAGAIN
二、poll 机制 (闹钟)
案例:APP程序读取按键值 - poll 机制(非阻塞的话直接定时0即可,一直阻塞的话第三个参数设置-1)
poll机制流程细节图
代码流程图
三、fasync异步通知
案例:APP程序读取按键值 - fasync异步通知(建议使用)
fasync异步通知代码流程:
- APP程序打开文件,定义 signal 函数(SIGIO信号)
- 使用 fcntl 函数设置文件的flag,记录进程pid,设置 FASYNC/O_ASYNC 标志(会触发调用驱动层的 fasync 函数)
- 驱动层实现 fasync 函数,使用 fasync_helper 函数初始化 fasync_struct 结构体(保存了文件的标志位)
- 按下按键后调用 kill_fasync(&button_fasync, SIGIO, POLL_IN),会从 button_fasync->fa_file 中取出 pid,然后发送 SIGIO 信号
- 应用程序执行信号处理函数
PS:如果想要禁止 fasync 功能,屏蔽 FASYNC/O_ASYNC 标志位即可
fcntl(key_fd, F_SETFL, flags & ~O_ASYNC);
关键代码部分:
应用程序关键代码
/* F_SETOWN: 设置将接收SIGIO和SIGURG信号的进程id */
fcntl(key_fd, F_SETOWN, getpid());
/* F_GETFL: 取得fd的文件状态标志 */
flags = fcntl(key_fd, F_GETFL);
/* O_ASYNC: 当I/O可用的时候,允许SIGIO信号发送到进程组,
例如:当有数据可以读的时候 */
fcntl(key_fd, F_SETFL, flags | O_ASYNC); //启动驱动的fasync功能
驱动层关键代码:
struct fasync_struct *button_fasync;
...
...
static int key_fasync (int fd, struct file *filp, int on)
{
int retval;
retval = fasync_helper(fd, filp, on, &button_fasync);
if (retval < 0)
return retval;
return 0;
}
static struct file_operations key_ops = {
......
.fasync = key_fasync,
};
...
...
static irqreturn_t key_irq_handler(int irq, void *dev)
{
......
kill_fasync(&button_fasync, SIGIO, POLL_IN);
}