IO多路转接模型select()函数

简单版通信——一端轮流说一句

实现读写同步:

1.管道是半双工的,要想实现双向通信,需要设置两条管道

2.当写端没写或是管道内缓冲区数据为空,那么读端读数据会发生阻塞;

当写端断开,读端读取管道会返回零,会一直while(1)循环,需要ctrl+c结束

写端是指给管道内写入数据

读端是指从管道内读取数据

3.管道的缓冲是4k大小,标准输入大于4k,将会发生踩踏

对于1号管道,由于b端先打开写端,所以b先说话 a端 打开1号管道的读端 打开2号管道的写端

b端 打开1号管道的写端 打开2号管道的读端 这两个假如顺序错误,则会产生死锁,互相等待 比如说,打开不同管道的任意一端或者打开同一管道的同一端,会产生死锁

a端的代码如下:

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

//a端

int main(int argc, char* argv[])
{
	if (argc != 3)
	{
		printf("error args\n");
		return -1;
	}
	int fd1, fd2;
	
	//必须O_RDONLY
	//打开1号管道的读端
	fd1 = open(argv[1], O_RDONLY);
	if (-1 == fd1)
	{
		perror("open1");
		return -1;
	}

	//必须O_WRONLY
	//打开2号管道的写端
	fd2 = open(argv[2], O_WRONLY);
	if (-1 == fd2)
	{
		perror("open2");
		return -1;
	}

	printf("I am A");

	char buf[128] = {0};
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		read(fd1, buf, sizeof(buf));//读取一号管道的内容到buf里
		printf("%s\n", buf);//输出到屏幕
		memset(buf, 0, sizeof(buf));
		read(0, buf, sizeof(buf));//读取标准输入到buf里
		write(fd2, buf, strlen(buf) - 1);//把buf里的内容写到二号管道
	}
	return 0;
}

b端的代码如下:

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

//b端

int main(int argc, char* argv[])
{
	if (argc != 3)
	{
		printf("error args\n");
		return -1;
	}
	int fd1, fd2;
	
	//必须O_WRONLY
	//打开1号管道的写端
	fd1 = open(argv[1], O_WRONLY);
	if (-1 == fd1)
	{
		perror("open1");
		return -1;
	}

	//必须O_RDONLY
	//打开2号管道的读端
	fd2 = open(argv[2], O_RDONLY);
	if (-1 == fd2)
	{
		perror("open2");
		return -1;
	}

	printf("I am B");

	char buf[128] = {0};
	while(1)
	{
		memset(buf, 0, sizeof(buf));
		read(0, buf, sizeof(buf));//读取标准输入到buf里
		write(fd1, buf, strlen(buf) - 1);//把buf里的内容写到一号管道
		memset(buf, 0, sizeof(buf));
		read(fd2, buf, sizeof(buf));//读取二号管道的内容到buf里
		printf("%s\n", buf);//输出到屏幕上
	}
	return 0;
}
mkfifo 1.pipe
mkfifo 2.pipe
gcc a.c -o a
gcc b.c -o b
./a 1.pipe 2.pipe
./b 1.pipe 2.pipe

缺点是只能实现一端只能轮流说一句话

 

 

正常版通信select()函数——IO多路转接模型

 

文件描述符具有阻塞属性的是标准输入描述符0和读描述符fdr

让select监控文件描述符,0号描述符和fdr描述符,一旦观察到buf区里面有数据,马上通知,若0号描述符可读,则写出,返回;若fdr可读,则写出,返回;若两个都不可读,则阻塞。

 

select函数的相关接口

#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptionset,
            const struct timeval* timeout);
//maxfd:最大的文件描述符(其值应该为最大的文件描述符字+1)
//readset:内核读操作的描述符字集合
//writeset:内核写操作的描述符字集合
//exceptionset:内核异常操作的描述符字集合
//timeout:等待描述符就绪需要多少时间,NULL代表永远等下去,一个固定值代表固定时间,
//0代表根本不等待,检查描述字之后立即返回。
//其中readset,writeset,exceptionset都是fd_set集合    

void FD_ZERO(fd_set* fdset);//将所有fd清零
void FD_SET(int fd, fd_set* fdset);//增加一个fd*
void FD_CLR(int fd, fd_set* fdset);//删除一个fd*
int FD_ISSET(int fd, fd_set* fdset);//判断一个fd,是否有设置

 

