概述
一、应用程序对异步通知的处理包括以下三步:
1、注册信号处理函数
应用程序根据驱动程序所使用的信号来设置信号的处理函数,应用程序使用 signal 函数来
置信号的处理函数。
sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值, 第二个参数指定针对前面信号值的处理函数, 若为 SIG_IGN, 表示忽略该信
号; 若为 SIG_DFL, 表示采用系统默认方式处理信号; 若为用户自定义的函数, 则信号被捕获到后, 该函数
将被执行
2、将本应用程序的进程号告诉给内核
使用 fcntl(fd, F_SETOWN, getpid())将本应用程序的进程号告诉给内核。
3、开启异步通知
使用如下两行程序开启异步通知:
flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */
重点就是通过 fcntl 函数设置进程状态为 FASYNC,经过这一步,驱动程序中的 fasync 函
就会执行。
实例
void sigint_handler(int num)
{
printf("rnSIGINT signal!rn");
exit(0);
}
int main(void)
{
signal(SIGINT, sigint_handler);
while(1);
return 0;
}
二、驱动层
1 、首先我们需要在驱动程序中定义一个fasync_struct结构体指针变量,
2、如果要使用异步通知,需要在设备驱动中实现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_helper函数的前三个参数就是fasync函数的那三个参数,第四个参数就是要初始化的fasync_struct结构体指针变量。当应用程序通过“fcntl(fd, F_SETFL, flags | FASYNC)”改变fasync标记的时候,驱动程序file_operations操作集中的fasync函数就会执行。
static int xxx_fasync(int fd, struct file *filp, int on)
{
struct xxx_dev *dev = (xxx_dev)filp->private_data;
if (fasync_helper(fd, filp, on, &dev->async_queue) < 0)
return -EIO;
return 0;
}
static struct file_operations xxx_ops = {
......
.fasync = xxx_fasync,
......
};
3、在关闭驱动文件的时候需要在file_operations操作集中的release函数中释放fasync_struct,fasync_struct的释放函数同样为fasync_helper,release函数参数参考实例如下:
static int xxx_release(struct inode *inode, struct file *filp)
{
return xxx_fasync(-1, filp, 0); /* 删除异步通知 */
}
4、驱动层如何通知应用层
kill_fasync函数
当设备可以访问的时候,驱动程序需要向应用程序发出信号,相当于产生“中断”。kill_fasync函数负责发送指定的信号,kill_fasync函数原型如下所示:
void kill_fasync(struct fasync_struct **fp, int sig, int band)
函数参数和返回值含义如下:
fp:要操作的fasync_struct。
sig:要发送的信号。
band:可读时设置为POLL_IN,可写时设置为POLL_OUT。
返回值:无。
9.4 linux异步I/O
9.4.1 AIO概念与GNU C库 AIO
9.4.1.1 AIO概念
-
同步I/O:linux系统中最常用的输入输出(I/O)模型是同步I/O,在这个模型中,当请求发出后,应用程序就会阻塞,知道请求满足
-
异步I/O:I/O请求可能需要与其它进程产生交叠
-
Linux 系统中最常用的输入/输出(I/O)模型是同步 I/O
- 在这个模型中,当请求发出之后,应用程序就会阻塞,直到请求满足为止
- 调用应用程序在等待 I/O 请求完成时不需要使用任何中央处理单元(CPU)
- 在某些情况下,I/O 请求可能需要与其他进程产生交叠,可移植操作系统接口(POSIX)异步 I/O(AIO)应用程序接口(API)就提供了这种功能
9.4.1.1 AIO系列API:
- aio_read–异步读
- 作用:请求对一个有效的文件描述符进行异步读写操作
- 请求进行排队之后会立即返回
- 这个文件描述符可以表示一个文件、套接字,甚至管道
- 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
- 返回值
- 成功:返回0
- 失败:返回-1,并设置errno的值
- 作用:请求对一个有效的文件描述符进行异步读写操作
int aio_read( struct aiocb *aiocbp );
- 1
- aio_write–异步写
- 作用:请求一个异步写操作
- 请求进行排队之后会立即返回
- 这个文件描述符可以表示一个文件、套接字,甚至管道
- 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
- 返回值
- 成功:返回0
- 失败:返回-1,并设置errno的值
- 作用:请求一个异步写操作
int aio_write( struct aiocb *aiocbp );
- 1
- aio_error
- 作用:确定请求的状态
- 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
- 返回值
- EINPROGRESS:说明请求尚未完成
- ECANCELED:说明请求被应用程序取消
- 失败:返回-1,并设置errno的值
int aio_error( struct aiocb *aiocbp );
- 1
- aio_return–获得异步操作的返回值
- 异步 I/O 和标准块 I/O 之间的另外一个区别是不能立即访问这个函数的返回状态,因为并没有阻塞在 read()调用上
- 在标准的 read()调用中,返回状态是在该函数返回时提供的。但是在异步 I/O 中,我们要使用 aio_return()函数
- 只有在 aio_error()调用确定请求已经完成(可能成功,也可能发生了错误)之后,才会调用这个函数
- 参数aiocb:结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓存区
- 返回值
- 成功:返回所传输的字节数
- 失败:返回-1
ssize_t aio_return( struct aiocb *aiocbp );
- 1
- aio_suspend–挂起异步操作,直到异步请求完成为止
- 作用:挂起(或阻塞)调用进程,直到异步请求完成为止,调用者提供了一个 aiocb 引用列表,其中任何一个完成都会导致 aio_suspend()返回
int aio_suspend( const struct aiocb *const cblist[], int n, const struct timespec *timeout );
- 1
- aio_cancel–取消异步请求
- 作用:允许用户取消对某个文件描述符执行的一个或所有 I/O 请求
- 要求:
- 如果要取消一个请求,用户需提供文件描述符和 aiocb 引用
- 函数返回AIO_CANCELED:请求被成功取消
- 函数返回AIO_NOTCANCELED:请求完成
- 如果要取消对某个给定文件描述符的所有请求,用户需要提供这个文件的描述符以及一个对 aiocbp 的 NULL 引用
- 函数返回AIO_CANCELED:表明所有的请求都取消了
- 函数返回AIO_NOTCANCELED:表明至少有一个请求没有被取消
- 函数返回AIO_ALLDONE:表明没有一个请求可以被取消
- 使用 aio_error()来验证每个 AIO 请求
- aio_error()返回-1并且设置了errno被设置为ECANCELED:表明某个请求已经被取消了
- 如果要取消一个请求,用户需提供文件描述符和 aiocb 引用
int aio_cancel( int fd, struct aiocb *aiocbp );
- 1
- lio_listio–同时发起多个传输(一次系统调用可以启动大量的I/O操作)
- 作用:这个函数非常重要,它使得用户可以在一个系统调用(一次内核上下文切换)中启动大量的 I/O 操作
- 参数
- mode:可以是 LIO_WAIT 或 LIO_NOWAIT
- LIO_WAIT 会阻塞这个调用,直到所有的 I/O 都完成为止
- 在操作进行排队之后,LIO_NOWAIT 就会返回
- list :是一个 aiocb 引用的列表,最大元素的个数是由 nent 定义的
- 如果 list 的元素为 NULL,lio_listio()会将其忽略。
- mode:可以是 LIO_WAIT 或 LIO_NOWAIT
int lio_listio( int mode, struct aiocb *list[], int nent, struct sigevent *sig );
- 1
9.4.2 Linux内核AIO与libaio
- linux AIO也可以由内核空间实现,异步I/O是linux2.6以后版本内核的标准特性
- 对于块设备,AIO可以一次性发出大量的read/write调用并且通过通用块层的I/O调度来获得更好的性能,用户也可以减少过多的同步负载
- 对网络设备而言,在socket层面上,也可以使用AIO,让CPU和网卡的收发充分交叠以改善吞吐性能
- 用户空间中一般要结合libaio来进行内核AIO的系统调用
-
io_setup( )
-
//Initializes an asynchronous context for the current process
-
io_submit( )
-
//Submits one or more asynchronous I/O operations
-
io_getevents( )
-
//Gets the completion status of some outstanding asynchronous I/O operations
-
io_cancel( )
-
//Cancels an outstanding I/O operation
-
io_destroy( )
-
//Removes an asynchronous context for the current process
9.4.3 AIO与设备驱动
- 用户空间调用io_submit()之后,对应于用户传递的每个iocb结构,内核会生成一个与之对应的kiocb结构
-
通过is_sync_kiocb判断某kiocb是否为同步I/O请求
- 如果是返回真,表示为异步I/O请求
-
字符设备:必须明确应支持AIO(极少数是异步I/O操作)
- 字符设备驱动程序中file_operations 包含 3 个与 AIO 相关的成员函数,
-
ssize_t (*aio_read) (struct kiocb *iocb, char *buffer, size_t count, loff_t offset);
-
ssize_t (*aio_write) (struct kiocb *iocb, const char *buffer, size_t count, loff_t offset);
-
int (*aio_fsync) (struct kiocb *iocb, int datasync);
- 块设备和网络设备:本身是异步的
最后
以上就是英勇月光为你收集整理的linux驱动笔记(五):异步通知的全部内容,希望文章能够帮你解决linux驱动笔记(五):异步通知所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复