ARM技术论坛
直播中

嵌入式小能手

2年用户 1496经验值
擅长:嵌入式技术
私信 关注
[经验]

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

在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        
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
#include
#include
#include
#include
#include
#include
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:%d\n", 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

更多回帖

发帖
×
20
完善资料,
赚取积分