先定义一个集合数组,用于存储要监控的fd,再传数组的指针(对数组取地址)

fd_set rdset;
FD_ZERO(&rdset);//每次循环都需要清零

//告诉select函数要监视的文件描述符
FD_SET(0, &rdset);//每次循环都需要监视
FD_SET(fdr, &rdset);//每次循环都需要监视
//设置超时时间,若超过,还没有描述符就绪可读,则返回;若同时可读,也返回
//传入传出参数rdset

 

select里面对rdset做的操作

fd_set tmpset
memcpy(&tmpset, &rdset, sizeof(fd_set));
//内存拷贝可以拷贝任意格式的数据
FD_ZERO(&rdset);//清空
进行轮询:int FD_ISSET(int fd, fd_set* fdset);
//循环调用该函数测试描述符函数集

b端代码如下:

#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
//管道的写端
int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		printf("error args\n");
		return -1;
	}

	int fdw,fdr;
	fdw = open(argv[1], O_WRONLY);//必须O_WRONLY
	if(-1 == fdw)
	{
		perror("open");
		return -1;
	}
	fdr = open(argv[2], O_RDONLY);//必须O_RDONLY
	if(-1 == fdr)
	{
		perror("open");
		return -1;
	}

	printf("I am B\n");

	char buf[128];
	int ret;
	fd_set rdset;
	while(1)
	{
		FD_ZERO(&rdset);//每次循环都要重新设置rdset的值
		FD_SET(0, &rdset);
		FD_SET(fdr, &rdset);
		ret=select(fdr+1, &rdset, NULL, NULL, NULL);//传入传出参数
		//若ret大于零,则有描述符可读;
		//有一个描述符可读,ret等于1;
		//有两个描述符可读,ret等于2;
		//有n个描述符同时可读,ret等于n
		if(ret>0)
		{
			if(FD_ISSET(0, &rdset))//判断标准输入是否可读
			{
				memset(buf, 0, sizeof(buf));
				ret = read(0, buf, sizeof(buf));//读取标准输入
				if(0 == ret)
				//侦测对面是否按了ctrl+d或c
				{
					printf("byebye\n");
					close(fdr);
					close(fdw);
					break;
				}
				write(fdw, buf, strlen(buf)-1);//写到1号管道
			}
			if(FD_ISSET(fdr, &rdset))//判断fdr是否可读
			{
				memset(buf, 0, sizeof(buf));
				ret=read(fdr, buf, sizeof(buf));//从2号管道中读取数据
				if(0 == ret)
				{
					printf("byebye\n");
					close(fdr);
					close(fdw);
					break;
				}
				printf("%s\n", buf);
			}
		}
	}
	return 0;
}

a端代码如下

#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
//管道的读端
int main(int argc, char* argv[])
{
	if(argc != 3)
	{
		printf("error args\n");
		return -1;
	}
	int fdr, fdw;
	fdr = open(argv[1], O_RDONLY);//必须O_RDONLY
	if(-1 == fdr)
	{
		perror("open");
		return -1;
	}
	fdw = open(argv[2], O_WRONLY);//必须O_WRONLY
	if(-1 == fdw)
	{
		perror("open");
		return -1;
	}
	printf("I am O_A\n");
	char buf[128] = {0};
	fd_set rdset;
	int ret;
	while(1)
	{
		FD_ZERO(&rdset);
		FD_SET(0, &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));//如果管道内没有数据,read会阻塞
				if(0 == ret)
				{
					printf("byebye\n");
					close(fdr);
					close(fdw);
					break;
				}
				printf("%s\n", buf);
			}
			if (FD_ISSET(0, &rdset))
			{
				memset(buf, 0, sizeof(buf));
				ret = read(0, buf, sizeof(buf));//读取标准输入,写到2号管道
				if (ret > 0)
				//由于在标准输入发现要结束对话了,所以在这里判断
				{
					write(fdw, buf, strlen(buf)-1);
				}else{
					//等于0,按了ctrl+d;或者出错
					printf("byebye\n");
					close(fdr);
					close(fdw);
					break;
				}
			}
		}
	}
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值