文章目录
一些背景知识
异步IO也能解决多路IO的问题。
要结合fcntl和signal来实现。
下面这段从man手册里面摘录下来的描述对理解怎么使用异步IO挺有帮助的,我看完例程之后不怎么懂,然后慢慢查man手册,看完这段就明了很多了。
F_SETOWN (int)
Set the process ID or process group ID that will receive SIGIO
and SIGURG signals for events on the file descriptor fd. The
target process or process group ID is specified in arg. A
process ID is specified as a positive value; a process group ID
is specified as a negative value. Most commonly, the calling
process specifies itself as the owner (that is, arg is speci‐
fied as getpid(2)).
As well as setting the file descriptor owner, one must also
enable generation of signals on the file descriptor. This is
done by using the fcntl() F_SETFL command to set the O_ASYNC
file status flag on the file descriptor. Subsequently, a SIGIO
signal is sent whenever input or output becomes possible on the
file descriptor. The fcntl() F_SETSIG command can be used to
obtain delivery of a signal other than SIGIO.
首先 F_SETOWN 是fcntl里面的一个command。它的作用就是用来设置进程的ID或者进程组的ID,这些ID是那些会接收fd上产生的时间的SIGIO信号或者SIGURG信号。查了下SIGURG跟socket有关。
那些目标进程或者进程组的ID是在fcntl原型里面的arg里面指定的。
第一段最后讲到一般调用的进程会把自己设置为那个owner,所以arg用getpid(作用是获取当前进程的进程号)来指定ID号。
第二段说除了设置好fd的owner之后,还要使得这个fd能够产生信号,所以这里就需要通过用fcntl的F_SETFL把O_ASYNC这个flag的状态给fd加上。随后SIGIO就能在输入或者输出的时候发送了。
signal函数的原型
SYNOPSIS
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
第一个传signum,这里我们要用异步IO,所以设置为SIGIO,当然还有其他很多很多的signum,在man 7 signal里面有定义。
signal() sets the disposition of the signal signum to handler, which
is either SIG_IGN, SIG_DFL, or the address of a programmer-defined
function (a "signal handler").
description里面说signal函数就是给信号的signum设置一个处理程序,这个处理程序要么是SIG_IGN,SIG_DFL,要么就是一个程序员定义的一个函数的地址,通常直接把函数名丢进去,因为函数名就是该函数的地址。
还有一段需要注意的。
* If the disposition is set to a function, then first either the dis‐
position is reset to SIG_DFL, or the signal is blocked (see Porta‐
bility below), and then handler is called with argument signum. If
invocation of the handler caused the signal to be blocked, then the
signal is unblocked upon return from the handler.
我们这里会调用一个function的,所以要看这一段。
他就说如果是handler设置为一个function的话,开始的时候要么就会重新设置位SIG_DFL,要么就会足额的了,然后就会通过signum的值去调用handler,也就是调用我们的function,如果请求调用的时候引起了信号被阻塞的话,当处理完之后返回的时候就会解除阻塞了。
说了这么一大段,直接上代码
代码
#include<stdio.h>
#include<string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
int fd = -1;
// 这个就是我们signum要调用的handler
void func_mouse(int sig)
{
char mousebuf[120];
if (sig != SIGIO)
return;
memset(mousebuf, 0, sizeof(mousebuf));
read(fd,mousebuf,sizeof(mousebuf));
printf("what I read is %s\n",mousebuf);
}
int main(int argc,char* argv[])
{
int flag = -1;
char keyboardbuf[120];
//读鼠标的fd
fd = open("/dev/input/mouse0",O_RDONLY);
if (fd < 0)
{
perror("open:");
return -1;
}
//这两句就是给原fd加上O_ASYNC的flag
flag = fcntl(fd,F_GETFL);
flag = flag|O_ASYNC;
fcntl(fd,F_SETFL,flag);
//这里就把当前进程设置为调用异步IO事件的进程,所以用getpid
fcntl(fd, F_SETOWN, getpid());
//使用signal函数给这个异步IO的signum设置一个handler,
//就是产生了这个异步IO之后需要什么handler来处理。
signal(SIGIO, func_mouse);
//读键盘
while(1)
{
memset(keyboardbuf,0,sizeof(keyboardbuf));
read(0,keyboardbuf,5);
printf("what I read is %s\n",keyboardbuf);
}
close(fd); //其实这里close不close都一样
//因为上面一直while(1)根本下不来,而且最后关掉的时候进程都没了fd也顺带关了
return 0;
}
果然看完课程之后,看完别人的例程代码,还是自己研究一下才会印象深刻。