Linux系统编程——I/O多路转接模型之select
在这种模型下,如果请求的I/O操作阻塞,且它不是真正阻塞I/O,而是让其中的一个函数等待,在这期间,I/O还能进行其他操作。select()和poll()就属于这种模型。
下面根据该模型一步步创建双管道聊天窗口机制:
首先用mkfifo创建管道文件作为传入参数。
Makefile:
SRCS:=$(wildcard *.c)
ELFS:=$(SRCS:%.c=%)
all:$(ELFS)
@for i in $(ELFS);do gcc -o $${i} $${i}.c;done
clean:
rm -rf $(ELFS)
- 单管道:一端只读,一端只写,读端获取写端数据
read_pipe.c
#include <func.h>
int main(int argc, char* argv[])
{
ARGS_CHECK(argc, 2);
int fd = open(argv[1], O_RDONLY);
ERROR_CHECK(fd, -1, "open");
printf("read fd = %d\n", fd);
char buf[128] = {0};
read(fd, buf, sizeof(buf));
printf("buf = %s\n", buf);
return 0;
}
write_pipe.c
#include <func.h>
int main(int argc, char* argv[])
{
ARGS_CHECK(argc, 2);
int fd = open(argv[1], O_WRONLY);
ERROR_CHECK(fd, -1, "open");
printf("write fd = %d\n", fd);
sleep(3);
write(fd, "hello", 5);
return 0;
}
- 双管道模型
read_pipe.c
#include <func.h>
int main(int argc, char* argv[])
{
ARGS_CHECK(argc, 3);
int fdr = open(argv[1], O_RDONLY);
ERROR_CHECK(fdr, -1, "open");
int fdw = open(argv[2], O_WRONLY);
ERROR_CHECK(fdw, -1, "open");
printf("chat1 fdr = %d, fdw = %d\n", fdr, fdw);
char buf[128] = {0};
read(fdr, buf, sizeof(buf));
printf("buf = %s\n", buf);
write(fdw, "I am chat1",10);
return 0;
}
write_pipe.c
#include <func.h>
int main(int argc, char* argv[])
{
ARGS_CHECK(argc, 3);
int fdw = open(argv[1], O_WRONLY);
ERROR_CHECK(fdw, -1, "open");
int fdr = open(argv[2], O_RDONLY);
ERROR_CHECK(fdr, -1, "open");
printf("chat2 fdr = %d, fdw = %d\n", fdr, fdw);
char buf[128] = {0};
write(fdw, "I am chat2 ",10);
read(fdr, buf, sizeof(buf));
printf("buf = %s\n", buf);
return 0;
}
若写端顺序和读端顺序一样会造成死锁,写端必须先写再读,读端先读再写。
- 同步聊天模型
chat1.c
#include <func.h>
int main(int argc, char* argv[])
{
ARGS_CHECK(argc, 3);
int fdr = open(argv[1], O_RDONLY);
ERROR_CHECK(fdr, -1, "open");
int fdw = open(argv[2], O_WRONLY);
ERROR_CHECK(fdw, -1, "open");
printf("chat1 fdr = %d, fdw = %d\n", fdr, fdw);
char buf[128] = {0};
while(1)
{
memset(buf, 0, sizeof(buf));
read(fdr, buf, sizeof(buf));
printf("%s\n", buf);
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf));
write(fdw, buf, strlen(buf)-1);
}
return 0;
}
chat2.c
#include <func.h>
int main(int argc, char* argv[])
{
ARGS_CHECK(argc, 3);
int fdw = open(argv[1], O_WRONLY);
ERROR_CHECK(fdw, -1, "open");
int fdr = open(argv[2], O_RDONLY);
ERROR_CHECK(fdr, -1, "open");
printf("chat2 fdr = %d, fdw = %d\n", fdr, fdw);
char buf[128] = {0};
while(1)
{
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf));
write(fdw, buf, strlen(buf)-1);
memset(buf, 0, sizeof(buf));
read(fdr, buf, sizeof(buf));
printf("%s\n", buf);
}
return 0;
}
这个模型还有个问题,必须一人说一句,但在实际聊天过程中肯定不是这样的,继续改进。
- 异步聊天模型
#include <sys/select.h>
int select( int nfds, fd_set FAR* readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout);
nfds:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
readfds:(可选)指针,指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针,指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对阻塞操作则为NULL。
chat1.c
#include <func.h>
int main(int argc, char* argv[])
{
ARGS_CHECK(argc, 3);
int fdr = open(argv[1], O_RDONLY);
ERROR_CHECK(fdr, -1, "open");
int fdw = open(argv[2], O_WRONLY);
ERROR_CHECK(fdw, -1, "open");
printf("chat1 fdr = %d, fdw = %d\n", fdr, fdw);
char buf[128] = {0};
fd_set rdset;
int ret;
while(1)
{
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO, &rdset);
FD_SET(fdr, &rdset);
ret = select(fdr + 1, &rdset, NULL, NULL, NULL);
if(ret > 0)
{
if(FD_ISSET(fdr, &rdset))
{
memset(buf, 0, sizeof(buf));
ret = read(fdr, buf, sizeof(buf));
if(ret == 0)
{
printf("byebye!\n");
break;
}
printf("%s\n", buf);
}
if(FD_ISSET(0, &rdset))
{
memset(buf, 0, sizeof(buf));
ret = read(0, buf, sizeof(buf));
if(ret == 0)
{
printf("byebye!\n");
break;
}
write(fdw, buf, strlen(buf)-1);
}
}
}
return 0;
}
chat2.c
#include <func.h>
int main(int argc, char* argv[])
{
ARGS_CHECK(argc, 3);
int fdw = open(argv[1], O_WRONLY);
ERROR_CHECK(fdw, -1, "open");
int fdr = open(argv[2], O_RDONLY);
ERROR_CHECK(fdr, -1, "open");
printf("chat2 fdr = %d, fdw = %d\n", fdr, fdw);
char buf[128] = {0};
int ret;
fd_set rdset;
while(1)
{
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO, &rdset);
FD_SET(fdr, &rdset);
ret = select(fdr + 1, &rdset, NULL, NULL, NULL);
if(ret > 0)
{
if(FD_ISSET(fdr, &rdset))
{
memset(buf, 0,sizeof(buf));
ret = read(fdr, buf, sizeof(buf));
if(ret == 0)
{
printf("byebye!\n");
break;
}
printf("%s\n", buf);
}
if(FD_ISSET(0, &rdset))
{
memset(buf, 0, sizeof(buf));
ret = read(0, buf, sizeof(buf));
if(ret == 0)
{
printf("byebye!\n");
break;
}
write(fdw, buf, strlen(buf) - 1);
}
}
}
return 0;
}
效果图: