在应用程序中同时处理多路输入输出流时
1.若采用阻塞模式,将得不到预期的目的;
2.若采用非阻塞模式,对多个输入进行轮询,但又太浪费CPU时间;
3.若设置多个进程,又会产生资源的问题;
4.如果使用多线程,又涉及到临界资源访问的问题;
所以比较好的方法是使用I/O多路复用。
先构造一张有关描述符的表,然后调用select函数。
当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作。
select函数本身是阻塞的
只有当一个或者多个文件描述符准备就绪的时候,函数立即返回。
函数返回的的时候告诉我们文件描述符哪个已经就绪了。
功能:
实现IO多路复用
头文件:
#include
函数原型:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数
@nfds:监视的最大文件描述符+1
@readfds:要监视的读文件描述符集合,如果不关心,可以传NULL
@writefds:要监视的写文件描述符集合,如果不关心,可以传NULL
@exceptfds:要监视的异常的文件描述符集合,如果不关心,可以传NULL
(一般我们只关心readfds)
@timeout:超时时间
为0时非阻塞
为NULL时阻塞
为结构体时阻塞一定时间
返回值:
成功 返回就绪文件描述符的个数
失败 返回-1,置位错误码
超时 返回0
void FD_CLR(int fd, fd_set *set);
功能:
删除集合中的文件描述符
参数:
@fd:文件描述符
@set:构建要监视的文件描述符集合
int FD_ISSET(int fd, fd_set *set);
功能:
判断文件描述符是否在集合中
参数:
@fd:文件描述符
@set:构建要监视的文件描述符集合
返回值:
为0时不在里面
非0时在里面
void FD_SET(int fd, fd_set *set);
功能:
将文件描述符添加到集合中
参数:
@fd:文件描述符
@set:构建要监视的文件描述符集合
void FD_ZERO(fd_set *set);
功能:
清空集合
参数:
@set:构建要监视的文件描述符集合
注意:
1.select只能监视小于 FD_SETSIZE(1024) 的文件描述符。
2.select函数在返回时会将没有就绪的文件描述符在表中擦除,
所以,在循环中调用select时,每次需要重新填充集合。
三个写端(除了打开的管道文件不同基本一致)
其中一个代码为:
#include
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
int fd1;
char buf[128] = {0};
//只读方式打开文件
fd1 = open("fifo1", O_WRONLY);
while (1)
{
//每次获取数据前清空一下buf
memset(buf,0,sizeof(buf));
//从终端获取数据
fgets(buf, sizeof(buf), stdin);
//将获取数据的\n改为\0
buf[strlen(buf) - 1] = '\0';
//将数据写到管道文件中
write(fd1, buf, strlen(buf));
}
//关闭文件
close(fd1);
return 0;
}
读端代码:
#include
#include
#include
#include
#include
#include
#include
int main(int argc,const char * argv[])
{
//以只读的方式打开管道文件
int fd1=open("fifo1",O_RDONLY);
int fd2=open("fifo2",O_RDONLY);
int fd3=open("fifo3",O_RDONLY);
char buff[128]={0};
//定义文件描述符表的结构体变量(母体)
fd_set readfds;
//定义文件描述符表的结构体变量(子体)
fd_set readfds_msg;
//清空变量
FD_ZERO(&readfds);
FD_ZERO(&readfds_msg);
char buf[128]={0};
int max_fd=0;
int ret;
int i;
//将文件描述符放入表中
FD_SET(fd1,&readfds);
//将最大文件描述符保存在max_fd中
max_fd=max_fd>fd1?max_fd:fd1;
FD_SET(fd2,&readfds);
max_fd=max_fd>fd2?max_fd:fd2;
FD_SET(fd3,&readfds);
max_fd=max_fd>fd3?max_fd:fd3;
//循环读取输入到终端上
while(1)
{
//每次循环前将母本重新赋值给子本
//因为select每次调用都会擦除没有没有就绪的文件描述符
readfds_msg=readfds;
//select返回值为就绪的文件描述符个数
if((ret=select(max_fd+1,&readfds_msg,NULL,NULL,NULL))==-1)
{
perror("select error");
}
//将读取的数据写到终端中
for(i=3;i<max_fd+1&&ret!=0;i++)
{
//判断文件是否就绪,就绪将数据写到终端上
if(FD_ISSET(i,&readfds_msg))
{
memset(buf,0,sizeof(buf));
read(i,buf,sizeof(buf));
printf("[%d]号写端发来[%s]\n",i,buf);
ret--;
}
}
}
close(fd1);
close(fd2);
close(fd3);
return 0;
}