fcntl和signal函数实现异步通知

 1.什么是异步IO

        几乎可以认为:异步IO就是操作系统用软件实现的一套中断响应系统。

我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。

2.涉及的函数

(1)fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN)

(2)signal或者sigaction(SIGIO)

fcntl函数的功能()

#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd ,struct flock* lock);

int fcntl(int fd, int cmd, ... /* arg */ );

1.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL). 

(1)把鼠标的文件描述符设置为可以接受异步IO,还可以设置成很多其他属性,详细看man手册,所以操作都与下面代码类似

          flag = fcntl(mousefd,F_GETFL);  //F_GETFL取得fd的文件状态标志

          flag |= O_ASYNC;

          fcntl(mousefd , F_SETFL,flag) ;   //F_SETFL设置给arg描述符状态标志

(2)fcntl的文件状态标志总共有7个:前三个属性在open一个文件时可以选择,fcntl不能更改

         O_RDONLY , O_WRONLY , O_RDWR , O_APPEND , O_NONBLOCK , O_SYNC和O_ASYNC

(3)可更改的几个标志如下面的描述:
         O_NONBLOCK   非阻塞I/O,如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,则read或write调用将返回-1和EAGAIN错误
         O_APPEND     强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
         O_DIRECT     最小化或去掉reading和writing的缓存影响。系统将企图避免缓存你的读或写的数据。如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响。如果这个标志用的不够好,将大大的降低性能
         O_ASYNC      当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候

2. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN). 

什么是异步通知呢?首先这个机制是通过linux内核实现的,所以我们需要将一个打开的文件进行异步IO处理,需要这个文件设备驱动本身是支持异步IO机制的,只有驱动实现了这套处理机制,内核才能操作。比如鼠标,键盘,等等输入设备都是支持该机制.

异步通知的意思就是,一旦设备就绪,则主动通知应用程序,应用程序 根本就不需要查询设备状态,类似于中断的概念,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来 等待信号的到达。下面我们就看一下在linux中机制的实现方式。

在linux中,异步通知是使用信号来实现的,而在linux,大概有30种信号,比如大家熟悉的ctrl+c的SIGINT信号,进程能够忽略或者捕获除过SIGSTOP和SIGKILL的全部信号,当信号背捕获以后,有相应的函数来处理它。

(1)F_GETOWN   取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回的是负值(arg被忽略)

(2)F_SETOWN   设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明(arg绝对值的一个进程组ID),否则arg将被认为是进程id

(3)把异步IO事件的接收进程设置为当前进程

        fcntl(mousefd,F_SETOWN,getpid());

(4)一旦鼠标发送IO事件,使用signal信号捕获,并执行服务函数

        signal(SIGIO,func),func函数是我们自己定义编写的功能函数,只是通过signal函数进行绑定,一旦信号发送,捕获并执行func函数。

编写以下代码,就能同时读取键盘和鼠标

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

int mousefd = -1;

// 绑定到SIGIO信号,在函数内处理异步通知事件
void func(int sig)
{
	char buf[200] = {0};
	
	if (sig != SIGIO)
		return;

	read(mousefd, buf, 50);
	printf("鼠标读出的内容是:[%s].\n", buf);
}

int main(void)
{
	// 读取鼠标
	char buf[200];
	int flag = -1;
	
	mousefd = open("/dev/input/mouse1", O_RDONLY);
	if (mousefd < 0)
	{
		perror("open:");
		return -1;
	}	
	// 把鼠标的文件描述符设置为可以接受异步IO
	flag = fcntl(mousefd, F_GETFL);    //F_GETFL取得fd的文件状态标志
	flag |= O_ASYNC;                //意思是启用异步通知
	fcntl(mousefd, F_SETFL, flag);    

    //上两句可以合并成:fcntl(mousefd, F_SETFL, flag|O_ASYNC); 意思也是启用异步通知

	// 把异步IO事件的接收进程设置为当前进程
	fcntl(mousefd, F_SETOWN, getpid());//设置一个用来接受SIGIO信号的进程,后面写这个进程的ID
	
	// 注册当前进程的SIGIO信号捕获函数
	signal(SIGIO, func);
	
	// 读键盘
	while (1)
	{
		memset(buf, 0, sizeof(buf));
		//printf("before 键盘 read.\n");
		read(0, buf, 5);
		printf("键盘读出的内容是:[%s].\n", buf);
	}
		
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值