多路复用select
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
- select函数参数解析:
maxfd: 最大的文件描述符(最大描述符值 + 1,并不是文件描述符个数)
readset: 内核读操作的描述符字集合
writeset:内核写操作的描述符字集合
exceptfds:内核异常操作的描述符字集合
timeout:等待描述符就绪需要多少时间。NULL 代表永远等下去,固定值代表等待固定时间,0 代表根本不等待,检查描述字之后立即返回。
select返回值:失败返回-1,成功返回就绪文件描述符的数值。
fd_set是一个数组,以下为用法:
fd_set rdset;//定义一个rdset的集合
FD_ZERO(&rdset);//清空数组
FD_SET(0,&rdset);//监控0(标准输入文件描述符)
FD_SET(fdr,&rdset);//监控fdr描述符
FD_ISSET(fdr,&rdset);//判断fdr是否有设置
FD_CLR(fdr,&rdset);//删除fdr这个文件描述符
//注意,定义的rdset是一个传入传出参数
- select的退出机制
一般情况下:写端(write)如果没有往缓冲区写数据,读端会等待。
写端如果断开(ctrl+c),read端会接收到0
select 的退出机制:
当管道写端先关闭时,读端使用read时会返回0(相当于发送了一个EOF)
即select发现写端关闭,告知我们读端一直可读(一直打印)
读端可读代码:
用户Mark2
#include "func.h"
int main(int argc,char *argv[])
{
ARGS_CHECK(argc,3);
int fdr=open(argv[1],O_RDONLY);//open 1st mkfifo in the way of reading
ERROR_CHECK(fdr,-1,"open");
int fdw=open(argv[2],O_WRONLY);//open 2nd mkfifo in the way of writing
ERROR_CHECK(fdw,-1,"open");
printf("I am mark2,girl\n");
char buf[128]={0};
int ret;
fd_set rdset;//定义一个文件描述符集合
while(1)
{
FD_ZERO(&rdset);
FD_SET(0,&rdset);//监视STDIN_FILENO
FD_SET(fdr,&rdset);//监视 fdr
ret=select(fdr+1,&rdset,NULL,NULL,NULL);
if(FD_ISSET(0,&rdset))//有数据从键盘传输过来,需要写
{
memset(buf,0,sizeof(buf));
ret=read(STDIN_FILENO,buf,sizeof(buf));
//if(ret==0)//ctrl+d
//{
// printf("user has log out 2\n");
// break;
//}
ret=write(fdw,buf,strlen(buf)-1);
}
if(FD_ISSET(fdr,&rdset))//有数据从对端传输过来,需要读
{
memset(buf,0,sizeof(buf));
ret=read(fdr,buf,sizeof(buf));
ERROR_CHECK(ret,-1,"read");
//if(0==ret)//ctrl+c
//{
// printf("user has log out 2\n");
// break;
//}
ret=write(fdw,buf,strlen(buf)-1);
}
if(FD_ISSET(fdr,&rdset))//有数据从对端传输过来,需要读
{
memset(buf,0,sizeof(buf));
ret=read(fdr,buf,sizeof(buf));
ERROR_CHECK(ret,-1,"read");
//if(0==ret)//ctrl+c
//{
// printf("user has log out\n");
// break;
//}
printf("%s\n",buf);
}
}
return 0;
}
用户Mark1
#include "func.h"
int main(int argc,char *argv[])
{
ARGS_CHECK(argc,3);
int fdw=open(argv[1],O_WRONLY);
if(-1==fdw)
{
perror("open");
return -1;
}
int fdr=open(argv[2],O_RDONLY);//以只读的方式打开第二条管道
ERROR_CHECK(fdr,-1,"open");
printf("I am mark1\n");//测试死锁
char buf[128]={0};
int ret;
fd_set rdset;
while(1)
{
FD_ZERO(&rdset);
FD_SET(0,&rdset);
FD_SET(fdr,&rdset);
ret=select(fdr+1,&rdset,NULL,NULL,NULL);
if(FD_ISSET(0,&rdset))
{
memset(buf,0,sizeof(buf));
ret=read(STDIN_FILENO,buf,sizeof(buf)-1);
//if(ret==0)//ctrl+d
//{
// printf("user has log out 2\n");
// break;
//}
ret=write(fdw,buf,strlen(buf)-1);
}
if(FD_ISSET(fdr,&rdset))
{
memset(buf,0,sizeof(buf));
ret=read(fdr,buf,sizeof(buf));
ERROR_CHECK(ret,-1,"read");
//if(0==ret)//ctrl+c
//{
// printf("user has log out\n");
// break;
//}
printf("%s\n",buf);
}
}
return 0;
}
注:read==0可代表读到文件末尾(可以man 2 read查看返回值)
将注释去除即为select的使用