扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

飞凌嵌入式ElfBoard-文件I/O的深入学习之异步I/O

2025/12/08
398
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

在I/O多路复用中,进程通过系统调用select或poll来主动查询文件描述符上是否可以执行I/O操作。而在异步I/O中,当文件描述符上可以执行I/O操作时,进程可以请求内核为自己发送一个信号。之后进程就可以执行任何其它的任务直到文件描述符可以执行I/O操作为止,此时内核会发送信号给进程。

异步I/O通常也称为信号驱动I/O。

要使用异步I/O,程序需要按照如下步骤来执行:

1.通过指定O_NONBLOCK标志使能非阻塞I/O。

2.通过指定O_ASYNC标志使能异步I/O。

3.设置异步I/O事件的接收进程。也就是当文件描述符上可执行I/O操作时会发送信号通知该进程,通常将调用进程设置为异步I/O事件的接收进程。

4.为内核发送的通知信号注册一个信号处理函数。默认情况下,异步I/O的通知信号是 SIGIO,所以内核会给进程发送信号SIGIO。

5.之后,进程就可以执行其它任务了,当I/O操作就绪时,内核会向进程发送一个SIGIO信号,当进程接收到信号时,会执行预先注册好的信号处理函数,就可以在信号处理函数中进行I/O操作。

需要注意的是,在调用open时无法通过指定O_ASYNC标志来使能异步I/O,但可以使用fcntl函数配置对应的文件描述符。

添加O_ASYNC标志使能异步I/O:

int flag;

flag = fcntl(0, F_GETFL); //先获取原来的 flag

flag |= O_ASYNC; //将 O_ASYNC 标志添加到 flag

fcntl(fd, F_SETFL, flag); //重新设置 flag

为文件描述符设置异步I/O事件的接收进程,也就是设置异步I/O的所有者。同样也是通过fcntl函数进行设置,操作命令cmd设置为F_SETOWN,第三个参数传入接收进程的进程ID(PID),通常将调用进程的PID传入:

fcntl(fd, F_SETOWN, getpid());

对于信号处理,可以使用signal,用于设置信号处理方式最简单的接口,可将信号的处理方式设置为捕获信号、忽略信号以及系统默认操作。

1.头文件

#include <signal.h>

2.函数原型

typedef void (*sig_t)(int);

sig_t signal(int signum, sig_t handler);

3.参数

signum:表示指定需要进行设置的信号,可使用信号名(宏)或信号的数字编号。

handler:sig_t类型的函数指针,指向信号对应的信号处理函数,当进程接收到信号后会自动执行该处理函数;参数handler既可以设置为用户自定义的函数,也就是捕获信号时需要执行的处理函数,也可以设置为SIG_IGN或SIG_DFL,SIG_IGN表示此进程需要忽略该信号,SIG_DFL 则表示设置为系统默认操作。sig_t函数指针的int类型参数指的是,当前触发该函数的信号,可将多个信号绑定到同一个信号处理函数上,此时就可通过此参数来判断当前触发的是哪个信号。

SIG_IGN、SIG_DFL分别取值如下:

#define SIG_ERR ((sig_t) -1) /* Error return. */

#define SIG_DFL ((sig_t) 0) /* Default action. */

#define SIG_IGN ((sig_t) 1) /* Ignore signal. */。

通过signal函数为SIGIO信号注册一个信号处理函数,当进程接收到内核发送过来的SIGIO信号时,会执行该处理函数,所以应该在处理函数当中执行相应的I/O操作。

4.返回值

此函数的返回值也是一个sig_t类型的函数指针,成功情况下的返回值则是指向在此之前的信号处理函数;如果出错则返回SIG_ERR,并会设置errno。

5.示例:(异步I/O方式读取鼠标数据,进程接收到SIGIO信号后读取鼠标数据)

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <signal.h>

static int fd;

static int num = 0;

static void sigio_handler(int sig)

{

char buf[100];

int ret;

num++;

if (sig != SIGIO)                    //判断获取的信号是否是SIGIO

return;

ret = read(fd, buf, sizeof(buf));

if (ret > 0)

printf("event2 num:%dn", ret);

if (num == 10) {                     //读取次数到10时结束

close(fd);

exit(0);

}

}

int main()

{

int flag;

fd = open("/dev/input/event2", O_RDONLY | O_NONBLOCK);

if (fd < 0) {

perror("event2 open error");

return -1;

}

flag = fcntl(fd,F_GETFL);            //使能异步I/O

flag = O_ASYNC;

fcntl(fd,F_SETFL,flag);

fcntl(fd,F_SETOWN,getpid());        //设置异步I/O的所有者

signal(SIGIO,sigio_handler);         //注册SIGIO信号的处理函数

while(1);

sleep(1);

return 0;

}

6)编译运行并查看测试结果

event2 num:48

event2 num:72

event2 num:72

event2 num:48

event2 num:72

event2 num:48

event2 num:48

event2 num:48

event2 num:48

event2 num:48

相关推荐